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 {
|
||||
return when(type) {
|
||||
"aztec" -> "BlockchainType.AZTEC"
|
||||
"eth" -> "BlockchainType.ETHEREUM"
|
||||
"bitcoin" -> "BlockchainType.BITCOIN"
|
||||
"starknet" -> "BlockchainType.STARKNET"
|
||||
|
||||
Submodule emerald-grpc updated: 0986b4b9db...38db8b3fef
@@ -5,6 +5,7 @@ enum class BlockchainType(
|
||||
) {
|
||||
UNKNOWN(ApiType.JSON_RPC),
|
||||
BITCOIN(ApiType.JSON_RPC),
|
||||
AZTEC(ApiType.JSON_RPC),
|
||||
ETHEREUM(ApiType.JSON_RPC),
|
||||
STARKNET(ApiType.JSON_RPC),
|
||||
POLKADOT(ApiType.JSON_RPC),
|
||||
@@ -19,4 +20,4 @@ enum class BlockchainType(
|
||||
|
||||
enum class ApiType {
|
||||
JSON_RPC, REST;
|
||||
}
|
||||
}
|
||||
|
||||
Submodule foundation/src/main/resources/public updated: 23fcd39890...91a3853af9
@@ -1,5 +1,6 @@
|
||||
package io.emeraldpay.dshackle.upstream
|
||||
|
||||
import io.emeraldpay.dshackle.BlockchainType.AZTEC
|
||||
import io.emeraldpay.dshackle.BlockchainType.BITCOIN
|
||||
import io.emeraldpay.dshackle.BlockchainType.COSMOS
|
||||
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.foundation.ChainOptions
|
||||
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.DefaultBitcoinMethods
|
||||
import io.emeraldpay.dshackle.upstream.calls.DefaultCosmosMethods
|
||||
@@ -47,6 +49,7 @@ class CallTargetsHolder {
|
||||
): CallMethods {
|
||||
val created = when (chain.type) {
|
||||
BITCOIN -> DefaultBitcoinMethods(options.providesBalance == true)
|
||||
AZTEC -> DefaultAztecMethods()
|
||||
ETHEREUM -> DefaultEthereumMethods(chain)
|
||||
STARKNET -> DefaultStarknetMethods(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
|
||||
|
||||
import io.emeraldpay.dshackle.BlockchainType.AZTEC
|
||||
import io.emeraldpay.dshackle.BlockchainType.BITCOIN
|
||||
import io.emeraldpay.dshackle.BlockchainType.COSMOS
|
||||
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.UpstreamValidator
|
||||
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.calls.CallMethods
|
||||
import io.emeraldpay.dshackle.upstream.calls.CallSelector
|
||||
@@ -113,6 +115,7 @@ object ChainSpecificRegistry {
|
||||
@JvmStatic
|
||||
fun resolve(chain: Chain): ChainSpecific {
|
||||
return when (chain.type) {
|
||||
AZTEC -> AztecChainSpecific
|
||||
ETHEREUM -> EthereumChainSpecific
|
||||
STARKNET -> StarknetChainSpecific
|
||||
POLKADOT -> PolkadotChainSpecific
|
||||
|
||||
Reference in New Issue
Block a user