Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
49 commits
Select commit Hold shift + click to select a range
0a126ef
Add ws handler
StephenButtolph Dec 24, 2025
3120f57
WIP Support all APIs
StephenButtolph Dec 26, 2025
81ae52f
WIP Support all APIs
StephenButtolph Dec 26, 2025
ec2463d
Add additional interface check
StephenButtolph Dec 26, 2025
e23cad2
Add account manager
StephenButtolph Dec 26, 2025
e99e8cb
Add comment
StephenButtolph Dec 26, 2025
c5ce239
cleanup comment
StephenButtolph Dec 26, 2025
f8f5b89
Working with e2e test
StephenButtolph Dec 31, 2025
a46765f
comment newNetwork
StephenButtolph Dec 31, 2025
65e2a74
Use shared constants
StephenButtolph Dec 31, 2025
a7e7ad7
Simplify vm.Init
StephenButtolph Dec 31, 2025
dc8f38c
Split the network and gossiper construction
StephenButtolph Dec 31, 2025
c779c8e
nits
StephenButtolph Dec 31, 2025
b253805
nit
StephenButtolph Dec 31, 2025
baaecc6
nit
StephenButtolph Dec 31, 2025
0e5c983
comment
StephenButtolph Dec 31, 2025
83a2d14
Testing cleanup
StephenButtolph Dec 31, 2025
77a6b2d
Sanity check
StephenButtolph Dec 31, 2025
e4ea46d
lint
StephenButtolph Dec 31, 2025
1f0f3f6
Update avalanchego version
StephenButtolph Dec 31, 2025
f6ec068
merged
StephenButtolph Jan 3, 2026
6a582ed
merged
StephenButtolph Jan 4, 2026
e3ddf04
wtf
StephenButtolph Jan 4, 2026
4672c91
reduce diff
StephenButtolph Jan 4, 2026
22cfae6
nits
StephenButtolph Jan 4, 2026
691b440
merged
StephenButtolph Jan 4, 2026
b723fc2
comment
StephenButtolph Jan 4, 2026
e737a8c
merged
StephenButtolph Jan 4, 2026
4fb327a
nit
StephenButtolph Jan 4, 2026
d822765
Update avalanchego version
StephenButtolph Jan 8, 2026
5832b79
merged
StephenButtolph Jan 8, 2026
6c9c89f
merged
StephenButtolph Jan 13, 2026
a2c3500
Merge branch 'main' into StephenButtolph/api-support
StephenButtolph Jan 14, 2026
f90c2e4
merged wip
StephenButtolph Jan 28, 2026
1842c26
reduce diff
StephenButtolph Jan 28, 2026
26deded
reduce diff
StephenButtolph Jan 28, 2026
f1c2dbb
reduce diff
StephenButtolph Jan 28, 2026
08825c5
reduce diff
StephenButtolph Jan 28, 2026
515e0c9
reduce diff
StephenButtolph Jan 28, 2026
f809253
merged
StephenButtolph Jan 28, 2026
41c32e4
merged
StephenButtolph Jan 29, 2026
f4ba4ec
merged
StephenButtolph Feb 5, 2026
00efae5
merged
StephenButtolph Feb 5, 2026
36e376e
merged
StephenButtolph Feb 5, 2026
db69f7e
nit
StephenButtolph Feb 5, 2026
05569c0
merged
StephenButtolph Feb 10, 2026
042feb0
reduce diff
StephenButtolph Feb 10, 2026
b43e937
nit
StephenButtolph Feb 10, 2026
bcc3eab
merged
StephenButtolph Feb 10, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
218 changes: 207 additions & 11 deletions sae/rpc.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,40 +10,50 @@
"math/big"
"strconv"
"sync"
"time"

"github.com/ava-labs/avalanchego/network/p2p"
"github.com/ava-labs/avalanchego/version"
ethereum "github.com/ava-labs/libevm"
"github.com/ava-labs/libevm/accounts"
"github.com/ava-labs/libevm/common"
"github.com/ava-labs/libevm/common/hexutil"
"github.com/ava-labs/libevm/consensus"
"github.com/ava-labs/libevm/core"
"github.com/ava-labs/libevm/core/bloombits"
"github.com/ava-labs/libevm/core/rawdb"
"github.com/ava-labs/libevm/core/state"
"github.com/ava-labs/libevm/core/txpool"
"github.com/ava-labs/libevm/core/types"
"github.com/ava-labs/libevm/core/vm"
"github.com/ava-labs/libevm/crypto"
"github.com/ava-labs/libevm/eth/filters"
"github.com/ava-labs/libevm/eth/tracers"
"github.com/ava-labs/libevm/ethdb"
"github.com/ava-labs/libevm/event"
"github.com/ava-labs/libevm/libevm/debug"
"github.com/ava-labs/libevm/libevm/ethapi"
"github.com/ava-labs/libevm/params"
"github.com/ava-labs/libevm/rpc"
"go.uber.org/zap"

"github.com/ava-labs/strevm/txgossip"
)

type APIBackend interface {

Check failure on line 43 in sae/rpc.go

View workflow job for this annotation

GitHub Actions / golangci-lint

exported: exported type APIBackend should have comment or be unexported (revive)
filters.Backend
ethapi.Backend
tracers.Backend
}

// APIBackend returns an API backend backed by the VM.
func (vm *VM) APIBackend() ethapi.Backend {
func (vm *VM) APIBackend() APIBackend {
return vm.apiBackend
}

func (vm *VM) ethRPCServer() (*rpc.Server, error) {
b := vm.APIBackend()
s := rpc.NewServer()

// Even if this function errors, we should close API to prevent a goroutine
// from leaking.
filterSystem := filters.NewFilterSystem(b, filters.Config{})
filterAPI := filters.NewFilterAPI(filterSystem, false /*isLightClient*/)
vm.toClose = append(vm.toClose, func() error {
Expand Down Expand Up @@ -78,43 +88,129 @@
// - eth_syncing
{"eth", ethapi.NewEthereumAPI(b)},
// Standard Ethereum node APIs:
// - eth_gasPrice
// - eth_syncing
//
// Undocumented APIs:
// - eth_feeHistory
// - eth_maxPriorityFeePerGas
{"eth", ethapi.NewEthereumAPI(b)},
// Standard Ethereum node APIs:
// - eth_blockNumber
// - eth_call
// - eth_chainId
// - eth_estimateGas
// - eth_getBalance
// - eth_getBlockByHash
// - eth_getBlockByNumber
// - eth_getCode
// - eth_getStorageAt
// - eth_getUncleByBlockHashAndIndex
// - eth_getUncleByBlockNumberAndIndex
// - eth_getUncleCountByBlockHash
// - eth_getUncleCountByBlockNumber
//
// Geth-specific APIs:
// - eth_createAccessList
// - eth_getHeaderByHash
// - eth_getHeaderByNumber
//
// Undocumented APIs:
// - eth_getBlockReceipts
// - eth_getProof
{"eth", ethapi.NewBlockChainAPI(b)},
// Standard Ethereum node APIs:
// - eth_getBlockTransactionCountByHash
// - eth_getBlockTransactionCountByNumber
// - eth_getTransactionByBlockHashAndIndex
// - eth_getTransactionByBlockNumberAndIndex
// - eth_getTransactionByHash
// - eth_getTransactionCount
// - eth_getTransactionReceipt
// - eth_sendRawTransaction
// - eth_sendTransaction
// - eth_sign
// - eth_signTransaction
//
// Undocumented APIs:
// - eth_fillTransaction
// - eth_getRawTransactionByBlockHashAndIndex
// - eth_getRawTransactionByBlockNumberAndIndex
// - eth_getRawTransactionByHash
// - eth_pendingTransactions
// - eth_resend
{"eth", ethapi.NewTransactionAPI(b, new(ethapi.AddrLocker))},
// Standard Ethereum node APIs:
// - eth_getFilterChanges
// - eth_getFilterLogs
// - eth_getLogs
// - eth_newBlockFilter
// - eth_newFilter
// - eth_newPendingTransactionFilter
// - eth_uninstallFilter
//
// Geth-specific APIs:
// - eth_subscribe
// - newHeads
// - newPendingTransactions
// - logs
{"eth", filterAPI},
// Geth-specific APIs:
// - debug_chainDbCompact
// - debug_chainDbProperty
// - debug_dbAncient
// - debug_dbAncients
// - debug_dbGet
// - debug_getRawBlock
// - debug_getRawHeader
// - debug_getRawReceipts
// - debug_getRawTransaction
// - debug_printBlock
// - debug_setHead
{"debug", ethapi.NewDebugAPI(b)},
// Geth-specific APIs:
// - debug_intermediateRoots
// - debug_standardTraceBadBlockToFile
// - debug_standardTraceBlockToFile
// - debug_traceBadBlock
// - debug_traceBlock
// - debug_traceBlockByHash
// - debug_traceBlockByNumber
// - debug_traceBlockFromFile
// - debug_traceCall
// - debug_traceChain
// - debug_traceTransaction
{"debug", tracers.NewAPI(b)},
}
// Unsupported APIs:
//
// Standard Ethereum node APIs:
// - eth_protocolVersion
// - eth_coinbase
// - eth_mining
// - eth_hashrate
// - eth_accounts
//
// Block and state inspection APIs:
// - debug_accountRange
// - debug_dumpBlock
// - debug_getAccessibleState
// - debug_getBadBlocks
// - debug_getModifiedAccountsByHash
// - debug_getModifiedAccountsByNumber
// - debug_getTrieFlushInterval
// - debug_preimage
// - debug_setTrieFlushInterval
// - debug_storageRangeAt
//
// The admin namespace.
// The clique namespace.
// The les namespace.
// The miner namespace.
// The personal namespace.
//
// The graphql service.
s := rpc.NewServer()

if vm.config.RPCConfig.EnableProfiling {
apis = append(apis, api{
Expand Down Expand Up @@ -200,24 +296,27 @@
return s.chainID
}

var _ APIBackend = (*ethAPIBackend)(nil)

// TODO: Rename to apiBackend
type ethAPIBackend struct {
*txgossip.Set
vm *VM
accountManager *accounts.Manager

ethapi.Backend // TODO(arr4n) remove in favour of `var _ ethapi.Backend = (*ethAPIBackend)(nil)`
}

func (b *ethAPIBackend) ChainConfig() *params.ChainConfig {
return b.vm.exec.ChainConfig()
}

func (b *ethAPIBackend) RPCTxFeeCap() float64 {
return 0 // TODO(arr4n)
// TODO(StephenButtolph) Expose this as a config.
return 1 // 1 AVAX
}

func (b *ethAPIBackend) UnprotectedAllowed() bool {
return false
// TODO(StephenButtolph) Expose this as a config and default to false.
return true
}

func (b *ethAPIBackend) AccountManager() *accounts.Manager {
Expand All @@ -241,6 +340,43 @@
return ethereum.SyncProgress{}
}

func (b *ethAPIBackend) SuggestGasTipCap(ctx context.Context) (*big.Int, error) {
panic(errUnimplemented)
}

func (b *ethAPIBackend) FeeHistory(ctx context.Context, blockCount uint64, lastBlock rpc.BlockNumber, rewardPercentiles []float64) (*big.Int, [][]*big.Int, []*big.Int, []float64, error) {
panic(errUnimplemented)
}

func (b *ethAPIBackend) ChainDb() ethdb.Database {
return b.vm.db
}

func (b *ethAPIBackend) ExtRPCEnabled() bool {
// We never recommend to expose the RPC externally. Additionally, this is
// only used as an additional security measure for the personal API, which
// we do not support in its entirety.
return false
}

func (b *ethAPIBackend) RPCGasCap() uint64 {
// TODO(StephenButtolph) Expose this as a config.
return 25_000_000
}

func (b *ethAPIBackend) RPCEVMTimeout() time.Duration {
// TODO(StephenButtolph) Expose this as a config.
return 5 * time.Second
}

func (b *ethAPIBackend) SetHead(number uint64) {
// SAE does not support reorgs. We ignore any attempts to override the chain
// head.
b.vm.log().Warn("ignoring attempt to override the chain head",
zap.Uint64("number", number),
)
}

func (b *ethAPIBackend) HeaderByNumber(ctx context.Context, n rpc.BlockNumber) (*types.Header, error) {
return readByNumber(b, n, rawdb.ReadHeader)
}
Expand All @@ -263,6 +399,14 @@
return readByHash(b, hash, rawdb.ReadBlock), nil
}

func (b *ethAPIBackend) HeaderByNumberOrHash(ctx context.Context, blockNrOrHash rpc.BlockNumberOrHash) (*types.Header, error) {
panic(errUnimplemented)
}

func (b *ethAPIBackend) BlockByNumberOrHash(ctx context.Context, blockNrOrHash rpc.BlockNumberOrHash) (*types.Block, error) {
panic(errUnimplemented)
}

func (b *ethAPIBackend) GetTransaction(ctx context.Context, txHash common.Hash) (exists bool, tx *types.Transaction, blockHash common.Hash, blockNumber uint64, index uint64, err error) {
tx, blockHash, blockNumber, index = rawdb.ReadTransaction(b.vm.db, txHash)
if tx == nil {
Expand Down Expand Up @@ -359,7 +503,7 @@
return b.vm.exec.SubscribeChainHeadEvent(ch)
}

func (b *ethAPIBackend) SubscribeChainSideEvent(chan<- core.ChainSideEvent) event.Subscription {
func (*ethAPIBackend) SubscribeChainSideEvent(chan<- core.ChainSideEvent) event.Subscription {
// SAE never reorgs, so there are no side events.
return newNoopSubscription()
}
Expand All @@ -368,7 +512,7 @@
return b.Set.Pool.SubscribeTransactions(ch, true)
}

func (b *ethAPIBackend) SubscribeRemovedLogsEvent(chan<- core.RemovedLogsEvent) event.Subscription {
func (*ethAPIBackend) SubscribeRemovedLogsEvent(chan<- core.RemovedLogsEvent) event.Subscription {
// SAE never reorgs, so no logs are ever removed.
return newNoopSubscription()
}
Expand All @@ -377,12 +521,64 @@
return b.vm.exec.SubscribeLogsEvent(ch)
}

func (b *ethAPIBackend) SubscribePendingLogsEvent(chan<- []*types.Log) event.Subscription {
func (*ethAPIBackend) SubscribePendingLogsEvent(chan<- []*types.Log) event.Subscription {
// In SAE, "pending" refers to the execution status. There are no logs known
// for transactions pending execution.
return newNoopSubscription()
}

func (b *ethAPIBackend) StateAndHeaderByNumber(ctx context.Context, number rpc.BlockNumber) (*state.StateDB, *types.Header, error) {
panic(errUnimplemented)
}

func (b *ethAPIBackend) StateAndHeaderByNumberOrHash(ctx context.Context, blockNrOrHash rpc.BlockNumberOrHash) (*state.StateDB, *types.Header, error) {
panic(errUnimplemented)
}

func (b *ethAPIBackend) PendingBlockAndReceipts() (*types.Block, types.Receipts) {
panic(errUnimplemented)
}

func (b *ethAPIBackend) GetReceipts(ctx context.Context, hash common.Hash) (types.Receipts, error) {
panic(errUnimplemented)
}

func (b *ethAPIBackend) GetEVM(ctx context.Context, msg *core.Message, state *state.StateDB, header *types.Header, vmConfig *vm.Config, blockCtx *vm.BlockContext) *vm.EVM {
panic(errUnimplemented)
}

func (b *ethAPIBackend) GetPoolNonce(ctx context.Context, addr common.Address) (uint64, error) {
return b.Pool.Nonce(addr), nil
}

func (b *ethAPIBackend) Engine() consensus.Engine {
panic(errUnimplemented)
}

func (b *ethAPIBackend) GetBody(ctx context.Context, hash common.Hash, number rpc.BlockNumber) (*types.Body, error) {
panic(errUnimplemented)
}

func (b *ethAPIBackend) GetLogs(ctx context.Context, blockHash common.Hash, number uint64) ([][]*types.Log, error) {
panic(errUnimplemented)
}

func (b *ethAPIBackend) BloomStatus() (uint64, uint64) {
panic(errUnimplemented)
}

func (b *ethAPIBackend) ServiceFilter(ctx context.Context, session *bloombits.MatcherSession) {
panic(errUnimplemented)
}

func (b *ethAPIBackend) StateAtBlock(ctx context.Context, block *types.Block, reexec uint64, base *state.StateDB, readOnly bool, preferDisk bool) (*state.StateDB, tracers.StateReleaseFunc, error) {
panic(errUnimplemented)
}

func (b *ethAPIBackend) StateAtTransaction(ctx context.Context, block *types.Block, txIndex int, reexec uint64) (*core.Message, vm.BlockContext, *state.StateDB, tracers.StateReleaseFunc, error) {
panic(errUnimplemented)
}

type noopSubscription struct {
once sync.Once
err chan error
Expand Down
9 changes: 0 additions & 9 deletions sae/temporary.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,7 @@ package sae
// interim identifiers needed for development over multiple PRs.

import (
"context"
"errors"

"github.com/ava-labs/libevm/core/types"
)

var errUnimplemented = errors.New("unimplemented")

// TODO(arr4n) remove this method once no longer embedding [ethapi.Backend] in
// [ethAPIBackend] as it's only required for disambiguation.
func (b *ethAPIBackend) SendTx(ctx context.Context, tx *types.Transaction) error {
return b.Set.SendTx(ctx, tx)
}
3 changes: 1 addition & 2 deletions sae/vm.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@ import (
"github.com/ava-labs/libevm/core/txpool/legacypool"
"github.com/ava-labs/libevm/core/types"
"github.com/ava-labs/libevm/ethdb"
"github.com/ava-labs/libevm/libevm/ethapi"
"github.com/ava-labs/libevm/params"
"github.com/ava-labs/libevm/triedb"
"github.com/prometheus/client_golang/prometheus"
Expand Down Expand Up @@ -60,7 +59,7 @@ type VM struct {

exec *saexec.Executor
mempool *txgossip.Set
apiBackend ethapi.Backend
apiBackend *ethAPIBackend
newTxs chan struct{}

toClose [](func() error)
Expand Down
Loading