-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
This request is extending the devnet functionality to more fully handle contract processing by adding support for the following calls: * trace_call, * trace_transaction * debug_accountAt, * eth_getCode * eth_estimateGas * eth_gasPrice It also contains an initial rationalization of the devnet subscription code to use the erigon client code directly rather than using its own intermediate subscription management. This is used to implement a general purpose block waiter - which can be used in any scenario step - rather than being specific to transaction processing. This pull also contains an end to end tested sync processor for bor and associated support services: * Heimdall (supports sync event transfer) * Faucet - allows the creation and funding of arbitary test specific accounts (cross chain) Notes and Caveats: * Code generation for contracts requires `--evm-version paris`. For chains which don't support push0 for solc over 0.8.19 * The bor log processing post the application of sync events causes a panic - this will be the subject of a seperate smaller push as it is not devnet specific * The bor code seems to make repeated calls for the same sync events and also reverts requests - this needs further investigation. This is the behaviour of the current implementation and may be required - although it does seem to generate repeat processing - which could be avoided.
- Loading branch information
Showing
43 changed files
with
1,929 additions
and
451 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
package blocks | ||
|
||
import ( | ||
"context" | ||
"fmt" | ||
|
||
libcommon "github.com/ledgerwatch/erigon-lib/common" | ||
"github.com/ledgerwatch/erigon/cmd/devnet/devnet" | ||
"github.com/ledgerwatch/erigon/cmd/devnet/requests" | ||
) | ||
|
||
var CompletionChecker = BlockHandlerFunc( | ||
func(ctx context.Context, node devnet.Node, block *requests.BlockResult, transaction *requests.Transaction) error { | ||
transactionHash := libcommon.HexToHash(transaction.Hash) | ||
traceResults, err := node.TraceTransaction(transactionHash) | ||
|
||
if err != nil { | ||
return fmt.Errorf("Failed to trace transaction: %s: %w", transaction.Hash, err) | ||
} | ||
|
||
for _, traceResult := range traceResults { | ||
if traceResult.TransactionHash == transactionHash { | ||
if len(traceResult.Error) != 0 { | ||
return fmt.Errorf("Transaction error: %s", traceResult.Error) | ||
} | ||
|
||
break | ||
} | ||
} | ||
|
||
return nil | ||
}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
package blocks | ||
|
||
import ( | ||
"context" | ||
"fmt" | ||
|
||
"github.com/ledgerwatch/erigon/cmd/devnet/devnet" | ||
"github.com/ledgerwatch/erigon/cmd/devnet/devnetutils" | ||
) | ||
|
||
func BaseFeeFromBlock(ctx context.Context) (uint64, error) { | ||
var val uint64 | ||
res, err := devnet.SelectNode(ctx).GetBlockDetailsByNumber("latest", false) | ||
if err != nil { | ||
return 0, fmt.Errorf("failed to get base fee from block: %v\n", err) | ||
} | ||
|
||
if v, ok := res["baseFeePerGas"]; !ok { | ||
return val, fmt.Errorf("baseFeePerGas field missing from response") | ||
} else { | ||
val = devnetutils.HexToInt(v.(string)) | ||
} | ||
|
||
return val, err | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,171 @@ | ||
package blocks | ||
|
||
import ( | ||
"context" | ||
|
||
ethereum "github.com/ledgerwatch/erigon" | ||
libcommon "github.com/ledgerwatch/erigon-lib/common" | ||
"github.com/ledgerwatch/erigon/cmd/devnet/devnet" | ||
"github.com/ledgerwatch/erigon/cmd/devnet/requests" | ||
"github.com/ledgerwatch/erigon/core/types" | ||
"github.com/ledgerwatch/log/v3" | ||
) | ||
|
||
type BlockHandler interface { | ||
Handle(ctx context.Context, node devnet.Node, block *requests.BlockResult, transaction *requests.Transaction) error | ||
} | ||
|
||
type BlockHandlerFunc func(ctx context.Context, node devnet.Node, block *requests.BlockResult, transaction *requests.Transaction) error | ||
|
||
func (f BlockHandlerFunc) Handle(ctx context.Context, node devnet.Node, block *requests.BlockResult, transaction *requests.Transaction) error { | ||
return f(ctx, node, block, transaction) | ||
} | ||
|
||
type BlockMap map[libcommon.Hash]*requests.BlockResult | ||
|
||
type waitResult struct { | ||
err error | ||
blockMap BlockMap | ||
} | ||
|
||
type blockWaiter struct { | ||
result chan waitResult | ||
hashes chan map[libcommon.Hash]struct{} | ||
waitHashes map[libcommon.Hash]struct{} | ||
headersSub ethereum.Subscription | ||
handler BlockHandler | ||
logger log.Logger | ||
} | ||
|
||
type Waiter interface { | ||
Await(libcommon.Hash) (*requests.BlockResult, error) | ||
AwaitMany(...libcommon.Hash) (BlockMap, error) | ||
} | ||
|
||
type waitError struct { | ||
err error | ||
} | ||
|
||
func (w waitError) Await(libcommon.Hash) (*requests.BlockResult, error) { | ||
return nil, w.err | ||
} | ||
|
||
func (w waitError) AwaitMany(...libcommon.Hash) (BlockMap, error) { | ||
return nil, w.err | ||
} | ||
|
||
type wait struct { | ||
waiter *blockWaiter | ||
} | ||
|
||
func (w wait) Await(hash libcommon.Hash) (*requests.BlockResult, error) { | ||
w.waiter.hashes <- map[libcommon.Hash]struct{}{hash: {}} | ||
res := <-w.waiter.result | ||
|
||
if len(res.blockMap) > 0 { | ||
for _, block := range res.blockMap { | ||
return block, res.err | ||
} | ||
} | ||
|
||
return nil, res.err | ||
} | ||
|
||
func (w wait) AwaitMany(hashes ...libcommon.Hash) (BlockMap, error) { | ||
if len(hashes) == 0 { | ||
return nil, nil | ||
} | ||
|
||
hashMap := map[libcommon.Hash]struct{}{} | ||
|
||
for _, hash := range hashes { | ||
hashMap[hash] = struct{}{} | ||
} | ||
|
||
w.waiter.hashes <- hashMap | ||
|
||
res := <-w.waiter.result | ||
return res.blockMap, res.err | ||
} | ||
|
||
func BlockWaiter(ctx context.Context, handler BlockHandler) (Waiter, context.CancelFunc) { | ||
ctx, cancel := context.WithCancel(ctx) | ||
|
||
node := devnet.SelectBlockProducer(ctx) | ||
|
||
waiter := &blockWaiter{ | ||
result: make(chan waitResult, 1), | ||
hashes: make(chan map[libcommon.Hash]struct{}, 1), | ||
handler: handler, | ||
logger: devnet.Logger(ctx), | ||
} | ||
|
||
var err error | ||
|
||
headers := make(chan types.Header) | ||
waiter.headersSub, err = node.Subscribe(ctx, requests.Methods.ETHNewHeads, headers) | ||
|
||
if err != nil { | ||
defer close(waiter.result) | ||
return waitError{err}, cancel | ||
} | ||
|
||
go waiter.receive(ctx, node, headers) | ||
|
||
return wait{waiter}, cancel | ||
} | ||
|
||
func (c *blockWaiter) receive(ctx context.Context, node devnet.Node, headers chan types.Header) { | ||
blockMap := map[libcommon.Hash]*requests.BlockResult{} | ||
|
||
defer close(c.result) | ||
|
||
for header := range headers { | ||
select { | ||
case <-ctx.Done(): | ||
c.headersSub.Unsubscribe() | ||
c.result <- waitResult{blockMap: blockMap, err: ctx.Err()} | ||
return | ||
default: | ||
} | ||
|
||
blockNum := header.Number | ||
|
||
block, err := node.GetBlockByNumber(blockNum.Uint64(), true) | ||
|
||
if err != nil { | ||
c.logger.Error("Block waiter failed to get block", "err", err) | ||
continue | ||
} | ||
|
||
if len(block.Transactions) > 0 && c.waitHashes == nil { | ||
c.waitHashes = <-c.hashes | ||
} | ||
|
||
for i := range block.Transactions { | ||
tx := &block.Transactions[i] // avoid implicit memory aliasing | ||
|
||
txHash := libcommon.HexToHash(tx.Hash) | ||
|
||
if _, ok := c.waitHashes[txHash]; ok { | ||
c.logger.Info("Tx included into block", "txHash", txHash, "blockNum", block.BlockNumber) | ||
blockMap[txHash] = block | ||
delete(c.waitHashes, txHash) | ||
|
||
if len(c.waitHashes) == 0 { | ||
c.headersSub.Unsubscribe() | ||
res := waitResult{ | ||
err: c.handler.Handle(ctx, node, block, tx), | ||
} | ||
|
||
if res.err == nil { | ||
res.blockMap = blockMap | ||
} | ||
|
||
c.result <- res | ||
return | ||
} | ||
} | ||
} | ||
} | ||
} |
Oops, something went wrong.