add aztec networks (#785)
* add aztec networks * fix linter errors & checkout from main (public) * fix AztecLowerBoundService
This commit is contained in:
@@ -128,6 +128,7 @@ open class CodeGen(private val config: ChainsConfig) {
|
|||||||
|
|
||||||
private fun type(type: String): String {
|
private fun type(type: String): String {
|
||||||
return when(type) {
|
return when(type) {
|
||||||
|
"aztec" -> "BlockchainType.AZTEC"
|
||||||
"eth" -> "BlockchainType.ETHEREUM"
|
"eth" -> "BlockchainType.ETHEREUM"
|
||||||
"bitcoin" -> "BlockchainType.BITCOIN"
|
"bitcoin" -> "BlockchainType.BITCOIN"
|
||||||
"starknet" -> "BlockchainType.STARKNET"
|
"starknet" -> "BlockchainType.STARKNET"
|
||||||
|
|||||||
Submodule emerald-grpc updated: 0986b4b9db...38db8b3fef
@@ -5,6 +5,7 @@ enum class BlockchainType(
|
|||||||
) {
|
) {
|
||||||
UNKNOWN(ApiType.JSON_RPC),
|
UNKNOWN(ApiType.JSON_RPC),
|
||||||
BITCOIN(ApiType.JSON_RPC),
|
BITCOIN(ApiType.JSON_RPC),
|
||||||
|
AZTEC(ApiType.JSON_RPC),
|
||||||
ETHEREUM(ApiType.JSON_RPC),
|
ETHEREUM(ApiType.JSON_RPC),
|
||||||
STARKNET(ApiType.JSON_RPC),
|
STARKNET(ApiType.JSON_RPC),
|
||||||
POLKADOT(ApiType.JSON_RPC),
|
POLKADOT(ApiType.JSON_RPC),
|
||||||
@@ -19,4 +20,4 @@ enum class BlockchainType(
|
|||||||
|
|
||||||
enum class ApiType {
|
enum class ApiType {
|
||||||
JSON_RPC, REST;
|
JSON_RPC, REST;
|
||||||
}
|
}
|
||||||
|
|||||||
Submodule foundation/src/main/resources/public updated: 23fcd39890...91a3853af9
@@ -1,5 +1,6 @@
|
|||||||
package io.emeraldpay.dshackle.upstream
|
package io.emeraldpay.dshackle.upstream
|
||||||
|
|
||||||
|
import io.emeraldpay.dshackle.BlockchainType.AZTEC
|
||||||
import io.emeraldpay.dshackle.BlockchainType.BITCOIN
|
import io.emeraldpay.dshackle.BlockchainType.BITCOIN
|
||||||
import io.emeraldpay.dshackle.BlockchainType.COSMOS
|
import io.emeraldpay.dshackle.BlockchainType.COSMOS
|
||||||
import io.emeraldpay.dshackle.BlockchainType.ETHEREUM
|
import io.emeraldpay.dshackle.BlockchainType.ETHEREUM
|
||||||
@@ -16,6 +17,7 @@ import io.emeraldpay.dshackle.Chain
|
|||||||
import io.emeraldpay.dshackle.config.UpstreamsConfig
|
import io.emeraldpay.dshackle.config.UpstreamsConfig
|
||||||
import io.emeraldpay.dshackle.foundation.ChainOptions
|
import io.emeraldpay.dshackle.foundation.ChainOptions
|
||||||
import io.emeraldpay.dshackle.upstream.calls.CallMethods
|
import io.emeraldpay.dshackle.upstream.calls.CallMethods
|
||||||
|
import io.emeraldpay.dshackle.upstream.calls.DefaultAztecMethods
|
||||||
import io.emeraldpay.dshackle.upstream.calls.DefaultBeaconChainMethods
|
import io.emeraldpay.dshackle.upstream.calls.DefaultBeaconChainMethods
|
||||||
import io.emeraldpay.dshackle.upstream.calls.DefaultBitcoinMethods
|
import io.emeraldpay.dshackle.upstream.calls.DefaultBitcoinMethods
|
||||||
import io.emeraldpay.dshackle.upstream.calls.DefaultCosmosMethods
|
import io.emeraldpay.dshackle.upstream.calls.DefaultCosmosMethods
|
||||||
@@ -47,6 +49,7 @@ class CallTargetsHolder {
|
|||||||
): CallMethods {
|
): CallMethods {
|
||||||
val created = when (chain.type) {
|
val created = when (chain.type) {
|
||||||
BITCOIN -> DefaultBitcoinMethods(options.providesBalance == true)
|
BITCOIN -> DefaultBitcoinMethods(options.providesBalance == true)
|
||||||
|
AZTEC -> DefaultAztecMethods()
|
||||||
ETHEREUM -> DefaultEthereumMethods(chain)
|
ETHEREUM -> DefaultEthereumMethods(chain)
|
||||||
STARKNET -> DefaultStarknetMethods(chain)
|
STARKNET -> DefaultStarknetMethods(chain)
|
||||||
POLKADOT -> DefaultPolkadotMethods(chain)
|
POLKADOT -> DefaultPolkadotMethods(chain)
|
||||||
|
|||||||
@@ -0,0 +1,177 @@
|
|||||||
|
package io.emeraldpay.dshackle.upstream.aztec
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.databind.JsonNode
|
||||||
|
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.reader.ChainReader
|
||||||
|
import io.emeraldpay.dshackle.upstream.ChainRequest
|
||||||
|
import io.emeraldpay.dshackle.upstream.GenericSingleCallValidator
|
||||||
|
import io.emeraldpay.dshackle.upstream.SingleValidator
|
||||||
|
import io.emeraldpay.dshackle.upstream.Upstream
|
||||||
|
import io.emeraldpay.dshackle.upstream.UpstreamAvailability
|
||||||
|
import io.emeraldpay.dshackle.upstream.ValidateUpstreamSettingsResult
|
||||||
|
import io.emeraldpay.dshackle.upstream.generic.AbstractPollChainSpecific
|
||||||
|
import io.emeraldpay.dshackle.upstream.lowerbound.LowerBoundService
|
||||||
|
import io.emeraldpay.dshackle.upstream.rpcclient.ListParams
|
||||||
|
import org.slf4j.LoggerFactory
|
||||||
|
import reactor.core.publisher.Mono
|
||||||
|
import java.math.BigInteger
|
||||||
|
import java.time.Instant
|
||||||
|
|
||||||
|
object AztecChainSpecific : AbstractPollChainSpecific() {
|
||||||
|
private val log = LoggerFactory.getLogger(AztecChainSpecific::class.java)
|
||||||
|
|
||||||
|
override fun parseBlock(data: ByteArray, upstreamId: String, api: ChainReader): Mono<BlockContainer> {
|
||||||
|
val root = Global.objectMapper.readTree(data)
|
||||||
|
val height = parseLong(
|
||||||
|
findNode(
|
||||||
|
root,
|
||||||
|
"number",
|
||||||
|
"header.number",
|
||||||
|
"blockNumber",
|
||||||
|
"header.blockNumber",
|
||||||
|
"header.globalVariables.blockNumber",
|
||||||
|
),
|
||||||
|
) ?: 0L
|
||||||
|
val hashValue = parseText(findNode(root, "hash", "header.hash", "blockHash", "header.blockHash"))
|
||||||
|
val parentValue = parseText(
|
||||||
|
findNode(
|
||||||
|
root,
|
||||||
|
"parentHash",
|
||||||
|
"header.parentHash",
|
||||||
|
"parent_hash",
|
||||||
|
"header.parent_hash",
|
||||||
|
"prevHash",
|
||||||
|
"header.prevHash",
|
||||||
|
),
|
||||||
|
)
|
||||||
|
val timestamp = parseInstant(
|
||||||
|
findNode(
|
||||||
|
root,
|
||||||
|
"timestamp",
|
||||||
|
"header.timestamp",
|
||||||
|
"header.globalVariables.timestamp",
|
||||||
|
),
|
||||||
|
) ?: Instant.EPOCH
|
||||||
|
|
||||||
|
if (hashValue == null) {
|
||||||
|
log.warn("Aztec block hash is missing in response from upstream {}", upstreamId)
|
||||||
|
}
|
||||||
|
|
||||||
|
return Mono.just(
|
||||||
|
BlockContainer(
|
||||||
|
height = height,
|
||||||
|
hash = BlockId.from(hashValue ?: "0x0"),
|
||||||
|
difficulty = BigInteger.ZERO,
|
||||||
|
timestamp = timestamp,
|
||||||
|
full = false,
|
||||||
|
json = data,
|
||||||
|
parsed = root,
|
||||||
|
transactions = emptyList(),
|
||||||
|
upstreamId = upstreamId,
|
||||||
|
parentHash = parentValue?.let { BlockId.from(it) },
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getFromHeader(data: ByteArray, upstreamId: String, api: ChainReader): Mono<BlockContainer> {
|
||||||
|
throw NotImplementedError()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun listenNewHeadsRequest(): ChainRequest {
|
||||||
|
throw NotImplementedError()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun unsubscribeNewHeadsRequest(subId: Any): ChainRequest {
|
||||||
|
throw NotImplementedError()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun upstreamValidators(
|
||||||
|
chain: Chain,
|
||||||
|
upstream: Upstream,
|
||||||
|
options: Options,
|
||||||
|
config: ChainConfig,
|
||||||
|
): List<SingleValidator<UpstreamAvailability>> {
|
||||||
|
return listOf(
|
||||||
|
GenericSingleCallValidator(
|
||||||
|
ChainRequest("node_isReady", ListParams()),
|
||||||
|
upstream,
|
||||||
|
) { data ->
|
||||||
|
val raw = Global.objectMapper.readTree(data)
|
||||||
|
val ready = when {
|
||||||
|
raw.isBoolean -> raw.asBoolean()
|
||||||
|
raw.isTextual -> raw.asText().equals("true", ignoreCase = true)
|
||||||
|
else -> raw.asBoolean(false)
|
||||||
|
}
|
||||||
|
if (ready) UpstreamAvailability.OK else UpstreamAvailability.SYNCING
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun upstreamSettingsValidators(
|
||||||
|
chain: Chain,
|
||||||
|
upstream: Upstream,
|
||||||
|
options: Options,
|
||||||
|
config: ChainConfig,
|
||||||
|
): List<SingleValidator<ValidateUpstreamSettingsResult>> {
|
||||||
|
return emptyList()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun lowerBoundService(chain: Chain, upstream: Upstream): LowerBoundService {
|
||||||
|
return AztecLowerBoundService(chain, upstream)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun latestBlockRequest(): ChainRequest =
|
||||||
|
ChainRequest("node_getBlock", ListParams("latest"))
|
||||||
|
|
||||||
|
private fun findNode(root: JsonNode, vararg paths: String): JsonNode? {
|
||||||
|
for (path in paths) {
|
||||||
|
var current: JsonNode? = root
|
||||||
|
for (part in path.split(".")) {
|
||||||
|
current = current?.get(part)
|
||||||
|
if (current == null || current.isMissingNode) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (current != null && !current.isMissingNode && !current.isNull) {
|
||||||
|
return current
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun parseText(node: JsonNode?): String? {
|
||||||
|
if (node == null || node.isNull || node.isMissingNode) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
return node.asText().ifBlank { null }
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun parseLong(node: JsonNode?): Long? {
|
||||||
|
if (node == null || node.isNull || node.isMissingNode) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
return when {
|
||||||
|
node.isNumber -> node.asLong()
|
||||||
|
node.isTextual -> parseNumericString(node.asText())
|
||||||
|
else -> null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun parseNumericString(value: String): Long? {
|
||||||
|
val trimmed = value.trim()
|
||||||
|
if (trimmed.isEmpty()) return null
|
||||||
|
val isHex = trimmed.startsWith("0x") || trimmed.startsWith("0X")
|
||||||
|
val raw = if (isHex) trimmed.substring(2) else trimmed
|
||||||
|
return runCatching { BigInteger(raw, if (isHex) 16 else 10).toLong() }.getOrNull()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun parseInstant(node: JsonNode?): Instant? {
|
||||||
|
val ts = parseLong(node) ?: return null
|
||||||
|
return if (ts >= 1_000_000_000_000L) Instant.ofEpochMilli(ts) else Instant.ofEpochSecond(ts)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
package io.emeraldpay.dshackle.upstream.aztec
|
||||||
|
|
||||||
|
import io.emeraldpay.dshackle.Chain
|
||||||
|
import io.emeraldpay.dshackle.upstream.Upstream
|
||||||
|
import io.emeraldpay.dshackle.upstream.lowerbound.LowerBoundDetector
|
||||||
|
import io.emeraldpay.dshackle.upstream.lowerbound.LowerBoundService
|
||||||
|
|
||||||
|
class AztecLowerBoundService(
|
||||||
|
chain: Chain,
|
||||||
|
private val upstream: Upstream,
|
||||||
|
) : LowerBoundService(chain, upstream) {
|
||||||
|
override fun detectors(): List<LowerBoundDetector> {
|
||||||
|
return listOf(AztecLowerBoundStateDetector(upstream.getChain()))
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
package io.emeraldpay.dshackle.upstream.aztec
|
||||||
|
|
||||||
|
import io.emeraldpay.dshackle.Chain
|
||||||
|
import io.emeraldpay.dshackle.upstream.lowerbound.LowerBoundData
|
||||||
|
import io.emeraldpay.dshackle.upstream.lowerbound.LowerBoundDetector
|
||||||
|
import io.emeraldpay.dshackle.upstream.lowerbound.LowerBoundType
|
||||||
|
import reactor.core.publisher.Flux
|
||||||
|
|
||||||
|
class AztecLowerBoundStateDetector(
|
||||||
|
chain: Chain,
|
||||||
|
) : LowerBoundDetector(chain) {
|
||||||
|
|
||||||
|
override fun period(): Long {
|
||||||
|
return 120
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun internalDetectLowerBound(): Flux<LowerBoundData> {
|
||||||
|
return Flux.just(LowerBoundData(1, LowerBoundType.STATE))
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun types(): Set<LowerBoundType> {
|
||||||
|
return setOf(LowerBoundType.STATE)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,94 @@
|
|||||||
|
package io.emeraldpay.dshackle.upstream.calls
|
||||||
|
|
||||||
|
import io.emeraldpay.dshackle.quorum.AlwaysQuorum
|
||||||
|
import io.emeraldpay.dshackle.quorum.BroadcastQuorum
|
||||||
|
import io.emeraldpay.dshackle.quorum.CallQuorum
|
||||||
|
import io.emeraldpay.dshackle.upstream.ethereum.rpc.RpcException
|
||||||
|
|
||||||
|
class DefaultAztecMethods : CallMethods {
|
||||||
|
|
||||||
|
private val broadcast = setOf(
|
||||||
|
"node_sendTx",
|
||||||
|
)
|
||||||
|
|
||||||
|
private val allowedMethods: Set<String> = setOf(
|
||||||
|
"node_getBlockNumber",
|
||||||
|
"node_getProvenBlockNumber",
|
||||||
|
"node_getL2Tips",
|
||||||
|
"node_getBlock",
|
||||||
|
"node_getBlocks",
|
||||||
|
"node_getBlockHeader",
|
||||||
|
"node_sendTx",
|
||||||
|
"node_getTxReceipt",
|
||||||
|
"node_getTxEffect",
|
||||||
|
"node_getTxByHash",
|
||||||
|
"node_getPendingTxs",
|
||||||
|
"node_getPendingTxCount",
|
||||||
|
"node_isValidTx",
|
||||||
|
"node_simulatePublicCalls",
|
||||||
|
"node_getPublicStorageAt",
|
||||||
|
"node_getWorldStateSyncStatus",
|
||||||
|
"node_findLeavesIndexes",
|
||||||
|
"node_getNullifierSiblingPath",
|
||||||
|
"node_getNoteHashSiblingPath",
|
||||||
|
"node_getArchiveSiblingPath",
|
||||||
|
"node_getPublicDataSiblingPath",
|
||||||
|
"node_getNullifierMembershipWitness",
|
||||||
|
"node_getLowNullifierMembershipWitness",
|
||||||
|
"node_getPublicDataWitness",
|
||||||
|
"node_getArchiveMembershipWitness",
|
||||||
|
"node_getNoteHashMembershipWitness",
|
||||||
|
"node_getL1ToL2MessageMembershipWitness",
|
||||||
|
"node_getL1ToL2MessageBlock",
|
||||||
|
"node_isL1ToL2MessageSynced",
|
||||||
|
"node_getL2ToL1Messages",
|
||||||
|
"node_getPrivateLogs",
|
||||||
|
"node_getPublicLogs",
|
||||||
|
"node_getContractClassLogs",
|
||||||
|
"node_getLogsByTags",
|
||||||
|
"node_getContractClass",
|
||||||
|
"node_getContract",
|
||||||
|
"node_isReady",
|
||||||
|
"node_getNodeInfo",
|
||||||
|
"node_getNodeVersion",
|
||||||
|
"node_getVersion",
|
||||||
|
"node_getChainId",
|
||||||
|
"node_getL1ContractAddresses",
|
||||||
|
"node_getProtocolContractAddresses",
|
||||||
|
"node_getEncodedEnr",
|
||||||
|
"node_getCurrentBaseFees",
|
||||||
|
"node_getValidatorsStats",
|
||||||
|
"node_getValidatorStats",
|
||||||
|
"node_registerContractFunctionSignatures",
|
||||||
|
"node_getAllowedPublicSetup",
|
||||||
|
)
|
||||||
|
|
||||||
|
override fun createQuorumFor(method: String): CallQuorum {
|
||||||
|
return when {
|
||||||
|
broadcast.contains(method) -> BroadcastQuorum()
|
||||||
|
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 -> emptySet()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getSupportedMethods(): Set<String> {
|
||||||
|
return allowedMethods.toSortedSet()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
package io.emeraldpay.dshackle.upstream.generic
|
package io.emeraldpay.dshackle.upstream.generic
|
||||||
|
|
||||||
|
import io.emeraldpay.dshackle.BlockchainType.AZTEC
|
||||||
import io.emeraldpay.dshackle.BlockchainType.BITCOIN
|
import io.emeraldpay.dshackle.BlockchainType.BITCOIN
|
||||||
import io.emeraldpay.dshackle.BlockchainType.COSMOS
|
import io.emeraldpay.dshackle.BlockchainType.COSMOS
|
||||||
import io.emeraldpay.dshackle.BlockchainType.ETHEREUM
|
import io.emeraldpay.dshackle.BlockchainType.ETHEREUM
|
||||||
@@ -32,6 +33,7 @@ import io.emeraldpay.dshackle.upstream.UpstreamRpcMethodsDetector
|
|||||||
import io.emeraldpay.dshackle.upstream.UpstreamSettingsDetector
|
import io.emeraldpay.dshackle.upstream.UpstreamSettingsDetector
|
||||||
import io.emeraldpay.dshackle.upstream.UpstreamValidator
|
import io.emeraldpay.dshackle.upstream.UpstreamValidator
|
||||||
import io.emeraldpay.dshackle.upstream.ValidateUpstreamSettingsResult
|
import io.emeraldpay.dshackle.upstream.ValidateUpstreamSettingsResult
|
||||||
|
import io.emeraldpay.dshackle.upstream.aztec.AztecChainSpecific
|
||||||
import io.emeraldpay.dshackle.upstream.beaconchain.BeaconChainSpecific
|
import io.emeraldpay.dshackle.upstream.beaconchain.BeaconChainSpecific
|
||||||
import io.emeraldpay.dshackle.upstream.calls.CallMethods
|
import io.emeraldpay.dshackle.upstream.calls.CallMethods
|
||||||
import io.emeraldpay.dshackle.upstream.calls.CallSelector
|
import io.emeraldpay.dshackle.upstream.calls.CallSelector
|
||||||
@@ -113,6 +115,7 @@ object ChainSpecificRegistry {
|
|||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun resolve(chain: Chain): ChainSpecific {
|
fun resolve(chain: Chain): ChainSpecific {
|
||||||
return when (chain.type) {
|
return when (chain.type) {
|
||||||
|
AZTEC -> AztecChainSpecific
|
||||||
ETHEREUM -> EthereumChainSpecific
|
ETHEREUM -> EthereumChainSpecific
|
||||||
STARKNET -> StarknetChainSpecific
|
STARKNET -> StarknetChainSpecific
|
||||||
POLKADOT -> PolkadotChainSpecific
|
POLKADOT -> PolkadotChainSpecific
|
||||||
|
|||||||
Reference in New Issue
Block a user