near protocol support
This commit is contained in:
@@ -118,6 +118,7 @@ open class CodeGen(private val config: ChainsConfig) {
|
|||||||
"starknet" -> "BlockchainType.STARKNET"
|
"starknet" -> "BlockchainType.STARKNET"
|
||||||
"polkadot" -> "BlockchainType.POLKADOT"
|
"polkadot" -> "BlockchainType.POLKADOT"
|
||||||
"solana" -> "BlockchainType.SOLANA"
|
"solana" -> "BlockchainType.SOLANA"
|
||||||
|
"near" -> "BlockchainType.NEAR"
|
||||||
else -> throw IllegalArgumentException("unknown blockchain type $type")
|
else -> throw IllegalArgumentException("unknown blockchain type $type")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Submodule emerald-grpc updated: 50776f993e...8e188a1634
@@ -1,5 +1,5 @@
|
|||||||
package io.emeraldpay.dshackle
|
package io.emeraldpay.dshackle
|
||||||
|
|
||||||
enum class BlockchainType {
|
enum class BlockchainType {
|
||||||
UNKNOWN, BITCOIN, ETHEREUM, STARKNET, POLKADOT, SOLANA;
|
UNKNOWN, BITCOIN, ETHEREUM, STARKNET, POLKADOT, SOLANA, NEAR;
|
||||||
}
|
}
|
||||||
@@ -1275,3 +1275,31 @@ chain-settings:
|
|||||||
grpcId: 10063
|
grpcId: 10063
|
||||||
short-names: [ telos-testnet ]
|
short-names: [ telos-testnet ]
|
||||||
chain-id: 0x29
|
chain-id: 0x29
|
||||||
|
- id: near
|
||||||
|
label: Near
|
||||||
|
type: near
|
||||||
|
settings:
|
||||||
|
fork-choice: quorum
|
||||||
|
expected-block-time: 1s
|
||||||
|
lags:
|
||||||
|
syncing: 40
|
||||||
|
lagging: 20
|
||||||
|
chains:
|
||||||
|
- id: Mainnet
|
||||||
|
priority: 100
|
||||||
|
code: NEAR_MAINNET
|
||||||
|
grpcId: 1050
|
||||||
|
short-names: [ near ]
|
||||||
|
chain-id: 0x0
|
||||||
|
- id: Testnet
|
||||||
|
priority: 5
|
||||||
|
code: NEAR_TESTNET
|
||||||
|
grpcId: 10064
|
||||||
|
short-names: [ near-testnet ]
|
||||||
|
chain-id: 0x0
|
||||||
|
- id: Betanet
|
||||||
|
priority: 1
|
||||||
|
code: NEAR_BETANET
|
||||||
|
grpcId: 10065
|
||||||
|
short-names: [ near-betanet ]
|
||||||
|
chain-id: 0x0
|
||||||
@@ -2,6 +2,7 @@ package io.emeraldpay.dshackle.upstream
|
|||||||
|
|
||||||
import io.emeraldpay.dshackle.BlockchainType.BITCOIN
|
import io.emeraldpay.dshackle.BlockchainType.BITCOIN
|
||||||
import io.emeraldpay.dshackle.BlockchainType.ETHEREUM
|
import io.emeraldpay.dshackle.BlockchainType.ETHEREUM
|
||||||
|
import io.emeraldpay.dshackle.BlockchainType.NEAR
|
||||||
import io.emeraldpay.dshackle.BlockchainType.POLKADOT
|
import io.emeraldpay.dshackle.BlockchainType.POLKADOT
|
||||||
import io.emeraldpay.dshackle.BlockchainType.SOLANA
|
import io.emeraldpay.dshackle.BlockchainType.SOLANA
|
||||||
import io.emeraldpay.dshackle.BlockchainType.STARKNET
|
import io.emeraldpay.dshackle.BlockchainType.STARKNET
|
||||||
@@ -29,6 +30,7 @@ class CallTargetsHolder {
|
|||||||
STARKNET -> DefaultStarknetMethods(chain)
|
STARKNET -> DefaultStarknetMethods(chain)
|
||||||
POLKADOT -> DefaultPolkadotMethods()
|
POLKADOT -> DefaultPolkadotMethods()
|
||||||
SOLANA -> DefaultSolanaMethods()
|
SOLANA -> DefaultSolanaMethods()
|
||||||
|
NEAR -> DefaultNearMethods()
|
||||||
UNKNOWN -> throw IllegalArgumentException("unknown chain")
|
UNKNOWN -> throw IllegalArgumentException("unknown chain")
|
||||||
}
|
}
|
||||||
callTargets[chain] = created
|
callTargets[chain] = created
|
||||||
|
|||||||
@@ -0,0 +1,63 @@
|
|||||||
|
package io.emeraldpay.dshackle.upstream
|
||||||
|
|
||||||
|
import io.emeraldpay.dshackle.quorum.AlwaysQuorum
|
||||||
|
import io.emeraldpay.dshackle.quorum.BroadcastQuorum
|
||||||
|
import io.emeraldpay.dshackle.quorum.CallQuorum
|
||||||
|
import io.emeraldpay.dshackle.upstream.calls.CallMethods
|
||||||
|
import io.emeraldpay.dshackle.upstream.ethereum.rpc.RpcException
|
||||||
|
|
||||||
|
class DefaultNearMethods : CallMethods {
|
||||||
|
|
||||||
|
private val all = setOf(
|
||||||
|
"view_access_key",
|
||||||
|
"query",
|
||||||
|
"EXPERIMENTAL_changes",
|
||||||
|
"block",
|
||||||
|
"chunk",
|
||||||
|
"EXPERIMENTAL_changes_in_block",
|
||||||
|
"gas_price",
|
||||||
|
"status",
|
||||||
|
"network_info",
|
||||||
|
"validators",
|
||||||
|
"tx",
|
||||||
|
"EXPERIMENTAL_tx_status",
|
||||||
|
"EXPERIMENTAL_receipt",
|
||||||
|
)
|
||||||
|
|
||||||
|
private val add = setOf(
|
||||||
|
"broadcast_tx_async",
|
||||||
|
"broadcast_tx_commit",
|
||||||
|
)
|
||||||
|
|
||||||
|
private val allowedMethods: Set<String> = all + add
|
||||||
|
|
||||||
|
override fun createQuorumFor(method: String): CallQuorum {
|
||||||
|
return when {
|
||||||
|
add.contains(method) -> BroadcastQuorum()
|
||||||
|
all.contains(method) -> AlwaysQuorum()
|
||||||
|
else -> AlwaysQuorum()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun isCallable(method: String): Boolean {
|
||||||
|
return allowedMethods.contains(method)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun isHardcoded(method: String): Boolean {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun executeHardcoded(method: String): ByteArray {
|
||||||
|
throw RpcException(-32601, "Method not found")
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getGroupMethods(groupName: String): Set<String> =
|
||||||
|
when (groupName) {
|
||||||
|
"default" -> getSupportedMethods()
|
||||||
|
else -> emptyList()
|
||||||
|
}.toSet()
|
||||||
|
|
||||||
|
override fun getSupportedMethods(): Set<String> {
|
||||||
|
return allowedMethods.toSortedSet()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,6 +2,7 @@ package io.emeraldpay.dshackle.upstream.generic
|
|||||||
|
|
||||||
import io.emeraldpay.dshackle.BlockchainType.BITCOIN
|
import io.emeraldpay.dshackle.BlockchainType.BITCOIN
|
||||||
import io.emeraldpay.dshackle.BlockchainType.ETHEREUM
|
import io.emeraldpay.dshackle.BlockchainType.ETHEREUM
|
||||||
|
import io.emeraldpay.dshackle.BlockchainType.NEAR
|
||||||
import io.emeraldpay.dshackle.BlockchainType.POLKADOT
|
import io.emeraldpay.dshackle.BlockchainType.POLKADOT
|
||||||
import io.emeraldpay.dshackle.BlockchainType.SOLANA
|
import io.emeraldpay.dshackle.BlockchainType.SOLANA
|
||||||
import io.emeraldpay.dshackle.BlockchainType.STARKNET
|
import io.emeraldpay.dshackle.BlockchainType.STARKNET
|
||||||
@@ -26,6 +27,7 @@ import io.emeraldpay.dshackle.upstream.calls.CallMethods
|
|||||||
import io.emeraldpay.dshackle.upstream.calls.CallSelector
|
import io.emeraldpay.dshackle.upstream.calls.CallSelector
|
||||||
import io.emeraldpay.dshackle.upstream.ethereum.EthereumChainSpecific
|
import io.emeraldpay.dshackle.upstream.ethereum.EthereumChainSpecific
|
||||||
import io.emeraldpay.dshackle.upstream.ethereum.WsSubscriptions
|
import io.emeraldpay.dshackle.upstream.ethereum.WsSubscriptions
|
||||||
|
import io.emeraldpay.dshackle.upstream.near.NearChainSpecific
|
||||||
import io.emeraldpay.dshackle.upstream.polkadot.PolkadotChainSpecific
|
import io.emeraldpay.dshackle.upstream.polkadot.PolkadotChainSpecific
|
||||||
import io.emeraldpay.dshackle.upstream.rpcclient.JsonRpcRequest
|
import io.emeraldpay.dshackle.upstream.rpcclient.JsonRpcRequest
|
||||||
import io.emeraldpay.dshackle.upstream.solana.SolanaChainSpecific
|
import io.emeraldpay.dshackle.upstream.solana.SolanaChainSpecific
|
||||||
@@ -74,6 +76,7 @@ object ChainSpecificRegistry {
|
|||||||
STARKNET -> StarknetChainSpecific
|
STARKNET -> StarknetChainSpecific
|
||||||
POLKADOT -> PolkadotChainSpecific
|
POLKADOT -> PolkadotChainSpecific
|
||||||
SOLANA -> SolanaChainSpecific
|
SOLANA -> SolanaChainSpecific
|
||||||
|
NEAR -> NearChainSpecific
|
||||||
BITCOIN -> throw IllegalArgumentException("bitcoin should use custom streams implementation")
|
BITCOIN -> throw IllegalArgumentException("bitcoin should use custom streams implementation")
|
||||||
UNKNOWN -> throw IllegalArgumentException("unknown chain")
|
UNKNOWN -> throw IllegalArgumentException("unknown chain")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,110 @@
|
|||||||
|
package io.emeraldpay.dshackle.upstream.near
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonIgnoreProperties
|
||||||
|
import com.fasterxml.jackson.annotation.JsonProperty
|
||||||
|
import io.emeraldpay.dshackle.Chain
|
||||||
|
import io.emeraldpay.dshackle.Global
|
||||||
|
import io.emeraldpay.dshackle.config.ChainsConfig.ChainConfig
|
||||||
|
import io.emeraldpay.dshackle.data.BlockContainer
|
||||||
|
import io.emeraldpay.dshackle.data.BlockId
|
||||||
|
import io.emeraldpay.dshackle.foundation.ChainOptions.Options
|
||||||
|
import io.emeraldpay.dshackle.upstream.LowerBoundBlockDetector
|
||||||
|
import io.emeraldpay.dshackle.upstream.SingleCallValidator
|
||||||
|
import io.emeraldpay.dshackle.upstream.Upstream
|
||||||
|
import io.emeraldpay.dshackle.upstream.UpstreamAvailability
|
||||||
|
import io.emeraldpay.dshackle.upstream.UpstreamValidator
|
||||||
|
import io.emeraldpay.dshackle.upstream.generic.AbstractPollChainSpecific
|
||||||
|
import io.emeraldpay.dshackle.upstream.generic.GenericUpstreamValidator
|
||||||
|
import io.emeraldpay.dshackle.upstream.rpcclient.JsonRpcRequest
|
||||||
|
import java.math.BigInteger
|
||||||
|
import java.time.Instant
|
||||||
|
import java.util.concurrent.TimeUnit
|
||||||
|
|
||||||
|
object NearChainSpecific : AbstractPollChainSpecific() {
|
||||||
|
override fun parseBlock(data: ByteArray, upstreamId: String): BlockContainer {
|
||||||
|
val block = Global.objectMapper.readValue(data, NearBlock::class.java).header
|
||||||
|
|
||||||
|
return BlockContainer(
|
||||||
|
height = block.height,
|
||||||
|
hash = BlockId.fromBase64(block.hash),
|
||||||
|
difficulty = BigInteger.ZERO,
|
||||||
|
timestamp = Instant.ofEpochMilli(TimeUnit.MILLISECONDS.convert(block.timestamp, TimeUnit.NANOSECONDS)),
|
||||||
|
full = false,
|
||||||
|
json = data,
|
||||||
|
parsed = block,
|
||||||
|
transactions = emptyList(),
|
||||||
|
upstreamId = upstreamId,
|
||||||
|
parentHash = BlockId.fromBase64(block.prevHash),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun parseHeader(data: ByteArray, upstreamId: String): BlockContainer {
|
||||||
|
throw NotImplementedError()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun listenNewHeadsRequest(): JsonRpcRequest {
|
||||||
|
throw NotImplementedError()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun unsubscribeNewHeadsRequest(subId: String): JsonRpcRequest {
|
||||||
|
throw NotImplementedError()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun validator(
|
||||||
|
chain: Chain,
|
||||||
|
upstream: Upstream,
|
||||||
|
options: Options,
|
||||||
|
config: ChainConfig,
|
||||||
|
): UpstreamValidator {
|
||||||
|
return GenericUpstreamValidator(
|
||||||
|
upstream,
|
||||||
|
options,
|
||||||
|
SingleCallValidator(
|
||||||
|
JsonRpcRequest("status", listOf()),
|
||||||
|
) { data ->
|
||||||
|
validate(data)
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun lowerBoundBlockDetector(chain: Chain, upstream: Upstream): LowerBoundBlockDetector {
|
||||||
|
return NearLowerBoundBlockDetector(chain, upstream)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun validate(data: ByteArray): UpstreamAvailability {
|
||||||
|
val resp = Global.objectMapper.readValue(data, NearStatus::class.java)
|
||||||
|
return if (resp.syncInfo.syncing) {
|
||||||
|
UpstreamAvailability.SYNCING
|
||||||
|
} else {
|
||||||
|
UpstreamAvailability.OK
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun latestBlockRequest(): JsonRpcRequest =
|
||||||
|
JsonRpcRequest("block", mapOf("finality" to "optimistic"))
|
||||||
|
}
|
||||||
|
|
||||||
|
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||||
|
data class NearBlock(
|
||||||
|
@JsonProperty("header") var header: NearHeader,
|
||||||
|
)
|
||||||
|
|
||||||
|
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||||
|
data class NearHeader(
|
||||||
|
@JsonProperty("height") var height: Long,
|
||||||
|
@JsonProperty("hash") var hash: String,
|
||||||
|
@JsonProperty("prev_hash") var prevHash: String,
|
||||||
|
@JsonProperty("timestamp") var timestamp: Long,
|
||||||
|
)
|
||||||
|
|
||||||
|
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||||
|
data class NearStatus(
|
||||||
|
@JsonProperty("sync_info") var syncInfo: NearSync,
|
||||||
|
)
|
||||||
|
|
||||||
|
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||||
|
data class NearSync(
|
||||||
|
@JsonProperty("syncing") var syncing: Boolean,
|
||||||
|
@JsonProperty("earliest_block_height") var earliestHeight: Long,
|
||||||
|
@JsonProperty("earliest_block_time") var earliestBlockTime: Instant,
|
||||||
|
)
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
package io.emeraldpay.dshackle.upstream.near
|
||||||
|
|
||||||
|
import io.emeraldpay.dshackle.Chain
|
||||||
|
import io.emeraldpay.dshackle.Global
|
||||||
|
import io.emeraldpay.dshackle.upstream.LowerBoundBlockDetector
|
||||||
|
import io.emeraldpay.dshackle.upstream.Upstream
|
||||||
|
import io.emeraldpay.dshackle.upstream.rpcclient.JsonRpcRequest
|
||||||
|
import reactor.core.publisher.Mono
|
||||||
|
|
||||||
|
class NearLowerBoundBlockDetector(
|
||||||
|
chain: Chain,
|
||||||
|
val upstream: Upstream,
|
||||||
|
) : LowerBoundBlockDetector(chain, upstream) {
|
||||||
|
|
||||||
|
override fun lowerBlockDetect(): Mono<LowerBlockData> {
|
||||||
|
return upstream.getIngressReader().read(JsonRpcRequest("status", listOf())).map {
|
||||||
|
val resp = Global.objectMapper.readValue(it.getResult(), NearStatus::class.java)
|
||||||
|
LowerBlockData(resp.syncInfo.earliestHeight, null, resp.syncInfo.earliestBlockTime.toEpochMilli())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun periodRequest(): Long {
|
||||||
|
return 120
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -29,6 +29,7 @@ data class JsonRpcRequest(
|
|||||||
val nonce: Long?,
|
val nonce: Long?,
|
||||||
val selector: BlockchainOuterClass.Selector?,
|
val selector: BlockchainOuterClass.Selector?,
|
||||||
val isStreamed: Boolean = false,
|
val isStreamed: Boolean = false,
|
||||||
|
val objParams: Map<Any, Any>? = null,
|
||||||
) {
|
) {
|
||||||
|
|
||||||
@JvmOverloads constructor(
|
@JvmOverloads constructor(
|
||||||
@@ -39,12 +40,17 @@ data class JsonRpcRequest(
|
|||||||
isStreamed: Boolean = false,
|
isStreamed: Boolean = false,
|
||||||
) : this(method, params, 1, nonce, selectors, isStreamed)
|
) : this(method, params, 1, nonce, selectors, isStreamed)
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
method: String,
|
||||||
|
objParams: Map<Any, Any>,
|
||||||
|
) : this(method, listOf(), 1, null, null, false, objParams)
|
||||||
|
|
||||||
fun toJson(): ByteArray {
|
fun toJson(): ByteArray {
|
||||||
val json = mapOf(
|
val json = mapOf(
|
||||||
"jsonrpc" to "2.0",
|
"jsonrpc" to "2.0",
|
||||||
"id" to id,
|
"id" to id,
|
||||||
"method" to method,
|
"method" to method,
|
||||||
"params" to params,
|
"params" to (objParams ?: params),
|
||||||
)
|
)
|
||||||
return Global.objectMapper.writeValueAsBytes(json)
|
return Global.objectMapper.writeValueAsBytes(json)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user