Skip to content

Commit 8e678b7

Browse files
envestccLiuhaai
andauthored
multiple fixes for archive node (#4698)
Co-authored-by: Liuhaai <haixiang@iotex.io>
1 parent ef0d25d commit 8e678b7

File tree

7 files changed

+202
-22
lines changed

7 files changed

+202
-22
lines changed

api/web3server.go

Lines changed: 84 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -229,6 +229,8 @@ func (svr *web3Handler) handleWeb3Req(ctx context.Context, web3Req *gjson.Result
229229
res, err = svr.getBlockTransactionCountByNumber(web3Req)
230230
case "eth_getTransactionReceipt":
231231
res, err = svr.getTransactionReceipt(web3Req)
232+
case "eth_getBlockReceipts":
233+
res, err = svr.getBlockReceipts(web3Req)
232234
case "eth_getStorageAt":
233235
res, err = svr.getStorageAt(web3Req)
234236
case "eth_getFilterLogs":
@@ -418,11 +420,11 @@ func (svr *web3Handler) getBalance(in *gjson.Result) (interface{}, error) {
418420
return nil, err
419421
}
420422
bnParam := in.Get("params.1")
421-
bn, err := parseBlockNumber(&bnParam)
423+
bn, err := parseBlockNumberOrHash(&bnParam)
422424
if err != nil {
423425
return nil, err
424426
}
425-
height, _, err := svr.blockNumberOrHashToHeight(rpc.BlockNumberOrHashWithNumber(bn))
427+
height, _, err := svr.blockNumberOrHashToHeight(bn)
426428
if err != nil {
427429
return nil, err
428430
}
@@ -651,11 +653,11 @@ func (svr *web3Handler) getCode(in *gjson.Result) (interface{}, error) {
651653
return nil, err
652654
}
653655
bnParam := in.Get("params.1")
654-
bn, err := parseBlockNumber(&bnParam)
656+
bn, err := parseBlockNumberOrHash(&bnParam)
655657
if err != nil {
656658
return nil, err
657659
}
658-
height, _, err := svr.blockNumberOrHashToHeight(rpc.BlockNumberOrHashWithNumber(bn))
660+
height, _, err := svr.blockNumberOrHashToHeight(bn)
659661
if err != nil {
660662
return nil, err
661663
}
@@ -868,6 +870,80 @@ func (svr *web3Handler) getTransactionReceipt(in *gjson.Result) (interface{}, er
868870

869871
}
870872

873+
func (svr *web3Handler) getBlockReceipts(in *gjson.Result) (interface{}, error) {
874+
// parse block parameter from request
875+
blkParam := in.Get("params.0")
876+
if !blkParam.Exists() {
877+
return nil, errInvalidFormat
878+
}
879+
bn, err := parseBlockNumberOrHash(&blkParam)
880+
if err != nil {
881+
return nil, err
882+
}
883+
height, _, err := svr.blockNumberOrHashToHeight(bn)
884+
if err != nil {
885+
return nil, err
886+
}
887+
888+
blk, err := svr.coreService.BlockByHeight(height)
889+
if err != nil {
890+
if errors.Cause(err) == ErrNotFound {
891+
return nil, nil
892+
}
893+
return nil, err
894+
}
895+
896+
// Build receipt array for all transactions in the block
897+
receipts := make([]*getReceiptResult, 0, len(blk.Block.Actions))
898+
blockHash := blk.Block.HashBlock()
899+
900+
// Get logs bloom filter
901+
var logsBloomStr string
902+
if logsBloom := blk.Block.LogsBloomfilter(); logsBloom != nil {
903+
logsBloomStr = hex.EncodeToString(logsBloom.Bytes())
904+
}
905+
906+
// Process each transaction in the block
907+
for i, selp := range blk.Block.Actions {
908+
receipt := blk.Receipts[i]
909+
910+
// Convert action to ethereum transaction to get type
911+
tx, err := selp.ToEthTx()
912+
if err != nil {
913+
if errors.Is(err, errUnsupportedAction) || errors.Is(err, action.ErrInvalidAct) {
914+
txHash, _ := selp.Hash()
915+
log.Logger("api").Debug("getBlockReceipts: unsupported action type",
916+
zap.String("action", fmt.Sprintf("%T", selp.Action())),
917+
log.Hex("actionHash", txHash[:]),
918+
zap.Int("index", i))
919+
continue // Skip unsupported actions
920+
}
921+
return nil, err
922+
}
923+
924+
// Get recipient and contract address
925+
to, contractAddr, err := getRecipientAndContractAddrFromAction(selp, receipt)
926+
if err != nil {
927+
return nil, err
928+
}
929+
930+
// Create receipt result
931+
receiptResult := &getReceiptResult{
932+
blockHash: blockHash,
933+
from: selp.SenderAddress(),
934+
to: to,
935+
contractAddress: contractAddr,
936+
logsBloom: logsBloomStr,
937+
receipt: receipt,
938+
txType: uint(tx.Type()),
939+
}
940+
941+
receipts = append(receipts, receiptResult)
942+
}
943+
944+
return receipts, nil
945+
}
946+
871947
func (svr *web3Handler) getBlockTransactionCountByNumber(in *gjson.Result) (interface{}, error) {
872948
blkNum := in.Get("params.0")
873949
if !blkNum.Exists() {
@@ -940,11 +1016,11 @@ func (svr *web3Handler) getStorageAt(in *gjson.Result) (interface{}, error) {
9401016
return nil, errInvalidFormat
9411017
}
9421018
bnParam := in.Get("params.2")
943-
bn, err := parseBlockNumber(&bnParam)
1019+
bn, err := parseBlockNumberOrHash(&bnParam)
9441020
if err != nil {
9451021
return nil, err
9461022
}
947-
height, _, err := svr.blockNumberOrHashToHeight(rpc.BlockNumberOrHashWithNumber(bn))
1023+
height, _, err := svr.blockNumberOrHashToHeight(bn)
9481024
if err != nil {
9491025
return nil, err
9501026
}
@@ -1238,11 +1314,11 @@ func (svr *web3Handler) traceCall(ctx context.Context, in *gjson.Result) (interf
12381314

12391315
func (svr *web3Handler) traceBlockByNumber(ctx context.Context, in *gjson.Result) (any, error) {
12401316
blkParam, tracerParam := in.Get("params.0"), in.Get("params.1")
1241-
blkNum, err := parseBlockNumber(&blkParam)
1317+
blkNum, err := parseBlockNumberOrHash(&blkParam)
12421318
if err != nil {
12431319
return nil, errors.Wrap(err, "failed to parse block number")
12441320
}
1245-
height, _, err := svr.blockNumberOrHashToHeight(rpc.BlockNumberOrHashWithNumber(blkNum))
1321+
height, _, err := svr.blockNumberOrHashToHeight(blkNum)
12461322
if err != nil {
12471323
return nil, err
12481324
}

api/web3server_integrity_test.go

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,10 @@ func TestWeb3ServerIntegrity(t *testing.T) {
166166
t.Run("eth_blobBaseFee", func(t *testing.T) {
167167
blobBaseFee(t, handler, bc, dao, actPool)
168168
})
169+
170+
t.Run("eth_getBlockReceipts", func(t *testing.T) {
171+
getBlockReceipts(t, handler, bc)
172+
})
169173
}
170174

171175
func setupTestServer() (*ServerV2, blockchain.Blockchain, blockdao.BlockDAO, actpool.ActPool, func()) {
@@ -880,3 +884,39 @@ func blobBaseFee(t *testing.T, handler *hTTPHandler, bc blockchain.Blockchain, d
880884
require.Equal("0x1", actual)
881885
}
882886
}
887+
888+
func getBlockReceipts(t *testing.T, handler *hTTPHandler, bc blockchain.Blockchain) {
889+
require := require.New(t)
890+
header, err := bc.BlockHeaderByHeight(1)
891+
require.NoError(err)
892+
blkHash := header.HashBlock()
893+
894+
for _, test := range []struct {
895+
params string
896+
expected int
897+
}{
898+
{fmt.Sprintf(`["0x%s"]`, hex.EncodeToString(blkHash[:])), 2},
899+
{`["0xa2e8e0c9cafbe93f2b7f7c9d32534bc6fde95f2185e5f2aaa6bf7ebdf1a6610a"]`, 0},
900+
} {
901+
result := serveTestHTTP(require, handler, "eth_getBlockReceipts", test.params)
902+
if test.expected == 0 {
903+
require.Nil(result)
904+
continue
905+
}
906+
actual, ok := result.([]interface{})
907+
require.True(ok, "Expected result to be []interface{}, got %T", result)
908+
require.Equal(test.expected, len(actual))
909+
actualByte, err := json.Marshal(result)
910+
require.NoError(err)
911+
require.JSONEq(fmt.Sprintf(`[
912+
{
913+
"blockHash":"0x%s", "blockNumber":"0x1", "contractAddress":null, "effectiveGasPrice":null, "cumulativeGasUsed":"0x2710", "from":"0xda7e12ef57c236a06117c5e0d04a228e7181cf36", "gasUsed":"0x2710", "logs":[], "logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "status":"0x1", "to":"0x6d714f86B9ED7D4aB8958950A594BccF9C327283", "transactionHash":"0x20451c75036f0e092c8bdd4d2472b71ec1d8c83d0c11228b2617e6e8f225ce8b", "transactionIndex":"0x0", "type":"0x0"
914+
},
915+
{
916+
"blockHash":"0x%s", "blockNumber":"0x1", "contractAddress":null, "effectiveGasPrice":null, "cumulativeGasUsed":"0x0", "from":"0x97186a21fa8e7955c0f154f960d588c3aca44f14", "gasUsed":"0x0",
917+
"logs": [{"address":"0xA576C141e5659137ddDa4223d209d4744b2106BE", "blockHash":"0x%s", "blockNumber":"0x1", "data":"0x1229696f316a757678356730363365753474733833326e756b7034766763776b32676e63356375396179641a143136303030303030303030303030303030303030", "logIndex":"0x0", "removed":false, "topics":[], "transactionHash":"0xe0c62efd2eff70c4de294cc8a8c9dba3ac43b998aff604dd70c4f99272567bbd", "transactionIndex":"0x0"}],
918+
"logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "status":"0x1", "to":"0xA576C141e5659137ddDa4223d209d4744b2106BE", "transactionHash":"0xe0c62efd2eff70c4de294cc8a8c9dba3ac43b998aff604dd70c4f99272567bbd", "transactionIndex":"0x0", "type":"0x0"
919+
}
920+
]`, hex.EncodeToString(blkHash[:]), hex.EncodeToString(blkHash[:]), hex.EncodeToString(blkHash[:])), string(actualByte))
921+
}
922+
}

api/web3server_utils.go

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -374,7 +374,7 @@ func parseCallObject(in *gjson.Result) (*callMsg, error) {
374374

375375
if accessList := in.Get("params.0.accessList"); accessList.Exists() {
376376
acl = types.AccessList{}
377-
log.L().Info("raw acl", zap.String("accessList", accessList.Raw))
377+
log.L().Debug("raw acl", zap.String("accessList", accessList.Raw))
378378
if err := json.Unmarshal([]byte(accessList.Raw), &acl); err != nil {
379379
return nil, errors.Wrapf(err, "failed to unmarshal access list %s", accessList.Raw)
380380
}
@@ -398,14 +398,13 @@ func parseCallObject(in *gjson.Result) (*callMsg, error) {
398398
}, nil
399399
}
400400

401-
// TODO: fix this to support eip 1898
402-
func parseBlockNumber(in *gjson.Result) (rpc.BlockNumber, error) {
401+
func parseBlockNumberOrHash(in *gjson.Result) (rpc.BlockNumberOrHash, error) {
403402
if !in.Exists() {
404-
return rpc.LatestBlockNumber, nil
403+
return rpc.BlockNumberOrHashWithNumber(rpc.LatestBlockNumber), nil
405404
}
406-
var height rpc.BlockNumber
407-
if err := height.UnmarshalJSON([]byte(in.String())); err != nil {
408-
return 0, err
405+
var height rpc.BlockNumberOrHash
406+
if err := height.UnmarshalJSON([]byte(in.Raw)); err != nil {
407+
return height, err
409408
}
410409
return height, nil
411410
}

blockindex/sync_indexers.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,11 @@ package blockindex
88
import (
99
"context"
1010

11+
"go.uber.org/zap"
12+
1113
"github.com/iotexproject/iotex-core/v2/blockchain/block"
1214
"github.com/iotexproject/iotex-core/v2/blockchain/blockdao"
15+
"github.com/iotexproject/iotex-core/v2/pkg/log"
1316
)
1417

1518
// SyncIndexers is a special index that includes multiple indexes,
@@ -106,6 +109,11 @@ func (ig *SyncIndexers) initStartHeight() error {
106109
indexStartHeight = startHeight
107110
}
108111
}
112+
log.L().Info("sync indexers",
113+
zap.Uint64("indexer", uint64(i)),
114+
zap.Uint64("tipHeight", tipHeight),
115+
zap.Uint64("startHeight", indexStartHeight),
116+
)
109117
ig.startHeights[i] = indexStartHeight
110118
if i == 0 || indexStartHeight < ig.minStartHeight {
111119
ig.minStartHeight = indexStartHeight

state/factory/statedb.go

Lines changed: 33 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -131,10 +131,17 @@ func (sdb *stateDB) Start(ctx context.Context) error {
131131
if err := sdb.dao.Start(ctx); err != nil {
132132
return err
133133
}
134+
erigonHeight := uint64(0)
134135
if sdb.erigonDB != nil {
135136
if err := sdb.erigonDB.Start(ctx); err != nil {
136137
return err
137138
}
139+
eh, err := sdb.erigonDB.Height()
140+
if err != nil {
141+
return errors.Wrap(err, "failed to get erigonDB height")
142+
}
143+
erigonHeight = eh
144+
log.L().Info("ErigonDB started", zap.Uint64("height", erigonHeight))
138145
}
139146
// check factory height
140147
h, err := sdb.dao.getHeight()
@@ -169,6 +176,18 @@ func (sdb *stateDB) Start(ctx context.Context) error {
169176
default:
170177
return err
171178
}
179+
if sdb.erigonDB != nil {
180+
// allow erigonDB to be 1 height ahead of state factory
181+
if erigonHeight != sdb.currentChainHeight && erigonHeight != sdb.currentChainHeight+1 {
182+
return errors.Errorf(
183+
"erigonDB height %d does not match state factory height %d",
184+
erigonHeight, sdb.currentChainHeight,
185+
)
186+
}
187+
}
188+
log.L().Info("State factory started",
189+
zap.Uint64("height", sdb.currentChainHeight),
190+
)
172191
return nil
173192
}
174193

@@ -264,6 +283,7 @@ func (sdb *stateDB) Validate(ctx context.Context, blk *block.Block) error {
264283
}
265284
if !isExist {
266285
if err = ws.ValidateBlock(ctx, blk); err != nil {
286+
ws.Close()
267287
return errors.Wrap(err, "failed to validate block with workingset in statedb")
268288
}
269289
if existed := sdb.addWorkingSetIfNotExist(blkHash, ws); existed != nil {
@@ -272,6 +292,7 @@ func (sdb *stateDB) Validate(ctx context.Context, blk *block.Block) error {
272292
}
273293
receipts, err := ws.Receipts()
274294
if err != nil {
295+
ws.Close()
275296
return err
276297
}
277298
blk.Receipts = receipts
@@ -336,7 +357,7 @@ func (sdb *stateDB) WorkingSet(ctx context.Context) (protocol.StateManagerWithCl
336357
}
337358

338359
func (sdb *stateDB) WorkingSetAtTransaction(ctx context.Context, height uint64, acts ...*action.SealedEnvelope) (protocol.StateManagerWithCloser, error) {
339-
ws, err := sdb.newReadOnlyWorkingSet(ctx, height)
360+
ws, err := sdb.newReadOnlyWorkingSet(ctx, height-1)
340361
if err != nil {
341362
return nil, err
342363
}
@@ -347,7 +368,17 @@ func (sdb *stateDB) WorkingSetAtTransaction(ctx context.Context, height uint64,
347368
}
348369
ws.store = newErigonWorkingSetStoreForSimulate(ws.store, e)
349370
}
371+
// handle panic to ensure workingset is closed
372+
defer func() {
373+
if r := recover(); r != nil {
374+
ws.Close()
375+
err = errors.Errorf("panic occurred while processing actions: %v", r)
376+
log.L().Error("Recovered from panic in WorkingSetAtTransaction", zap.Error(err))
377+
}
378+
}()
379+
ws.height++
350380
if err := ws.Process(ctx, acts); err != nil {
381+
ws.Close()
351382
return nil, err
352383
}
353384
return ws, nil
@@ -383,6 +414,7 @@ func (sdb *stateDB) PutBlock(ctx context.Context, blk *block.Block) error {
383414
if err != nil {
384415
return err
385416
}
417+
defer ws.Close()
386418
if !isExist {
387419
if !sdb.skipBlockValidationOnPut {
388420
err = ws.ValidateBlock(ctx, blk)
@@ -391,7 +423,6 @@ func (sdb *stateDB) PutBlock(ctx context.Context, blk *block.Block) error {
391423
}
392424
if err != nil {
393425
log.L().Error("Failed to update state.", zap.Error(err))
394-
ws.Close()
395426
return err
396427
}
397428
}

state/factory/workingsetstore_erigon.go

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -163,7 +163,9 @@ func (store *erigonWorkingSetStore) prepareCommit(ctx context.Context, tx kv.RwT
163163

164164
func (store *erigonWorkingSetStore) Commit(ctx context.Context) error {
165165
defer store.tx.Rollback()
166-
tx, err := store.db.rw.BeginRw(ctx)
166+
// BeginRw accounting for the context Done signal
167+
// statedb has been committed, so we should not use the context
168+
tx, err := store.db.rw.BeginRw(context.Background())
167169
if err != nil {
168170
return errors.Wrap(err, "failed to begin erigon working set store transaction")
169171
}
@@ -267,3 +269,24 @@ func (store *erigonWorkingSetStore) States(string, [][]byte) ([][]byte, [][]byte
267269
func (store *erigonWorkingSetStore) Digest() hash.Hash256 {
268270
return hash.ZeroHash256
269271
}
272+
273+
func (store *erigonDB) Height() (uint64, error) {
274+
var height uint64
275+
err := store.rw.View(context.Background(), func(tx kv.Tx) error {
276+
heightBytes, err := tx.GetOne(systemNS, heightKey)
277+
if err != nil {
278+
return errors.Wrap(err, "failed to get height from erigon working set store")
279+
}
280+
if len(heightBytes) == 0 {
281+
return nil // height not set yet
282+
}
283+
height256 := new(uint256.Int)
284+
height256.SetBytes(heightBytes)
285+
height = height256.Uint64()
286+
return nil
287+
})
288+
if err != nil {
289+
return 0, errors.Wrap(err, "failed to get height from erigon working set store")
290+
}
291+
return height, nil
292+
}

0 commit comments

Comments
 (0)