near protocol support
This commit is contained in:
@@ -118,6 +118,7 @@ open class CodeGen(private val config: ChainsConfig) {
|
||||
"starknet" -> "BlockchainType.STARKNET"
|
||||
"polkadot" -> "BlockchainType.POLKADOT"
|
||||
"solana" -> "BlockchainType.SOLANA"
|
||||
"near" -> "BlockchainType.NEAR"
|
||||
else -> throw IllegalArgumentException("unknown blockchain type $type")
|
||||
}
|
||||
}
|
||||
|
||||
Submodule emerald-grpc updated: 50776f993e...8e188a1634
@@ -1,5 +1,5 @@
|
||||
package io.emeraldpay.dshackle
|
||||
|
||||
enum class BlockchainType {
|
||||
UNKNOWN, BITCOIN, ETHEREUM, STARKNET, POLKADOT, SOLANA;
|
||||
}
|
||||
UNKNOWN, BITCOIN, ETHEREUM, STARKNET, POLKADOT, SOLANA, NEAR;
|
||||
}
|
||||
@@ -1275,3 +1275,31 @@ chain-settings:
|
||||
grpcId: 10063
|
||||
short-names: [ telos-testnet ]
|
||||
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.ETHEREUM
|
||||
import io.emeraldpay.dshackle.BlockchainType.NEAR
|
||||
import io.emeraldpay.dshackle.BlockchainType.POLKADOT
|
||||
import io.emeraldpay.dshackle.BlockchainType.SOLANA
|
||||
import io.emeraldpay.dshackle.BlockchainType.STARKNET
|
||||
@@ -29,6 +30,7 @@ class CallTargetsHolder {
|
||||
STARKNET -> DefaultStarknetMethods(chain)
|
||||
POLKADOT -> DefaultPolkadotMethods()
|
||||
SOLANA -> DefaultSolanaMethods()
|
||||
NEAR -> DefaultNearMethods()
|
||||
UNKNOWN -> throw IllegalArgumentException("unknown chain")
|
||||
}
|
||||
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.ETHEREUM
|
||||
import io.emeraldpay.dshackle.BlockchainType.NEAR
|
||||
import io.emeraldpay.dshackle.BlockchainType.POLKADOT
|
||||
import io.emeraldpay.dshackle.BlockchainType.SOLANA
|
||||
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.ethereum.EthereumChainSpecific
|
||||
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.rpcclient.JsonRpcRequest
|
||||
import io.emeraldpay.dshackle.upstream.solana.SolanaChainSpecific
|
||||
@@ -74,6 +76,7 @@ object ChainSpecificRegistry {
|
||||
STARKNET -> StarknetChainSpecific
|
||||
POLKADOT -> PolkadotChainSpecific
|
||||
SOLANA -> SolanaChainSpecific
|
||||
NEAR -> NearChainSpecific
|
||||
BITCOIN -> throw IllegalArgumentException("bitcoin should use custom streams implementation")
|
||||
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 selector: BlockchainOuterClass.Selector?,
|
||||
val isStreamed: Boolean = false,
|
||||
val objParams: Map<Any, Any>? = null,
|
||||
) {
|
||||
|
||||
@JvmOverloads constructor(
|
||||
@@ -39,12 +40,17 @@ data class JsonRpcRequest(
|
||||
isStreamed: Boolean = false,
|
||||
) : 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 {
|
||||
val json = mapOf(
|
||||
"jsonrpc" to "2.0",
|
||||
"id" to id,
|
||||
"method" to method,
|
||||
"params" to params,
|
||||
"params" to (objParams ?: params),
|
||||
)
|
||||
return Global.objectMapper.writeValueAsBytes(json)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user