Skip to content

Commit

Permalink
add toncenter
Browse files Browse the repository at this point in the history
  • Loading branch information
aarifkhamdi committed Oct 21, 2024
1 parent 2800556 commit a8d759e
Show file tree
Hide file tree
Showing 10 changed files with 265 additions and 3 deletions.
1 change: 1 addition & 0 deletions buildSrc/src/main/kotlin/chainsconfig.codegen.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,7 @@ open class CodeGen(private val config: ChainsConfig) {
"solana" -> "BlockchainType.SOLANA"
"near" -> "BlockchainType.NEAR"
"eth-beacon-chain" -> "BlockchainType.ETHEREUM_BEACON_CHAIN"
"ton-center" -> "BlockchainType.TON_CENTER"
"cosmos" -> "BlockchainType.COSMOS"
else -> throw IllegalArgumentException("unknown blockchain type $type")
}
Expand Down
2 changes: 1 addition & 1 deletion emerald-grpc
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@ enum class BlockchainType(
SOLANA(ApiType.JSON_RPC),
NEAR(ApiType.JSON_RPC),
ETHEREUM_BEACON_CHAIN(ApiType.REST),
COSMOS(ApiType.JSON_RPC);
COSMOS(ApiType.JSON_RPC),
TON_CENTER(ApiType.REST);
}

enum class ApiType {
Expand Down
2 changes: 1 addition & 1 deletion foundation/src/main/resources/public
3 changes: 3 additions & 0 deletions src/main/kotlin/io/emeraldpay/dshackle/Global.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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.ethereum.domain.TransactionId
import io.emeraldpay.dshackle.upstream.ethereum.subscribe.json.TransactionIdSerializer
import io.emeraldpay.dshackle.upstream.toncenter.TonCenterMasterchainInfo
import io.emeraldpay.dshackle.upstream.toncenter.TonCenterMasterchainInfoDeserializer
import java.text.SimpleDateFormat
import java.util.Locale
import java.util.TimeZone
Expand Down Expand Up @@ -81,6 +83,7 @@ class Global {
module.addDeserializer(RpcUnspent::class.java, RpcUnspentDeserializer())
module.addDeserializer(ChainRequest::class.java, ChainRequest.Deserializer())
module.addDeserializer(BeaconChainBlockHeader::class.java, BeaconChainBlockHeaderDeserializer())
module.addDeserializer(TonCenterMasterchainInfo::class.java, TonCenterMasterchainInfoDeserializer())

val objectMapper = ObjectMapper()
objectMapper.registerModule(module)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import io.emeraldpay.dshackle.BlockchainType.NEAR
import io.emeraldpay.dshackle.BlockchainType.POLKADOT
import io.emeraldpay.dshackle.BlockchainType.SOLANA
import io.emeraldpay.dshackle.BlockchainType.STARKNET
import io.emeraldpay.dshackle.BlockchainType.TON_CENTER
import io.emeraldpay.dshackle.BlockchainType.UNKNOWN
import io.emeraldpay.dshackle.Chain
import io.emeraldpay.dshackle.foundation.ChainOptions
Expand All @@ -18,6 +19,7 @@ import io.emeraldpay.dshackle.upstream.calls.DefaultCosmosMethods
import io.emeraldpay.dshackle.upstream.calls.DefaultEthereumMethods
import io.emeraldpay.dshackle.upstream.calls.DefaultPolkadotMethods
import io.emeraldpay.dshackle.upstream.calls.DefaultStarknetMethods
import io.emeraldpay.dshackle.upstream.calls.DefaultTonCenterHttpMethods
import org.springframework.stereotype.Component

@Component
Expand All @@ -38,6 +40,7 @@ class CallTargetsHolder {
NEAR -> DefaultNearMethods()
ETHEREUM_BEACON_CHAIN -> DefaultBeaconChainMethods()
COSMOS -> DefaultCosmosMethods()
TON_CENTER -> DefaultTonCenterHttpMethods()
UNKNOWN -> throw IllegalArgumentException("unknown chain")
}
callTargets[chain] = created
Expand Down
Original file line number Diff line number Diff line change
@@ -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 DefaultTonCenterHttpMethods : 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"
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import io.emeraldpay.dshackle.BlockchainType.NEAR
import io.emeraldpay.dshackle.BlockchainType.POLKADOT
import io.emeraldpay.dshackle.BlockchainType.SOLANA
import io.emeraldpay.dshackle.BlockchainType.STARKNET
import io.emeraldpay.dshackle.BlockchainType.TON_CENTER
import io.emeraldpay.dshackle.BlockchainType.UNKNOWN
import io.emeraldpay.dshackle.Chain
import io.emeraldpay.dshackle.cache.Caches
Expand Down Expand Up @@ -42,6 +43,7 @@ import io.emeraldpay.dshackle.upstream.near.NearChainSpecific
import io.emeraldpay.dshackle.upstream.polkadot.PolkadotChainSpecific
import io.emeraldpay.dshackle.upstream.solana.SolanaChainSpecific
import io.emeraldpay.dshackle.upstream.starknet.StarknetChainSpecific
import io.emeraldpay.dshackle.upstream.toncenter.TonCenterHttpSpecific
import org.apache.commons.collections4.Factory
import org.springframework.cloud.sleuth.Tracer
import reactor.core.publisher.Mono
Expand Down Expand Up @@ -110,6 +112,7 @@ object ChainSpecificRegistry {
SOLANA -> SolanaChainSpecific
NEAR -> NearChainSpecific
ETHEREUM_BEACON_CHAIN -> BeaconChainSpecific
TON_CENTER -> TonCenterHttpSpecific
COSMOS -> CosmosChainSpecific
BITCOIN -> throw IllegalArgumentException("bitcoin should use custom streams implementation")
UNKNOWN -> throw IllegalArgumentException("unknown chain")
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
package io.emeraldpay.dshackle.upstream.toncenter

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 TonCenterHttpSpecific : 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 TonCenterLowerBoundService(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<TonCenterMasterchainInfo>(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 TonCenterMasterchainInfoResultLast(
val workchain: Int,
val shard: String,
val seqno: Long,
val root_hash: String,
val file_hash: String,
)

data class TonCenterMasterchainInfoResult(
val last: TonCenterMasterchainInfoResultLast,
)

data class TonCenterMasterchainInfo(
val ok: Boolean,
val result: TonCenterMasterchainInfoResult,
)

class TonCenterMasterchainInfoDeserializer : JsonDeserializer<TonCenterMasterchainInfo>() {
override fun deserialize(p: JsonParser, ctxt: DeserializationContext): TonCenterMasterchainInfo {
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 TonCenterMasterchainInfo(
ok,
TonCenterMasterchainInfoResult(
TonCenterMasterchainInfoResultLast(
workchain,
shard,
seqno,
root_hash,
file_hash,
),
),
)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package io.emeraldpay.dshackle.upstream.toncenter

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 TonCenterLowerBoundService(
chain: Chain,
upstream: Upstream,
) : LowerBoundService(chain, upstream) {
override fun detectors(): List<LowerBoundDetector> {
return listOf()
}
}

0 comments on commit a8d759e

Please sign in to comment.