Skip to content

Commit

Permalink
solana support (#339)
Browse files Browse the repository at this point in the history
  • Loading branch information
a10zn8 authored Nov 13, 2023
1 parent e5f844b commit 2e80da5
Show file tree
Hide file tree
Showing 39 changed files with 513 additions and 249 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 @@ -117,6 +117,7 @@ open class CodeGen(private val config: ChainsConfig) {
"bitcoin" -> "BlockchainType.BITCOIN"
"starknet" -> "BlockchainType.STARKNET"
"polkadot" -> "BlockchainType.POLKADOT"
"solana" -> "BlockchainType.SOLANA"
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
@@ -1,5 +1,5 @@
package io.emeraldpay.dshackle

enum class BlockchainType {
UNKNOWN, BITCOIN, ETHEREUM, STARKNET, POLKADOT;
UNKNOWN, BITCOIN, ETHEREUM, STARKNET, POLKADOT, SOLANA;
}
23 changes: 23 additions & 0 deletions foundation/src/main/resources/chains.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -664,3 +664,26 @@ chain-settings:
short-names: [ vara-testnet ]
chain-id: 0x0
grpcId: 10036
- id: solana
label: solana
type: solana
settings:
expected-block-time: 1s
options:
validate-peers: false
lags:
syncing: 20
lagging: 10
chains:
- id: Mainnet
priority: 1
code: SOLANA_MAINNET
short-names: [ solana ]
chain-id: 0x0
grpcId: 1028
- id: Testnet
priority: 1
code: SOLANA_TESTMET
short-names: [ solana-testnet ]
chain-id: 0x0
grpcId: 10037
6 changes: 6 additions & 0 deletions src/main/kotlin/io/emeraldpay/dshackle/data/BlockId.kt
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ package io.emeraldpay.dshackle.data
import io.emeraldpay.dshackle.upstream.ethereum.json.BlockJson
import io.emeraldpay.etherjar.domain.BlockHash
import org.bouncycastle.util.encoders.Hex
import java.util.Base64

class BlockId(
value: ByteArray,
Expand Down Expand Up @@ -55,5 +56,10 @@ class BlockId(
val bytes = Hex.decode(even)
return BlockId(bytes)
}

fun fromBase64(id: String): BlockId {
val bytes = Base64.getDecoder().decode(id)
return BlockId(bytes)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,12 @@ open class NativeSubscribe(
val publisher = multistream.tryProxySubscribe(matcher, request) ?: run {
val method = request.method
val params: Any? = request.payload?.takeIf { !it.isEmpty }?.let {
objectMapper.readValue(it.newInput(), Map::class.java)
val raw = it.toStringUtf8()
if (raw.startsWith("{")) {
objectMapper.readValue(it.newInput(), Map::class.java)
} else {
objectMapper.readValue(it.newInput(), List::class.java)
}
}
subscribe(chain, method, params, matcher)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,6 @@ class SubscribeNodeStatus(
.build()
},
)
.addAllSupportedSubscriptions(up.getSubscriptionTopics())
.addAllSupportedMethods(up.getMethods().getSupportedMethods())
(up as? GrpcUpstream)?.let {
it.getBuildInfo().version?.let { version ->
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,6 @@ open class GenericUpstreamCreator(
connectorFactory,
cs::validator,
cs::labelDetector,
cs::subscriptionTopics,
)

upstream.start()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package io.emeraldpay.dshackle.upstream
import io.emeraldpay.dshackle.BlockchainType.BITCOIN
import io.emeraldpay.dshackle.BlockchainType.ETHEREUM
import io.emeraldpay.dshackle.BlockchainType.POLKADOT
import io.emeraldpay.dshackle.BlockchainType.SOLANA
import io.emeraldpay.dshackle.BlockchainType.STARKNET
import io.emeraldpay.dshackle.BlockchainType.UNKNOWN
import io.emeraldpay.dshackle.Chain
Expand All @@ -27,6 +28,7 @@ class CallTargetsHolder {
ETHEREUM -> DefaultEthereumMethods(chain)
STARKNET -> DefaultStarknetMethods(chain)
POLKADOT -> DefaultPolkadotMethods()
SOLANA -> DefaultSolanaMethods()
UNKNOWN -> throw IllegalArgumentException("unknown chain")
}
callTargets[chain] = created
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
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.etherjar.rpc.RpcException

class DefaultSolanaMethods : CallMethods {
companion object {
val subs = setOf(
"accountSubscribe" to "accountUnsubscribe",
"blockSubscribe" to "blockUnsubscribe",
"logsSubscribe" to "logsUnsubscribe",
"programSubscribe" to "programUnsubscribe",
"signatureSubscribe" to "signatureUnsubscribe",
"slotSubscribe" to "slotUnsubscribe",
)
}

private val all = setOf(
"getAccountInfo",
"getBalance",
"getBlock",
"getBlockHeight",
"getBlockProduction",
"getBlockCommitment",
"getBlocks",
"getBlocksWithLimit",
"getBlockTime",
"getClusterNodes",
"getEpochInfo",
"getEpochSchedule",
"getFeeForMessage",
"getFirstAvailableBlock",
"getGenesisHash",
"getHealth",
"getHighestSnapshotSlot",
"getInflationGovernor",
"getInflationRate",
"getInflationReward",
"getLargestAccounts",
"getLatestBlockhash",
"getLeaderSchedule",
"getMaxRetransmitSlot",
"getMaxShredInsertSlot",
"getMinimumBalanceForRentExemption",
"getMultipleAccounts",
"getProgramAccounts",
"getRecentPerformanceSamples",
"getRecentPrioritizationFees",
"getSignaturesForAddress",
"getSignatureStatuses",
"getSlot",
"getSlotLeader",
"getSlotLeaders",
"getStakeActivation",
"getStakeMinimumDelegation",
"getSupply",
"getTokenAccountBalance",
"getTokenAccountsByDelegate",
"getTokenAccountsByOwner",
"getTokenLargestAccounts",
"getTokenSupply",
"getTransaction",
"getTransactionCount",
"getVersion",
"getVoteAccounts",
"isBlockhashValid",
"minimumLedgerSlot",
"requestAirdrop",
"simulateTransaction",
)

private val add = setOf(
"sendTransaction",
)

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()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,5 +21,5 @@ package io.emeraldpay.dshackle.upstream
interface IngressSubscription {

fun getAvailableTopics(): List<String>
fun <T> get(topic: String): SubscriptionConnect<T>?
fun <T> get(topic: String, params: Any?): SubscriptionConnect<T>?
}
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ abstract class Multistream(
.multicast()
.directBestEffort<UpstreamChangeEvent>()

override fun getSubscriptionTopics(): List<String> {
fun getSubscriptionTopics(): List<String> {
return getEgressSubscription().getAvailableTopics()
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ open class NoIngressSubscription : IngressSubscription {
return listOf()
}

override fun <T> get(topic: String): SubscriptionConnect<T>? {
override fun <T> get(topic: String, params: Any?): SubscriptionConnect<T>? {
return null
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,6 @@ interface Upstream : Lifecycle {
fun getLag(): Long?
fun getLabels(): Collection<UpstreamsConfig.Labels>
fun getMethods(): CallMethods
fun getSubscriptionTopics(): List<String>
fun getId(): String
fun getCapabilities(): Set<Capability>
fun isGrpc(): Boolean
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,10 +59,6 @@ open class BitcoinRpcUpstream(
return directApi
}

override fun getSubscriptionTopics(): List<String> {
return listOf()
}

override fun getLabels(): Collection<UpstreamsConfig.Labels> {
return listOf(UpstreamsConfig.Labels())
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,12 +29,11 @@ class DefaultPolkadotMethods : CallMethods {

companion object {
val subs = setOf(
Pair("subscribe_newHead", "unsubscribe_newHead"),
Pair("chain_subscribeAllHeads", "chain_unsubscribeAllHeads"),
Pair("chain_subscribeFinalizedHeads", "chain_unsubscribeFinalizedHeads"),
Pair("chain_subscribeNewHeads", "chain_unsubscribeNewHeads"),
Pair("chain_subscribeRuntimeVersion", "chain_unsubscribeNewHeads"),
Pair("chain_subscribeRuntimeVersion", "chain_unsubscribeRuntimeVersion"),
"subscribe_newHead" to "unsubscribe_newHead",
"chain_subscribeAllHeads" to "chain_unsubscribeAllHeads",
"chain_subscribeFinalizedHeads" to "chain_unsubscribeFinalizedHeads",
"chain_subscribeNewHeads" to "chain_unsubscribeNewHeads",
"chain_subscribeRuntimeVersion" to "chain_unsubscribeRuntimeVersion",
)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import io.emeraldpay.dshackle.data.BlockContainer
import io.emeraldpay.dshackle.foundation.ChainOptions.Options
import io.emeraldpay.dshackle.reader.JsonRpcReader
import io.emeraldpay.dshackle.upstream.CachingReader
import io.emeraldpay.dshackle.upstream.Capability
import io.emeraldpay.dshackle.upstream.EgressSubscription
import io.emeraldpay.dshackle.upstream.Head
import io.emeraldpay.dshackle.upstream.IngressSubscription
Expand All @@ -23,15 +22,15 @@ import io.emeraldpay.dshackle.upstream.ethereum.subscribe.EthereumLabelsDetector
import io.emeraldpay.dshackle.upstream.ethereum.subscribe.EthereumWsIngressSubscription
import io.emeraldpay.dshackle.upstream.ethereum.subscribe.NoPendingTxes
import io.emeraldpay.dshackle.upstream.ethereum.subscribe.PendingTxesSource
import io.emeraldpay.dshackle.upstream.generic.AbstractPollChainSpecific
import io.emeraldpay.dshackle.upstream.generic.CachingReaderBuilder
import io.emeraldpay.dshackle.upstream.generic.ChainSpecific
import io.emeraldpay.dshackle.upstream.generic.GenericUpstream
import io.emeraldpay.dshackle.upstream.rpcclient.JsonRpcRequest
import org.springframework.cloud.sleuth.Tracer
import reactor.core.publisher.Mono
import reactor.core.scheduler.Scheduler

object EthereumChainSpecific : ChainSpecific {
object EthereumChainSpecific : AbstractPollChainSpecific() {
override fun parseBlock(data: ByteArray, upstreamId: String): BlockContainer {
return BlockContainer.fromEthereumJson(data, upstreamId)
}
Expand All @@ -58,6 +57,7 @@ object EthereumChainSpecific : ChainSpecific {
val pendingTxes: PendingTxesSource = (ms.getAll())
.filter { it is GenericUpstream }
.map { it as GenericUpstream }
.filter { it.getIngressSubscription() is EthereumIngressSubscription }
.mapNotNull {
(it.getIngressSubscription() as EthereumIngressSubscription).getPendingTxes()
}.let {
Expand Down Expand Up @@ -90,15 +90,6 @@ object EthereumChainSpecific : ChainSpecific {
return EthereumLabelsDetector(reader, chain)
}

override fun subscriptionTopics(upstream: GenericUpstream): List<String> {
val subs = if (upstream.getCapabilities().contains(Capability.WS_HEAD)) {
listOf(EthereumEgressSubscription.METHOD_NEW_HEADS, EthereumEgressSubscription.METHOD_LOGS)
} else {
listOf()
}
return upstream.getIngressSubscription().getAvailableTopics().plus(subs).toSet().toList()
}

override fun makeIngressSubscription(ws: WsSubscriptions): IngressSubscription {
return EthereumWsIngressSubscription(ws)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ class GenericWsHead(
}
.timeout(Duration.ofSeconds(60), Mono.error(RuntimeException("No response from subscribe to newHeads")))
.onErrorResume {
log.error("Error getting heads for $upstreamId - ${it.message}")
log.error("Error getting heads for $upstreamId", it)
subscribed = false
unsubscribe()
}
Expand Down

This file was deleted.

Loading

0 comments on commit 2e80da5

Please sign in to comment.