add toncenter (#580)

* add toncenter

* upd dep

* toncenter->ton

* toncenter->ton
This commit is contained in:
Aarif Khamdi
2024-10-23 11:57:42 +05:00
committed by GitHub
parent 184939a463
commit 750310702a
10 changed files with 263 additions and 3 deletions

View File

@@ -135,6 +135,7 @@ open class CodeGen(private val config: ChainsConfig) {
"solana" -> "BlockchainType.SOLANA" "solana" -> "BlockchainType.SOLANA"
"near" -> "BlockchainType.NEAR" "near" -> "BlockchainType.NEAR"
"eth-beacon-chain" -> "BlockchainType.ETHEREUM_BEACON_CHAIN" "eth-beacon-chain" -> "BlockchainType.ETHEREUM_BEACON_CHAIN"
"ton" -> "BlockchainType.TON"
"cosmos" -> "BlockchainType.COSMOS" "cosmos" -> "BlockchainType.COSMOS"
else -> throw IllegalArgumentException("unknown blockchain type $type") else -> throw IllegalArgumentException("unknown blockchain type $type")
} }

View File

@@ -11,7 +11,8 @@ enum class BlockchainType(
SOLANA(ApiType.JSON_RPC), SOLANA(ApiType.JSON_RPC),
NEAR(ApiType.JSON_RPC), NEAR(ApiType.JSON_RPC),
ETHEREUM_BEACON_CHAIN(ApiType.REST), ETHEREUM_BEACON_CHAIN(ApiType.REST),
COSMOS(ApiType.JSON_RPC); COSMOS(ApiType.JSON_RPC),
TON(ApiType.REST);
} }
enum class ApiType { enum class ApiType {

View File

@@ -33,6 +33,8 @@ import io.emeraldpay.dshackle.upstream.bitcoin.data.RpcUnspent
import io.emeraldpay.dshackle.upstream.bitcoin.data.RpcUnspentDeserializer import io.emeraldpay.dshackle.upstream.bitcoin.data.RpcUnspentDeserializer
import io.emeraldpay.dshackle.upstream.ethereum.domain.TransactionId import io.emeraldpay.dshackle.upstream.ethereum.domain.TransactionId
import io.emeraldpay.dshackle.upstream.ethereum.subscribe.json.TransactionIdSerializer import io.emeraldpay.dshackle.upstream.ethereum.subscribe.json.TransactionIdSerializer
import io.emeraldpay.dshackle.upstream.ton.TonMasterchainInfo
import io.emeraldpay.dshackle.upstream.ton.TonMasterchainInfoDeserializer
import java.text.SimpleDateFormat import java.text.SimpleDateFormat
import java.util.Locale import java.util.Locale
import java.util.TimeZone import java.util.TimeZone
@@ -81,6 +83,7 @@ class Global {
module.addDeserializer(RpcUnspent::class.java, RpcUnspentDeserializer()) module.addDeserializer(RpcUnspent::class.java, RpcUnspentDeserializer())
module.addDeserializer(ChainRequest::class.java, ChainRequest.Deserializer()) module.addDeserializer(ChainRequest::class.java, ChainRequest.Deserializer())
module.addDeserializer(BeaconChainBlockHeader::class.java, BeaconChainBlockHeaderDeserializer()) module.addDeserializer(BeaconChainBlockHeader::class.java, BeaconChainBlockHeaderDeserializer())
module.addDeserializer(TonMasterchainInfo::class.java, TonMasterchainInfoDeserializer())
val objectMapper = ObjectMapper() val objectMapper = ObjectMapper()
objectMapper.registerModule(module) objectMapper.registerModule(module)

View File

@@ -8,6 +8,7 @@ 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
import io.emeraldpay.dshackle.BlockchainType.TON
import io.emeraldpay.dshackle.BlockchainType.UNKNOWN import io.emeraldpay.dshackle.BlockchainType.UNKNOWN
import io.emeraldpay.dshackle.Chain import io.emeraldpay.dshackle.Chain
import io.emeraldpay.dshackle.foundation.ChainOptions import io.emeraldpay.dshackle.foundation.ChainOptions
@@ -18,6 +19,7 @@ import io.emeraldpay.dshackle.upstream.calls.DefaultCosmosMethods
import io.emeraldpay.dshackle.upstream.calls.DefaultEthereumMethods import io.emeraldpay.dshackle.upstream.calls.DefaultEthereumMethods
import io.emeraldpay.dshackle.upstream.calls.DefaultPolkadotMethods import io.emeraldpay.dshackle.upstream.calls.DefaultPolkadotMethods
import io.emeraldpay.dshackle.upstream.calls.DefaultStarknetMethods import io.emeraldpay.dshackle.upstream.calls.DefaultStarknetMethods
import io.emeraldpay.dshackle.upstream.calls.DefaultTonHttpMethods
import org.springframework.stereotype.Component import org.springframework.stereotype.Component
@Component @Component
@@ -38,6 +40,7 @@ class CallTargetsHolder {
NEAR -> DefaultNearMethods() NEAR -> DefaultNearMethods()
ETHEREUM_BEACON_CHAIN -> DefaultBeaconChainMethods() ETHEREUM_BEACON_CHAIN -> DefaultBeaconChainMethods()
COSMOS -> DefaultCosmosMethods() COSMOS -> DefaultCosmosMethods()
TON -> DefaultTonHttpMethods()
UNKNOWN -> throw IllegalArgumentException("unknown chain") UNKNOWN -> throw IllegalArgumentException("unknown chain")
} }
callTargets[chain] = created callTargets[chain] = created

View File

@@ -0,0 +1,100 @@
package io.emeraldpay.dshackle.upstream.calls
import io.emeraldpay.dshackle.quorum.AlwaysQuorum
import io.emeraldpay.dshackle.quorum.CallQuorum
import io.emeraldpay.dshackle.upstream.ethereum.rpc.RpcException
class DefaultTonHttpMethods : CallMethods {
// HTTP API section
private val accountHttpMethods = setOf(
getMethod("/getAddressInformation"),
getMethod("/getExtendedAddressInformation"),
getMethod("/getWalletInformation"),
getMethod("/getAddressBalance"),
getMethod("/getAddressState"),
getMethod("/packAddress"),
getMethod("/unpackAddress"),
getMethod("/getTokenData"),
getMethod("/detectAddress"),
)
private val blockHttpMethods = setOf(
getMethod("/getMasterchainInfo"),
getMethod("/getMasterchainBlockSignatures"),
getMethod("/getShardBlockProof"),
getMethod("/getConsensusBlock"),
getMethod("/lookupBlock"),
getMethod("/shards"),
getMethod("/getBlockHeader"),
getMethod("/getOutMsgQueueSizes"),
)
private val transactionHttpMethods = setOf(
getMethod("/getTransactions"),
getMethod("/getBlockTransactions"),
getMethod("/getBlockTransactionsExt"),
getMethod("/tryLocateTx"),
getMethod("/tryLocateResultTx"),
getMethod("/tryLocateSourceTx"),
)
private val getConfigHttpMethods = setOf(
getMethod("/getConfigParam"),
getMethod("/getConfigAll"),
)
private val runMethodHttpMethods = setOf(
postMethod("/runGetMethod"),
)
private val sendHttpMethods = setOf(
postMethod("/sendBoc"),
postMethod("/sendBocReturnHash"),
postMethod("/sendQuery"),
postMethod("/estimateFee"),
)
private val jsonRpcHttpMethods = setOf(
postMethod("/jsonRPC"),
)
private val allowedHttpMethods: Set<String> = accountHttpMethods +
blockHttpMethods +
transactionHttpMethods +
getConfigHttpMethods +
runMethodHttpMethods +
sendHttpMethods +
jsonRpcHttpMethods
override fun createQuorumFor(method: String): CallQuorum {
return AlwaysQuorum()
}
override fun isCallable(method: String): Boolean {
return allowedHttpMethods.contains(method)
}
override fun getSupportedMethods(): Set<String> {
return allowedHttpMethods.toSortedSet()
}
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> {
return when (groupName) {
"default" -> allowedHttpMethods
else -> emptyList()
}.toSet()
}
private fun getMethod(method: String) = "GET#$method"
private fun postMethod(method: String) = "POST#$method"
}

View File

@@ -8,6 +8,7 @@ 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
import io.emeraldpay.dshackle.BlockchainType.TON
import io.emeraldpay.dshackle.BlockchainType.UNKNOWN import io.emeraldpay.dshackle.BlockchainType.UNKNOWN
import io.emeraldpay.dshackle.Chain import io.emeraldpay.dshackle.Chain
import io.emeraldpay.dshackle.cache.Caches import io.emeraldpay.dshackle.cache.Caches
@@ -42,6 +43,7 @@ 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.solana.SolanaChainSpecific import io.emeraldpay.dshackle.upstream.solana.SolanaChainSpecific
import io.emeraldpay.dshackle.upstream.starknet.StarknetChainSpecific import io.emeraldpay.dshackle.upstream.starknet.StarknetChainSpecific
import io.emeraldpay.dshackle.upstream.ton.TonHttpSpecific
import org.apache.commons.collections4.Factory import org.apache.commons.collections4.Factory
import org.springframework.cloud.sleuth.Tracer import org.springframework.cloud.sleuth.Tracer
import reactor.core.publisher.Mono import reactor.core.publisher.Mono
@@ -110,6 +112,7 @@ object ChainSpecificRegistry {
SOLANA -> SolanaChainSpecific SOLANA -> SolanaChainSpecific
NEAR -> NearChainSpecific NEAR -> NearChainSpecific
ETHEREUM_BEACON_CHAIN -> BeaconChainSpecific ETHEREUM_BEACON_CHAIN -> BeaconChainSpecific
TON -> TonHttpSpecific
COSMOS -> CosmosChainSpecific COSMOS -> CosmosChainSpecific
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")

View File

@@ -0,0 +1,134 @@
package io.emeraldpay.dshackle.upstream.ton
import com.fasterxml.jackson.core.JsonParser
import com.fasterxml.jackson.databind.DeserializationContext
import com.fasterxml.jackson.databind.JsonDeserializer
import com.fasterxml.jackson.databind.JsonNode
import com.fasterxml.jackson.module.kotlin.readValue
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.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.RestParams
import reactor.core.publisher.Mono
import java.math.BigInteger
import java.time.Instant
fun generateRandomString(length: Int): String {
val allowedChars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
return (1..length)
.map { allowedChars.random() }
.joinToString("")
}
object TonHttpSpecific : AbstractPollChainSpecific() {
override fun getFromHeader(data: ByteArray, upstreamId: String, api: ChainReader): Mono<BlockContainer> {
throw NotImplementedError()
}
override fun listenNewHeadsRequest(): ChainRequest {
throw NotImplementedError()
}
override fun unsubscribeNewHeadsRequest(subId: String): ChainRequest {
throw NotImplementedError()
}
override fun lowerBoundService(chain: Chain, upstream: Upstream): LowerBoundService {
return TonLowerBoundService(chain, upstream)
}
override fun latestBlockRequest(): ChainRequest {
return ChainRequest("GET#/getMasterchainInfo", RestParams.emptyParams())
}
override fun parseBlock(data: ByteArray, upstreamId: String, api: ChainReader): Mono<BlockContainer> {
val blockHeader = Global.objectMapper.readValue<TonMasterchainInfo>(data)
return Mono.just(
BlockContainer(
height = blockHeader.result.last.seqno,
hash = BlockId.fromBase64(blockHeader.result.last.root_hash),
difficulty = BigInteger.ZERO,
timestamp = Instant.EPOCH,
full = false,
json = data,
parsed = blockHeader,
transactions = emptyList(),
upstreamId = upstreamId,
parentHash = BlockId.fromBase64(generateRandomString(32)),
),
)
}
override fun upstreamValidators(
chain: Chain,
upstream: Upstream,
options: Options,
config: ChainConfig,
): List<SingleValidator<UpstreamAvailability>> {
return emptyList()
}
override fun upstreamSettingsValidators(
chain: Chain,
upstream: Upstream,
options: Options,
config: ChainConfig,
): List<SingleValidator<ValidateUpstreamSettingsResult>> {
// add check generic block
return emptyList()
}
}
data class TonMasterchainInfoResultLast(
val workchain: Int,
val shard: String,
val seqno: Long,
val root_hash: String,
val file_hash: String,
)
data class TonMasterchainInfoResult(
val last: TonMasterchainInfoResultLast,
)
data class TonMasterchainInfo(
val ok: Boolean,
val result: TonMasterchainInfoResult,
)
class TonMasterchainInfoDeserializer : JsonDeserializer<TonMasterchainInfo>() {
override fun deserialize(p: JsonParser, ctxt: DeserializationContext): TonMasterchainInfo {
val node = p.readValueAsTree<JsonNode>()
val ok = node["ok"].booleanValue()
val workchain = node["result"]["last"]["workchain"].intValue()
val shard = node["result"]["last"]["shard"].textValue()
val seqno = node["result"]["last"]["seqno"].longValue()
val root_hash = node["result"]["last"]["root_hash"].textValue()
val file_hash = node["result"]["last"]["file_hash"].textValue()
return TonMasterchainInfo(
ok,
TonMasterchainInfoResult(
TonMasterchainInfoResultLast(
workchain,
shard,
seqno,
root_hash,
file_hash,
),
),
)
}
}

View File

@@ -0,0 +1,15 @@
package io.emeraldpay.dshackle.upstream.ton
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 TonLowerBoundService(
chain: Chain,
upstream: Upstream,
) : LowerBoundService(chain, upstream) {
override fun detectors(): List<LowerBoundDetector> {
return listOf()
}
}