Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add new rpc endpoints #3502

Merged
merged 4 commits into from
Sep 11, 2024
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
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
10 changes: 6 additions & 4 deletions packages/chainutil/evmtrace.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,13 @@ import (
"github.com/iotaledger/wasp/packages/isc"
)

func EVMTraceTransaction(
func EVMTrace(
ch chain.ChainCore,
aliasOutput *isc.AliasOutputWithID,
blockTime time.Time,
iscRequestsInBlock []isc.Request,
txIndex uint64,
txIndex *uint64,
blockNumber *uint64,
tracer *tracers.Tracer,
) error {
_, err := runISCTask(
Expand All @@ -24,8 +25,9 @@ func EVMTraceTransaction(
iscRequestsInBlock,
false,
&isc.EVMTracer{
Tracer: tracer,
TxIndex: txIndex,
Tracer: tracer,
TxIndex: txIndex,
BlockNumber: blockNumber,
},
)
return err
Expand Down
2 changes: 1 addition & 1 deletion packages/evm/jsonrpc/chainbackend.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ type ChainBackend interface {
EVMSendTransaction(tx *types.Transaction) error
EVMCall(aliasOutput *isc.AliasOutputWithID, callMsg ethereum.CallMsg) ([]byte, error)
EVMEstimateGas(aliasOutput *isc.AliasOutputWithID, callMsg ethereum.CallMsg) (uint64, error)
EVMTraceTransaction(aliasOutput *isc.AliasOutputWithID, blockTime time.Time, iscRequestsInBlock []isc.Request, txIndex uint64, tracer *tracers.Tracer) error
EVMTrace(aliasOutput *isc.AliasOutputWithID, blockTime time.Time, iscRequestsInBlock []isc.Request, txIndex *uint64, blockNumber *uint64, tracer *tracers.Tracer) error
FeePolicy(blockIndex uint32) (*gas.FeePolicy, error)
ISCChainID() *isc.ChainID
ISCCallView(chainState state.State, scName string, funName string, args dict.Dict) (dict.Dict, error)
Expand Down
95 changes: 83 additions & 12 deletions packages/evm/jsonrpc/evmchain.go
Original file line number Diff line number Diff line change
Expand Up @@ -425,6 +425,25 @@ func (e *EVMChain) TransactionByBlockNumberAndIndex(blockNumber *big.Int, index
return txs[index], block.Hash(), bn, nil
}

func (e *EVMChain) txsByBlockNumber(blockNumber *big.Int) (txs types.Transactions, err error) {
e.log.Debugf("TxsByBlockNumber(blockNumber=%v, index=%v)", blockNumber)
cachedTxs := e.index.TxsByBlockNumber(blockNumber)
if cachedTxs != nil {
return cachedTxs, nil
}
latestState, err := e.backend.ISCLatestState()
if err != nil {
return nil, err
}
db := blockchainDB(latestState)
block := db.GetBlockByNumber(blockNumber.Uint64())
if block == nil {
return nil, err
}

return block.Transactions(), nil
}

func (e *EVMChain) BlockByHash(hash common.Hash) *types.Block {
e.log.Debugf("BlockByHash(hash=%v)", hash)

Expand Down Expand Up @@ -639,44 +658,48 @@ func (e *EVMChain) iscRequestsInBlock(evmBlockNumber uint64) (*blocklog.BlockInf
}
iscBlockIndex := iscState.BlockIndex()
blocklogStatePartition := subrealm.NewReadOnly(iscState, kv.Key(blocklog.Contract.Hname().Bytes()))

return blocklog.GetRequestsInBlock(blocklogStatePartition, iscBlockIndex)
}

func (e *EVMChain) TraceTransaction(txHash common.Hash, config *tracers.TraceConfig) (any, error) {
e.log.Debugf("TraceTransaction(txHash=%v, config=?)", txHash)
func (e *EVMChain) Trace(config *tracers.TraceConfig, txIndex *uint64, txHash common.Hash, blockNumber uint64, blockHash common.Hash) (any, error) {
tracerType := "callTracer"
if config.Tracer != nil {
tracerType = *config.Tracer
}

_, blockHash, blockNumber, txIndex, err := e.TransactionByHash(txHash)
iscBlock, iscRequestsInBlock, err := e.iscRequestsInBlock(blockNumber)
if err != nil {
return nil, err
}
if blockNumber == 0 {
return nil, errors.New("tx not found")
}

iscBlock, iscRequestsInBlock, err := e.iscRequestsInBlock(blockNumber)
if err != nil {
return nil, err
var blockTxs types.Transactions
var txi int
if txIndex != nil {
txi = int(*txIndex)
} else {
blockTxs, err = e.txsByBlockNumber(new(big.Int).SetUint64(blockNumber))
if err != nil {
return nil, err
}
}

tracer, err := newTracer(tracerType, &tracers.Context{
BlockHash: blockHash,
BlockNumber: new(big.Int).SetUint64(blockNumber),
TxIndex: int(txIndex),
TxIndex: txi,
TxHash: txHash,
}, config.TracerConfig)
}, config.TracerConfig, blockTxs)
if err != nil {
return nil, err
}

err = e.backend.EVMTraceTransaction(
err = e.backend.EVMTrace(
iscBlock.PreviousAliasOutput,
iscBlock.Timestamp,
iscRequestsInBlock,
txIndex,
&blockNumber,
tracer,
)
if err != nil {
Expand All @@ -686,6 +709,54 @@ func (e *EVMChain) TraceTransaction(txHash common.Hash, config *tracers.TraceCon
return tracer.GetResult()
}

func (e *EVMChain) TraceTransaction(txHash common.Hash, config *tracers.TraceConfig) (any, error) {
e.log.Debugf("TraceTransaction(txHash=%v, config=?)", txHash)

_, blockHash, blockNumber, txIndex, err := e.TransactionByHash(txHash)
if err != nil {
return nil, err
}
if blockNumber == 0 {
return nil, errors.New("tx not found")
}

return e.Trace(config, &txIndex, txHash, blockNumber, blockHash)
}

func (e *EVMChain) TraceBlockByHash(blockHash common.Hash, config *tracers.TraceConfig) (any, error) {
e.log.Debugf("TraceBlockByHash(blockHash=%v, config=?)", blockHash)

block := e.BlockByHash(blockHash)
if block == nil {
return nil, errors.New("block not found")
}

return e.Trace(config, nil, common.Hash{}, block.Number().Uint64(), blockHash)
}

func (e *EVMChain) TraceBlockByNumber(blockNumber uint64, config *tracers.TraceConfig) (any, error) {
e.log.Debugf("TraceBlockByNumber(blockNumber=%v, config=?)", blockNumber)

block, err := e.BlockByNumber(big.NewInt(int64(blockNumber)))
if err != nil {
return nil, fmt.Errorf("block not found: %w", err)
}

return e.Trace(config, nil, common.Hash{}, blockNumber, block.Hash())
}

func (e *EVMChain) GetBlockReceipts(blockNumber rpc.BlockNumber) ([]*types.Receipt, error) {
e.log.Debugf("GetBlockReceipts(blockNumber=%v)", blockNumber)
bn := parseBlockNumber(blockNumber)
chainState, err := e.iscStateFromEVMBlockNumber(bn)
if err != nil {
return nil, err
}

db := blockchainDB(chainState)
return db.GetReceiptsByBlockNumber(bn.Uint64()), nil
}

var maxUint32 = big.NewInt(math.MaxUint32)

// the first EVM block (number 0) is "minted" at ISC block index 0 (init chain)
Expand Down
15 changes: 15 additions & 0 deletions packages/evm/jsonrpc/jsonrpcindex/index.go
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,21 @@ func (c *Index) TxByBlockNumberAndIndex(blockNumber *big.Int, txIndex uint64) (t
return txs[txIndex], block.Hash()
}

func (c *Index) TxsByBlockNumber(blockNumber *big.Int) types.Transactions {
if blockNumber == nil {
return nil
}
db := c.evmDBFromBlockIndex(uint32(blockNumber.Uint64()))
if db == nil {
return nil
}
block := db.GetBlockByNumber(blockNumber.Uint64())
if block == nil {
return nil
}
return block.Transactions()
}

// internals

const (
Expand Down
141 changes: 136 additions & 5 deletions packages/evm/jsonrpc/jsonrpctest/jsonrpc_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import (
"context"
"encoding/json"
"math/big"
"regexp"
"slices"
"strings"
"testing"
"time"
Expand Down Expand Up @@ -579,10 +579,141 @@ func TestRPCTraceTx(t *testing.T) {
tracers.TraceConfig{TracerConfig: []byte(`{"tracer": "callTracer"}`)},
)
require.NoError(t, err)
lastCallRegExp := regexp.MustCompile(`{.+"to":"0x([a-zA-Z0-9_.-]+)".*}`)
match1 := lastCallRegExp.Find(res1)
match2 := lastCallRegExp.Find(res2)
require.NotEqual(t, match1, match2)

trace1 := jsonrpc.CallFrame{}
err = json.Unmarshal(res1, &trace1)
require.NoError(t, err)

require.Equal(t, creatorAddress, trace1.From)
require.Equal(t, contractAddress, *trace1.To)
require.Equal(t, big.NewInt(123), trace1.Value)
expectedInput, err := contractABI.Pack("sendTo", common.Address{0x1}, big.NewInt(1))
require.NoError(t, err)
require.Equal(t, expectedInput, trace1.Input)
require.Empty(t, trace1.Error)
require.Empty(t, trace1.RevertReason)

require.Len(t, trace1.Calls, 1)
trace2 := trace1.Calls[0]
require.Equal(t, contractAddress, trace2.From)
require.Equal(t, common.Address{0x1}, *trace2.To)
require.Equal(t, big.NewInt(1), trace2.Value)
require.Empty(t, trace2.Input)
require.Empty(t, trace2.Error)
require.Empty(t, trace2.RevertReason)
}

func TestRPCTraceBlock(t *testing.T) {
env := newSoloTestEnv(t)
creator, creatorAddress := env.soloChain.NewEthereumAccountWithL2Funds()
creator2, creatorAddress2 := env.soloChain.NewEthereumAccountWithL2Funds()
contractABI, err := abi.JSON(strings.NewReader(evmtest.ISCTestContractABI))
require.NoError(t, err)
_, _, contractAddress := env.DeployEVMContract(creator, contractABI, evmtest.ISCTestContractBytecode)

// make it so that 2 requests are included in the same block
tx1 := types.MustSignNewTx(creator, types.NewEIP155Signer(big.NewInt(int64(env.ChainID))),
&types.LegacyTx{
Nonce: env.NonceAt(creatorAddress),
To: &contractAddress,
Value: big.NewInt(123),
Gas: 100000,
GasPrice: big.NewInt(10000000000),
Data: lo.Must(contractABI.Pack("sendTo", common.Address{0x1}, big.NewInt(2))),
})

tx2 := types.MustSignNewTx(creator2, types.NewEIP155Signer(big.NewInt(int64(env.ChainID))),
&types.LegacyTx{
Nonce: env.NonceAt(creatorAddress2),
To: &contractAddress,
Value: big.NewInt(321),
Gas: 100000,
GasPrice: big.NewInt(10000000000),
Data: lo.Must(contractABI.Pack("sendTo", common.Address{0x2}, big.NewInt(3))),
})

req1 := lo.Must(isc.NewEVMOffLedgerTxRequest(env.soloChain.ChainID, tx1))
req2 := lo.Must(isc.NewEVMOffLedgerTxRequest(env.soloChain.ChainID, tx2))
env.soloChain.WaitForRequestsMark()
env.soloChain.Env.AddRequestsToMempool(env.soloChain, []isc.Request{req1, req2})
require.True(t, env.soloChain.WaitForRequestsThrough(2, 180*time.Second))

bi := env.soloChain.GetLatestBlockInfo()
require.EqualValues(t, 2, bi.NumSuccessfulRequests)

var res1 json.RawMessage
// we have to use the raw client, because the normal client does not support debug methods
err = env.RawClient.CallContext(
context.Background(),
&res1,
"debug_traceBlockByNumber",
env.BlockNumber(),
tracers.TraceConfig{TracerConfig: []byte(`{"tracer": "callTracer"}`)},
)
require.NoError(t, err)

var res2 json.RawMessage
// we have to use the raw client, because the normal client does not support debug methods
err = env.RawClient.CallContext(
context.Background(),
&res2,
"debug_traceBlockByHash",
env.BlockByNumber(big.NewInt(int64(env.BlockNumber()))).Hash(),
tracers.TraceConfig{TracerConfig: []byte(`{"tracer": "callTracer"}`)},
)
require.NoError(t, err)

require.Equal(t, res1, res2, "debug_traceBlockByNumber and debug_traceBlockByNumber should produce equal results")

traceBlock := make([]jsonrpc.TxTraceResult, 0)
err = json.Unmarshal(res1, &traceBlock)
require.NoError(t, err)

require.Len(t, traceBlock, 2)

trace1 := traceBlock[slices.IndexFunc(traceBlock, func(v jsonrpc.TxTraceResult) bool {
return v.TxHash == tx1.Hash()
})].Result

trace2 := traceBlock[slices.IndexFunc(traceBlock, func(v jsonrpc.TxTraceResult) bool {
return v.TxHash == tx2.Hash()
})].Result

require.Equal(t, creatorAddress, trace1.From)
require.Equal(t, contractAddress, *trace1.To)
require.Equal(t, big.NewInt(123), trace1.Value)
expectedInput, err := contractABI.Pack("sendTo", common.Address{0x1}, big.NewInt(2))
require.NoError(t, err)
require.Equal(t, expectedInput, trace1.Input)
require.Empty(t, trace1.Error)
require.Empty(t, trace1.RevertReason)

require.Len(t, trace1.Calls, 1)
innerCall1 := trace1.Calls[0]
require.Equal(t, contractAddress, innerCall1.From)
require.Equal(t, common.Address{0x1}, *innerCall1.To)
require.Equal(t, big.NewInt(2), innerCall1.Value)
require.Empty(t, innerCall1.Input)
require.Empty(t, innerCall1.Error)
require.Empty(t, innerCall1.RevertReason)

require.Equal(t, creatorAddress2, trace2.From)
require.Equal(t, contractAddress, *trace2.To)
require.Equal(t, big.NewInt(321), trace2.Value)
expectedInput, err = contractABI.Pack("sendTo", common.Address{0x2}, big.NewInt(3))
require.NoError(t, err)
require.Equal(t, expectedInput, trace2.Input)
require.Empty(t, trace2.Error)
require.Empty(t, trace2.RevertReason)

require.Len(t, trace2.Calls, 1)
innerCall2 := trace2.Calls[0]
require.Equal(t, contractAddress, innerCall2.From)
require.Equal(t, common.Address{0x2}, *innerCall2.To)
require.Equal(t, big.NewInt(3), innerCall2.Value)
require.Empty(t, innerCall2.Input)
require.Empty(t, innerCall2.Error)
require.Empty(t, innerCall2.RevertReason)
}

func BenchmarkRPCEstimateGas(b *testing.B) {
Expand Down
23 changes: 23 additions & 0 deletions packages/evm/jsonrpc/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -451,6 +451,17 @@ func (e *EthService) Logs(ctx context.Context, q *RPCFilterQuery) (*rpc.Subscrip
return rpcSub, nil
}

func (e *EthService) GetBlockReceipts(blockNumber rpc.BlockNumber) ([]*types.Receipt, error) {
return withMetrics(e.metrics, "eth_getBlockReceipts", func() ([]*types.Receipt, error) {
receipts, err := e.evmChain.GetBlockReceipts(blockNumber)
if err != nil {
return nil, e.resolveError(err)
}

return receipts, nil
})
}

/*
Not implemented:
func (e *EthService) NewFilter()
Expand Down Expand Up @@ -543,6 +554,18 @@ func (d *DebugService) TraceTransaction(txHash common.Hash, config *tracers.Trac
})
}

func (d *DebugService) TraceBlockByNumber(blockNumber uint64, config *tracers.TraceConfig) (interface{}, error) {
return withMetrics(d.metrics, "debug_traceBlockByNumber", func() (interface{}, error) {
return d.evmChain.TraceBlockByNumber(blockNumber, config)
})
}

func (d *DebugService) TraceBlockByHash(blockHash common.Hash, config *tracers.TraceConfig) (interface{}, error) {
return withMetrics(d.metrics, "debug_traceBlockByHash", func() (interface{}, error) {
return d.evmChain.TraceBlockByHash(blockHash, config)
})
}

type EVMService struct {
evmChain *EVMChain
}
Expand Down
Loading
Loading