mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-07-23 13:16:42 +00:00
Merge branch 'main' into run-ci-on-release-branches
This commit is contained in:
commit
95405a2bb3
198 changed files with 10249 additions and 2213 deletions
23
.github/workflows/validate_pr.yml
vendored
Normal file
23
.github/workflows/validate_pr.yml
vendored
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
name: PR Format Validation
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
types: [opened, edited, synchronize]
|
||||
|
||||
jobs:
|
||||
validate-pr:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Check PR Title Format
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
script: |
|
||||
const prTitle = context.payload.pull_request.title;
|
||||
const titleRegex = /^(\.?[\w\s,{}/]+): .+/;
|
||||
|
||||
if (!titleRegex.test(prTitle)) {
|
||||
core.setFailed(`PR title "${prTitle}" does not match required format: directory, ...: description`);
|
||||
return;
|
||||
}
|
||||
|
||||
console.log('✅ PR title format is valid');
|
||||
|
|
@ -8,6 +8,7 @@ https://pkg.go.dev/badge/github.com/ethereum/go-ethereum
|
|||
[](https://goreportcard.com/report/github.com/ethereum/go-ethereum)
|
||||
[](https://app.travis-ci.com/github/ethereum/go-ethereum)
|
||||
[](https://discord.gg/nthXNEv)
|
||||
[](https://x.com/go_ethereum)
|
||||
|
||||
Automated builds are available for stable releases and the unstable master branch. Binary
|
||||
archives are published at https://geth.ethereum.org/downloads/.
|
||||
|
|
|
|||
|
|
@ -183,7 +183,7 @@ var (
|
|||
// Solidity: {{.Original.String}}
|
||||
func ({{ decapitalise $contract.Type}} *{{$contract.Type}}) Unpack{{.Normalized.Name}}Event(log *types.Log) (*{{$contract.Type}}{{.Normalized.Name}}, error) {
|
||||
event := "{{.Original.Name}}"
|
||||
if log.Topics[0] != {{ decapitalise $contract.Type}}.abi.Events[event].ID {
|
||||
if len(log.Topics) == 0 || log.Topics[0] != {{ decapitalise $contract.Type}}.abi.Events[event].ID {
|
||||
return nil, errors.New("event signature mismatch")
|
||||
}
|
||||
out := new({{$contract.Type}}{{.Normalized.Name}})
|
||||
|
|
|
|||
|
|
@ -360,7 +360,7 @@ func (CrowdsaleFundTransfer) ContractEventName() string {
|
|||
// Solidity: event FundTransfer(address backer, uint256 amount, bool isContribution)
|
||||
func (crowdsale *Crowdsale) UnpackFundTransferEvent(log *types.Log) (*CrowdsaleFundTransfer, error) {
|
||||
event := "FundTransfer"
|
||||
if log.Topics[0] != crowdsale.abi.Events[event].ID {
|
||||
if len(log.Topics) == 0 || log.Topics[0] != crowdsale.abi.Events[event].ID {
|
||||
return nil, errors.New("event signature mismatch")
|
||||
}
|
||||
out := new(CrowdsaleFundTransfer)
|
||||
|
|
|
|||
10
accounts/abi/abigen/testdata/v2/dao.go.txt
vendored
10
accounts/abi/abigen/testdata/v2/dao.go.txt
vendored
|
|
@ -606,7 +606,7 @@ func (DAOChangeOfRules) ContractEventName() string {
|
|||
// Solidity: event ChangeOfRules(uint256 minimumQuorum, uint256 debatingPeriodInMinutes, int256 majorityMargin)
|
||||
func (dAO *DAO) UnpackChangeOfRulesEvent(log *types.Log) (*DAOChangeOfRules, error) {
|
||||
event := "ChangeOfRules"
|
||||
if log.Topics[0] != dAO.abi.Events[event].ID {
|
||||
if len(log.Topics) == 0 || log.Topics[0] != dAO.abi.Events[event].ID {
|
||||
return nil, errors.New("event signature mismatch")
|
||||
}
|
||||
out := new(DAOChangeOfRules)
|
||||
|
|
@ -648,7 +648,7 @@ func (DAOMembershipChanged) ContractEventName() string {
|
|||
// Solidity: event MembershipChanged(address member, bool isMember)
|
||||
func (dAO *DAO) UnpackMembershipChangedEvent(log *types.Log) (*DAOMembershipChanged, error) {
|
||||
event := "MembershipChanged"
|
||||
if log.Topics[0] != dAO.abi.Events[event].ID {
|
||||
if len(log.Topics) == 0 || log.Topics[0] != dAO.abi.Events[event].ID {
|
||||
return nil, errors.New("event signature mismatch")
|
||||
}
|
||||
out := new(DAOMembershipChanged)
|
||||
|
|
@ -692,7 +692,7 @@ func (DAOProposalAdded) ContractEventName() string {
|
|||
// Solidity: event ProposalAdded(uint256 proposalID, address recipient, uint256 amount, string description)
|
||||
func (dAO *DAO) UnpackProposalAddedEvent(log *types.Log) (*DAOProposalAdded, error) {
|
||||
event := "ProposalAdded"
|
||||
if log.Topics[0] != dAO.abi.Events[event].ID {
|
||||
if len(log.Topics) == 0 || log.Topics[0] != dAO.abi.Events[event].ID {
|
||||
return nil, errors.New("event signature mismatch")
|
||||
}
|
||||
out := new(DAOProposalAdded)
|
||||
|
|
@ -736,7 +736,7 @@ func (DAOProposalTallied) ContractEventName() string {
|
|||
// Solidity: event ProposalTallied(uint256 proposalID, int256 result, uint256 quorum, bool active)
|
||||
func (dAO *DAO) UnpackProposalTalliedEvent(log *types.Log) (*DAOProposalTallied, error) {
|
||||
event := "ProposalTallied"
|
||||
if log.Topics[0] != dAO.abi.Events[event].ID {
|
||||
if len(log.Topics) == 0 || log.Topics[0] != dAO.abi.Events[event].ID {
|
||||
return nil, errors.New("event signature mismatch")
|
||||
}
|
||||
out := new(DAOProposalTallied)
|
||||
|
|
@ -780,7 +780,7 @@ func (DAOVoted) ContractEventName() string {
|
|||
// Solidity: event Voted(uint256 proposalID, bool position, address voter, string justification)
|
||||
func (dAO *DAO) UnpackVotedEvent(log *types.Log) (*DAOVoted, error) {
|
||||
event := "Voted"
|
||||
if log.Topics[0] != dAO.abi.Events[event].ID {
|
||||
if len(log.Topics) == 0 || log.Topics[0] != dAO.abi.Events[event].ID {
|
||||
return nil, errors.New("event signature mismatch")
|
||||
}
|
||||
out := new(DAOVoted)
|
||||
|
|
|
|||
|
|
@ -72,7 +72,7 @@ func (EventCheckerDynamic) ContractEventName() string {
|
|||
// Solidity: event dynamic(string indexed idxStr, bytes indexed idxDat, string str, bytes dat)
|
||||
func (eventChecker *EventChecker) UnpackDynamicEvent(log *types.Log) (*EventCheckerDynamic, error) {
|
||||
event := "dynamic"
|
||||
if log.Topics[0] != eventChecker.abi.Events[event].ID {
|
||||
if len(log.Topics) == 0 || log.Topics[0] != eventChecker.abi.Events[event].ID {
|
||||
return nil, errors.New("event signature mismatch")
|
||||
}
|
||||
out := new(EventCheckerDynamic)
|
||||
|
|
@ -112,7 +112,7 @@ func (EventCheckerEmpty) ContractEventName() string {
|
|||
// Solidity: event empty()
|
||||
func (eventChecker *EventChecker) UnpackEmptyEvent(log *types.Log) (*EventCheckerEmpty, error) {
|
||||
event := "empty"
|
||||
if log.Topics[0] != eventChecker.abi.Events[event].ID {
|
||||
if len(log.Topics) == 0 || log.Topics[0] != eventChecker.abi.Events[event].ID {
|
||||
return nil, errors.New("event signature mismatch")
|
||||
}
|
||||
out := new(EventCheckerEmpty)
|
||||
|
|
@ -154,7 +154,7 @@ func (EventCheckerIndexed) ContractEventName() string {
|
|||
// Solidity: event indexed(address indexed addr, int256 indexed num)
|
||||
func (eventChecker *EventChecker) UnpackIndexedEvent(log *types.Log) (*EventCheckerIndexed, error) {
|
||||
event := "indexed"
|
||||
if log.Topics[0] != eventChecker.abi.Events[event].ID {
|
||||
if len(log.Topics) == 0 || log.Topics[0] != eventChecker.abi.Events[event].ID {
|
||||
return nil, errors.New("event signature mismatch")
|
||||
}
|
||||
out := new(EventCheckerIndexed)
|
||||
|
|
@ -196,7 +196,7 @@ func (EventCheckerMixed) ContractEventName() string {
|
|||
// Solidity: event mixed(address indexed addr, int256 num)
|
||||
func (eventChecker *EventChecker) UnpackMixedEvent(log *types.Log) (*EventCheckerMixed, error) {
|
||||
event := "mixed"
|
||||
if log.Topics[0] != eventChecker.abi.Events[event].ID {
|
||||
if len(log.Topics) == 0 || log.Topics[0] != eventChecker.abi.Events[event].ID {
|
||||
return nil, errors.New("event signature mismatch")
|
||||
}
|
||||
out := new(EventCheckerMixed)
|
||||
|
|
@ -238,7 +238,7 @@ func (EventCheckerUnnamed) ContractEventName() string {
|
|||
// Solidity: event unnamed(uint256 indexed arg0, uint256 indexed arg1)
|
||||
func (eventChecker *EventChecker) UnpackUnnamedEvent(log *types.Log) (*EventCheckerUnnamed, error) {
|
||||
event := "unnamed"
|
||||
if log.Topics[0] != eventChecker.abi.Events[event].ID {
|
||||
if len(log.Topics) == 0 || log.Topics[0] != eventChecker.abi.Events[event].ID {
|
||||
return nil, errors.New("event signature mismatch")
|
||||
}
|
||||
out := new(EventCheckerUnnamed)
|
||||
|
|
|
|||
|
|
@ -134,7 +134,7 @@ func (NameConflictLog) ContractEventName() string {
|
|||
// Solidity: event log(int256 msg, int256 _msg)
|
||||
func (nameConflict *NameConflict) UnpackLogEvent(log *types.Log) (*NameConflictLog, error) {
|
||||
event := "log"
|
||||
if log.Topics[0] != nameConflict.abi.Events[event].ID {
|
||||
if len(log.Topics) == 0 || log.Topics[0] != nameConflict.abi.Events[event].ID {
|
||||
return nil, errors.New("event signature mismatch")
|
||||
}
|
||||
out := new(NameConflictLog)
|
||||
|
|
|
|||
|
|
@ -136,7 +136,7 @@ func (NumericMethodNameE1TestEvent) ContractEventName() string {
|
|||
// Solidity: event _1TestEvent(address _param)
|
||||
func (numericMethodName *NumericMethodName) UnpackE1TestEventEvent(log *types.Log) (*NumericMethodNameE1TestEvent, error) {
|
||||
event := "_1TestEvent"
|
||||
if log.Topics[0] != numericMethodName.abi.Events[event].ID {
|
||||
if len(log.Topics) == 0 || log.Topics[0] != numericMethodName.abi.Events[event].ID {
|
||||
return nil, errors.New("event signature mismatch")
|
||||
}
|
||||
out := new(NumericMethodNameE1TestEvent)
|
||||
|
|
|
|||
|
|
@ -114,7 +114,7 @@ func (OverloadBar) ContractEventName() string {
|
|||
// Solidity: event bar(uint256 i)
|
||||
func (overload *Overload) UnpackBarEvent(log *types.Log) (*OverloadBar, error) {
|
||||
event := "bar"
|
||||
if log.Topics[0] != overload.abi.Events[event].ID {
|
||||
if len(log.Topics) == 0 || log.Topics[0] != overload.abi.Events[event].ID {
|
||||
return nil, errors.New("event signature mismatch")
|
||||
}
|
||||
out := new(OverloadBar)
|
||||
|
|
@ -156,7 +156,7 @@ func (OverloadBar0) ContractEventName() string {
|
|||
// Solidity: event bar(uint256 i, uint256 j)
|
||||
func (overload *Overload) UnpackBar0Event(log *types.Log) (*OverloadBar0, error) {
|
||||
event := "bar0"
|
||||
if log.Topics[0] != overload.abi.Events[event].ID {
|
||||
if len(log.Topics) == 0 || log.Topics[0] != overload.abi.Events[event].ID {
|
||||
return nil, errors.New("event signature mismatch")
|
||||
}
|
||||
out := new(OverloadBar0)
|
||||
|
|
|
|||
2
accounts/abi/abigen/testdata/v2/token.go.txt
vendored
2
accounts/abi/abigen/testdata/v2/token.go.txt
vendored
|
|
@ -386,7 +386,7 @@ func (TokenTransfer) ContractEventName() string {
|
|||
// Solidity: event Transfer(address indexed from, address indexed to, uint256 value)
|
||||
func (token *Token) UnpackTransferEvent(log *types.Log) (*TokenTransfer, error) {
|
||||
event := "Transfer"
|
||||
if log.Topics[0] != token.abi.Events[event].ID {
|
||||
if len(log.Topics) == 0 || log.Topics[0] != token.abi.Events[event].ID {
|
||||
return nil, errors.New("event signature mismatch")
|
||||
}
|
||||
out := new(TokenTransfer)
|
||||
|
|
|
|||
4
accounts/abi/abigen/testdata/v2/tuple.go.txt
vendored
4
accounts/abi/abigen/testdata/v2/tuple.go.txt
vendored
|
|
@ -193,7 +193,7 @@ func (TupleTupleEvent) ContractEventName() string {
|
|||
// Solidity: event TupleEvent((uint256,uint256[],(uint256,uint256)[]) a, (uint256,uint256)[2][] b, (uint256,uint256)[][2] c, (uint256,uint256[],(uint256,uint256)[])[] d, uint256[] e)
|
||||
func (tuple *Tuple) UnpackTupleEventEvent(log *types.Log) (*TupleTupleEvent, error) {
|
||||
event := "TupleEvent"
|
||||
if log.Topics[0] != tuple.abi.Events[event].ID {
|
||||
if len(log.Topics) == 0 || log.Topics[0] != tuple.abi.Events[event].ID {
|
||||
return nil, errors.New("event signature mismatch")
|
||||
}
|
||||
out := new(TupleTupleEvent)
|
||||
|
|
@ -234,7 +234,7 @@ func (TupleTupleEvent2) ContractEventName() string {
|
|||
// Solidity: event TupleEvent2((uint8,uint8)[] arg0)
|
||||
func (tuple *Tuple) UnpackTupleEvent2Event(log *types.Log) (*TupleTupleEvent2, error) {
|
||||
event := "TupleEvent2"
|
||||
if log.Topics[0] != tuple.abi.Events[event].ID {
|
||||
if len(log.Topics) == 0 || log.Topics[0] != tuple.abi.Events[event].ID {
|
||||
return nil, errors.New("event signature mismatch")
|
||||
}
|
||||
out := new(TupleTupleEvent2)
|
||||
|
|
|
|||
|
|
@ -276,7 +276,7 @@ func (DBInsert) ContractEventName() string {
|
|||
// Solidity: event Insert(uint256 key, uint256 value, uint256 length)
|
||||
func (dB *DB) UnpackInsertEvent(log *types.Log) (*DBInsert, error) {
|
||||
event := "Insert"
|
||||
if log.Topics[0] != dB.abi.Events[event].ID {
|
||||
if len(log.Topics) == 0 || log.Topics[0] != dB.abi.Events[event].ID {
|
||||
return nil, errors.New("event signature mismatch")
|
||||
}
|
||||
out := new(DBInsert)
|
||||
|
|
@ -318,7 +318,7 @@ func (DBKeyedInsert) ContractEventName() string {
|
|||
// Solidity: event KeyedInsert(uint256 indexed key, uint256 value)
|
||||
func (dB *DB) UnpackKeyedInsertEvent(log *types.Log) (*DBKeyedInsert, error) {
|
||||
event := "KeyedInsert"
|
||||
if log.Topics[0] != dB.abi.Events[event].ID {
|
||||
if len(log.Topics) == 0 || log.Topics[0] != dB.abi.Events[event].ID {
|
||||
return nil, errors.New("event signature mismatch")
|
||||
}
|
||||
out := new(DBKeyedInsert)
|
||||
|
|
|
|||
|
|
@ -115,7 +115,7 @@ func (CBasic1) ContractEventName() string {
|
|||
// Solidity: event basic1(uint256 indexed id, uint256 data)
|
||||
func (c *C) UnpackBasic1Event(log *types.Log) (*CBasic1, error) {
|
||||
event := "basic1"
|
||||
if log.Topics[0] != c.abi.Events[event].ID {
|
||||
if len(log.Topics) == 0 || log.Topics[0] != c.abi.Events[event].ID {
|
||||
return nil, errors.New("event signature mismatch")
|
||||
}
|
||||
out := new(CBasic1)
|
||||
|
|
@ -157,7 +157,7 @@ func (CBasic2) ContractEventName() string {
|
|||
// Solidity: event basic2(bool indexed flag, uint256 data)
|
||||
func (c *C) UnpackBasic2Event(log *types.Log) (*CBasic2, error) {
|
||||
event := "basic2"
|
||||
if log.Topics[0] != c.abi.Events[event].ID {
|
||||
if len(log.Topics) == 0 || log.Topics[0] != c.abi.Events[event].ID {
|
||||
return nil, errors.New("event signature mismatch")
|
||||
}
|
||||
out := new(CBasic2)
|
||||
|
|
|
|||
|
|
@ -367,3 +367,28 @@ func TestErrors(t *testing.T) {
|
|||
t.Fatalf("bad unpacked error result: expected Arg4 to be false. got true")
|
||||
}
|
||||
}
|
||||
|
||||
func TestEventUnpackEmptyTopics(t *testing.T) {
|
||||
c := events.NewC()
|
||||
|
||||
for _, log := range []*types.Log{
|
||||
{Topics: []common.Hash{}},
|
||||
{Topics: nil},
|
||||
} {
|
||||
_, err := c.UnpackBasic1Event(log)
|
||||
if err == nil {
|
||||
t.Fatal("expected error when unpacking event with empty topics, got nil")
|
||||
}
|
||||
if err.Error() != "event signature mismatch" {
|
||||
t.Fatalf("expected 'event signature mismatch' error, got: %v", err)
|
||||
}
|
||||
|
||||
_, err = c.UnpackBasic2Event(log)
|
||||
if err == nil {
|
||||
t.Fatal("expected error when unpacking event with empty topics, got nil")
|
||||
}
|
||||
if err.Error() != "event signature mismatch" {
|
||||
t.Fatalf("expected 'event signature mismatch' error, got: %v", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -472,6 +472,11 @@ func (w *Wallet) selfDerive() {
|
|||
continue
|
||||
}
|
||||
pairing := w.Hub.pairing(w)
|
||||
if pairing == nil {
|
||||
w.lock.Unlock()
|
||||
reqc <- struct{}{}
|
||||
continue
|
||||
}
|
||||
|
||||
// Device lock obtained, derive the next batch of accounts
|
||||
var (
|
||||
|
|
@ -631,13 +636,13 @@ func (w *Wallet) Derive(path accounts.DerivationPath, pin bool) (accounts.Accoun
|
|||
}
|
||||
|
||||
if pin {
|
||||
pairing := w.Hub.pairing(w)
|
||||
pairing.Accounts[account.Address] = path
|
||||
if err := w.Hub.setPairing(w, pairing); err != nil {
|
||||
return accounts.Account{}, err
|
||||
if pairing := w.Hub.pairing(w); pairing != nil {
|
||||
pairing.Accounts[account.Address] = path
|
||||
if err := w.Hub.setPairing(w, pairing); err != nil {
|
||||
return accounts.Account{}, err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return account, nil
|
||||
}
|
||||
|
||||
|
|
@ -774,11 +779,11 @@ func (w *Wallet) SignTxWithPassphrase(account accounts.Account, passphrase strin
|
|||
// It first checks for the address in the list of pinned accounts, and if it is
|
||||
// not found, attempts to parse the derivation path from the account's URL.
|
||||
func (w *Wallet) findAccountPath(account accounts.Account) (accounts.DerivationPath, error) {
|
||||
pairing := w.Hub.pairing(w)
|
||||
if path, ok := pairing.Accounts[account.Address]; ok {
|
||||
return path, nil
|
||||
if pairing := w.Hub.pairing(w); pairing != nil {
|
||||
if path, ok := pairing.Accounts[account.Address]; ok {
|
||||
return path, nil
|
||||
}
|
||||
}
|
||||
|
||||
// Look for the path in the URL
|
||||
if account.URL.Scheme != w.Hub.scheme {
|
||||
return nil, fmt.Errorf("scheme %s does not match wallet scheme %s", account.URL.Scheme, w.Hub.scheme)
|
||||
|
|
|
|||
|
|
@ -166,7 +166,7 @@ func (w *ledgerDriver) SignTx(path accounts.DerivationPath, tx *types.Transactio
|
|||
return common.Address{}, nil, accounts.ErrWalletClosed
|
||||
}
|
||||
// Ensure the wallet is capable of signing the given transaction
|
||||
if chainID != nil && w.version[0] <= 1 && w.version[1] <= 0 && w.version[2] <= 2 {
|
||||
if chainID != nil && (w.version[0] < 1 || (w.version[0] == 1 && w.version[1] == 0 && w.version[2] < 3)) {
|
||||
//lint:ignore ST1005 brand name displayed on the console
|
||||
return common.Address{}, nil, fmt.Errorf("Ledger v%d.%d.%d doesn't support signing this transaction, please update to v1.0.3 at least", w.version[0], w.version[1], w.version[2])
|
||||
}
|
||||
|
|
|
|||
|
|
@ -269,7 +269,7 @@ func (s *Scheduler) addEvent(event Event) {
|
|||
s.Trigger()
|
||||
}
|
||||
|
||||
// filterEvent sorts each Event either as a request event or a server event,
|
||||
// filterEvents sorts each Event either as a request event or a server event,
|
||||
// depending on its type. Request events are also sorted in a map based on the
|
||||
// module that originally initiated the request. It also ensures that no events
|
||||
// related to a server are returned before EvRegistered or after EvUnregistered.
|
||||
|
|
|
|||
|
|
@ -124,6 +124,7 @@ var (
|
|||
"jammy", // 22.04, EOL: 04/2032
|
||||
"noble", // 24.04, EOL: 04/2034
|
||||
"oracular", // 24.10, EOL: 07/2025
|
||||
"plucky", // 25.04, EOL: 01/2026
|
||||
}
|
||||
|
||||
// This is where the tests should be unpacked.
|
||||
|
|
|
|||
|
|
@ -109,6 +109,7 @@ if one is set. Otherwise it prints the genesis from the datadir.`,
|
|||
utils.MetricsInfluxDBTokenFlag,
|
||||
utils.MetricsInfluxDBBucketFlag,
|
||||
utils.MetricsInfluxDBOrganizationFlag,
|
||||
utils.StateSizeTrackingFlag,
|
||||
utils.TxLookupLimitFlag,
|
||||
utils.VMTraceFlag,
|
||||
utils.VMTraceJsonConfigFlag,
|
||||
|
|
@ -292,7 +293,7 @@ func initGenesis(ctx *cli.Context) error {
|
|||
// Only create triedb if we're not using PBSS or genesis is empty on disk. Refer to
|
||||
// https://github.com/ethereum/go-ethereum/pull/25523 for why the triedb cannot be
|
||||
// re-created when using PBSS.
|
||||
triedb = utils.MakeTrieDatabase(ctx, chaindb, ctx.Bool(utils.CachePreimagesFlag.Name), false, genesis.IsVerkle())
|
||||
triedb = utils.MakeTrieDatabase(ctx, stack, chaindb, ctx.Bool(utils.CachePreimagesFlag.Name), false, genesis.IsVerkle())
|
||||
defer triedb.Close()
|
||||
}
|
||||
|
||||
|
|
@ -651,7 +652,7 @@ func dump(ctx *cli.Context) error {
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
triedb := utils.MakeTrieDatabase(ctx, db, true, true, false) // always enable preimage lookup
|
||||
triedb := utils.MakeTrieDatabase(ctx, stack, db, true, true, false) // always enable preimage lookup
|
||||
defer triedb.Close()
|
||||
|
||||
state, err := state.New(root, state.NewDatabase(triedb, nil))
|
||||
|
|
|
|||
|
|
@ -524,7 +524,7 @@ func dbDumpTrie(ctx *cli.Context) error {
|
|||
db := utils.MakeChainDatabase(ctx, stack, true)
|
||||
defer db.Close()
|
||||
|
||||
triedb := utils.MakeTrieDatabase(ctx, db, false, true, false)
|
||||
triedb := utils.MakeTrieDatabase(ctx, stack, db, false, true, false)
|
||||
defer triedb.Close()
|
||||
|
||||
var (
|
||||
|
|
@ -859,7 +859,7 @@ func inspectHistory(ctx *cli.Context) error {
|
|||
db := utils.MakeChainDatabase(ctx, stack, true)
|
||||
defer db.Close()
|
||||
|
||||
triedb := utils.MakeTrieDatabase(ctx, db, false, false, false)
|
||||
triedb := utils.MakeTrieDatabase(ctx, stack, db, false, false, false)
|
||||
defer triedb.Close()
|
||||
|
||||
var (
|
||||
|
|
|
|||
|
|
@ -200,6 +200,7 @@ var (
|
|||
utils.MetricsInfluxDBTokenFlag,
|
||||
utils.MetricsInfluxDBBucketFlag,
|
||||
utils.MetricsInfluxDBOrganizationFlag,
|
||||
utils.StateSizeTrackingFlag,
|
||||
}
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -217,7 +217,7 @@ func verifyState(ctx *cli.Context) error {
|
|||
log.Error("Failed to load head block")
|
||||
return errors.New("no head block")
|
||||
}
|
||||
triedb := utils.MakeTrieDatabase(ctx, chaindb, false, true, false)
|
||||
triedb := utils.MakeTrieDatabase(ctx, stack, chaindb, false, true, false)
|
||||
defer triedb.Close()
|
||||
|
||||
var (
|
||||
|
|
@ -282,7 +282,7 @@ func traverseState(ctx *cli.Context) error {
|
|||
chaindb := utils.MakeChainDatabase(ctx, stack, true)
|
||||
defer chaindb.Close()
|
||||
|
||||
triedb := utils.MakeTrieDatabase(ctx, chaindb, false, true, false)
|
||||
triedb := utils.MakeTrieDatabase(ctx, stack, chaindb, false, true, false)
|
||||
defer triedb.Close()
|
||||
|
||||
headBlock := rawdb.ReadHeadBlock(chaindb)
|
||||
|
|
@ -391,7 +391,7 @@ func traverseRawState(ctx *cli.Context) error {
|
|||
chaindb := utils.MakeChainDatabase(ctx, stack, true)
|
||||
defer chaindb.Close()
|
||||
|
||||
triedb := utils.MakeTrieDatabase(ctx, chaindb, false, true, false)
|
||||
triedb := utils.MakeTrieDatabase(ctx, stack, chaindb, false, true, false)
|
||||
defer triedb.Close()
|
||||
|
||||
headBlock := rawdb.ReadHeadBlock(chaindb)
|
||||
|
|
@ -558,7 +558,7 @@ func dumpState(ctx *cli.Context) error {
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
triedb := utils.MakeTrieDatabase(ctx, db, false, true, false)
|
||||
triedb := utils.MakeTrieDatabase(ctx, stack, db, false, true, false)
|
||||
defer triedb.Close()
|
||||
|
||||
snapConfig := snapshot.Config{
|
||||
|
|
@ -640,7 +640,7 @@ func snapshotExportPreimages(ctx *cli.Context) error {
|
|||
chaindb := utils.MakeChainDatabase(ctx, stack, true)
|
||||
defer chaindb.Close()
|
||||
|
||||
triedb := utils.MakeTrieDatabase(ctx, chaindb, false, true, false)
|
||||
triedb := utils.MakeTrieDatabase(ctx, stack, chaindb, false, true, false)
|
||||
defer triedb.Close()
|
||||
|
||||
var root common.Hash
|
||||
|
|
|
|||
|
|
@ -1133,7 +1133,10 @@ func (s *Suite) testBadBlobTx(t *utesting.T, tx *types.Transaction, badTx *types
|
|||
// transmit the same tx but with correct sidecar from the good peer.
|
||||
|
||||
var req *eth.GetPooledTransactionsPacket
|
||||
req, err = readUntil[eth.GetPooledTransactionsPacket](context.Background(), conn)
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 12*time.Second)
|
||||
defer cancel()
|
||||
|
||||
req, err = readUntil[eth.GetPooledTransactionsPacket](ctx, conn)
|
||||
if err != nil {
|
||||
errc <- fmt.Errorf("reading pooled tx request failed: %v", err)
|
||||
return
|
||||
|
|
|
|||
|
|
@ -425,7 +425,7 @@ func MakePreState(db ethdb.Database, accounts types.GenesisAlloc) *state.StateDB
|
|||
sdb := state.NewDatabase(tdb, nil)
|
||||
statedb, _ := state.New(types.EmptyRootHash, sdb)
|
||||
for addr, a := range accounts {
|
||||
statedb.SetCode(addr, a.Code)
|
||||
statedb.SetCode(addr, a.Code, tracing.CodeChangeUnspecified)
|
||||
statedb.SetNonce(addr, a.Nonce, tracing.NonceChangeGenesis)
|
||||
statedb.SetBalance(addr, uint256.MustFromBig(a.Balance), tracing.BalanceIncreaseGenesisBalance)
|
||||
for k, v := range a.Storage {
|
||||
|
|
|
|||
|
|
@ -322,7 +322,7 @@ func runCmd(ctx *cli.Context) error {
|
|||
}
|
||||
} else {
|
||||
if len(code) > 0 {
|
||||
prestate.SetCode(receiver, code)
|
||||
prestate.SetCode(receiver, code, tracing.CodeChangeUnspecified)
|
||||
}
|
||||
execFunc = func() ([]byte, uint64, error) {
|
||||
// don't mutate the state!
|
||||
|
|
|
|||
|
|
@ -281,6 +281,12 @@ var (
|
|||
Usage: "Scheme to use for storing ethereum state ('hash' or 'path')",
|
||||
Category: flags.StateCategory,
|
||||
}
|
||||
StateSizeTrackingFlag = &cli.BoolFlag{
|
||||
Name: "state.size-tracking",
|
||||
Usage: "Enable state size tracking, retrieve state size with debug_stateSize.",
|
||||
Value: ethconfig.Defaults.EnableStateSizeTracking,
|
||||
Category: flags.StateCategory,
|
||||
}
|
||||
StateHistoryFlag = &cli.Uint64Flag{
|
||||
Name: "history.state",
|
||||
Usage: "Number of recent blocks to retain state history for, only relevant in state.scheme=path (default = 90,000 blocks, 0 = entire chain)",
|
||||
|
|
@ -1741,6 +1747,9 @@ func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *ethconfig.Config) {
|
|||
cfg.EthDiscoveryURLs = SplitAndTrim(urls)
|
||||
}
|
||||
}
|
||||
if ctx.Bool(StateSizeTrackingFlag.Name) {
|
||||
cfg.EnableStateSizeTracking = true
|
||||
}
|
||||
// Override any default configs for hard coded networks.
|
||||
switch {
|
||||
case ctx.Bool(MainnetFlag.Name):
|
||||
|
|
@ -2223,6 +2232,9 @@ func MakeChain(ctx *cli.Context, stack *node.Node, readonly bool) (*core.BlockCh
|
|||
// - DATADIR/triedb/merkle.journal
|
||||
// - DATADIR/triedb/verkle.journal
|
||||
TrieJournalDirectory: stack.ResolvePath("triedb"),
|
||||
|
||||
// Enable state size tracking if enabled
|
||||
StateSizeTracking: ctx.Bool(StateSizeTrackingFlag.Name),
|
||||
}
|
||||
if options.ArchiveMode && !options.Preimages {
|
||||
options.Preimages = true
|
||||
|
|
@ -2284,7 +2296,7 @@ func MakeConsolePreloads(ctx *cli.Context) []string {
|
|||
}
|
||||
|
||||
// MakeTrieDatabase constructs a trie database based on the configured scheme.
|
||||
func MakeTrieDatabase(ctx *cli.Context, disk ethdb.Database, preimage bool, readOnly bool, isVerkle bool) *triedb.Database {
|
||||
func MakeTrieDatabase(ctx *cli.Context, stack *node.Node, disk ethdb.Database, preimage bool, readOnly bool, isVerkle bool) *triedb.Database {
|
||||
config := &triedb.Config{
|
||||
Preimages: preimage,
|
||||
IsVerkle: isVerkle,
|
||||
|
|
@ -2300,10 +2312,13 @@ func MakeTrieDatabase(ctx *cli.Context, disk ethdb.Database, preimage bool, read
|
|||
config.HashDB = hashdb.Defaults
|
||||
return triedb.NewDatabase(disk, config)
|
||||
}
|
||||
var pathConfig pathdb.Config
|
||||
if readOnly {
|
||||
config.PathDB = pathdb.ReadOnly
|
||||
pathConfig = *pathdb.ReadOnly
|
||||
} else {
|
||||
config.PathDB = pathdb.Defaults
|
||||
pathConfig = *pathdb.Defaults
|
||||
}
|
||||
pathConfig.JournalDirectory = stack.ResolvePath("triedb")
|
||||
config.PathDB = &pathConfig
|
||||
return triedb.NewDatabase(disk, config)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -154,7 +154,7 @@ func (st *bucketStats) print(name string) {
|
|||
name, st.count, float64(st.blocks)/float64(st.count), float64(st.logs)/float64(st.count), st.runtime/time.Duration(st.count))
|
||||
}
|
||||
|
||||
// writeQueries serializes the generated errors to the error file.
|
||||
// writeErrors serializes the generated errors to the error file.
|
||||
func writeErrors(errorFile string, errors []*filterQuery) {
|
||||
file, err := os.Create(errorFile)
|
||||
if err != nil {
|
||||
|
|
|
|||
30
common/eta.go
Normal file
30
common/eta.go
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
// Copyright 2025 The go-ethereum Authors
|
||||
// This file is part of the go-ethereum library.
|
||||
//
|
||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package common
|
||||
|
||||
import "time"
|
||||
|
||||
// CalculateETA calculates the estimated remaining time based on the
|
||||
// number of finished task, remaining task, and the time cost for finished task.
|
||||
func CalculateETA(done, left uint64, elapsed time.Duration) time.Duration {
|
||||
if done == 0 || elapsed.Milliseconds() == 0 {
|
||||
return 0
|
||||
}
|
||||
|
||||
speed := float64(done) / float64(elapsed.Milliseconds())
|
||||
return time.Duration(float64(left)/speed) * time.Millisecond
|
||||
}
|
||||
60
common/eta_test.go
Normal file
60
common/eta_test.go
Normal file
|
|
@ -0,0 +1,60 @@
|
|||
// Copyright 2025 The go-ethereum Authors
|
||||
// This file is part of the go-ethereum library.
|
||||
//
|
||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package common
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestCalculateETA(t *testing.T) {
|
||||
type args struct {
|
||||
done uint64
|
||||
left uint64
|
||||
elapsed time.Duration
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
want time.Duration
|
||||
}{
|
||||
{
|
||||
name: "zero done",
|
||||
args: args{done: 0, left: 100, elapsed: time.Second},
|
||||
want: 0,
|
||||
},
|
||||
{
|
||||
name: "zero elapsed",
|
||||
args: args{done: 1, left: 100, elapsed: 0},
|
||||
want: 0,
|
||||
},
|
||||
{
|
||||
name: "@Jolly23 's case",
|
||||
args: args{done: 16858580, left: 41802252, elapsed: 66179848 * time.Millisecond},
|
||||
want: 164098440 * time.Millisecond,
|
||||
// wrong msg: msg="Indexing state history" processed=16858580 left=41802252 elapsed=18h22m59.848s eta=11h36m42.252s
|
||||
// should be around 45.58 hours
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if got := CalculateETA(tt.args.done, tt.args.left, tt.args.elapsed); got != tt.want {
|
||||
t.Errorf("CalculateETA() = %v ms, want %v ms", got.Milliseconds(), tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
@ -168,10 +168,13 @@ type BlockChainConfig struct {
|
|||
TrieNoAsyncFlush bool // Whether the asynchronous buffer flushing is disallowed
|
||||
TrieJournalDirectory string // Directory path to the journal used for persisting trie data across node restarts
|
||||
|
||||
Preimages bool // Whether to store preimage of trie key to the disk
|
||||
StateHistory uint64 // Number of blocks from head whose state histories are reserved.
|
||||
StateScheme string // Scheme used to store ethereum states and merkle tree nodes on top
|
||||
ArchiveMode bool // Whether to enable the archive mode
|
||||
Preimages bool // Whether to store preimage of trie key to the disk
|
||||
StateScheme string // Scheme used to store ethereum states and merkle tree nodes on top
|
||||
ArchiveMode bool // Whether to enable the archive mode
|
||||
|
||||
// Number of blocks from the chain head for which state histories are retained.
|
||||
// If set to 0, all state histories across the entire chain will be retained;
|
||||
StateHistory uint64
|
||||
|
||||
// State snapshot related options
|
||||
SnapshotLimit int // Memory allowance (MB) to use for caching snapshot entries in memory
|
||||
|
|
@ -193,6 +196,9 @@ type BlockChainConfig struct {
|
|||
// If the value is zero, all transactions of the entire chain will be indexed.
|
||||
// If the value is -1, indexing is disabled.
|
||||
TxLookupLimit int64
|
||||
|
||||
// StateSizeTracking indicates whether the state size tracking is enabled.
|
||||
StateSizeTracking bool
|
||||
}
|
||||
|
||||
// DefaultConfig returns the default config.
|
||||
|
|
@ -330,6 +336,7 @@ type BlockChain struct {
|
|||
prefetcher Prefetcher
|
||||
processor Processor // Block transaction processor interface
|
||||
logger *tracing.Hooks
|
||||
stateSizer *state.SizeTracker // State size tracking
|
||||
|
||||
lastForkReadyAlert time.Time // Last time there was a fork readiness print out
|
||||
}
|
||||
|
|
@ -523,6 +530,17 @@ func NewBlockChain(db ethdb.Database, genesis *Genesis, engine consensus.Engine,
|
|||
if bc.cfg.TxLookupLimit >= 0 {
|
||||
bc.txIndexer = newTxIndexer(uint64(bc.cfg.TxLookupLimit), bc)
|
||||
}
|
||||
|
||||
// Start state size tracker
|
||||
if bc.cfg.StateSizeTracking {
|
||||
stateSizer, err := state.NewSizeTracker(bc.db, bc.triedb)
|
||||
if err == nil {
|
||||
bc.stateSizer = stateSizer
|
||||
log.Info("Enabled state size metrics")
|
||||
} else {
|
||||
log.Info("Failed to setup size tracker", "err", err)
|
||||
}
|
||||
}
|
||||
return bc, nil
|
||||
}
|
||||
|
||||
|
|
@ -1249,6 +1267,10 @@ func (bc *BlockChain) stopWithoutSaving() {
|
|||
// Signal shutdown to all goroutines.
|
||||
bc.InterruptInsert(true)
|
||||
|
||||
// Stop state size tracker
|
||||
if bc.stateSizer != nil {
|
||||
bc.stateSizer.Stop()
|
||||
}
|
||||
// Now wait for all chain modifications to end and persistent goroutines to exit.
|
||||
//
|
||||
// Note: Close waits for the mutex to become available, i.e. any running chain
|
||||
|
|
@ -1583,10 +1605,14 @@ func (bc *BlockChain) writeBlockWithState(block *types.Block, receipts []*types.
|
|||
log.Crit("Failed to write block into disk", "err", err)
|
||||
}
|
||||
// Commit all cached state changes into underlying memory database.
|
||||
root, err := statedb.Commit(block.NumberU64(), bc.chainConfig.IsEIP158(block.Number()), bc.chainConfig.IsCancun(block.Number(), block.Time()))
|
||||
root, stateUpdate, err := statedb.CommitWithUpdate(block.NumberU64(), bc.chainConfig.IsEIP158(block.Number()), bc.chainConfig.IsCancun(block.Number(), block.Time()))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// Emit the state update to the state sizestats if it's active
|
||||
if bc.stateSizer != nil {
|
||||
bc.stateSizer.Notify(stateUpdate)
|
||||
}
|
||||
// If node is running in path mode, skip explicit gc operation
|
||||
// which is unnecessary in this mode.
|
||||
if bc.triedb.Scheme() == rawdb.PathScheme {
|
||||
|
|
@ -2011,7 +2037,10 @@ func (bc *BlockChain) processBlock(parentRoot common.Hash, block *types.Block, s
|
|||
// If we are past Byzantium, enable prefetching to pull in trie node paths
|
||||
// while processing transactions. Before Byzantium the prefetcher is mostly
|
||||
// useless due to the intermediate root hashing after each transaction.
|
||||
var witness *stateless.Witness
|
||||
var (
|
||||
witness *stateless.Witness
|
||||
witnessStats *stateless.WitnessStats
|
||||
)
|
||||
if bc.chainConfig.IsByzantium(block.Number()) {
|
||||
// Generate witnesses either if we're self-testing, or if it's the
|
||||
// only block being inserted. A bit crude, but witnesses are huge,
|
||||
|
|
@ -2021,8 +2050,11 @@ func (bc *BlockChain) processBlock(parentRoot common.Hash, block *types.Block, s
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if bc.cfg.VmConfig.EnableWitnessStats {
|
||||
witnessStats = stateless.NewWitnessStats()
|
||||
}
|
||||
}
|
||||
statedb.StartPrefetcher("chain", witness)
|
||||
statedb.StartPrefetcher("chain", witness, witnessStats)
|
||||
defer statedb.StopPrefetcher()
|
||||
}
|
||||
|
||||
|
|
@ -2083,6 +2115,7 @@ func (bc *BlockChain) processBlock(parentRoot common.Hash, block *types.Block, s
|
|||
return nil, fmt.Errorf("stateless self-validation receipt root mismatch (cross: %x local: %x)", crossReceiptRoot, block.ReceiptHash())
|
||||
}
|
||||
}
|
||||
|
||||
xvtime := time.Since(xvstart)
|
||||
proctime := time.Since(startTime) // processing + validation + cross validation
|
||||
|
||||
|
|
@ -2118,6 +2151,11 @@ func (bc *BlockChain) processBlock(parentRoot common.Hash, block *types.Block, s
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// Report the collected witness statistics
|
||||
if witnessStats != nil {
|
||||
witnessStats.ReportMetrics()
|
||||
}
|
||||
|
||||
// Update the metrics touched during block commit
|
||||
accountCommitTimer.Update(statedb.AccountCommits) // Account commits are complete, we can mark them
|
||||
storageCommitTimer.Update(statedb.StorageCommits) // Storage commits are complete, we can mark them
|
||||
|
|
@ -2776,3 +2814,8 @@ func (bc *BlockChain) SetTrieFlushInterval(interval time.Duration) {
|
|||
func (bc *BlockChain) GetTrieFlushInterval() time.Duration {
|
||||
return time.Duration(bc.flushInterval.Load())
|
||||
}
|
||||
|
||||
// StateSizer returns the state size tracker, or nil if it's not initialized
|
||||
func (bc *BlockChain) StateSizer() *state.SizeTracker {
|
||||
return bc.stateSizer
|
||||
}
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ const (
|
|||
headLogDelay = time.Second // head indexing log info delay (do not log if finished faster)
|
||||
)
|
||||
|
||||
// updateLoop initializes and updates the log index structure according to the
|
||||
// indexerLoop initializes and updates the log index structure according to the
|
||||
// current targetView.
|
||||
func (f *FilterMaps) indexerLoop() {
|
||||
defer f.closeWg.Done()
|
||||
|
|
@ -221,7 +221,7 @@ func (f *FilterMaps) processSingleEvent(blocking bool) bool {
|
|||
return true
|
||||
}
|
||||
|
||||
// setTargetView updates the target chain view of the iterator.
|
||||
// setTarget updates the target chain view of the iterator.
|
||||
func (f *FilterMaps) setTarget(target targetUpdate) {
|
||||
f.targetView = target.targetView
|
||||
f.historyCutoff = target.historyCutoff
|
||||
|
|
|
|||
|
|
@ -153,7 +153,7 @@ func hashAlloc(ga *types.GenesisAlloc, isVerkle bool) (common.Hash, error) {
|
|||
if account.Balance != nil {
|
||||
statedb.AddBalance(addr, uint256.MustFromBig(account.Balance), tracing.BalanceIncreaseGenesisBalance)
|
||||
}
|
||||
statedb.SetCode(addr, account.Code)
|
||||
statedb.SetCode(addr, account.Code, tracing.CodeChangeGenesis)
|
||||
statedb.SetNonce(addr, account.Nonce, tracing.NonceChangeGenesis)
|
||||
for key, value := range account.Storage {
|
||||
statedb.SetState(addr, key, value)
|
||||
|
|
@ -179,7 +179,7 @@ func flushAlloc(ga *types.GenesisAlloc, triedb *triedb.Database) (common.Hash, e
|
|||
// already captures the allocations.
|
||||
statedb.AddBalance(addr, uint256.MustFromBig(account.Balance), tracing.BalanceIncreaseGenesisBalance)
|
||||
}
|
||||
statedb.SetCode(addr, account.Code)
|
||||
statedb.SetCode(addr, account.Code, tracing.CodeChangeGenesis)
|
||||
statedb.SetNonce(addr, account.Nonce, tracing.NonceChangeGenesis)
|
||||
for key, value := range account.Storage {
|
||||
statedb.SetState(addr, key, value)
|
||||
|
|
|
|||
|
|
@ -174,16 +174,3 @@ func UpdateUncleanShutdownMarker(db ethdb.KeyValueStore) {
|
|||
log.Warn("Failed to write unclean-shutdown marker", "err", err)
|
||||
}
|
||||
}
|
||||
|
||||
// ReadTransitionStatus retrieves the eth2 transition status from the database
|
||||
func ReadTransitionStatus(db ethdb.KeyValueReader) []byte {
|
||||
data, _ := db.Get(transitionStatusKey)
|
||||
return data
|
||||
}
|
||||
|
||||
// WriteTransitionStatus stores the eth2 transition status to the database
|
||||
func WriteTransitionStatus(db ethdb.KeyValueWriter, data []byte) {
|
||||
if err := db.Put(transitionStatusKey, data); err != nil {
|
||||
log.Crit("Failed to store the eth2 transition status", "err", err)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -119,13 +119,6 @@ func WriteStateID(db ethdb.KeyValueWriter, root common.Hash, id uint64) {
|
|||
}
|
||||
}
|
||||
|
||||
// DeleteStateID deletes the specified state lookup from the database.
|
||||
func DeleteStateID(db ethdb.KeyValueWriter, root common.Hash) {
|
||||
if err := db.Delete(stateIDKey(root)); err != nil {
|
||||
log.Crit("Failed to delete state ID", "err", err)
|
||||
}
|
||||
}
|
||||
|
||||
// ReadPersistentStateID retrieves the id of the persistent state from the database.
|
||||
func ReadPersistentStateID(db ethdb.KeyValueReader) uint64 {
|
||||
data, _ := db.Get(persistentStateIDKey)
|
||||
|
|
|
|||
|
|
@ -48,12 +48,16 @@ func basicRead(t *testing.T, newFn func(kinds []string) ethdb.AncientStore) {
|
|||
)
|
||||
defer db.Close()
|
||||
|
||||
db.ModifyAncients(func(op ethdb.AncientWriteOp) error {
|
||||
if _, err := db.ModifyAncients(func(op ethdb.AncientWriteOp) error {
|
||||
for i := 0; i < len(data); i++ {
|
||||
op.AppendRaw("a", uint64(i), data[i])
|
||||
if err := op.AppendRaw("a", uint64(i), data[i]); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}); err != nil {
|
||||
t.Fatalf("Failed to write ancient data %v", err)
|
||||
}
|
||||
db.TruncateTail(10)
|
||||
db.TruncateHead(90)
|
||||
|
||||
|
|
@ -109,12 +113,16 @@ func batchRead(t *testing.T, newFn func(kinds []string) ethdb.AncientStore) {
|
|||
)
|
||||
defer db.Close()
|
||||
|
||||
db.ModifyAncients(func(op ethdb.AncientWriteOp) error {
|
||||
if _, err := db.ModifyAncients(func(op ethdb.AncientWriteOp) error {
|
||||
for i := 0; i < 100; i++ {
|
||||
op.AppendRaw("a", uint64(i), data[i])
|
||||
if err := op.AppendRaw("a", uint64(i), data[i]); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}); err != nil {
|
||||
t.Fatalf("Failed to write ancient data %v", err)
|
||||
}
|
||||
db.TruncateTail(10)
|
||||
db.TruncateHead(90)
|
||||
|
||||
|
|
@ -189,7 +197,9 @@ func basicWrite(t *testing.T, newFn func(kinds []string) ethdb.AncientStore) {
|
|||
// The ancient write to tables should be aligned
|
||||
_, err := db.ModifyAncients(func(op ethdb.AncientWriteOp) error {
|
||||
for i := 0; i < 100; i++ {
|
||||
op.AppendRaw("a", uint64(i), dataA[i])
|
||||
if err := op.AppendRaw("a", uint64(i), dataA[i]); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
|
@ -200,8 +210,12 @@ func basicWrite(t *testing.T, newFn func(kinds []string) ethdb.AncientStore) {
|
|||
// Test normal ancient write
|
||||
size, err := db.ModifyAncients(func(op ethdb.AncientWriteOp) error {
|
||||
for i := 0; i < 100; i++ {
|
||||
op.AppendRaw("a", uint64(i), dataA[i])
|
||||
op.AppendRaw("b", uint64(i), dataB[i])
|
||||
if err := op.AppendRaw("a", uint64(i), dataA[i]); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := op.AppendRaw("b", uint64(i), dataB[i]); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
|
@ -217,8 +231,12 @@ func basicWrite(t *testing.T, newFn func(kinds []string) ethdb.AncientStore) {
|
|||
db.TruncateHead(90)
|
||||
_, err = db.ModifyAncients(func(op ethdb.AncientWriteOp) error {
|
||||
for i := 90; i < 100; i++ {
|
||||
op.AppendRaw("a", uint64(i), dataA[i])
|
||||
op.AppendRaw("b", uint64(i), dataB[i])
|
||||
if err := op.AppendRaw("a", uint64(i), dataA[i]); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := op.AppendRaw("b", uint64(i), dataB[i]); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
|
@ -227,11 +245,15 @@ func basicWrite(t *testing.T, newFn func(kinds []string) ethdb.AncientStore) {
|
|||
}
|
||||
|
||||
// Write should work after truncating everything
|
||||
db.TruncateTail(0)
|
||||
db.TruncateHead(0)
|
||||
_, err = db.ModifyAncients(func(op ethdb.AncientWriteOp) error {
|
||||
for i := 0; i < 100; i++ {
|
||||
op.AppendRaw("a", uint64(i), dataA[i])
|
||||
op.AppendRaw("b", uint64(i), dataB[i])
|
||||
if err := op.AppendRaw("a", uint64(i), dataA[i]); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := op.AppendRaw("b", uint64(i), dataB[i]); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
|
@ -245,14 +267,18 @@ func nonMutable(t *testing.T, newFn func(kinds []string) ethdb.AncientStore) {
|
|||
defer db.Close()
|
||||
|
||||
// We write 100 zero-bytes to the freezer and immediately mutate the slice
|
||||
db.ModifyAncients(func(op ethdb.AncientWriteOp) error {
|
||||
if _, err := db.ModifyAncients(func(op ethdb.AncientWriteOp) error {
|
||||
data := make([]byte, 100)
|
||||
op.AppendRaw("a", uint64(0), data)
|
||||
if err := op.AppendRaw("a", uint64(0), data); err != nil {
|
||||
return err
|
||||
}
|
||||
for i := range data {
|
||||
data[i] = 0xff
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}); err != nil {
|
||||
t.Fatalf("Failed to write ancient data %v", err)
|
||||
}
|
||||
// Now read it.
|
||||
data, err := db.Ancient("a", uint64(0))
|
||||
if err != nil {
|
||||
|
|
@ -275,23 +301,31 @@ func TestResettableAncientSuite(t *testing.T, newFn func(kinds []string) ethdb.R
|
|||
)
|
||||
defer db.Close()
|
||||
|
||||
db.ModifyAncients(func(op ethdb.AncientWriteOp) error {
|
||||
if _, err := db.ModifyAncients(func(op ethdb.AncientWriteOp) error {
|
||||
for i := 0; i < 100; i++ {
|
||||
op.AppendRaw("a", uint64(i), data[i])
|
||||
if err := op.AppendRaw("a", uint64(i), data[i]); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}); err != nil {
|
||||
t.Fatalf("Failed to write ancient data %v", err)
|
||||
}
|
||||
db.TruncateTail(10)
|
||||
db.TruncateHead(90)
|
||||
|
||||
// Ancient write should work after resetting
|
||||
db.Reset()
|
||||
db.ModifyAncients(func(op ethdb.AncientWriteOp) error {
|
||||
if _, err := db.ModifyAncients(func(op ethdb.AncientWriteOp) error {
|
||||
for i := 0; i < 100; i++ {
|
||||
op.AppendRaw("a", uint64(i), data[i])
|
||||
if err := op.AppendRaw("a", uint64(i), data[i]); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}); err != nil {
|
||||
t.Fatalf("Failed to write ancient data %v", err)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -18,13 +18,17 @@ package rawdb
|
|||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"maps"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"slices"
|
||||
"strings"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
|
|
@ -32,7 +36,7 @@ import (
|
|||
"github.com/ethereum/go-ethereum/ethdb"
|
||||
"github.com/ethereum/go-ethereum/ethdb/memorydb"
|
||||
"github.com/ethereum/go-ethereum/log"
|
||||
"github.com/olekukonko/tablewriter"
|
||||
"golang.org/x/sync/errgroup"
|
||||
)
|
||||
|
||||
var ErrDeleteRangeInterrupted = errors.New("safe delete range operation interrupted")
|
||||
|
|
@ -363,36 +367,36 @@ func (c counter) Percentage(current uint64) string {
|
|||
return fmt.Sprintf("%d", current*100/uint64(c))
|
||||
}
|
||||
|
||||
// stat stores sizes and count for a parameter
|
||||
// stat provides lock-free statistics aggregation using atomic operations
|
||||
type stat struct {
|
||||
size common.StorageSize
|
||||
count counter
|
||||
size uint64
|
||||
count uint64
|
||||
}
|
||||
|
||||
// Add size to the stat and increase the counter by 1
|
||||
func (s *stat) Add(size common.StorageSize) {
|
||||
s.size += size
|
||||
s.count++
|
||||
func (s *stat) empty() bool {
|
||||
return atomic.LoadUint64(&s.count) == 0
|
||||
}
|
||||
|
||||
func (s *stat) Size() string {
|
||||
return s.size.String()
|
||||
func (s *stat) add(size common.StorageSize) {
|
||||
atomic.AddUint64(&s.size, uint64(size))
|
||||
atomic.AddUint64(&s.count, 1)
|
||||
}
|
||||
|
||||
func (s *stat) Count() string {
|
||||
return s.count.String()
|
||||
func (s *stat) sizeString() string {
|
||||
return common.StorageSize(atomic.LoadUint64(&s.size)).String()
|
||||
}
|
||||
|
||||
func (s *stat) countString() string {
|
||||
return counter(atomic.LoadUint64(&s.count)).String()
|
||||
}
|
||||
|
||||
// InspectDatabase traverses the entire database and checks the size
|
||||
// of all different categories of data.
|
||||
func InspectDatabase(db ethdb.Database, keyPrefix, keyStart []byte) error {
|
||||
it := db.NewIterator(keyPrefix, keyStart)
|
||||
defer it.Release()
|
||||
|
||||
var (
|
||||
count int64
|
||||
start = time.Now()
|
||||
logged = time.Now()
|
||||
start = time.Now()
|
||||
count atomic.Int64
|
||||
total atomic.Uint64
|
||||
|
||||
// Key-value store statistics
|
||||
headers stat
|
||||
|
|
@ -428,144 +432,200 @@ func InspectDatabase(db ethdb.Database, keyPrefix, keyStart []byte) error {
|
|||
metadata stat
|
||||
unaccounted stat
|
||||
|
||||
// Totals
|
||||
total common.StorageSize
|
||||
|
||||
// This map tracks example keys for unaccounted data.
|
||||
// For each unique two-byte prefix, the first unaccounted key encountered
|
||||
// by the iterator will be stored.
|
||||
unaccountedKeys = make(map[[2]byte][]byte)
|
||||
unaccountedMu sync.Mutex
|
||||
)
|
||||
// Inspect key-value database first.
|
||||
for it.Next() {
|
||||
var (
|
||||
key = it.Key()
|
||||
size = common.StorageSize(len(key) + len(it.Value()))
|
||||
)
|
||||
total += size
|
||||
switch {
|
||||
case bytes.HasPrefix(key, headerPrefix) && len(key) == (len(headerPrefix)+8+common.HashLength):
|
||||
headers.Add(size)
|
||||
case bytes.HasPrefix(key, blockBodyPrefix) && len(key) == (len(blockBodyPrefix)+8+common.HashLength):
|
||||
bodies.Add(size)
|
||||
case bytes.HasPrefix(key, blockReceiptsPrefix) && len(key) == (len(blockReceiptsPrefix)+8+common.HashLength):
|
||||
receipts.Add(size)
|
||||
case bytes.HasPrefix(key, headerPrefix) && bytes.HasSuffix(key, headerTDSuffix):
|
||||
tds.Add(size)
|
||||
case bytes.HasPrefix(key, headerPrefix) && bytes.HasSuffix(key, headerHashSuffix):
|
||||
numHashPairings.Add(size)
|
||||
case bytes.HasPrefix(key, headerNumberPrefix) && len(key) == (len(headerNumberPrefix)+common.HashLength):
|
||||
hashNumPairings.Add(size)
|
||||
case IsLegacyTrieNode(key, it.Value()):
|
||||
legacyTries.Add(size)
|
||||
case bytes.HasPrefix(key, stateIDPrefix) && len(key) == len(stateIDPrefix)+common.HashLength:
|
||||
stateLookups.Add(size)
|
||||
case IsAccountTrieNode(key):
|
||||
accountTries.Add(size)
|
||||
case IsStorageTrieNode(key):
|
||||
storageTries.Add(size)
|
||||
case bytes.HasPrefix(key, CodePrefix) && len(key) == len(CodePrefix)+common.HashLength:
|
||||
codes.Add(size)
|
||||
case bytes.HasPrefix(key, txLookupPrefix) && len(key) == (len(txLookupPrefix)+common.HashLength):
|
||||
txLookups.Add(size)
|
||||
case bytes.HasPrefix(key, SnapshotAccountPrefix) && len(key) == (len(SnapshotAccountPrefix)+common.HashLength):
|
||||
accountSnaps.Add(size)
|
||||
case bytes.HasPrefix(key, SnapshotStoragePrefix) && len(key) == (len(SnapshotStoragePrefix)+2*common.HashLength):
|
||||
storageSnaps.Add(size)
|
||||
case bytes.HasPrefix(key, PreimagePrefix) && len(key) == (len(PreimagePrefix)+common.HashLength):
|
||||
preimages.Add(size)
|
||||
case bytes.HasPrefix(key, configPrefix) && len(key) == (len(configPrefix)+common.HashLength):
|
||||
metadata.Add(size)
|
||||
case bytes.HasPrefix(key, genesisPrefix) && len(key) == (len(genesisPrefix)+common.HashLength):
|
||||
metadata.Add(size)
|
||||
case bytes.HasPrefix(key, skeletonHeaderPrefix) && len(key) == (len(skeletonHeaderPrefix)+8):
|
||||
beaconHeaders.Add(size)
|
||||
case bytes.HasPrefix(key, CliqueSnapshotPrefix) && len(key) == 7+common.HashLength:
|
||||
cliqueSnaps.Add(size)
|
||||
|
||||
// new log index
|
||||
case bytes.HasPrefix(key, filterMapRowPrefix) && len(key) <= len(filterMapRowPrefix)+9:
|
||||
filterMapRows.Add(size)
|
||||
case bytes.HasPrefix(key, filterMapLastBlockPrefix) && len(key) == len(filterMapLastBlockPrefix)+4:
|
||||
filterMapLastBlock.Add(size)
|
||||
case bytes.HasPrefix(key, filterMapBlockLVPrefix) && len(key) == len(filterMapBlockLVPrefix)+8:
|
||||
filterMapBlockLV.Add(size)
|
||||
|
||||
// old log index (deprecated)
|
||||
case bytes.HasPrefix(key, bloomBitsPrefix) && len(key) == (len(bloomBitsPrefix)+10+common.HashLength):
|
||||
bloomBits.Add(size)
|
||||
case bytes.HasPrefix(key, bloomBitsMetaPrefix) && len(key) < len(bloomBitsMetaPrefix)+8:
|
||||
bloomBits.Add(size)
|
||||
|
||||
// Path-based historic state indexes
|
||||
case bytes.HasPrefix(key, StateHistoryIndexPrefix) && len(key) >= len(StateHistoryIndexPrefix)+common.HashLength:
|
||||
stateIndex.Add(size)
|
||||
|
||||
// Verkle trie data is detected, determine the sub-category
|
||||
case bytes.HasPrefix(key, VerklePrefix):
|
||||
remain := key[len(VerklePrefix):]
|
||||
inspectRange := func(ctx context.Context, r byte) error {
|
||||
var s []byte
|
||||
if len(keyStart) > 0 {
|
||||
switch {
|
||||
case IsAccountTrieNode(remain):
|
||||
verkleTries.Add(size)
|
||||
case bytes.HasPrefix(remain, stateIDPrefix) && len(remain) == len(stateIDPrefix)+common.HashLength:
|
||||
verkleStateLookups.Add(size)
|
||||
case bytes.Equal(remain, persistentStateIDKey):
|
||||
metadata.Add(size)
|
||||
case bytes.Equal(remain, trieJournalKey):
|
||||
metadata.Add(size)
|
||||
case bytes.Equal(remain, snapSyncStatusFlagKey):
|
||||
metadata.Add(size)
|
||||
case r < keyStart[0]:
|
||||
return nil
|
||||
case r == keyStart[0]:
|
||||
s = keyStart[1:]
|
||||
default:
|
||||
unaccounted.Add(size)
|
||||
// entire key range is included for inspection
|
||||
}
|
||||
}
|
||||
it := db.NewIterator(append(keyPrefix, r), s)
|
||||
defer it.Release()
|
||||
|
||||
// Metadata keys
|
||||
case slices.ContainsFunc(knownMetadataKeys, func(x []byte) bool { return bytes.Equal(x, key) }):
|
||||
metadata.Add(size)
|
||||
for it.Next() {
|
||||
var (
|
||||
key = it.Key()
|
||||
size = common.StorageSize(len(key) + len(it.Value()))
|
||||
)
|
||||
total.Add(uint64(size))
|
||||
count.Add(1)
|
||||
|
||||
default:
|
||||
unaccounted.Add(size)
|
||||
if len(key) >= 2 {
|
||||
prefix := [2]byte(key[:2])
|
||||
if _, ok := unaccountedKeys[prefix]; !ok {
|
||||
unaccountedKeys[prefix] = bytes.Clone(key)
|
||||
switch {
|
||||
case bytes.HasPrefix(key, headerPrefix) && len(key) == (len(headerPrefix)+8+common.HashLength):
|
||||
headers.add(size)
|
||||
case bytes.HasPrefix(key, blockBodyPrefix) && len(key) == (len(blockBodyPrefix)+8+common.HashLength):
|
||||
bodies.add(size)
|
||||
case bytes.HasPrefix(key, blockReceiptsPrefix) && len(key) == (len(blockReceiptsPrefix)+8+common.HashLength):
|
||||
receipts.add(size)
|
||||
case bytes.HasPrefix(key, headerPrefix) && bytes.HasSuffix(key, headerTDSuffix):
|
||||
tds.add(size)
|
||||
case bytes.HasPrefix(key, headerPrefix) && bytes.HasSuffix(key, headerHashSuffix):
|
||||
numHashPairings.add(size)
|
||||
case bytes.HasPrefix(key, headerNumberPrefix) && len(key) == (len(headerNumberPrefix)+common.HashLength):
|
||||
hashNumPairings.add(size)
|
||||
case IsLegacyTrieNode(key, it.Value()):
|
||||
legacyTries.add(size)
|
||||
case bytes.HasPrefix(key, stateIDPrefix) && len(key) == len(stateIDPrefix)+common.HashLength:
|
||||
stateLookups.add(size)
|
||||
case IsAccountTrieNode(key):
|
||||
accountTries.add(size)
|
||||
case IsStorageTrieNode(key):
|
||||
storageTries.add(size)
|
||||
case bytes.HasPrefix(key, CodePrefix) && len(key) == len(CodePrefix)+common.HashLength:
|
||||
codes.add(size)
|
||||
case bytes.HasPrefix(key, txLookupPrefix) && len(key) == (len(txLookupPrefix)+common.HashLength):
|
||||
txLookups.add(size)
|
||||
case bytes.HasPrefix(key, SnapshotAccountPrefix) && len(key) == (len(SnapshotAccountPrefix)+common.HashLength):
|
||||
accountSnaps.add(size)
|
||||
case bytes.HasPrefix(key, SnapshotStoragePrefix) && len(key) == (len(SnapshotStoragePrefix)+2*common.HashLength):
|
||||
storageSnaps.add(size)
|
||||
case bytes.HasPrefix(key, PreimagePrefix) && len(key) == (len(PreimagePrefix)+common.HashLength):
|
||||
preimages.add(size)
|
||||
case bytes.HasPrefix(key, configPrefix) && len(key) == (len(configPrefix)+common.HashLength):
|
||||
metadata.add(size)
|
||||
case bytes.HasPrefix(key, genesisPrefix) && len(key) == (len(genesisPrefix)+common.HashLength):
|
||||
metadata.add(size)
|
||||
case bytes.HasPrefix(key, skeletonHeaderPrefix) && len(key) == (len(skeletonHeaderPrefix)+8):
|
||||
beaconHeaders.add(size)
|
||||
case bytes.HasPrefix(key, CliqueSnapshotPrefix) && len(key) == 7+common.HashLength:
|
||||
cliqueSnaps.add(size)
|
||||
|
||||
// new log index
|
||||
case bytes.HasPrefix(key, filterMapRowPrefix) && len(key) <= len(filterMapRowPrefix)+9:
|
||||
filterMapRows.add(size)
|
||||
case bytes.HasPrefix(key, filterMapLastBlockPrefix) && len(key) == len(filterMapLastBlockPrefix)+4:
|
||||
filterMapLastBlock.add(size)
|
||||
case bytes.HasPrefix(key, filterMapBlockLVPrefix) && len(key) == len(filterMapBlockLVPrefix)+8:
|
||||
filterMapBlockLV.add(size)
|
||||
|
||||
// old log index (deprecated)
|
||||
case bytes.HasPrefix(key, bloomBitsPrefix) && len(key) == (len(bloomBitsPrefix)+10+common.HashLength):
|
||||
bloomBits.add(size)
|
||||
case bytes.HasPrefix(key, bloomBitsMetaPrefix) && len(key) < len(bloomBitsMetaPrefix)+8:
|
||||
bloomBits.add(size)
|
||||
|
||||
// Path-based historic state indexes
|
||||
case bytes.HasPrefix(key, StateHistoryIndexPrefix) && len(key) >= len(StateHistoryIndexPrefix)+common.HashLength:
|
||||
stateIndex.add(size)
|
||||
|
||||
// Verkle trie data is detected, determine the sub-category
|
||||
case bytes.HasPrefix(key, VerklePrefix):
|
||||
remain := key[len(VerklePrefix):]
|
||||
switch {
|
||||
case IsAccountTrieNode(remain):
|
||||
verkleTries.add(size)
|
||||
case bytes.HasPrefix(remain, stateIDPrefix) && len(remain) == len(stateIDPrefix)+common.HashLength:
|
||||
verkleStateLookups.add(size)
|
||||
case bytes.Equal(remain, persistentStateIDKey):
|
||||
metadata.add(size)
|
||||
case bytes.Equal(remain, trieJournalKey):
|
||||
metadata.add(size)
|
||||
case bytes.Equal(remain, snapSyncStatusFlagKey):
|
||||
metadata.add(size)
|
||||
default:
|
||||
unaccounted.add(size)
|
||||
}
|
||||
|
||||
// Metadata keys
|
||||
case slices.ContainsFunc(knownMetadataKeys, func(x []byte) bool { return bytes.Equal(x, key) }):
|
||||
metadata.add(size)
|
||||
|
||||
default:
|
||||
unaccounted.add(size)
|
||||
if len(key) >= 2 {
|
||||
prefix := [2]byte(key[:2])
|
||||
unaccountedMu.Lock()
|
||||
if _, ok := unaccountedKeys[prefix]; !ok {
|
||||
unaccountedKeys[prefix] = bytes.Clone(key)
|
||||
}
|
||||
unaccountedMu.Unlock()
|
||||
}
|
||||
}
|
||||
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return ctx.Err()
|
||||
default:
|
||||
}
|
||||
}
|
||||
count++
|
||||
if count%1000 == 0 && time.Since(logged) > 8*time.Second {
|
||||
log.Info("Inspecting database", "count", count, "elapsed", common.PrettyDuration(time.Since(start)))
|
||||
logged = time.Now()
|
||||
}
|
||||
|
||||
return it.Error()
|
||||
}
|
||||
|
||||
var (
|
||||
eg, ctx = errgroup.WithContext(context.Background())
|
||||
workers = runtime.NumCPU()
|
||||
)
|
||||
eg.SetLimit(workers)
|
||||
|
||||
// Progress reporter
|
||||
done := make(chan struct{})
|
||||
go func() {
|
||||
ticker := time.NewTicker(8 * time.Second)
|
||||
defer ticker.Stop()
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-ticker.C:
|
||||
log.Info("Inspecting database", "count", count.Load(), "size", common.StorageSize(total.Load()), "elapsed", common.PrettyDuration(time.Since(start)))
|
||||
case <-done:
|
||||
return
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
// Inspect key-value database in parallel.
|
||||
for i := 0; i < 256; i++ {
|
||||
eg.Go(func() error { return inspectRange(ctx, byte(i)) })
|
||||
}
|
||||
|
||||
if err := eg.Wait(); err != nil {
|
||||
close(done)
|
||||
return err
|
||||
}
|
||||
close(done)
|
||||
|
||||
// Display the database statistic of key-value store.
|
||||
stats := [][]string{
|
||||
{"Key-Value store", "Headers", headers.Size(), headers.Count()},
|
||||
{"Key-Value store", "Bodies", bodies.Size(), bodies.Count()},
|
||||
{"Key-Value store", "Receipt lists", receipts.Size(), receipts.Count()},
|
||||
{"Key-Value store", "Difficulties (deprecated)", tds.Size(), tds.Count()},
|
||||
{"Key-Value store", "Block number->hash", numHashPairings.Size(), numHashPairings.Count()},
|
||||
{"Key-Value store", "Block hash->number", hashNumPairings.Size(), hashNumPairings.Count()},
|
||||
{"Key-Value store", "Transaction index", txLookups.Size(), txLookups.Count()},
|
||||
{"Key-Value store", "Log index filter-map rows", filterMapRows.Size(), filterMapRows.Count()},
|
||||
{"Key-Value store", "Log index last-block-of-map", filterMapLastBlock.Size(), filterMapLastBlock.Count()},
|
||||
{"Key-Value store", "Log index block-lv", filterMapBlockLV.Size(), filterMapBlockLV.Count()},
|
||||
{"Key-Value store", "Log bloombits (deprecated)", bloomBits.Size(), bloomBits.Count()},
|
||||
{"Key-Value store", "Contract codes", codes.Size(), codes.Count()},
|
||||
{"Key-Value store", "Hash trie nodes", legacyTries.Size(), legacyTries.Count()},
|
||||
{"Key-Value store", "Path trie state lookups", stateLookups.Size(), stateLookups.Count()},
|
||||
{"Key-Value store", "Path trie account nodes", accountTries.Size(), accountTries.Count()},
|
||||
{"Key-Value store", "Path trie storage nodes", storageTries.Size(), storageTries.Count()},
|
||||
{"Key-Value store", "Path state history indexes", stateIndex.Size(), stateIndex.Count()},
|
||||
{"Key-Value store", "Verkle trie nodes", verkleTries.Size(), verkleTries.Count()},
|
||||
{"Key-Value store", "Verkle trie state lookups", verkleStateLookups.Size(), verkleStateLookups.Count()},
|
||||
{"Key-Value store", "Trie preimages", preimages.Size(), preimages.Count()},
|
||||
{"Key-Value store", "Account snapshot", accountSnaps.Size(), accountSnaps.Count()},
|
||||
{"Key-Value store", "Storage snapshot", storageSnaps.Size(), storageSnaps.Count()},
|
||||
{"Key-Value store", "Beacon sync headers", beaconHeaders.Size(), beaconHeaders.Count()},
|
||||
{"Key-Value store", "Clique snapshots", cliqueSnaps.Size(), cliqueSnaps.Count()},
|
||||
{"Key-Value store", "Singleton metadata", metadata.Size(), metadata.Count()},
|
||||
{"Key-Value store", "Headers", headers.sizeString(), headers.countString()},
|
||||
{"Key-Value store", "Bodies", bodies.sizeString(), bodies.countString()},
|
||||
{"Key-Value store", "Receipt lists", receipts.sizeString(), receipts.countString()},
|
||||
{"Key-Value store", "Difficulties (deprecated)", tds.sizeString(), tds.countString()},
|
||||
{"Key-Value store", "Block number->hash", numHashPairings.sizeString(), numHashPairings.countString()},
|
||||
{"Key-Value store", "Block hash->number", hashNumPairings.sizeString(), hashNumPairings.countString()},
|
||||
{"Key-Value store", "Transaction index", txLookups.sizeString(), txLookups.countString()},
|
||||
{"Key-Value store", "Log index filter-map rows", filterMapRows.sizeString(), filterMapRows.countString()},
|
||||
{"Key-Value store", "Log index last-block-of-map", filterMapLastBlock.sizeString(), filterMapLastBlock.countString()},
|
||||
{"Key-Value store", "Log index block-lv", filterMapBlockLV.sizeString(), filterMapBlockLV.countString()},
|
||||
{"Key-Value store", "Log bloombits (deprecated)", bloomBits.sizeString(), bloomBits.countString()},
|
||||
{"Key-Value store", "Contract codes", codes.sizeString(), codes.countString()},
|
||||
{"Key-Value store", "Hash trie nodes", legacyTries.sizeString(), legacyTries.countString()},
|
||||
{"Key-Value store", "Path trie state lookups", stateLookups.sizeString(), stateLookups.countString()},
|
||||
{"Key-Value store", "Path trie account nodes", accountTries.sizeString(), accountTries.countString()},
|
||||
{"Key-Value store", "Path trie storage nodes", storageTries.sizeString(), storageTries.countString()},
|
||||
{"Key-Value store", "Path state history indexes", stateIndex.sizeString(), stateIndex.countString()},
|
||||
{"Key-Value store", "Verkle trie nodes", verkleTries.sizeString(), verkleTries.countString()},
|
||||
{"Key-Value store", "Verkle trie state lookups", verkleStateLookups.sizeString(), verkleStateLookups.countString()},
|
||||
{"Key-Value store", "Trie preimages", preimages.sizeString(), preimages.countString()},
|
||||
{"Key-Value store", "Account snapshot", accountSnaps.sizeString(), accountSnaps.countString()},
|
||||
{"Key-Value store", "Storage snapshot", storageSnaps.sizeString(), storageSnaps.countString()},
|
||||
{"Key-Value store", "Beacon sync headers", beaconHeaders.sizeString(), beaconHeaders.countString()},
|
||||
{"Key-Value store", "Clique snapshots", cliqueSnaps.sizeString(), cliqueSnaps.countString()},
|
||||
{"Key-Value store", "Singleton metadata", metadata.sizeString(), metadata.countString()},
|
||||
}
|
||||
|
||||
// Inspect all registered append-only file store then.
|
||||
ancients, err := inspectFreezers(db)
|
||||
if err != nil {
|
||||
|
|
@ -580,16 +640,17 @@ func InspectDatabase(db ethdb.Database, keyPrefix, keyStart []byte) error {
|
|||
fmt.Sprintf("%d", ancient.count()),
|
||||
})
|
||||
}
|
||||
total += ancient.size()
|
||||
total.Add(uint64(ancient.size()))
|
||||
}
|
||||
table := tablewriter.NewWriter(os.Stdout)
|
||||
|
||||
table := newTableWriter(os.Stdout)
|
||||
table.SetHeader([]string{"Database", "Category", "Size", "Items"})
|
||||
table.SetFooter([]string{"", "Total", total.String(), " "})
|
||||
table.SetFooter([]string{"", "Total", common.StorageSize(total.Load()).String(), fmt.Sprintf("%d", count.Load())})
|
||||
table.AppendBulk(stats)
|
||||
table.Render()
|
||||
|
||||
if unaccounted.size > 0 {
|
||||
log.Error("Database contains unaccounted data", "size", unaccounted.size, "count", unaccounted.count)
|
||||
if !unaccounted.empty() {
|
||||
log.Error("Database contains unaccounted data", "size", unaccounted.sizeString(), "count", unaccounted.countString())
|
||||
for _, e := range slices.SortedFunc(maps.Values(unaccountedKeys), bytes.Compare) {
|
||||
log.Error(fmt.Sprintf(" example key: %x", e))
|
||||
}
|
||||
|
|
|
|||
208
core/rawdb/database_tablewriter_tinygo.go
Normal file
208
core/rawdb/database_tablewriter_tinygo.go
Normal file
|
|
@ -0,0 +1,208 @@
|
|||
// Copyright 2025 The go-ethereum Authors
|
||||
// This file is part of the go-ethereum library.
|
||||
//
|
||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
// TODO: naive stub implementation for tablewriter
|
||||
|
||||
//go:build tinygo
|
||||
// +build tinygo
|
||||
|
||||
package rawdb
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const (
|
||||
cellPadding = 1 // Number of spaces on each side of cell content
|
||||
totalPadding = 2 * cellPadding // Total padding per cell. Its two because we pad equally on both sides
|
||||
)
|
||||
|
||||
type Table struct {
|
||||
out io.Writer
|
||||
headers []string
|
||||
footer []string
|
||||
rows [][]string
|
||||
}
|
||||
|
||||
func newTableWriter(w io.Writer) *Table {
|
||||
return &Table{out: w}
|
||||
}
|
||||
|
||||
// SetHeader sets the header row for the table. Headers define the column names
|
||||
// and determine the number of columns for the entire table.
|
||||
//
|
||||
// All data rows and footer must have the same number of columns as the headers.
|
||||
//
|
||||
// Note: Headers are required - tables without headers will fail validation.
|
||||
func (t *Table) SetHeader(headers []string) {
|
||||
t.headers = headers
|
||||
}
|
||||
|
||||
// SetFooter sets an optional footer row for the table.
|
||||
//
|
||||
// The footer must have the same number of columns as the headers, or validation will fail.
|
||||
func (t *Table) SetFooter(footer []string) {
|
||||
t.footer = footer
|
||||
}
|
||||
|
||||
// AppendBulk sets all data rows for the table at once, replacing any existing rows.
|
||||
//
|
||||
// Each row must have the same number of columns as the headers, or validation
|
||||
// will fail during Render().
|
||||
func (t *Table) AppendBulk(rows [][]string) {
|
||||
t.rows = rows
|
||||
}
|
||||
|
||||
// Render outputs the complete table to the configured writer. The table is rendered
|
||||
// with headers, data rows, and optional footer.
|
||||
//
|
||||
// If validation fails, an error message is written to the output and rendering stops.
|
||||
func (t *Table) Render() {
|
||||
if err := t.render(); err != nil {
|
||||
fmt.Fprintf(t.out, "Error: %v\n", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func (t *Table) render() error {
|
||||
if err := t.validateColumnCount(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
widths := t.calculateColumnWidths()
|
||||
rowSeparator := t.buildRowSeparator(widths)
|
||||
|
||||
if len(t.headers) > 0 {
|
||||
t.printRow(t.headers, widths)
|
||||
fmt.Fprintln(t.out, rowSeparator)
|
||||
}
|
||||
|
||||
for _, row := range t.rows {
|
||||
t.printRow(row, widths)
|
||||
}
|
||||
|
||||
if len(t.footer) > 0 {
|
||||
fmt.Fprintln(t.out, rowSeparator)
|
||||
t.printRow(t.footer, widths)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// validateColumnCount checks that all rows and footer match the header column count
|
||||
func (t *Table) validateColumnCount() error {
|
||||
if len(t.headers) == 0 {
|
||||
return errors.New("table must have headers")
|
||||
}
|
||||
|
||||
expectedCols := len(t.headers)
|
||||
|
||||
// Check all rows have same column count as headers
|
||||
for i, row := range t.rows {
|
||||
if len(row) != expectedCols {
|
||||
return fmt.Errorf("row %d has %d columns, expected %d", i, len(row), expectedCols)
|
||||
}
|
||||
}
|
||||
|
||||
// Check footer has same column count as headers (if present)
|
||||
footerPresent := len(t.footer) > 0
|
||||
if footerPresent && len(t.footer) != expectedCols {
|
||||
return fmt.Errorf("footer has %d columns, expected %d", len(t.footer), expectedCols)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// calculateColumnWidths determines the minimum width needed for each column.
|
||||
//
|
||||
// This is done by finding the longest content in each column across headers, rows, and footer.
|
||||
//
|
||||
// Returns an int slice where widths[i] is the display width for column i (including padding).
|
||||
func (t *Table) calculateColumnWidths() []int {
|
||||
// Headers define the number of columns
|
||||
cols := len(t.headers)
|
||||
if cols == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Track maximum content width for each column (before padding)
|
||||
widths := make([]int, cols)
|
||||
|
||||
// Start with header widths
|
||||
for i, h := range t.headers {
|
||||
widths[i] = len(h)
|
||||
}
|
||||
|
||||
// Find max width needed for data cells in each column
|
||||
for _, row := range t.rows {
|
||||
for i, cell := range row {
|
||||
widths[i] = max(widths[i], len(cell))
|
||||
}
|
||||
}
|
||||
|
||||
// Find max width needed for footer in each column
|
||||
for i, f := range t.footer {
|
||||
widths[i] = max(widths[i], len(f))
|
||||
}
|
||||
|
||||
for i := range widths {
|
||||
widths[i] += totalPadding
|
||||
}
|
||||
|
||||
return widths
|
||||
}
|
||||
|
||||
// buildRowSeparator creates a horizontal line to separate table rows.
|
||||
//
|
||||
// It generates a string with dashes (-) for each column width, joined by plus signs (+).
|
||||
//
|
||||
// Example output: "----------+--------+-----------"
|
||||
func (t *Table) buildRowSeparator(widths []int) string {
|
||||
parts := make([]string, len(widths))
|
||||
for i, w := range widths {
|
||||
parts[i] = strings.Repeat("-", w)
|
||||
}
|
||||
return strings.Join(parts, "+")
|
||||
}
|
||||
|
||||
// printRow outputs a single row to the table writer.
|
||||
//
|
||||
// Each cell is padded with spaces and separated by pipe characters (|).
|
||||
//
|
||||
// Example output: " Database | Size | Items "
|
||||
func (t *Table) printRow(row []string, widths []int) {
|
||||
for i, cell := range row {
|
||||
if i > 0 {
|
||||
fmt.Fprint(t.out, "|")
|
||||
}
|
||||
|
||||
// Calculate centering pad without padding
|
||||
contentWidth := widths[i] - totalPadding
|
||||
cellLen := len(cell)
|
||||
leftPadCentering := (contentWidth - cellLen) / 2
|
||||
rightPadCentering := contentWidth - cellLen - leftPadCentering
|
||||
|
||||
// Build padded cell with centering
|
||||
leftPadding := strings.Repeat(" ", cellPadding+leftPadCentering)
|
||||
rightPadding := strings.Repeat(" ", cellPadding+rightPadCentering)
|
||||
|
||||
fmt.Fprintf(t.out, "%s%s%s", leftPadding, cell, rightPadding)
|
||||
}
|
||||
fmt.Fprintln(t.out)
|
||||
}
|
||||
124
core/rawdb/database_tablewriter_tinygo_test.go
Normal file
124
core/rawdb/database_tablewriter_tinygo_test.go
Normal file
|
|
@ -0,0 +1,124 @@
|
|||
// Copyright 2025 The go-ethereum Authors
|
||||
// This file is part of the go-ethereum library.
|
||||
//
|
||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//go:build tinygo
|
||||
// +build tinygo
|
||||
|
||||
package rawdb
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestTableWriterTinyGo(t *testing.T) {
|
||||
var buf bytes.Buffer
|
||||
table := newTableWriter(&buf)
|
||||
|
||||
headers := []string{"Database", "Size", "Items", "Status"}
|
||||
rows := [][]string{
|
||||
{"chaindata", "2.5 GB", "1,234,567", "Active"},
|
||||
{"state", "890 MB", "456,789", "Active"},
|
||||
{"ancient", "15.2 GB", "2,345,678", "Readonly"},
|
||||
{"logs", "120 MB", "89,012", "Active"},
|
||||
}
|
||||
footer := []string{"Total", "18.71 GB", "4,125,046", "-"}
|
||||
|
||||
table.SetHeader(headers)
|
||||
table.AppendBulk(rows)
|
||||
table.SetFooter(footer)
|
||||
table.Render()
|
||||
|
||||
output := buf.String()
|
||||
t.Logf("Table output using custom stub implementation:\n%s", output)
|
||||
}
|
||||
|
||||
func TestTableWriterValidationErrors(t *testing.T) {
|
||||
// Test missing headers
|
||||
t.Run("MissingHeaders", func(t *testing.T) {
|
||||
var buf bytes.Buffer
|
||||
table := newTableWriter(&buf)
|
||||
|
||||
rows := [][]string{{"x", "y", "z"}}
|
||||
|
||||
table.AppendBulk(rows)
|
||||
table.Render()
|
||||
|
||||
output := buf.String()
|
||||
if !strings.Contains(output, "table must have headers") {
|
||||
t.Errorf("Expected error for missing headers, got: %s", output)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("NotEnoughRowColumns", func(t *testing.T) {
|
||||
var buf bytes.Buffer
|
||||
table := newTableWriter(&buf)
|
||||
|
||||
headers := []string{"A", "B", "C"}
|
||||
badRows := [][]string{
|
||||
{"x", "y"}, // Missing column
|
||||
}
|
||||
|
||||
table.SetHeader(headers)
|
||||
table.AppendBulk(badRows)
|
||||
table.Render()
|
||||
|
||||
output := buf.String()
|
||||
if !strings.Contains(output, "row 0 has 2 columns, expected 3") {
|
||||
t.Errorf("Expected validation error for row 0, got: %s", output)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("TooManyRowColumns", func(t *testing.T) {
|
||||
var buf bytes.Buffer
|
||||
table := newTableWriter(&buf)
|
||||
|
||||
headers := []string{"A", "B", "C"}
|
||||
badRows := [][]string{
|
||||
{"p", "q", "r", "s"}, // Extra column
|
||||
}
|
||||
|
||||
table.SetHeader(headers)
|
||||
table.AppendBulk(badRows)
|
||||
table.Render()
|
||||
|
||||
output := buf.String()
|
||||
if !strings.Contains(output, "row 0 has 4 columns, expected 3") {
|
||||
t.Errorf("Expected validation error for row 0, got: %s", output)
|
||||
}
|
||||
})
|
||||
|
||||
// Test mismatched footer columns
|
||||
t.Run("MismatchedFooterColumns", func(t *testing.T) {
|
||||
var buf bytes.Buffer
|
||||
table := newTableWriter(&buf)
|
||||
|
||||
headers := []string{"A", "B", "C"}
|
||||
rows := [][]string{{"x", "y", "z"}}
|
||||
footer := []string{"total", "sum"} // Missing column
|
||||
|
||||
table.SetHeader(headers)
|
||||
table.AppendBulk(rows)
|
||||
table.SetFooter(footer)
|
||||
table.Render()
|
||||
|
||||
output := buf.String()
|
||||
if !strings.Contains(output, "footer has 2 columns, expected 3") {
|
||||
t.Errorf("Expected validation error for footer, got: %s", output)
|
||||
}
|
||||
})
|
||||
}
|
||||
33
core/rawdb/database_tablewriter_unix.go
Normal file
33
core/rawdb/database_tablewriter_unix.go
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
// Copyright 2025 The go-ethereum Authors
|
||||
// This file is part of the go-ethereum library.
|
||||
//
|
||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//go:build !tinygo
|
||||
// +build !tinygo
|
||||
|
||||
package rawdb
|
||||
|
||||
import (
|
||||
"io"
|
||||
|
||||
"github.com/olekukonko/tablewriter"
|
||||
)
|
||||
|
||||
// Re-export the real tablewriter types and functions
|
||||
type Table = tablewriter.Table
|
||||
|
||||
func newTableWriter(w io.Writer) *Table {
|
||||
return tablewriter.NewWriter(w)
|
||||
}
|
||||
|
|
@ -95,7 +95,7 @@ var (
|
|||
uncleanShutdownKey = []byte("unclean-shutdown") // config prefix for the db
|
||||
|
||||
// transitionStatusKey tracks the eth2 transition status.
|
||||
transitionStatusKey = []byte("eth2-transition")
|
||||
transitionStatusKey = []byte("eth2-transition") // deprecated
|
||||
|
||||
// snapSyncStatusFlagKey flags that status of snap sync.
|
||||
snapSyncStatusFlagKey = []byte("SnapSyncStatus")
|
||||
|
|
@ -384,21 +384,48 @@ func accountHistoryIndexKey(addressHash common.Hash) []byte {
|
|||
|
||||
// storageHistoryIndexKey = StateHistoryStorageMetadataPrefix + addressHash + storageHash
|
||||
func storageHistoryIndexKey(addressHash common.Hash, storageHash common.Hash) []byte {
|
||||
return append(append(StateHistoryStorageMetadataPrefix, addressHash.Bytes()...), storageHash.Bytes()...)
|
||||
totalLen := len(StateHistoryStorageMetadataPrefix) + 2*common.HashLength
|
||||
out := make([]byte, totalLen)
|
||||
|
||||
off := 0
|
||||
off += copy(out[off:], StateHistoryStorageMetadataPrefix)
|
||||
off += copy(out[off:], addressHash.Bytes())
|
||||
copy(out[off:], storageHash.Bytes())
|
||||
|
||||
return out
|
||||
}
|
||||
|
||||
// accountHistoryIndexBlockKey = StateHistoryAccountBlockPrefix + addressHash + blockID
|
||||
func accountHistoryIndexBlockKey(addressHash common.Hash, blockID uint32) []byte {
|
||||
var buf [4]byte
|
||||
binary.BigEndian.PutUint32(buf[:], blockID)
|
||||
return append(append(StateHistoryAccountBlockPrefix, addressHash.Bytes()...), buf[:]...)
|
||||
var buf4 [4]byte
|
||||
binary.BigEndian.PutUint32(buf4[:], blockID)
|
||||
|
||||
totalLen := len(StateHistoryAccountBlockPrefix) + common.HashLength + 4
|
||||
out := make([]byte, totalLen)
|
||||
|
||||
off := 0
|
||||
off += copy(out[off:], StateHistoryAccountBlockPrefix)
|
||||
off += copy(out[off:], addressHash.Bytes())
|
||||
copy(out[off:], buf4[:])
|
||||
|
||||
return out
|
||||
}
|
||||
|
||||
// storageHistoryIndexBlockKey = StateHistoryStorageBlockPrefix + addressHash + storageHash + blockID
|
||||
func storageHistoryIndexBlockKey(addressHash common.Hash, storageHash common.Hash, blockID uint32) []byte {
|
||||
var buf [4]byte
|
||||
binary.BigEndian.PutUint32(buf[:], blockID)
|
||||
return append(append(append(StateHistoryStorageBlockPrefix, addressHash.Bytes()...), storageHash.Bytes()...), buf[:]...)
|
||||
var buf4 [4]byte
|
||||
binary.BigEndian.PutUint32(buf4[:], blockID)
|
||||
|
||||
totalLen := len(StateHistoryStorageBlockPrefix) + 2*common.HashLength + 4
|
||||
out := make([]byte, totalLen)
|
||||
|
||||
off := 0
|
||||
off += copy(out[off:], StateHistoryStorageBlockPrefix)
|
||||
off += copy(out[off:], addressHash.Bytes())
|
||||
off += copy(out[off:], storageHash.Bytes())
|
||||
copy(out[off:], buf4[:])
|
||||
|
||||
return out
|
||||
}
|
||||
|
||||
// transitionStateKey = transitionStatusKey + hash
|
||||
|
|
|
|||
|
|
@ -81,11 +81,19 @@ type Trie interface {
|
|||
// be returned.
|
||||
GetAccount(address common.Address) (*types.StateAccount, error)
|
||||
|
||||
// PrefetchAccount attempts to resolve specific accounts from the database
|
||||
// to accelerate subsequent trie operations.
|
||||
PrefetchAccount([]common.Address) error
|
||||
|
||||
// GetStorage returns the value for key stored in the trie. The value bytes
|
||||
// must not be modified by the caller. If a node was not found in the database,
|
||||
// a trie.MissingNodeError is returned.
|
||||
GetStorage(addr common.Address, key []byte) ([]byte, error)
|
||||
|
||||
// PrefetchStorage attempts to resolve specific storage slots from the database
|
||||
// to accelerate subsequent trie operations.
|
||||
PrefetchStorage(addr common.Address, keys [][]byte) error
|
||||
|
||||
// UpdateAccount abstracts an account write to the trie. It encodes the
|
||||
// provided account object with associated algorithm and then updates it
|
||||
// in the trie with provided address.
|
||||
|
|
@ -122,7 +130,7 @@ type Trie interface {
|
|||
|
||||
// Witness returns a set containing all trie nodes that have been accessed.
|
||||
// The returned map could be nil if the witness is empty.
|
||||
Witness() map[string]struct{}
|
||||
Witness() map[string][]byte
|
||||
|
||||
// NodeIterator returns an iterator that returns nodes of the trie. Iteration
|
||||
// starts at the key after the given start key. And error will be returned
|
||||
|
|
|
|||
|
|
@ -160,11 +160,8 @@ func prune(snaptree *snapshot.Tree, root common.Hash, maindb ethdb.Database, sta
|
|||
|
||||
var eta time.Duration // Realistically will never remain uninited
|
||||
if done := binary.BigEndian.Uint64(key[:8]); done > 0 {
|
||||
var (
|
||||
left = math.MaxUint64 - binary.BigEndian.Uint64(key[:8])
|
||||
speed = done/uint64(time.Since(pstart)/time.Millisecond+1) + 1 // +1s to avoid division by zero
|
||||
)
|
||||
eta = time.Duration(left/speed) * time.Millisecond
|
||||
left := math.MaxUint64 - binary.BigEndian.Uint64(key[:8])
|
||||
eta = common.CalculateETA(done, left, time.Since(pstart))
|
||||
}
|
||||
if time.Since(logged) > 8*time.Second {
|
||||
log.Info("Pruning state data", "nodes", count, "skipped", skipped, "size", size,
|
||||
|
|
|
|||
|
|
@ -232,7 +232,7 @@ type trieReader struct {
|
|||
lock sync.Mutex // Lock for protecting concurrent read
|
||||
}
|
||||
|
||||
// trieReader constructs a trie reader of the specific state. An error will be
|
||||
// newTrieReader constructs a trie reader of the specific state. An error will be
|
||||
// returned if the associated trie specified by root is not existent.
|
||||
func newTrieReader(root common.Hash, db *triedb.Database, cache *utils.PointCache) (*trieReader, error) {
|
||||
var (
|
||||
|
|
@ -253,7 +253,7 @@ func newTrieReader(root common.Hash, db *triedb.Database, cache *utils.PointCach
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
tr = trie.NewTransitionTree(mpt, tr.(*trie.VerkleTrie), false)
|
||||
tr = trie.NewTransitionTrie(mpt, tr.(*trie.VerkleTrie), false)
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
|
|
|
|||
|
|
@ -171,20 +171,16 @@ func (stat *generateStats) report() {
|
|||
// If there's progress on the account trie, estimate the time to finish crawling it
|
||||
if done := binary.BigEndian.Uint64(stat.head[:8]) / stat.accounts; done > 0 {
|
||||
var (
|
||||
left = (math.MaxUint64 - binary.BigEndian.Uint64(stat.head[:8])) / stat.accounts
|
||||
speed = done/uint64(time.Since(stat.start)/time.Millisecond+1) + 1 // +1s to avoid division by zero
|
||||
eta = time.Duration(left/speed) * time.Millisecond
|
||||
left = (math.MaxUint64 - binary.BigEndian.Uint64(stat.head[:8])) / stat.accounts
|
||||
eta = common.CalculateETA(done, left, time.Since(stat.start))
|
||||
)
|
||||
// If there are large contract crawls in progress, estimate their finish time
|
||||
for acc, head := range stat.slotsHead {
|
||||
start := stat.slotsStart[acc]
|
||||
if done := binary.BigEndian.Uint64(head[:8]); done > 0 {
|
||||
var (
|
||||
left = math.MaxUint64 - binary.BigEndian.Uint64(head[:8])
|
||||
speed = done/uint64(time.Since(start)/time.Millisecond+1) + 1 // +1s to avoid division by zero
|
||||
)
|
||||
left := math.MaxUint64 - binary.BigEndian.Uint64(head[:8])
|
||||
// Override the ETA if larger than the largest until now
|
||||
if slotETA := time.Duration(left/speed) * time.Millisecond; eta < slotETA {
|
||||
if slotETA := common.CalculateETA(done, left, time.Since(start)); eta < slotETA {
|
||||
eta = slotETA
|
||||
}
|
||||
}
|
||||
|
|
|
|||
638
core/state/state_sizer.go
Normal file
638
core/state/state_sizer.go
Normal file
|
|
@ -0,0 +1,638 @@
|
|||
// Copyright 2025 The go-ethereum Authors
|
||||
// This file is part of the go-ethereum library.
|
||||
//
|
||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package state
|
||||
|
||||
import (
|
||||
"container/heap"
|
||||
"errors"
|
||||
"fmt"
|
||||
"maps"
|
||||
"runtime"
|
||||
"slices"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/core/rawdb"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/ethdb"
|
||||
"github.com/ethereum/go-ethereum/log"
|
||||
"github.com/ethereum/go-ethereum/triedb"
|
||||
"golang.org/x/sync/errgroup"
|
||||
)
|
||||
|
||||
const (
|
||||
statEvictThreshold = 128 // the depth of statistic to be preserved
|
||||
)
|
||||
|
||||
// Database key scheme for states.
|
||||
var (
|
||||
accountKeySize = int64(len(rawdb.SnapshotAccountPrefix) + common.HashLength)
|
||||
storageKeySize = int64(len(rawdb.SnapshotStoragePrefix) + common.HashLength*2)
|
||||
accountTrienodePrefixSize = int64(len(rawdb.TrieNodeAccountPrefix))
|
||||
storageTrienodePrefixSize = int64(len(rawdb.TrieNodeStoragePrefix) + common.HashLength)
|
||||
codeKeySize = int64(len(rawdb.CodePrefix) + common.HashLength)
|
||||
)
|
||||
|
||||
// SizeStats represents either the current state size statistics or the size
|
||||
// differences resulting from a state transition.
|
||||
type SizeStats struct {
|
||||
StateRoot common.Hash // State root hash at the time of measurement
|
||||
BlockNumber uint64 // Associated block number at the time of measurement
|
||||
|
||||
Accounts int64 // Total number of accounts in the state
|
||||
AccountBytes int64 // Total storage size used by all account data (in bytes)
|
||||
Storages int64 // Total number of storage slots across all accounts
|
||||
StorageBytes int64 // Total storage size used by all storage slot data (in bytes)
|
||||
AccountTrienodes int64 // Total number of account trie nodes in the state
|
||||
AccountTrienodeBytes int64 // Total storage size occupied by account trie nodes (in bytes)
|
||||
StorageTrienodes int64 // Total number of storage trie nodes in the state
|
||||
StorageTrienodeBytes int64 // Total storage size occupied by storage trie nodes (in bytes)
|
||||
ContractCodes int64 // Total number of contract codes in the state
|
||||
ContractCodeBytes int64 // Total size of all contract code (in bytes)
|
||||
}
|
||||
|
||||
func (s SizeStats) String() string {
|
||||
return fmt.Sprintf("Accounts: %d(%s), Storages: %d(%s), AccountTrienodes: %d(%s), StorageTrienodes: %d(%s), Codes: %d(%s)",
|
||||
s.Accounts, common.StorageSize(s.AccountBytes),
|
||||
s.Storages, common.StorageSize(s.StorageBytes),
|
||||
s.AccountTrienodes, common.StorageSize(s.AccountTrienodeBytes),
|
||||
s.StorageTrienodes, common.StorageSize(s.StorageTrienodeBytes),
|
||||
s.ContractCodes, common.StorageSize(s.ContractCodeBytes),
|
||||
)
|
||||
}
|
||||
|
||||
// add applies the given state diffs and produces a new version of the statistics.
|
||||
func (s SizeStats) add(diff SizeStats) SizeStats {
|
||||
s.StateRoot = diff.StateRoot
|
||||
s.BlockNumber = diff.BlockNumber
|
||||
|
||||
s.Accounts += diff.Accounts
|
||||
s.AccountBytes += diff.AccountBytes
|
||||
s.Storages += diff.Storages
|
||||
s.StorageBytes += diff.StorageBytes
|
||||
s.AccountTrienodes += diff.AccountTrienodes
|
||||
s.AccountTrienodeBytes += diff.AccountTrienodeBytes
|
||||
s.StorageTrienodes += diff.StorageTrienodes
|
||||
s.StorageTrienodeBytes += diff.StorageTrienodeBytes
|
||||
s.ContractCodes += diff.ContractCodes
|
||||
s.ContractCodeBytes += diff.ContractCodeBytes
|
||||
return s
|
||||
}
|
||||
|
||||
// calSizeStats measures the state size changes of the provided state update.
|
||||
func calSizeStats(update *stateUpdate) (SizeStats, error) {
|
||||
stats := SizeStats{
|
||||
BlockNumber: update.blockNumber,
|
||||
StateRoot: update.root,
|
||||
}
|
||||
|
||||
// Measure the account changes
|
||||
for addr, oldValue := range update.accountsOrigin {
|
||||
addrHash := crypto.Keccak256Hash(addr.Bytes())
|
||||
newValue, exists := update.accounts[addrHash]
|
||||
if !exists {
|
||||
return SizeStats{}, fmt.Errorf("account %x not found", addr)
|
||||
}
|
||||
oldLen, newLen := len(oldValue), len(newValue)
|
||||
|
||||
switch {
|
||||
case oldLen > 0 && newLen == 0:
|
||||
// Account deletion
|
||||
stats.Accounts -= 1
|
||||
stats.AccountBytes -= accountKeySize + int64(oldLen)
|
||||
case oldLen == 0 && newLen > 0:
|
||||
// Account creation
|
||||
stats.Accounts += 1
|
||||
stats.AccountBytes += accountKeySize + int64(newLen)
|
||||
default:
|
||||
// Account update
|
||||
stats.AccountBytes += int64(newLen - oldLen)
|
||||
}
|
||||
}
|
||||
|
||||
// Measure storage changes
|
||||
for addr, slots := range update.storagesOrigin {
|
||||
addrHash := crypto.Keccak256Hash(addr.Bytes())
|
||||
subset, exists := update.storages[addrHash]
|
||||
if !exists {
|
||||
return SizeStats{}, fmt.Errorf("storage %x not found", addr)
|
||||
}
|
||||
for key, oldValue := range slots {
|
||||
var (
|
||||
exists bool
|
||||
newValue []byte
|
||||
)
|
||||
if update.rawStorageKey {
|
||||
newValue, exists = subset[crypto.Keccak256Hash(key.Bytes())]
|
||||
} else {
|
||||
newValue, exists = subset[key]
|
||||
}
|
||||
if !exists {
|
||||
return SizeStats{}, fmt.Errorf("storage slot %x-%x not found", addr, key)
|
||||
}
|
||||
oldLen, newLen := len(oldValue), len(newValue)
|
||||
|
||||
switch {
|
||||
case oldLen > 0 && newLen == 0:
|
||||
// Storage deletion
|
||||
stats.Storages -= 1
|
||||
stats.StorageBytes -= storageKeySize + int64(oldLen)
|
||||
case oldLen == 0 && newLen > 0:
|
||||
// Storage creation
|
||||
stats.Storages += 1
|
||||
stats.StorageBytes += storageKeySize + int64(newLen)
|
||||
default:
|
||||
// Storage update
|
||||
stats.StorageBytes += int64(newLen - oldLen)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Measure trienode changes
|
||||
for owner, subset := range update.nodes.Sets {
|
||||
var (
|
||||
keyPrefix int64
|
||||
isAccount = owner == (common.Hash{})
|
||||
)
|
||||
if isAccount {
|
||||
keyPrefix = accountTrienodePrefixSize
|
||||
} else {
|
||||
keyPrefix = storageTrienodePrefixSize
|
||||
}
|
||||
|
||||
// Iterate over Origins since every modified node has an origin entry
|
||||
for path, oldNode := range subset.Origins {
|
||||
newNode, exists := subset.Nodes[path]
|
||||
if !exists {
|
||||
return SizeStats{}, fmt.Errorf("node %x-%v not found", owner, path)
|
||||
}
|
||||
keySize := keyPrefix + int64(len(path))
|
||||
|
||||
switch {
|
||||
case len(oldNode) > 0 && len(newNode.Blob) == 0:
|
||||
// Node deletion
|
||||
if isAccount {
|
||||
stats.AccountTrienodes -= 1
|
||||
stats.AccountTrienodeBytes -= keySize + int64(len(oldNode))
|
||||
} else {
|
||||
stats.StorageTrienodes -= 1
|
||||
stats.StorageTrienodeBytes -= keySize + int64(len(oldNode))
|
||||
}
|
||||
case len(oldNode) == 0 && len(newNode.Blob) > 0:
|
||||
// Node creation
|
||||
if isAccount {
|
||||
stats.AccountTrienodes += 1
|
||||
stats.AccountTrienodeBytes += keySize + int64(len(newNode.Blob))
|
||||
} else {
|
||||
stats.StorageTrienodes += 1
|
||||
stats.StorageTrienodeBytes += keySize + int64(len(newNode.Blob))
|
||||
}
|
||||
default:
|
||||
// Node update
|
||||
if isAccount {
|
||||
stats.AccountTrienodeBytes += int64(len(newNode.Blob) - len(oldNode))
|
||||
} else {
|
||||
stats.StorageTrienodeBytes += int64(len(newNode.Blob) - len(oldNode))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Measure code changes. Note that the reported contract code size may be slightly
|
||||
// inaccurate due to database deduplication (code is stored by its hash). However,
|
||||
// this deviation is negligible and acceptable for measurement purposes.
|
||||
for _, code := range update.codes {
|
||||
stats.ContractCodes += 1
|
||||
stats.ContractCodeBytes += codeKeySize + int64(len(code.blob))
|
||||
}
|
||||
return stats, nil
|
||||
}
|
||||
|
||||
type stateSizeQuery struct {
|
||||
root *common.Hash // nil means latest
|
||||
err error // non-nil if the state size is not yet initialized
|
||||
result chan *SizeStats // nil means the state is unknown
|
||||
}
|
||||
|
||||
// SizeTracker handles the state size initialization and tracks of state size metrics.
|
||||
type SizeTracker struct {
|
||||
db ethdb.KeyValueStore
|
||||
triedb *triedb.Database
|
||||
abort chan struct{}
|
||||
aborted chan struct{}
|
||||
updateCh chan *stateUpdate
|
||||
queryCh chan *stateSizeQuery
|
||||
}
|
||||
|
||||
// NewSizeTracker creates a new state size tracker and starts it automatically
|
||||
func NewSizeTracker(db ethdb.KeyValueStore, triedb *triedb.Database) (*SizeTracker, error) {
|
||||
if triedb.Scheme() != rawdb.PathScheme {
|
||||
return nil, errors.New("state size tracker is not compatible with hash mode")
|
||||
}
|
||||
t := &SizeTracker{
|
||||
db: db,
|
||||
triedb: triedb,
|
||||
abort: make(chan struct{}),
|
||||
aborted: make(chan struct{}),
|
||||
updateCh: make(chan *stateUpdate),
|
||||
queryCh: make(chan *stateSizeQuery),
|
||||
}
|
||||
go t.run()
|
||||
return t, nil
|
||||
}
|
||||
|
||||
func (t *SizeTracker) Stop() {
|
||||
close(t.abort)
|
||||
<-t.aborted
|
||||
}
|
||||
|
||||
// sizeStatsHeap is a heap.Interface implementation over statesize statistics for
|
||||
// retrieving the oldest statistics for eviction.
|
||||
type sizeStatsHeap []SizeStats
|
||||
|
||||
func (h sizeStatsHeap) Len() int { return len(h) }
|
||||
func (h sizeStatsHeap) Less(i, j int) bool { return h[i].BlockNumber < h[j].BlockNumber }
|
||||
func (h sizeStatsHeap) Swap(i, j int) { h[i], h[j] = h[j], h[i] }
|
||||
|
||||
func (h *sizeStatsHeap) Push(x any) {
|
||||
*h = append(*h, x.(SizeStats))
|
||||
}
|
||||
|
||||
func (h *sizeStatsHeap) Pop() any {
|
||||
old := *h
|
||||
n := len(old)
|
||||
x := old[n-1]
|
||||
*h = old[0 : n-1]
|
||||
return x
|
||||
}
|
||||
|
||||
// run performs the state size initialization and handles updates
|
||||
func (t *SizeTracker) run() {
|
||||
defer close(t.aborted)
|
||||
|
||||
var last common.Hash
|
||||
stats, err := t.init() // launch background thread for state size init
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
h := sizeStatsHeap(slices.Collect(maps.Values(stats)))
|
||||
heap.Init(&h)
|
||||
|
||||
for {
|
||||
select {
|
||||
case u := <-t.updateCh:
|
||||
base, found := stats[u.originRoot]
|
||||
if !found {
|
||||
log.Debug("Ignored the state size without parent", "parent", u.originRoot, "root", u.root, "number", u.blockNumber)
|
||||
continue
|
||||
}
|
||||
diff, err := calSizeStats(u)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
stat := base.add(diff)
|
||||
stats[u.root] = stat
|
||||
last = u.root
|
||||
|
||||
heap.Push(&h, stats[u.root])
|
||||
for u.blockNumber-h[0].BlockNumber > statEvictThreshold {
|
||||
delete(stats, h[0].StateRoot)
|
||||
heap.Pop(&h)
|
||||
}
|
||||
log.Debug("Update state size", "number", stat.BlockNumber, "root", stat.StateRoot, "stat", stat)
|
||||
|
||||
case r := <-t.queryCh:
|
||||
var root common.Hash
|
||||
if r.root != nil {
|
||||
root = *r.root
|
||||
} else {
|
||||
root = last
|
||||
}
|
||||
if s, ok := stats[root]; ok {
|
||||
r.result <- &s
|
||||
} else {
|
||||
r.result <- nil
|
||||
}
|
||||
|
||||
case <-t.abort:
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type buildResult struct {
|
||||
stat SizeStats
|
||||
root common.Hash
|
||||
blockNumber uint64
|
||||
elapsed time.Duration
|
||||
err error
|
||||
}
|
||||
|
||||
func (t *SizeTracker) init() (map[common.Hash]SizeStats, error) {
|
||||
// Wait for snapshot completion and then init
|
||||
ticker := time.NewTicker(10 * time.Second)
|
||||
defer ticker.Stop()
|
||||
|
||||
wait:
|
||||
for {
|
||||
select {
|
||||
case <-ticker.C:
|
||||
if t.triedb.SnapshotCompleted() {
|
||||
break wait
|
||||
}
|
||||
case <-t.updateCh:
|
||||
continue
|
||||
case r := <-t.queryCh:
|
||||
r.err = errors.New("state size is not initialized yet")
|
||||
r.result <- nil
|
||||
case <-t.abort:
|
||||
return nil, errors.New("size tracker closed")
|
||||
}
|
||||
}
|
||||
|
||||
var (
|
||||
updates = make(map[common.Hash]*stateUpdate)
|
||||
children = make(map[common.Hash][]common.Hash)
|
||||
done chan buildResult
|
||||
)
|
||||
|
||||
for {
|
||||
select {
|
||||
case u := <-t.updateCh:
|
||||
updates[u.root] = u
|
||||
children[u.originRoot] = append(children[u.originRoot], u.root)
|
||||
log.Debug("Received state update", "root", u.root, "blockNumber", u.blockNumber)
|
||||
|
||||
case r := <-t.queryCh:
|
||||
r.err = errors.New("state size is not initialized yet")
|
||||
r.result <- nil
|
||||
|
||||
case <-ticker.C:
|
||||
// Only check timer if build hasn't started yet
|
||||
if done != nil {
|
||||
continue
|
||||
}
|
||||
root := rawdb.ReadSnapshotRoot(t.db)
|
||||
if root == (common.Hash{}) {
|
||||
continue
|
||||
}
|
||||
entry, exists := updates[root]
|
||||
if !exists {
|
||||
continue
|
||||
}
|
||||
done = make(chan buildResult)
|
||||
go t.build(entry.root, entry.blockNumber, done)
|
||||
log.Info("Measuring persistent state size", "root", root.Hex(), "number", entry.blockNumber)
|
||||
|
||||
case result := <-done:
|
||||
if result.err != nil {
|
||||
return nil, result.err
|
||||
}
|
||||
var (
|
||||
stats = make(map[common.Hash]SizeStats)
|
||||
apply func(root common.Hash, stat SizeStats) error
|
||||
)
|
||||
apply = func(root common.Hash, base SizeStats) error {
|
||||
for _, child := range children[root] {
|
||||
entry, ok := updates[child]
|
||||
if !ok {
|
||||
return fmt.Errorf("the state update is not found, %x", child)
|
||||
}
|
||||
diff, err := calSizeStats(entry)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
stats[child] = base.add(diff)
|
||||
if err := apply(child, stats[child]); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
if err := apply(result.root, result.stat); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Set initial latest stats
|
||||
stats[result.root] = result.stat
|
||||
log.Info("Measured persistent state size", "root", result.root, "number", result.blockNumber, "stat", result.stat, "elapsed", common.PrettyDuration(result.elapsed))
|
||||
return stats, nil
|
||||
|
||||
case <-t.abort:
|
||||
return nil, errors.New("size tracker closed")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (t *SizeTracker) build(root common.Hash, blockNumber uint64, done chan buildResult) {
|
||||
// Metrics will be directly updated by each goroutine
|
||||
var (
|
||||
accounts, accountBytes int64
|
||||
storages, storageBytes int64
|
||||
codes, codeBytes int64
|
||||
|
||||
accountTrienodes, accountTrienodeBytes int64
|
||||
storageTrienodes, storageTrienodeBytes int64
|
||||
|
||||
group errgroup.Group
|
||||
start = time.Now()
|
||||
)
|
||||
|
||||
// Start all table iterations concurrently with direct metric updates
|
||||
group.Go(func() error {
|
||||
count, bytes, err := t.iterateTableParallel(t.abort, rawdb.SnapshotAccountPrefix, "account")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
accounts, accountBytes = count, bytes
|
||||
return nil
|
||||
})
|
||||
|
||||
group.Go(func() error {
|
||||
count, bytes, err := t.iterateTableParallel(t.abort, rawdb.SnapshotStoragePrefix, "storage")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
storages, storageBytes = count, bytes
|
||||
return nil
|
||||
})
|
||||
|
||||
group.Go(func() error {
|
||||
count, bytes, err := t.iterateTableParallel(t.abort, rawdb.TrieNodeAccountPrefix, "accountnode")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
accountTrienodes, accountTrienodeBytes = count, bytes
|
||||
return nil
|
||||
})
|
||||
|
||||
group.Go(func() error {
|
||||
count, bytes, err := t.iterateTableParallel(t.abort, rawdb.TrieNodeStoragePrefix, "storagenode")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
storageTrienodes, storageTrienodeBytes = count, bytes
|
||||
return nil
|
||||
})
|
||||
|
||||
group.Go(func() error {
|
||||
count, bytes, err := t.iterateTable(t.abort, rawdb.CodePrefix, "contractcode")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
codes, codeBytes = count, bytes
|
||||
return nil
|
||||
})
|
||||
|
||||
// Wait for all goroutines to complete
|
||||
if err := group.Wait(); err != nil {
|
||||
done <- buildResult{err: err}
|
||||
} else {
|
||||
stat := SizeStats{
|
||||
StateRoot: root,
|
||||
BlockNumber: blockNumber,
|
||||
Accounts: accounts,
|
||||
AccountBytes: accountBytes,
|
||||
Storages: storages,
|
||||
StorageBytes: storageBytes,
|
||||
AccountTrienodes: accountTrienodes,
|
||||
AccountTrienodeBytes: accountTrienodeBytes,
|
||||
StorageTrienodes: storageTrienodes,
|
||||
StorageTrienodeBytes: storageTrienodeBytes,
|
||||
ContractCodes: codes,
|
||||
ContractCodeBytes: codeBytes,
|
||||
}
|
||||
done <- buildResult{
|
||||
root: root,
|
||||
blockNumber: blockNumber,
|
||||
stat: stat,
|
||||
elapsed: time.Since(start),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// iterateTable performs iteration over a specific table and returns the results.
|
||||
func (t *SizeTracker) iterateTable(closed chan struct{}, prefix []byte, name string) (int64, int64, error) {
|
||||
var (
|
||||
start = time.Now()
|
||||
logged = time.Now()
|
||||
count, bytes int64
|
||||
)
|
||||
|
||||
iter := t.db.NewIterator(prefix, nil)
|
||||
defer iter.Release()
|
||||
|
||||
log.Debug("Iterating state", "category", name)
|
||||
for iter.Next() {
|
||||
count++
|
||||
bytes += int64(len(iter.Key()) + len(iter.Value()))
|
||||
|
||||
if time.Since(logged) > time.Second*8 {
|
||||
logged = time.Now()
|
||||
|
||||
select {
|
||||
case <-closed:
|
||||
log.Debug("State iteration cancelled", "category", name)
|
||||
return 0, 0, errors.New("size tracker closed")
|
||||
default:
|
||||
log.Debug("Iterating state", "category", name, "count", count, "size", common.StorageSize(bytes))
|
||||
}
|
||||
}
|
||||
}
|
||||
// Check for iterator errors
|
||||
if err := iter.Error(); err != nil {
|
||||
log.Error("Iterator error", "category", name, "err", err)
|
||||
return 0, 0, err
|
||||
}
|
||||
log.Debug("Finished state iteration", "category", name, "count", count, "size", common.StorageSize(bytes), "elapsed", common.PrettyDuration(time.Since(start)))
|
||||
return count, bytes, nil
|
||||
}
|
||||
|
||||
// iterateTableParallel performs parallel iteration over a table by splitting into
|
||||
// hex ranges. For storage tables, it splits on the first byte of the account hash
|
||||
// (after the prefix).
|
||||
func (t *SizeTracker) iterateTableParallel(closed chan struct{}, prefix []byte, name string) (int64, int64, error) {
|
||||
var (
|
||||
totalCount int64
|
||||
totalBytes int64
|
||||
|
||||
start = time.Now()
|
||||
workers = runtime.NumCPU()
|
||||
group errgroup.Group
|
||||
mu sync.Mutex
|
||||
)
|
||||
group.SetLimit(workers)
|
||||
log.Debug("Starting parallel state iteration", "category", name, "workers", workers)
|
||||
|
||||
if len(prefix) > 0 {
|
||||
if blob, err := t.db.Get(prefix); err == nil && len(blob) > 0 {
|
||||
// If there's a direct hit on the prefix, include it in the stats
|
||||
totalCount = 1
|
||||
totalBytes = int64(len(prefix) + len(blob))
|
||||
}
|
||||
}
|
||||
for i := 0; i < 256; i++ {
|
||||
h := byte(i)
|
||||
group.Go(func() error {
|
||||
count, bytes, err := t.iterateTable(closed, slices.Concat(prefix, []byte{h}), fmt.Sprintf("%s-%02x", name, h))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
mu.Lock()
|
||||
totalCount += count
|
||||
totalBytes += bytes
|
||||
mu.Unlock()
|
||||
return nil
|
||||
})
|
||||
}
|
||||
if err := group.Wait(); err != nil {
|
||||
return 0, 0, err
|
||||
}
|
||||
log.Debug("Finished parallel state iteration", "category", name, "count", totalCount, "size", common.StorageSize(totalBytes), "elapsed", common.PrettyDuration(time.Since(start)))
|
||||
return totalCount, totalBytes, nil
|
||||
}
|
||||
|
||||
// Notify is an async method used to send the state update to the size tracker.
|
||||
// It ignores empty updates (where no state changes occurred).
|
||||
// If the channel is full, it drops the update to avoid blocking.
|
||||
func (t *SizeTracker) Notify(update *stateUpdate) {
|
||||
if update == nil || update.empty() {
|
||||
return
|
||||
}
|
||||
select {
|
||||
case t.updateCh <- update:
|
||||
case <-t.abort:
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Query returns the state size specified by the root, or nil if not available.
|
||||
// If the root is nil, query the size of latest chain head;
|
||||
// If the root is non-nil, query the size of the specified state;
|
||||
func (t *SizeTracker) Query(root *common.Hash) (*SizeStats, error) {
|
||||
r := &stateSizeQuery{
|
||||
root: root,
|
||||
result: make(chan *SizeStats, 1),
|
||||
}
|
||||
select {
|
||||
case <-t.aborted:
|
||||
return nil, errors.New("state sizer has been closed")
|
||||
case t.queryCh <- r:
|
||||
return <-r.result, r.err
|
||||
}
|
||||
}
|
||||
231
core/state/state_sizer_test.go
Normal file
231
core/state/state_sizer_test.go
Normal file
|
|
@ -0,0 +1,231 @@
|
|||
// Copyright 2025 The go-ethereum Authors
|
||||
// This file is part of the go-ethereum library.
|
||||
//
|
||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package state
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/core/rawdb"
|
||||
"github.com/ethereum/go-ethereum/core/tracing"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/triedb"
|
||||
"github.com/ethereum/go-ethereum/triedb/pathdb"
|
||||
"github.com/holiman/uint256"
|
||||
)
|
||||
|
||||
func TestSizeTracker(t *testing.T) {
|
||||
db := rawdb.NewMemoryDatabase()
|
||||
defer db.Close()
|
||||
|
||||
tdb := triedb.NewDatabase(db, &triedb.Config{PathDB: pathdb.Defaults})
|
||||
sdb := NewDatabase(tdb, nil)
|
||||
|
||||
// Generate 50 blocks to establish a baseline
|
||||
baselineBlockNum := uint64(50)
|
||||
currentRoot := types.EmptyRootHash
|
||||
|
||||
addr1 := common.BytesToAddress([]byte{1, 0, 0, 1})
|
||||
addr2 := common.BytesToAddress([]byte{1, 0, 0, 2})
|
||||
addr3 := common.BytesToAddress([]byte{1, 0, 0, 3})
|
||||
|
||||
// Create initial state with fixed accounts
|
||||
state, _ := New(currentRoot, sdb)
|
||||
state.AddBalance(addr1, uint256.NewInt(1000), tracing.BalanceChangeUnspecified)
|
||||
state.SetNonce(addr1, 1, tracing.NonceChangeUnspecified)
|
||||
state.SetState(addr1, common.HexToHash("0x1111"), common.HexToHash("0xaaaa"))
|
||||
state.SetState(addr1, common.HexToHash("0x2222"), common.HexToHash("0xbbbb"))
|
||||
|
||||
state.AddBalance(addr2, uint256.NewInt(2000), tracing.BalanceChangeUnspecified)
|
||||
state.SetNonce(addr2, 2, tracing.NonceChangeUnspecified)
|
||||
state.SetCode(addr2, []byte{0x60, 0x80, 0x60, 0x40, 0x52}, tracing.CodeChangeUnspecified)
|
||||
|
||||
state.AddBalance(addr3, uint256.NewInt(3000), tracing.BalanceChangeUnspecified)
|
||||
state.SetNonce(addr3, 3, tracing.NonceChangeUnspecified)
|
||||
|
||||
currentRoot, _, err := state.CommitWithUpdate(1, true, false)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to commit initial state: %v", err)
|
||||
}
|
||||
if err := tdb.Commit(currentRoot, false); err != nil {
|
||||
t.Fatalf("Failed to commit initial trie: %v", err)
|
||||
}
|
||||
|
||||
for i := 1; i < 50; i++ { // blocks 2-50
|
||||
blockNum := uint64(i + 1)
|
||||
|
||||
newState, err := New(currentRoot, sdb)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create new state at block %d: %v", blockNum, err)
|
||||
}
|
||||
testAddr := common.BigToAddress(uint256.NewInt(uint64(i + 100)).ToBig())
|
||||
newState.AddBalance(testAddr, uint256.NewInt(uint64((i+1)*1000)), tracing.BalanceChangeUnspecified)
|
||||
newState.SetNonce(testAddr, uint64(i+10), tracing.NonceChangeUnspecified)
|
||||
|
||||
if i%2 == 0 {
|
||||
newState.SetState(addr1, common.BigToHash(uint256.NewInt(uint64(i+0x1000)).ToBig()), common.BigToHash(uint256.NewInt(uint64(i+0x2000)).ToBig()))
|
||||
}
|
||||
if i%3 == 0 {
|
||||
newState.SetCode(testAddr, []byte{byte(i), 0x60, 0x80, byte(i + 1), 0x52}, tracing.CodeChangeUnspecified)
|
||||
}
|
||||
root, _, err := newState.CommitWithUpdate(blockNum, true, false)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to commit state at block %d: %v", blockNum, err)
|
||||
}
|
||||
if err := tdb.Commit(root, false); err != nil {
|
||||
t.Fatalf("Failed to commit trie at block %d: %v", blockNum, err)
|
||||
}
|
||||
currentRoot = root
|
||||
}
|
||||
baselineRoot := currentRoot
|
||||
|
||||
// Wait for snapshot completion
|
||||
for !tdb.SnapshotCompleted() {
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
}
|
||||
|
||||
// Calculate baseline from the intermediate persisted state
|
||||
baselineTracker := &SizeTracker{
|
||||
db: db,
|
||||
triedb: tdb,
|
||||
abort: make(chan struct{}),
|
||||
}
|
||||
done := make(chan buildResult)
|
||||
|
||||
go baselineTracker.build(baselineRoot, baselineBlockNum, done)
|
||||
var baselineResult buildResult
|
||||
select {
|
||||
case baselineResult = <-done:
|
||||
if baselineResult.err != nil {
|
||||
t.Fatalf("Failed to get baseline stats: %v", baselineResult.err)
|
||||
}
|
||||
case <-time.After(30 * time.Second):
|
||||
t.Fatal("Timeout waiting for baseline stats")
|
||||
}
|
||||
baseline := baselineResult.stat
|
||||
|
||||
// Now start the tracker and notify it of updates that happen AFTER the baseline
|
||||
tracker, err := NewSizeTracker(db, tdb)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create size tracker: %v", err)
|
||||
}
|
||||
defer tracker.Stop()
|
||||
|
||||
var trackedUpdates []SizeStats
|
||||
currentRoot = baselineRoot
|
||||
|
||||
// Generate additional blocks beyond the baseline and track them
|
||||
for i := 49; i < 130; i++ { // blocks 51-132
|
||||
blockNum := uint64(i + 2)
|
||||
newState, err := New(currentRoot, sdb)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create new state at block %d: %v", blockNum, err)
|
||||
}
|
||||
testAddr := common.BigToAddress(uint256.NewInt(uint64(i + 100)).ToBig())
|
||||
newState.AddBalance(testAddr, uint256.NewInt(uint64((i+1)*1000)), tracing.BalanceChangeUnspecified)
|
||||
newState.SetNonce(testAddr, uint64(i+10), tracing.NonceChangeUnspecified)
|
||||
|
||||
if i%2 == 0 {
|
||||
newState.SetState(addr1, common.BigToHash(uint256.NewInt(uint64(i+0x1000)).ToBig()), common.BigToHash(uint256.NewInt(uint64(i+0x2000)).ToBig()))
|
||||
}
|
||||
if i%3 == 0 {
|
||||
newState.SetCode(testAddr, []byte{byte(i), 0x60, 0x80, byte(i + 1), 0x52}, tracing.CodeChangeUnspecified)
|
||||
}
|
||||
root, update, err := newState.CommitWithUpdate(blockNum, true, false)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to commit state at block %d: %v", blockNum, err)
|
||||
}
|
||||
if err := tdb.Commit(root, false); err != nil {
|
||||
t.Fatalf("Failed to commit trie at block %d: %v", blockNum, err)
|
||||
}
|
||||
|
||||
diff, err := calSizeStats(update)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to calculate size stats for block %d: %v", blockNum, err)
|
||||
}
|
||||
trackedUpdates = append(trackedUpdates, diff)
|
||||
tracker.Notify(update)
|
||||
currentRoot = root
|
||||
}
|
||||
finalRoot := rawdb.ReadSnapshotRoot(db)
|
||||
|
||||
// Ensure all commits are flushed to disk
|
||||
if err := tdb.Close(); err != nil {
|
||||
t.Fatalf("Failed to close triedb: %v", err)
|
||||
}
|
||||
// Reopen the database to simulate a restart
|
||||
tdb = triedb.NewDatabase(db, &triedb.Config{PathDB: pathdb.Defaults})
|
||||
defer tdb.Close()
|
||||
|
||||
finalTracker := &SizeTracker{
|
||||
db: db,
|
||||
triedb: tdb,
|
||||
abort: make(chan struct{}),
|
||||
}
|
||||
finalDone := make(chan buildResult)
|
||||
|
||||
go finalTracker.build(finalRoot, uint64(132), finalDone)
|
||||
var result buildResult
|
||||
select {
|
||||
case result = <-finalDone:
|
||||
if result.err != nil {
|
||||
t.Fatalf("Failed to build final stats: %v", result.err)
|
||||
}
|
||||
case <-time.After(30 * time.Second):
|
||||
t.Fatal("Timeout waiting for final stats")
|
||||
}
|
||||
actualStats := result.stat
|
||||
|
||||
expectedStats := baseline
|
||||
for _, diff := range trackedUpdates {
|
||||
expectedStats = expectedStats.add(diff)
|
||||
}
|
||||
|
||||
// The final measured stats should match our calculated expected stats exactly
|
||||
if actualStats.Accounts != expectedStats.Accounts {
|
||||
t.Errorf("Account count mismatch: baseline(%d) + tracked_changes = %d, but final_measurement = %d", baseline.Accounts, expectedStats.Accounts, actualStats.Accounts)
|
||||
}
|
||||
if actualStats.AccountBytes != expectedStats.AccountBytes {
|
||||
t.Errorf("Account bytes mismatch: expected %d, got %d", expectedStats.AccountBytes, actualStats.AccountBytes)
|
||||
}
|
||||
if actualStats.Storages != expectedStats.Storages {
|
||||
t.Errorf("Storage count mismatch: baseline(%d) + tracked_changes = %d, but final_measurement = %d", baseline.Storages, expectedStats.Storages, actualStats.Storages)
|
||||
}
|
||||
if actualStats.StorageBytes != expectedStats.StorageBytes {
|
||||
t.Errorf("Storage bytes mismatch: expected %d, got %d", expectedStats.StorageBytes, actualStats.StorageBytes)
|
||||
}
|
||||
if actualStats.ContractCodes != expectedStats.ContractCodes {
|
||||
t.Errorf("Contract code count mismatch: baseline(%d) + tracked_changes = %d, but final_measurement = %d", baseline.ContractCodes, expectedStats.ContractCodes, actualStats.ContractCodes)
|
||||
}
|
||||
if actualStats.ContractCodeBytes != expectedStats.ContractCodeBytes {
|
||||
t.Errorf("Contract code bytes mismatch: expected %d, got %d", expectedStats.ContractCodeBytes, actualStats.ContractCodeBytes)
|
||||
}
|
||||
// TODO: failed on github actions, need to investigate
|
||||
// if actualStats.AccountTrienodes != expectedStats.AccountTrienodes {
|
||||
// t.Errorf("Account trie nodes mismatch: expected %d, got %d", expectedStats.AccountTrienodes, actualStats.AccountTrienodes)
|
||||
// }
|
||||
// if actualStats.AccountTrienodeBytes != expectedStats.AccountTrienodeBytes {
|
||||
// t.Errorf("Account trie node bytes mismatch: expected %d, got %d", expectedStats.AccountTrienodeBytes, actualStats.AccountTrienodeBytes)
|
||||
// }
|
||||
if actualStats.StorageTrienodes != expectedStats.StorageTrienodes {
|
||||
t.Errorf("Storage trie nodes mismatch: expected %d, got %d", expectedStats.StorageTrienodes, actualStats.StorageTrienodes)
|
||||
}
|
||||
if actualStats.StorageTrienodeBytes != expectedStats.StorageTrienodeBytes {
|
||||
t.Errorf("Storage trie node bytes mismatch: expected %d, got %d", expectedStats.StorageTrienodeBytes, actualStats.StorageTrienodeBytes)
|
||||
}
|
||||
}
|
||||
|
|
@ -136,7 +136,8 @@ type StateDB struct {
|
|||
journal *journal
|
||||
|
||||
// State witness if cross validation is needed
|
||||
witness *stateless.Witness
|
||||
witness *stateless.Witness
|
||||
witnessStats *stateless.WitnessStats
|
||||
|
||||
// Measurements gathered during execution for debugging purposes
|
||||
AccountReads time.Duration
|
||||
|
|
@ -191,12 +192,13 @@ func NewWithReader(root common.Hash, db Database, reader Reader) (*StateDB, erro
|
|||
// StartPrefetcher initializes a new trie prefetcher to pull in nodes from the
|
||||
// state trie concurrently while the state is mutated so that when we reach the
|
||||
// commit phase, most of the needed data is already hot.
|
||||
func (s *StateDB) StartPrefetcher(namespace string, witness *stateless.Witness) {
|
||||
func (s *StateDB) StartPrefetcher(namespace string, witness *stateless.Witness, witnessStats *stateless.WitnessStats) {
|
||||
// Terminate any previously running prefetcher
|
||||
s.StopPrefetcher()
|
||||
|
||||
// Enable witness collection if requested
|
||||
s.witness = witness
|
||||
s.witnessStats = witnessStats
|
||||
|
||||
// With the switch to the Proof-of-Stake consensus algorithm, block production
|
||||
// rewards are now handled at the consensus layer. Consequently, a block may
|
||||
|
|
@ -456,7 +458,7 @@ func (s *StateDB) SetNonce(addr common.Address, nonce uint64, reason tracing.Non
|
|||
}
|
||||
}
|
||||
|
||||
func (s *StateDB) SetCode(addr common.Address, code []byte) (prev []byte) {
|
||||
func (s *StateDB) SetCode(addr common.Address, code []byte, reason tracing.CodeChangeReason) (prev []byte) {
|
||||
stateObject := s.getOrNewStateObject(addr)
|
||||
if stateObject != nil {
|
||||
return stateObject.SetCode(crypto.Keccak256Hash(code), code)
|
||||
|
|
@ -858,9 +860,17 @@ func (s *StateDB) IntermediateRoot(deleteEmptyObjects bool) common.Hash {
|
|||
continue
|
||||
}
|
||||
if trie := obj.getPrefetchedTrie(); trie != nil {
|
||||
s.witness.AddState(trie.Witness())
|
||||
witness := trie.Witness()
|
||||
s.witness.AddState(witness)
|
||||
if s.witnessStats != nil {
|
||||
s.witnessStats.Add(witness, obj.addrHash)
|
||||
}
|
||||
} else if obj.trie != nil {
|
||||
s.witness.AddState(obj.trie.Witness())
|
||||
witness := obj.trie.Witness()
|
||||
s.witness.AddState(witness)
|
||||
if s.witnessStats != nil {
|
||||
s.witnessStats.Add(witness, obj.addrHash)
|
||||
}
|
||||
}
|
||||
}
|
||||
// Pull in only-read and non-destructed trie witnesses
|
||||
|
|
@ -874,9 +884,17 @@ func (s *StateDB) IntermediateRoot(deleteEmptyObjects bool) common.Hash {
|
|||
continue
|
||||
}
|
||||
if trie := obj.getPrefetchedTrie(); trie != nil {
|
||||
s.witness.AddState(trie.Witness())
|
||||
witness := trie.Witness()
|
||||
s.witness.AddState(witness)
|
||||
if s.witnessStats != nil {
|
||||
s.witnessStats.Add(witness, obj.addrHash)
|
||||
}
|
||||
} else if obj.trie != nil {
|
||||
s.witness.AddState(obj.trie.Witness())
|
||||
witness := obj.trie.Witness()
|
||||
s.witness.AddState(witness)
|
||||
if s.witnessStats != nil {
|
||||
s.witnessStats.Add(witness, obj.addrHash)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -942,7 +960,11 @@ func (s *StateDB) IntermediateRoot(deleteEmptyObjects bool) common.Hash {
|
|||
|
||||
// If witness building is enabled, gather the account trie witness
|
||||
if s.witness != nil {
|
||||
s.witness.AddState(s.trie.Witness())
|
||||
witness := s.trie.Witness()
|
||||
s.witness.AddState(witness)
|
||||
if s.witnessStats != nil {
|
||||
s.witnessStats.Add(witness, common.Hash{})
|
||||
}
|
||||
}
|
||||
return hash
|
||||
}
|
||||
|
|
@ -1133,7 +1155,7 @@ func (s *StateDB) GetTrie() Trie {
|
|||
|
||||
// commit gathers the state mutations accumulated along with the associated
|
||||
// trie changes, resetting all internal flags with the new state as the base.
|
||||
func (s *StateDB) commit(deleteEmptyObjects bool, noStorageWiping bool) (*stateUpdate, error) {
|
||||
func (s *StateDB) commit(deleteEmptyObjects bool, noStorageWiping bool, blockNumber uint64) (*stateUpdate, error) {
|
||||
// Short circuit in case any database failure occurred earlier.
|
||||
if s.dbErr != nil {
|
||||
return nil, fmt.Errorf("commit aborted due to earlier error: %v", s.dbErr)
|
||||
|
|
@ -1285,13 +1307,13 @@ func (s *StateDB) commit(deleteEmptyObjects bool, noStorageWiping bool) (*stateU
|
|||
origin := s.originalRoot
|
||||
s.originalRoot = root
|
||||
|
||||
return newStateUpdate(noStorageWiping, origin, root, deletes, updates, nodes), nil
|
||||
return newStateUpdate(noStorageWiping, origin, root, blockNumber, deletes, updates, nodes), nil
|
||||
}
|
||||
|
||||
// commitAndFlush is a wrapper of commit which also commits the state mutations
|
||||
// to the configured data stores.
|
||||
func (s *StateDB) commitAndFlush(block uint64, deleteEmptyObjects bool, noStorageWiping bool) (*stateUpdate, error) {
|
||||
ret, err := s.commit(deleteEmptyObjects, noStorageWiping)
|
||||
ret, err := s.commit(deleteEmptyObjects, noStorageWiping, block)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
@ -1356,6 +1378,16 @@ func (s *StateDB) Commit(block uint64, deleteEmptyObjects bool, noStorageWiping
|
|||
return ret.root, nil
|
||||
}
|
||||
|
||||
// CommitWithUpdate writes the state mutations and returns both the root hash and the state update.
|
||||
// This is useful for tracking state changes at the blockchain level.
|
||||
func (s *StateDB) CommitWithUpdate(block uint64, deleteEmptyObjects bool, noStorageWiping bool) (common.Hash, *stateUpdate, error) {
|
||||
ret, err := s.commitAndFlush(block, deleteEmptyObjects, noStorageWiping)
|
||||
if err != nil {
|
||||
return common.Hash{}, nil, err
|
||||
}
|
||||
return ret.root, ret, nil
|
||||
}
|
||||
|
||||
// Prepare handles the preparatory steps for executing a state transition with.
|
||||
// This method must be invoked before state transition.
|
||||
//
|
||||
|
|
|
|||
|
|
@ -89,7 +89,7 @@ func newStateTestAction(addr common.Address, r *rand.Rand, index int) testAction
|
|||
code := make([]byte, 16)
|
||||
binary.BigEndian.PutUint64(code, uint64(a.args[0]))
|
||||
binary.BigEndian.PutUint64(code[8:], uint64(a.args[1]))
|
||||
s.SetCode(addr, code)
|
||||
s.SetCode(addr, code, tracing.CodeChangeUnspecified)
|
||||
},
|
||||
args: make([]int64, 2),
|
||||
},
|
||||
|
|
|
|||
|
|
@ -189,14 +189,20 @@ func (s *hookedStateDB) SetNonce(address common.Address, nonce uint64, reason tr
|
|||
}
|
||||
}
|
||||
|
||||
func (s *hookedStateDB) SetCode(address common.Address, code []byte) []byte {
|
||||
prev := s.inner.SetCode(address, code)
|
||||
if s.hooks.OnCodeChange != nil {
|
||||
func (s *hookedStateDB) SetCode(address common.Address, code []byte, reason tracing.CodeChangeReason) []byte {
|
||||
prev := s.inner.SetCode(address, code, reason)
|
||||
if s.hooks.OnCodeChangeV2 != nil || s.hooks.OnCodeChange != nil {
|
||||
prevHash := types.EmptyCodeHash
|
||||
if len(prev) != 0 {
|
||||
prevHash = crypto.Keccak256Hash(prev)
|
||||
}
|
||||
s.hooks.OnCodeChange(address, prevHash, prev, crypto.Keccak256Hash(code), code)
|
||||
codeHash := crypto.Keccak256Hash(code)
|
||||
|
||||
if s.hooks.OnCodeChangeV2 != nil {
|
||||
s.hooks.OnCodeChangeV2(address, prevHash, prev, codeHash, code, reason)
|
||||
} else if s.hooks.OnCodeChange != nil {
|
||||
s.hooks.OnCodeChange(address, prevHash, prev, codeHash, code)
|
||||
}
|
||||
}
|
||||
return prev
|
||||
}
|
||||
|
|
@ -224,8 +230,12 @@ func (s *hookedStateDB) SelfDestruct(address common.Address) uint256.Int {
|
|||
s.hooks.OnBalanceChange(address, prev.ToBig(), new(big.Int), tracing.BalanceDecreaseSelfdestruct)
|
||||
}
|
||||
|
||||
if s.hooks.OnCodeChange != nil && len(prevCode) > 0 {
|
||||
s.hooks.OnCodeChange(address, prevCodeHash, prevCode, types.EmptyCodeHash, nil)
|
||||
if len(prevCode) > 0 {
|
||||
if s.hooks.OnCodeChangeV2 != nil {
|
||||
s.hooks.OnCodeChangeV2(address, prevCodeHash, prevCode, types.EmptyCodeHash, nil, tracing.CodeChangeSelfDestruct)
|
||||
} else if s.hooks.OnCodeChange != nil {
|
||||
s.hooks.OnCodeChange(address, prevCodeHash, prevCode, types.EmptyCodeHash, nil)
|
||||
}
|
||||
}
|
||||
|
||||
return prev
|
||||
|
|
@ -242,12 +252,16 @@ func (s *hookedStateDB) SelfDestruct6780(address common.Address) (uint256.Int, b
|
|||
|
||||
prev, changed := s.inner.SelfDestruct6780(address)
|
||||
|
||||
if s.hooks.OnBalanceChange != nil && changed && !prev.IsZero() {
|
||||
if s.hooks.OnBalanceChange != nil && !prev.IsZero() {
|
||||
s.hooks.OnBalanceChange(address, prev.ToBig(), new(big.Int), tracing.BalanceDecreaseSelfdestruct)
|
||||
}
|
||||
|
||||
if s.hooks.OnCodeChange != nil && changed && len(prevCode) > 0 {
|
||||
s.hooks.OnCodeChange(address, prevCodeHash, prevCode, types.EmptyCodeHash, nil)
|
||||
if changed && len(prevCode) > 0 {
|
||||
if s.hooks.OnCodeChangeV2 != nil {
|
||||
s.hooks.OnCodeChangeV2(address, prevCodeHash, prevCode, types.EmptyCodeHash, nil, tracing.CodeChangeSelfDestruct)
|
||||
} else if s.hooks.OnCodeChange != nil {
|
||||
s.hooks.OnCodeChange(address, prevCodeHash, prevCode, types.EmptyCodeHash, nil)
|
||||
}
|
||||
}
|
||||
|
||||
return prev, changed
|
||||
|
|
|
|||
|
|
@ -114,7 +114,7 @@ func TestHooks(t *testing.T) {
|
|||
sdb.AddBalance(common.Address{0xaa}, uint256.NewInt(100), tracing.BalanceChangeUnspecified)
|
||||
sdb.SubBalance(common.Address{0xaa}, uint256.NewInt(50), tracing.BalanceChangeTransfer)
|
||||
sdb.SetNonce(common.Address{0xaa}, 1337, tracing.NonceChangeGenesis)
|
||||
sdb.SetCode(common.Address{0xaa}, []byte{0x13, 37})
|
||||
sdb.SetCode(common.Address{0xaa}, []byte{0x13, 37}, tracing.CodeChangeUnspecified)
|
||||
sdb.SetState(common.Address{0xaa}, common.HexToHash("0x01"), common.HexToHash("0x11"))
|
||||
sdb.SetState(common.Address{0xaa}, common.HexToHash("0x01"), common.HexToHash("0x22"))
|
||||
sdb.SetTransientState(common.Address{0xaa}, common.HexToHash("0x02"), common.HexToHash("0x01"))
|
||||
|
|
|
|||
|
|
@ -65,7 +65,7 @@ func TestUpdateLeaks(t *testing.T) {
|
|||
state.SetState(addr, common.BytesToHash([]byte{i, i, i}), common.BytesToHash([]byte{i, i, i, i}))
|
||||
}
|
||||
if i%3 == 0 {
|
||||
state.SetCode(addr, []byte{i, i, i, i, i})
|
||||
state.SetCode(addr, []byte{i, i, i, i, i}, tracing.CodeChangeUnspecified)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -101,7 +101,7 @@ func TestIntermediateLeaks(t *testing.T) {
|
|||
state.SetState(addr, common.Hash{i, i, i, tweak}, common.Hash{i, i, i, i, tweak})
|
||||
}
|
||||
if i%3 == 0 {
|
||||
state.SetCode(addr, []byte{i, i, i, i, i, tweak})
|
||||
state.SetCode(addr, []byte{i, i, i, i, i, tweak}, tracing.CodeChangeUnspecified)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -374,7 +374,7 @@ func newTestAction(addr common.Address, r *rand.Rand) testAction {
|
|||
code := make([]byte, 16)
|
||||
binary.BigEndian.PutUint64(code, uint64(a.args[0]))
|
||||
binary.BigEndian.PutUint64(code[8:], uint64(a.args[1]))
|
||||
s.SetCode(addr, code)
|
||||
s.SetCode(addr, code, tracing.CodeChangeUnspecified)
|
||||
},
|
||||
args: make([]int64, 2),
|
||||
},
|
||||
|
|
@ -403,7 +403,7 @@ func newTestAction(addr common.Address, r *rand.Rand) testAction {
|
|||
// which would cause a difference in state when unrolling
|
||||
// the journal. (CreateContact assumes created was false prior to
|
||||
// invocation, and the journal rollback sets it to false).
|
||||
s.SetCode(addr, []byte{1})
|
||||
s.SetCode(addr, []byte{1}, tracing.CodeChangeUnspecified)
|
||||
}
|
||||
},
|
||||
},
|
||||
|
|
@ -731,7 +731,7 @@ func TestCopyCommitCopy(t *testing.T) {
|
|||
sval := common.HexToHash("bbb")
|
||||
|
||||
state.SetBalance(addr, uint256.NewInt(42), tracing.BalanceChangeUnspecified) // Change the account trie
|
||||
state.SetCode(addr, []byte("hello")) // Change an external metadata
|
||||
state.SetCode(addr, []byte("hello"), tracing.CodeChangeUnspecified) // Change an external metadata
|
||||
state.SetState(addr, skey, sval) // Change the storage trie
|
||||
|
||||
if balance := state.GetBalance(addr); balance.Cmp(uint256.NewInt(42)) != 0 {
|
||||
|
|
@ -804,7 +804,7 @@ func TestCopyCopyCommitCopy(t *testing.T) {
|
|||
sval := common.HexToHash("bbb")
|
||||
|
||||
state.SetBalance(addr, uint256.NewInt(42), tracing.BalanceChangeUnspecified) // Change the account trie
|
||||
state.SetCode(addr, []byte("hello")) // Change an external metadata
|
||||
state.SetCode(addr, []byte("hello"), tracing.CodeChangeUnspecified) // Change an external metadata
|
||||
state.SetState(addr, skey, sval) // Change the storage trie
|
||||
|
||||
if balance := state.GetBalance(addr); balance.Cmp(uint256.NewInt(42)) != 0 {
|
||||
|
|
@ -874,7 +874,7 @@ func TestCommitCopy(t *testing.T) {
|
|||
sval1, sval2 := common.HexToHash("b1"), common.HexToHash("b2")
|
||||
|
||||
state.SetBalance(addr, uint256.NewInt(42), tracing.BalanceChangeUnspecified) // Change the account trie
|
||||
state.SetCode(addr, []byte("hello")) // Change an external metadata
|
||||
state.SetCode(addr, []byte("hello"), tracing.CodeChangeUnspecified) // Change an external metadata
|
||||
state.SetState(addr, skey1, sval1) // Change the storage trie
|
||||
|
||||
if balance := state.GetBalance(addr); balance.Cmp(uint256.NewInt(42)) != 0 {
|
||||
|
|
@ -987,10 +987,10 @@ func testMissingTrieNodes(t *testing.T, scheme string) {
|
|||
addr := common.BytesToAddress([]byte("so"))
|
||||
{
|
||||
state.SetBalance(addr, uint256.NewInt(1), tracing.BalanceChangeUnspecified)
|
||||
state.SetCode(addr, []byte{1, 2, 3})
|
||||
state.SetCode(addr, []byte{1, 2, 3}, tracing.CodeChangeUnspecified)
|
||||
a2 := common.BytesToAddress([]byte("another"))
|
||||
state.SetBalance(a2, uint256.NewInt(100), tracing.BalanceChangeUnspecified)
|
||||
state.SetCode(a2, []byte{1, 2, 4})
|
||||
state.SetCode(a2, []byte{1, 2, 4}, tracing.CodeChangeUnspecified)
|
||||
root, _ = state.Commit(0, false, false)
|
||||
t.Logf("root: %x", root)
|
||||
// force-flush
|
||||
|
|
|
|||
|
|
@ -64,8 +64,10 @@ type accountUpdate struct {
|
|||
// execution. It contains information about mutated contract codes, accounts,
|
||||
// and storage slots, along with their original values.
|
||||
type stateUpdate struct {
|
||||
originRoot common.Hash // hash of the state before applying mutation
|
||||
root common.Hash // hash of the state after applying mutation
|
||||
originRoot common.Hash // hash of the state before applying mutation
|
||||
root common.Hash // hash of the state after applying mutation
|
||||
blockNumber uint64 // Associated block number
|
||||
|
||||
accounts map[common.Hash][]byte // accounts stores mutated accounts in 'slim RLP' encoding
|
||||
accountsOrigin map[common.Address][]byte // accountsOrigin stores the original values of mutated accounts in 'slim RLP' encoding
|
||||
|
||||
|
|
@ -95,7 +97,7 @@ func (sc *stateUpdate) empty() bool {
|
|||
//
|
||||
// rawStorageKey is a flag indicating whether to use the raw storage slot key or
|
||||
// the hash of the slot key for constructing state update object.
|
||||
func newStateUpdate(rawStorageKey bool, originRoot common.Hash, root common.Hash, deletes map[common.Hash]*accountDelete, updates map[common.Hash]*accountUpdate, nodes *trienode.MergedNodeSet) *stateUpdate {
|
||||
func newStateUpdate(rawStorageKey bool, originRoot common.Hash, root common.Hash, blockNumber uint64, deletes map[common.Hash]*accountDelete, updates map[common.Hash]*accountUpdate, nodes *trienode.MergedNodeSet) *stateUpdate {
|
||||
var (
|
||||
accounts = make(map[common.Hash][]byte)
|
||||
accountsOrigin = make(map[common.Address][]byte)
|
||||
|
|
@ -164,6 +166,7 @@ func newStateUpdate(rawStorageKey bool, originRoot common.Hash, root common.Hash
|
|||
return &stateUpdate{
|
||||
originRoot: originRoot,
|
||||
root: root,
|
||||
blockNumber: blockNumber,
|
||||
accounts: accounts,
|
||||
accountsOrigin: accountsOrigin,
|
||||
storages: storages,
|
||||
|
|
|
|||
|
|
@ -388,6 +388,10 @@ func (sf *subfetcher) loop() {
|
|||
sf.tasks = nil
|
||||
sf.lock.Unlock()
|
||||
|
||||
var (
|
||||
addresses []common.Address
|
||||
slots [][]byte
|
||||
)
|
||||
for _, task := range tasks {
|
||||
if task.addr != nil {
|
||||
key := *task.addr
|
||||
|
|
@ -400,6 +404,7 @@ func (sf *subfetcher) loop() {
|
|||
sf.dupsCross++
|
||||
continue
|
||||
}
|
||||
sf.seenReadAddr[key] = struct{}{}
|
||||
} else {
|
||||
if _, ok := sf.seenReadAddr[key]; ok {
|
||||
sf.dupsCross++
|
||||
|
|
@ -409,7 +414,9 @@ func (sf *subfetcher) loop() {
|
|||
sf.dupsWrite++
|
||||
continue
|
||||
}
|
||||
sf.seenWriteAddr[key] = struct{}{}
|
||||
}
|
||||
addresses = append(addresses, *task.addr)
|
||||
} else {
|
||||
key := *task.slot
|
||||
if task.read {
|
||||
|
|
@ -421,6 +428,7 @@ func (sf *subfetcher) loop() {
|
|||
sf.dupsCross++
|
||||
continue
|
||||
}
|
||||
sf.seenReadSlot[key] = struct{}{}
|
||||
} else {
|
||||
if _, ok := sf.seenReadSlot[key]; ok {
|
||||
sf.dupsCross++
|
||||
|
|
@ -430,25 +438,19 @@ func (sf *subfetcher) loop() {
|
|||
sf.dupsWrite++
|
||||
continue
|
||||
}
|
||||
sf.seenWriteSlot[key] = struct{}{}
|
||||
}
|
||||
slots = append(slots, key.Bytes())
|
||||
}
|
||||
if task.addr != nil {
|
||||
sf.trie.GetAccount(*task.addr)
|
||||
} else {
|
||||
sf.trie.GetStorage(sf.addr, (*task.slot)[:])
|
||||
}
|
||||
if len(addresses) != 0 {
|
||||
if err := sf.trie.PrefetchAccount(addresses); err != nil {
|
||||
log.Error("Failed to prefetch accounts", "err", err)
|
||||
}
|
||||
if task.read {
|
||||
if task.addr != nil {
|
||||
sf.seenReadAddr[*task.addr] = struct{}{}
|
||||
} else {
|
||||
sf.seenReadSlot[*task.slot] = struct{}{}
|
||||
}
|
||||
} else {
|
||||
if task.addr != nil {
|
||||
sf.seenWriteAddr[*task.addr] = struct{}{}
|
||||
} else {
|
||||
sf.seenWriteSlot[*task.slot] = struct{}{}
|
||||
}
|
||||
}
|
||||
if len(slots) != 0 {
|
||||
if err := sf.trie.PrefetchStorage(sf.addr, slots); err != nil {
|
||||
log.Error("Failed to prefetch storage", "err", err)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -39,7 +39,7 @@ func filledStateDB() *StateDB {
|
|||
sval := common.HexToHash("bbb")
|
||||
|
||||
state.SetBalance(addr, uint256.NewInt(42), tracing.BalanceChangeUnspecified) // Change the account trie
|
||||
state.SetCode(addr, []byte("hello")) // Change an external metadata
|
||||
state.SetCode(addr, []byte("hello"), tracing.CodeChangeUnspecified) // Change an external metadata
|
||||
state.SetState(addr, skey, sval) // Change the storage trie
|
||||
for i := 0; i < 100; i++ {
|
||||
sk := common.BigToHash(big.NewInt(int64(i)))
|
||||
|
|
@ -81,7 +81,7 @@ func TestVerklePrefetcher(t *testing.T) {
|
|||
sval := testrand.Hash()
|
||||
|
||||
state.SetBalance(addr, uint256.NewInt(42), tracing.BalanceChangeUnspecified) // Change the account trie
|
||||
state.SetCode(addr, []byte("hello")) // Change an external metadata
|
||||
state.SetCode(addr, []byte("hello"), tracing.CodeChangeUnspecified) // Change an external metadata
|
||||
state.SetState(addr, skey, sval) // Change the storage trie
|
||||
root, _ := state.Commit(0, true, false)
|
||||
|
||||
|
|
|
|||
|
|
@ -111,12 +111,6 @@ func (p *statePrefetcher) Prefetch(block *types.Block, statedb *state.StateDB, c
|
|||
fails.Add(1)
|
||||
return nil // Ugh, something went horribly wrong, bail out
|
||||
}
|
||||
// Pre-load trie nodes for the intermediate root.
|
||||
//
|
||||
// This operation incurs significant memory allocations due to
|
||||
// trie hashing and node decoding. TODO(rjl493456442): investigate
|
||||
// ways to mitigate this overhead.
|
||||
stateCpy.IntermediateRoot(true)
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
|
|
|||
|
|
@ -110,15 +110,15 @@ func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg
|
|||
requests = [][]byte{}
|
||||
// EIP-6110
|
||||
if err := ParseDepositLogs(&requests, allLogs, p.config); err != nil {
|
||||
return nil, err
|
||||
return nil, fmt.Errorf("failed to parse deposit logs: %w", err)
|
||||
}
|
||||
// EIP-7002
|
||||
if err := ProcessWithdrawalQueue(&requests, evm); err != nil {
|
||||
return nil, err
|
||||
return nil, fmt.Errorf("failed to process withdrawal queue: %w", err)
|
||||
}
|
||||
// EIP-7251
|
||||
if err := ProcessConsolidationQueue(&requests, evm); err != nil {
|
||||
return nil, err
|
||||
return nil, fmt.Errorf("failed to process consolidation queue: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -642,12 +642,12 @@ func (st *stateTransition) applyAuthorization(auth *types.SetCodeAuthorization)
|
|||
st.state.SetNonce(authority, auth.Nonce+1, tracing.NonceChangeAuthorization)
|
||||
if auth.Address == (common.Address{}) {
|
||||
// Delegation to zero address means clear.
|
||||
st.state.SetCode(authority, nil)
|
||||
st.state.SetCode(authority, nil, tracing.CodeChangeAuthorizationClear)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Otherwise install delegation to auth.Address.
|
||||
st.state.SetCode(authority, types.AddressToDelegation(auth.Address))
|
||||
st.state.SetCode(authority, types.AddressToDelegation(auth.Address), tracing.CodeChangeAuthorization)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
|
|||
78
core/stateless/stats.go
Normal file
78
core/stateless/stats.go
Normal file
|
|
@ -0,0 +1,78 @@
|
|||
// Copyright 2025 The go-ethereum Authors
|
||||
// This file is part of the go-ethereum library.
|
||||
//
|
||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package stateless
|
||||
|
||||
import (
|
||||
"maps"
|
||||
"slices"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/metrics"
|
||||
)
|
||||
|
||||
var accountTrieLeavesAtDepth [16]*metrics.Counter
|
||||
var storageTrieLeavesAtDepth [16]*metrics.Counter
|
||||
|
||||
func init() {
|
||||
for i := 0; i < 16; i++ {
|
||||
accountTrieLeavesAtDepth[i] = metrics.NewRegisteredCounter("witness/trie/account/leaves/depth_"+strconv.Itoa(i), nil)
|
||||
storageTrieLeavesAtDepth[i] = metrics.NewRegisteredCounter("witness/trie/storage/leaves/depth_"+strconv.Itoa(i), nil)
|
||||
}
|
||||
}
|
||||
|
||||
// WitnessStats aggregates statistics for account and storage trie accesses.
|
||||
type WitnessStats struct {
|
||||
accountTrieLeaves [16]int64
|
||||
storageTrieLeaves [16]int64
|
||||
}
|
||||
|
||||
// NewWitnessStats creates a new WitnessStats collector.
|
||||
func NewWitnessStats() *WitnessStats {
|
||||
return &WitnessStats{}
|
||||
}
|
||||
|
||||
// Add records trie access depths from the given node paths.
|
||||
// If `owner` is the zero hash, accesses are attributed to the account trie;
|
||||
// otherwise, they are attributed to the storage trie of that account.
|
||||
func (s *WitnessStats) Add(nodes map[string][]byte, owner common.Hash) {
|
||||
// Extract paths from the nodes map
|
||||
paths := slices.Collect(maps.Keys(nodes))
|
||||
sort.Strings(paths)
|
||||
|
||||
for i, path := range paths {
|
||||
// If current path is a prefix of the next path, it's not a leaf.
|
||||
// The last path is always a leaf.
|
||||
if i == len(paths)-1 || !strings.HasPrefix(paths[i+1], paths[i]) {
|
||||
if owner == (common.Hash{}) {
|
||||
s.accountTrieLeaves[len(path)] += 1
|
||||
} else {
|
||||
s.storageTrieLeaves[len(path)] += 1
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ReportMetrics reports the collected statistics to the global metrics registry.
|
||||
func (s *WitnessStats) ReportMetrics() {
|
||||
for i := 0; i < 16; i++ {
|
||||
accountTrieLeavesAtDepth[i].Inc(s.accountTrieLeaves[i])
|
||||
storageTrieLeavesAtDepth[i].Inc(s.storageTrieLeaves[i])
|
||||
}
|
||||
}
|
||||
161
core/stateless/stats_test.go
Normal file
161
core/stateless/stats_test.go
Normal file
|
|
@ -0,0 +1,161 @@
|
|||
// Copyright 2025 The go-ethereum Authors
|
||||
// This file is part of the go-ethereum library.
|
||||
//
|
||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package stateless
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
)
|
||||
|
||||
func TestWitnessStatsAdd(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
nodes map[string][]byte
|
||||
owner common.Hash
|
||||
expectedAccountLeaves map[int64]int64
|
||||
expectedStorageLeaves map[int64]int64
|
||||
}{
|
||||
{
|
||||
name: "empty nodes",
|
||||
nodes: map[string][]byte{},
|
||||
owner: common.Hash{},
|
||||
},
|
||||
{
|
||||
name: "single account trie leaf at depth 0",
|
||||
nodes: map[string][]byte{
|
||||
"": []byte("data"),
|
||||
},
|
||||
owner: common.Hash{},
|
||||
expectedAccountLeaves: map[int64]int64{0: 1},
|
||||
},
|
||||
{
|
||||
name: "single account trie leaf",
|
||||
nodes: map[string][]byte{
|
||||
"abc": []byte("data"),
|
||||
},
|
||||
owner: common.Hash{},
|
||||
expectedAccountLeaves: map[int64]int64{3: 1},
|
||||
},
|
||||
{
|
||||
name: "account trie with internal nodes",
|
||||
nodes: map[string][]byte{
|
||||
"a": []byte("data1"),
|
||||
"ab": []byte("data2"),
|
||||
"abc": []byte("data3"),
|
||||
},
|
||||
owner: common.Hash{},
|
||||
expectedAccountLeaves: map[int64]int64{3: 1}, // Only "abc" is a leaf
|
||||
},
|
||||
{
|
||||
name: "multiple account trie branches",
|
||||
nodes: map[string][]byte{
|
||||
"a": []byte("data1"),
|
||||
"ab": []byte("data2"),
|
||||
"abc": []byte("data3"),
|
||||
"b": []byte("data4"),
|
||||
"bc": []byte("data5"),
|
||||
"bcd": []byte("data6"),
|
||||
},
|
||||
owner: common.Hash{},
|
||||
expectedAccountLeaves: map[int64]int64{3: 2}, // "abc" (3) + "bcd" (3)
|
||||
},
|
||||
{
|
||||
name: "siblings are all leaves",
|
||||
nodes: map[string][]byte{
|
||||
"aa": []byte("data1"),
|
||||
"ab": []byte("data2"),
|
||||
"ac": []byte("data3"),
|
||||
},
|
||||
owner: common.Hash{},
|
||||
expectedAccountLeaves: map[int64]int64{2: 3},
|
||||
},
|
||||
{
|
||||
name: "storage trie leaves",
|
||||
nodes: map[string][]byte{
|
||||
"1": []byte("data1"),
|
||||
"12": []byte("data2"),
|
||||
"123": []byte("data3"),
|
||||
"124": []byte("data4"),
|
||||
},
|
||||
owner: common.HexToHash("0x1234"),
|
||||
expectedStorageLeaves: map[int64]int64{3: 2}, // "123" (3) + "124" (3)
|
||||
},
|
||||
{
|
||||
name: "complex trie structure",
|
||||
nodes: map[string][]byte{
|
||||
"1": []byte("data1"),
|
||||
"12": []byte("data2"),
|
||||
"123": []byte("data3"),
|
||||
"124": []byte("data4"),
|
||||
"2": []byte("data5"),
|
||||
"23": []byte("data6"),
|
||||
"234": []byte("data7"),
|
||||
"235": []byte("data8"),
|
||||
"3": []byte("data9"),
|
||||
},
|
||||
owner: common.Hash{},
|
||||
expectedAccountLeaves: map[int64]int64{1: 1, 3: 4}, // "123"(3) + "124"(3) + "234"(3) + "235"(3) + "3"(1)
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
stats := NewWitnessStats()
|
||||
stats.Add(tt.nodes, tt.owner)
|
||||
|
||||
var expectedAccountTrieLeaves [16]int64
|
||||
for depth, count := range tt.expectedAccountLeaves {
|
||||
expectedAccountTrieLeaves[depth] = count
|
||||
}
|
||||
var expectedStorageTrieLeaves [16]int64
|
||||
for depth, count := range tt.expectedStorageLeaves {
|
||||
expectedStorageTrieLeaves[depth] = count
|
||||
}
|
||||
|
||||
// Check account trie depth
|
||||
if stats.accountTrieLeaves != expectedAccountTrieLeaves {
|
||||
t.Errorf("Account trie total depth = %v, want %v", stats.accountTrieLeaves, expectedAccountTrieLeaves)
|
||||
}
|
||||
|
||||
// Check storage trie depth
|
||||
if stats.storageTrieLeaves != expectedStorageTrieLeaves {
|
||||
t.Errorf("Storage trie total depth = %v, want %v", stats.storageTrieLeaves, expectedStorageTrieLeaves)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkWitnessStatsAdd(b *testing.B) {
|
||||
// Create a realistic trie node structure
|
||||
nodes := make(map[string][]byte)
|
||||
for i := 0; i < 100; i++ {
|
||||
base := string(rune('a' + i%26))
|
||||
nodes[base] = []byte("data")
|
||||
for j := 0; j < 9; j++ {
|
||||
key := base + string(rune('0'+j))
|
||||
nodes[key] = []byte("data")
|
||||
}
|
||||
}
|
||||
|
||||
stats := NewWitnessStats()
|
||||
b.ResetTimer()
|
||||
|
||||
for i := 0; i < b.N; i++ {
|
||||
stats.Add(nodes, common.Hash{})
|
||||
}
|
||||
}
|
||||
|
|
@ -58,7 +58,7 @@ func NewWitness(context *types.Header, chain HeaderReader) (*Witness, error) {
|
|||
}
|
||||
headers = append(headers, parent)
|
||||
}
|
||||
// Create the wtness with a reconstructed gutted out block
|
||||
// Create the witness with a reconstructed gutted out block
|
||||
return &Witness{
|
||||
context: context,
|
||||
Headers: headers,
|
||||
|
|
@ -88,14 +88,16 @@ func (w *Witness) AddCode(code []byte) {
|
|||
}
|
||||
|
||||
// AddState inserts a batch of MPT trie nodes into the witness.
|
||||
func (w *Witness) AddState(nodes map[string]struct{}) {
|
||||
func (w *Witness) AddState(nodes map[string][]byte) {
|
||||
if len(nodes) == 0 {
|
||||
return
|
||||
}
|
||||
w.lock.Lock()
|
||||
defer w.lock.Unlock()
|
||||
|
||||
maps.Copy(w.State, nodes)
|
||||
for _, value := range nodes {
|
||||
w.State[string(value)] = struct{}{}
|
||||
}
|
||||
}
|
||||
|
||||
// Copy deep-copies the witness object. Witness.Block isn't deep-copied as it
|
||||
|
|
|
|||
|
|
@ -4,6 +4,27 @@ All notable changes to the tracing interface will be documented in this file.
|
|||
|
||||
## [Unreleased]
|
||||
|
||||
### Deprecated methods
|
||||
|
||||
- `OnCodeChange(addr common.Address, prevCodeHash common.Hash, prevCode []byte, codeHash common.Hash, code []byte)`: This hook is deprecated in favor of `OnCodeChangeV2` which includes a reason parameter ([#32525](https://github.com/ethereum/go-ethereum/pull/32525)).
|
||||
|
||||
### New methods
|
||||
|
||||
- `OnCodeChangeV2(addr common.Address, prevCodeHash common.Hash, prevCode []byte, codeHash common.Hash, code []byte, reason CodeChangeReason)`: This hook is called when a code change occurs. It is a successor to `OnCodeChange` with an additional reason parameter ([#32525](https://github.com/ethereum/go-ethereum/pull/32525)).
|
||||
|
||||
### New types
|
||||
|
||||
- `CodeChangeReason` is a new type used to provide a reason for code changes. It includes various reasons such as contract creation, genesis initialization, EIP-7702 authorization, self-destruct, and revert operations ([#32525](https://github.com/ethereum/go-ethereum/pull/32525)).
|
||||
|
||||
## [v1.15.4](https://github.com/ethereum/go-ethereum/releases/tag/v1.15.4)
|
||||
|
||||
### Modified types
|
||||
|
||||
- `GasChangeReason` has been extended with auto-generated String() methods for better debugging and logging ([#31234](https://github.com/ethereum/go-ethereum/pull/31234)).
|
||||
- `NonceChangeReason` has been extended with auto-generated String() methods for better debugging and logging ([#31234](https://github.com/ethereum/go-ethereum/pull/31234)).
|
||||
|
||||
## [v1.15.0](https://github.com/ethereum/go-ethereum/releases/tag/v1.15.0)
|
||||
|
||||
The tracing interface has been extended with backwards-compatible changes to support more use-cases and simplify tracer code. The most notable change is a state journaling library which emits reverse events when a call is reverted.
|
||||
|
||||
### Deprecated methods
|
||||
|
|
@ -23,8 +44,13 @@ The tracing interface has been extended with backwards-compatible changes to sup
|
|||
|
||||
### Modified types
|
||||
|
||||
- `VMContext.StateDB` has been extended with `GetCodeHash(addr common.Address) common.Hash` method used to retrieve the code hash an account.
|
||||
- `VMContext.StateDB` has been extended with the following method:
|
||||
- `GetCodeHash(addr common.Address) common.Hash` method used to retrieve the code hash of an account.
|
||||
- `BlockEvent` has been modified:
|
||||
- The `TD` (Total Difficulty) field has been removed ([#30744](https://github.com/ethereum/go-ethereum/pull/30744)).
|
||||
- `BalanceChangeReason` has been extended with the `BalanceChangeRevert` reason. More on that below.
|
||||
- `GasChangeReason` has been extended with the following reason:
|
||||
- `GasChangeTxDataFloor` is the amount of extra gas the transaction has to pay to reach the minimum gas requirement for the transaction data. This change will always be a negative change.
|
||||
|
||||
### State journaling
|
||||
|
||||
|
|
@ -49,6 +75,26 @@ The state changes that are covered by the journaling library are:
|
|||
- `OnCodeChange`
|
||||
- `OnStorageChange`
|
||||
|
||||
## [v1.14.12](https://github.com/ethereum/go-ethereum/releases/tag/v1.14.12)
|
||||
|
||||
This release contains a change in behavior for `OnCodeChange` hook and an extension to the StateDB interface.
|
||||
|
||||
### Modified types
|
||||
|
||||
- `VMContext.StateDB` has been extended with the following method:
|
||||
- `GetTransientState(addr common.Address, slot common.Hash) common.Hash` method used to access contract transient storage ([#30531](https://github.com/ethereum/go-ethereum/pull/30531)).
|
||||
|
||||
### `OnCodeChange` change
|
||||
|
||||
The `OnCodeChange` hook is now called when the code of a contract is removed due to a selfdestruct. Previously, no code change was emitted on such occasions.
|
||||
|
||||
## [v1.14.10](https://github.com/ethereum/go-ethereum/releases/tag/v1.14.10)
|
||||
|
||||
### Modified types
|
||||
|
||||
- `OpContext` has been extended with the following method:
|
||||
- `ContractCode() []byte` provides access to the contract bytecode within the OpContext interface ([#30466](https://github.com/ethereum/go-ethereum/pull/30466)).
|
||||
|
||||
## [v1.14.9](https://github.com/ethereum/go-ethereum/releases/tag/v1.14.9)
|
||||
|
||||
### Modified types
|
||||
|
|
@ -56,13 +102,6 @@ The state changes that are covered by the journaling library are:
|
|||
- `GasChangeReason` has been extended with the following reasons which will be enabled only post-Verkle. There shouldn't be any gas changes with those reasons prior to the fork.
|
||||
- `GasChangeWitnessContractCollisionCheck` flags the event of adding to the witness when checking for contract address collision.
|
||||
|
||||
## [v1.14.12]
|
||||
|
||||
This release contains a change in behavior for `OnCodeChange` hook.
|
||||
|
||||
### `OnCodeChange` change
|
||||
|
||||
The `OnCodeChange` hook is now called when the code of a contract is removed due to a selfdestruct. Previously, no code change was emitted on such occasions.
|
||||
|
||||
## [v1.14.4]
|
||||
|
||||
|
|
@ -148,7 +187,12 @@ The hooks `CaptureStart` and `CaptureEnd` have been removed. These hooks signale
|
|||
- `CaptureState` -> `OnOpcode(pc uint64, op byte, gas, cost uint64, scope tracing.OpContext, rData []byte, depth int, err error)`. `op` is of type `byte` which can be cast to `vm.OpCode` when necessary. A `*vm.ScopeContext` is not passed anymore. It is replaced by `tracing.OpContext` which offers access to the memory, stack and current contract.
|
||||
- `CaptureFault` -> `OnFault(pc uint64, op byte, gas, cost uint64, scope tracing.OpContext, depth int, err error)`. Similar to above.
|
||||
|
||||
[unreleased]: https://github.com/ethereum/go-ethereum/compare/v1.14.8...master
|
||||
[v1.14.0]: https://github.com/ethereum/go-ethereum/releases/tag/v1.14.0
|
||||
[v1.14.3]: https://github.com/ethereum/go-ethereum/releases/tag/v1.14.3
|
||||
[unreleased]: https://github.com/ethereum/go-ethereum/compare/v1.16.3...master
|
||||
[v1.15.4]: https://github.com/ethereum/go-ethereum/releases/tag/v1.15.4
|
||||
[v1.15.0]: https://github.com/ethereum/go-ethereum/releases/tag/v1.15.0
|
||||
[v1.14.12]: https://github.com/ethereum/go-ethereum/releases/tag/v1.14.12
|
||||
[v1.14.10]: https://github.com/ethereum/go-ethereum/releases/tag/v1.14.10
|
||||
[v1.14.9]: https://github.com/ethereum/go-ethereum/releases/tag/v1.14.9
|
||||
[v1.14.4]: https://github.com/ethereum/go-ethereum/releases/tag/v1.14.4
|
||||
[v1.14.3]: https://github.com/ethereum/go-ethereum/releases/tag/v1.14.3
|
||||
[v1.14.0]: https://github.com/ethereum/go-ethereum/releases/tag/v1.14.0
|
||||
|
|
|
|||
29
core/tracing/gen_code_change_reason_stringer.go
Normal file
29
core/tracing/gen_code_change_reason_stringer.go
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
// Code generated by "stringer -type=CodeChangeReason -trimprefix=CodeChange -output gen_code_change_reason_stringer.go"; DO NOT EDIT.
|
||||
|
||||
package tracing
|
||||
|
||||
import "strconv"
|
||||
|
||||
func _() {
|
||||
// An "invalid array index" compiler error signifies that the constant values have changed.
|
||||
// Re-run the stringer command to generate them again.
|
||||
var x [1]struct{}
|
||||
_ = x[CodeChangeUnspecified-0]
|
||||
_ = x[CodeChangeContractCreation-1]
|
||||
_ = x[CodeChangeGenesis-2]
|
||||
_ = x[CodeChangeAuthorization-3]
|
||||
_ = x[CodeChangeAuthorizationClear-4]
|
||||
_ = x[CodeChangeSelfDestruct-5]
|
||||
_ = x[CodeChangeRevert-6]
|
||||
}
|
||||
|
||||
const _CodeChangeReason_name = "UnspecifiedContractCreationGenesisAuthorizationAuthorizationClearSelfDestructRevert"
|
||||
|
||||
var _CodeChangeReason_index = [...]uint8{0, 11, 27, 34, 47, 65, 77, 83}
|
||||
|
||||
func (i CodeChangeReason) String() string {
|
||||
if i >= CodeChangeReason(len(_CodeChangeReason_index)-1) {
|
||||
return "CodeChangeReason(" + strconv.FormatInt(int64(i), 10) + ")"
|
||||
}
|
||||
return _CodeChangeReason_name[_CodeChangeReason_index[i]:_CodeChangeReason_index[i+1]]
|
||||
}
|
||||
|
|
@ -177,6 +177,9 @@ type (
|
|||
// CodeChangeHook is called when the code of an account changes.
|
||||
CodeChangeHook = func(addr common.Address, prevCodeHash common.Hash, prevCode []byte, codeHash common.Hash, code []byte)
|
||||
|
||||
// CodeChangeHookV2 is called when the code of an account changes.
|
||||
CodeChangeHookV2 = func(addr common.Address, prevCodeHash common.Hash, prevCode []byte, codeHash common.Hash, code []byte, reason CodeChangeReason)
|
||||
|
||||
// StorageChangeHook is called when the storage of an account changes.
|
||||
StorageChangeHook = func(addr common.Address, slot common.Hash, prev, new common.Hash)
|
||||
|
||||
|
|
@ -211,6 +214,7 @@ type Hooks struct {
|
|||
OnNonceChange NonceChangeHook
|
||||
OnNonceChangeV2 NonceChangeHookV2
|
||||
OnCodeChange CodeChangeHook
|
||||
OnCodeChangeV2 CodeChangeHookV2
|
||||
OnStorageChange StorageChangeHook
|
||||
OnLog LogHook
|
||||
// Block hash read
|
||||
|
|
@ -372,3 +376,31 @@ const (
|
|||
// It is only emitted when the tracer has opted in to use the journaling wrapper (WrapWithJournal).
|
||||
NonceChangeRevert NonceChangeReason = 6
|
||||
)
|
||||
|
||||
// CodeChangeReason is used to indicate the reason for a code change.
|
||||
type CodeChangeReason byte
|
||||
|
||||
//go:generate go run golang.org/x/tools/cmd/stringer -type=CodeChangeReason -trimprefix=CodeChange -output gen_code_change_reason_stringer.go
|
||||
|
||||
const (
|
||||
CodeChangeUnspecified CodeChangeReason = 0
|
||||
|
||||
// CodeChangeContractCreation is when a new contract is deployed via CREATE/CREATE2 operations.
|
||||
CodeChangeContractCreation CodeChangeReason = 1
|
||||
|
||||
// CodeChangeGenesis is when contract code is set during blockchain genesis or initial setup.
|
||||
CodeChangeGenesis CodeChangeReason = 2
|
||||
|
||||
// CodeChangeAuthorization is when code is set via EIP-7702 Set Code Authorization.
|
||||
CodeChangeAuthorization CodeChangeReason = 3
|
||||
|
||||
// CodeChangeAuthorizationClear is when EIP-7702 delegation is cleared by setting to zero address.
|
||||
CodeChangeAuthorizationClear CodeChangeReason = 4
|
||||
|
||||
// CodeChangeSelfDestruct is when contract code is cleared due to self-destruct.
|
||||
CodeChangeSelfDestruct CodeChangeReason = 5
|
||||
|
||||
// CodeChangeRevert is emitted when the code is reverted back to a previous value due to call failure.
|
||||
// It is only emitted when the tracer has opted in to use the journaling wrapper (WrapWithJournal).
|
||||
CodeChangeRevert CodeChangeReason = 6
|
||||
)
|
||||
|
|
|
|||
|
|
@ -42,12 +42,15 @@ func WrapWithJournal(hooks *Hooks) (*Hooks, error) {
|
|||
return nil, errors.New("wrapping nil tracer")
|
||||
}
|
||||
// No state change to journal, return the wrapped hooks as is
|
||||
if hooks.OnBalanceChange == nil && hooks.OnNonceChange == nil && hooks.OnNonceChangeV2 == nil && hooks.OnCodeChange == nil && hooks.OnStorageChange == nil {
|
||||
if hooks.OnBalanceChange == nil && hooks.OnNonceChange == nil && hooks.OnNonceChangeV2 == nil && hooks.OnCodeChange == nil && hooks.OnCodeChangeV2 == nil && hooks.OnStorageChange == nil {
|
||||
return hooks, nil
|
||||
}
|
||||
if hooks.OnNonceChange != nil && hooks.OnNonceChangeV2 != nil {
|
||||
return nil, errors.New("cannot have both OnNonceChange and OnNonceChangeV2")
|
||||
}
|
||||
if hooks.OnCodeChange != nil && hooks.OnCodeChangeV2 != nil {
|
||||
return nil, errors.New("cannot have both OnCodeChange and OnCodeChangeV2")
|
||||
}
|
||||
|
||||
// Create a new Hooks instance and copy all hooks
|
||||
wrapped := *hooks
|
||||
|
|
@ -72,6 +75,9 @@ func WrapWithJournal(hooks *Hooks) (*Hooks, error) {
|
|||
if hooks.OnCodeChange != nil {
|
||||
wrapped.OnCodeChange = j.OnCodeChange
|
||||
}
|
||||
if hooks.OnCodeChangeV2 != nil {
|
||||
wrapped.OnCodeChangeV2 = j.OnCodeChangeV2
|
||||
}
|
||||
if hooks.OnStorageChange != nil {
|
||||
wrapped.OnStorageChange = j.OnStorageChange
|
||||
}
|
||||
|
|
@ -174,6 +180,19 @@ func (j *journal) OnCodeChange(addr common.Address, prevCodeHash common.Hash, pr
|
|||
}
|
||||
}
|
||||
|
||||
func (j *journal) OnCodeChangeV2(addr common.Address, prevCodeHash common.Hash, prevCode []byte, codeHash common.Hash, code []byte, reason CodeChangeReason) {
|
||||
j.entries = append(j.entries, codeChange{
|
||||
addr: addr,
|
||||
prevCodeHash: prevCodeHash,
|
||||
prevCode: prevCode,
|
||||
newCodeHash: codeHash,
|
||||
newCode: code,
|
||||
})
|
||||
if j.hooks.OnCodeChangeV2 != nil {
|
||||
j.hooks.OnCodeChangeV2(addr, prevCodeHash, prevCode, codeHash, code, reason)
|
||||
}
|
||||
}
|
||||
|
||||
func (j *journal) OnStorageChange(addr common.Address, slot common.Hash, prev, new common.Hash) {
|
||||
j.entries = append(j.entries, storageChange{addr: addr, slot: slot, prev: prev, new: new})
|
||||
if j.hooks.OnStorageChange != nil {
|
||||
|
|
@ -225,7 +244,9 @@ func (n nonceChange) revert(hooks *Hooks) {
|
|||
}
|
||||
|
||||
func (c codeChange) revert(hooks *Hooks) {
|
||||
if hooks.OnCodeChange != nil {
|
||||
if hooks.OnCodeChangeV2 != nil {
|
||||
hooks.OnCodeChangeV2(c.addr, c.newCodeHash, c.newCode, c.prevCodeHash, c.prevCode, CodeChangeRevert)
|
||||
} else if hooks.OnCodeChange != nil {
|
||||
hooks.OnCodeChange(c.addr, c.newCodeHash, c.newCode, c.prevCodeHash, c.prevCode)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@ import (
|
|||
"testing"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
)
|
||||
|
||||
type testTracer struct {
|
||||
|
|
@ -56,6 +57,11 @@ func (t *testTracer) OnCodeChange(addr common.Address, prevCodeHash common.Hash,
|
|||
t.code = code
|
||||
}
|
||||
|
||||
func (t *testTracer) OnCodeChangeV2(addr common.Address, prevCodeHash common.Hash, prevCode []byte, codeHash common.Hash, code []byte, reason CodeChangeReason) {
|
||||
t.t.Logf("OnCodeChangeV2(%v, %v -> %v, %v)", addr, prevCodeHash, codeHash, reason)
|
||||
t.code = code
|
||||
}
|
||||
|
||||
func (t *testTracer) OnStorageChange(addr common.Address, slot common.Hash, prev common.Hash, new common.Hash) {
|
||||
t.t.Logf("OnStorageCodeChange(%v, %v, %v -> %v)", addr, slot, prev, new)
|
||||
if t.storage == nil {
|
||||
|
|
@ -232,6 +238,27 @@ func TestOnNonceChangeV2(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestOnCodeChangeV2(t *testing.T) {
|
||||
tr := &testTracer{t: t}
|
||||
wr, err := WrapWithJournal(&Hooks{OnCodeChangeV2: tr.OnCodeChangeV2})
|
||||
if err != nil {
|
||||
t.Fatalf("failed to wrap test tracer: %v", err)
|
||||
}
|
||||
|
||||
addr := common.HexToAddress("0x1234")
|
||||
code := []byte{1, 2, 3}
|
||||
{
|
||||
wr.OnEnter(2, 0, addr, addr, nil, 1000, big.NewInt(0))
|
||||
wr.OnCodeChangeV2(addr, common.Hash{}, nil, crypto.Keccak256Hash(code), code, CodeChangeContractCreation)
|
||||
wr.OnExit(2, nil, 100, nil, true)
|
||||
}
|
||||
|
||||
// After revert, code should be nil
|
||||
if tr.code != nil {
|
||||
t.Fatalf("unexpected code after revert: %v", tr.code)
|
||||
}
|
||||
}
|
||||
|
||||
func TestAllHooksCalled(t *testing.T) {
|
||||
tracer := newTracerAllHooks()
|
||||
hooks := tracer.hooks()
|
||||
|
|
@ -298,6 +325,7 @@ func newTracerAllHooks() *tracerAllHooks {
|
|||
t.hooksCalled[hooksType.Field(i).Name] = false
|
||||
}
|
||||
delete(t.hooksCalled, "OnNonceChange")
|
||||
delete(t.hooksCalled, "OnCodeChange")
|
||||
return t
|
||||
}
|
||||
|
||||
|
|
@ -322,7 +350,7 @@ func (t *tracerAllHooks) hooks() *Hooks {
|
|||
hooksValue := reflect.ValueOf(h).Elem()
|
||||
for i := 0; i < hooksValue.NumField(); i++ {
|
||||
field := hooksValue.Type().Field(i)
|
||||
if field.Name == "OnNonceChange" {
|
||||
if field.Name == "OnNonceChange" || field.Name == "OnCodeChange" {
|
||||
continue
|
||||
}
|
||||
hookMethod := reflect.MakeFunc(field.Type, func(args []reflect.Value) []reflect.Value {
|
||||
|
|
|
|||
|
|
@ -1298,6 +1298,13 @@ func (p *BlobPool) GetMetadata(hash common.Hash) *txpool.TxMetadata {
|
|||
}
|
||||
|
||||
// GetBlobs returns a number of blobs and proofs for the given versioned hashes.
|
||||
// Blobpool must place responses in the order given in the request, using null
|
||||
// for any missing blobs.
|
||||
//
|
||||
// For instance, if the request is [A_versioned_hash, B_versioned_hash,
|
||||
// C_versioned_hash] and blobpool has data for blobs A and C, but doesn't have
|
||||
// data for B, the response MUST be [A, null, C].
|
||||
//
|
||||
// This is a utility method for the engine API, enabling consensus clients to
|
||||
// retrieve blobs from the pools directly instead of the network.
|
||||
func (p *BlobPool) GetBlobs(vhashes []common.Hash, version byte) ([]*kzg4844.Blob, []kzg4844.Commitment, [][]kzg4844.Proof, error) {
|
||||
|
|
@ -1317,26 +1324,30 @@ func (p *BlobPool) GetBlobs(vhashes []common.Hash, version byte) ([]*kzg4844.Blo
|
|||
if _, ok := filled[vhash]; ok {
|
||||
continue
|
||||
}
|
||||
// Retrieve the corresponding blob tx with the vhash
|
||||
// Retrieve the corresponding blob tx with the vhash, skip blob resolution
|
||||
// if it's not found locally and place the null instead.
|
||||
p.lock.RLock()
|
||||
txID, exists := p.lookup.storeidOfBlob(vhash)
|
||||
p.lock.RUnlock()
|
||||
if !exists {
|
||||
return nil, nil, nil, fmt.Errorf("blob with vhash %x is not found", vhash)
|
||||
continue
|
||||
}
|
||||
data, err := p.store.Get(txID)
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
log.Error("Tracked blob transaction missing from store", "id", txID, "err", err)
|
||||
continue
|
||||
}
|
||||
|
||||
// Decode the blob transaction
|
||||
tx := new(types.Transaction)
|
||||
if err := rlp.DecodeBytes(data, tx); err != nil {
|
||||
return nil, nil, nil, err
|
||||
log.Error("Blobs corrupted for traced transaction", "id", txID, "err", err)
|
||||
continue
|
||||
}
|
||||
sidecar := tx.BlobTxSidecar()
|
||||
if sidecar == nil {
|
||||
return nil, nil, nil, fmt.Errorf("blob tx without sidecar %x", tx.Hash())
|
||||
log.Error("Blob tx without sidecar", "hash", tx.Hash(), "id", txID)
|
||||
continue
|
||||
}
|
||||
// Traverse the blobs in the transaction
|
||||
for i, hash := range tx.BlobHashes() {
|
||||
|
|
@ -1397,6 +1408,31 @@ func (p *BlobPool) AvailableBlobs(vhashes []common.Hash) int {
|
|||
return available
|
||||
}
|
||||
|
||||
// convertSidecar converts the legacy sidecar in the submitted transactions
|
||||
// if Osaka fork has been activated.
|
||||
func (p *BlobPool) convertSidecar(txs []*types.Transaction) ([]*types.Transaction, []error) {
|
||||
head := p.chain.CurrentBlock()
|
||||
if !p.chain.Config().IsOsaka(head.Number, head.Time) {
|
||||
return txs, make([]error, len(txs))
|
||||
}
|
||||
var errs []error
|
||||
for _, tx := range txs {
|
||||
sidecar := tx.BlobTxSidecar()
|
||||
if sidecar == nil {
|
||||
errs = append(errs, errors.New("missing sidecar in blob transaction"))
|
||||
continue
|
||||
}
|
||||
if sidecar.Version == types.BlobSidecarVersion0 {
|
||||
if err := sidecar.ToV1(); err != nil {
|
||||
errs = append(errs, err)
|
||||
continue
|
||||
}
|
||||
}
|
||||
errs = append(errs, nil)
|
||||
}
|
||||
return txs, errs
|
||||
}
|
||||
|
||||
// Add inserts a set of blob transactions into the pool if they pass validation (both
|
||||
// consensus validity and pool restrictions).
|
||||
//
|
||||
|
|
@ -1404,10 +1440,14 @@ func (p *BlobPool) AvailableBlobs(vhashes []common.Hash) int {
|
|||
// related to the add is finished. Only use this during tests for determinism.
|
||||
func (p *BlobPool) Add(txs []*types.Transaction, sync bool) []error {
|
||||
var (
|
||||
errs []error
|
||||
adds = make([]*types.Transaction, 0, len(txs))
|
||||
errs = make([]error, len(txs))
|
||||
)
|
||||
txs, errs = p.convertSidecar(txs)
|
||||
for i, tx := range txs {
|
||||
if errs[i] != nil {
|
||||
continue
|
||||
}
|
||||
errs[i] = p.add(tx)
|
||||
if errs[i] == nil {
|
||||
adds = append(adds, tx.WithoutBlobTxSidecar())
|
||||
|
|
|
|||
|
|
@ -24,9 +24,11 @@ import (
|
|||
"fmt"
|
||||
"math"
|
||||
"math/big"
|
||||
"math/rand"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"slices"
|
||||
"sync"
|
||||
"testing"
|
||||
|
||||
|
|
@ -40,6 +42,7 @@ import (
|
|||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/crypto/kzg4844"
|
||||
"github.com/ethereum/go-ethereum/internal/testrand"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
"github.com/ethereum/go-ethereum/rlp"
|
||||
"github.com/holiman/billy"
|
||||
|
|
@ -47,11 +50,12 @@ import (
|
|||
)
|
||||
|
||||
var (
|
||||
testBlobs []*kzg4844.Blob
|
||||
testBlobCommits []kzg4844.Commitment
|
||||
testBlobProofs []kzg4844.Proof
|
||||
testBlobVHashes [][32]byte
|
||||
testBlobIndices = make(map[[32]byte]int)
|
||||
testBlobs []*kzg4844.Blob
|
||||
testBlobCommits []kzg4844.Commitment
|
||||
testBlobProofs []kzg4844.Proof
|
||||
testBlobCellProofs [][]kzg4844.Proof
|
||||
testBlobVHashes [][32]byte
|
||||
testBlobIndices = make(map[[32]byte]int)
|
||||
)
|
||||
|
||||
const testMaxBlobsPerBlock = 6
|
||||
|
|
@ -67,6 +71,9 @@ func init() {
|
|||
testBlobProof, _ := kzg4844.ComputeBlobProof(testBlob, testBlobCommit)
|
||||
testBlobProofs = append(testBlobProofs, testBlobProof)
|
||||
|
||||
testBlobCellProof, _ := kzg4844.ComputeCellProofs(testBlob)
|
||||
testBlobCellProofs = append(testBlobCellProofs, testBlobCellProof)
|
||||
|
||||
testBlobVHash := kzg4844.CalcBlobHashV1(sha256.New(), &testBlobCommit)
|
||||
testBlobIndices[testBlobVHash] = len(testBlobVHashes)
|
||||
testBlobVHashes = append(testBlobVHashes, testBlobVHash)
|
||||
|
|
@ -257,8 +264,8 @@ func makeUnsignedTx(nonce uint64, gasTipCap uint64, gasFeeCap uint64, blobFeeCap
|
|||
return makeUnsignedTxWithTestBlob(nonce, gasTipCap, gasFeeCap, blobFeeCap, rnd.Intn(len(testBlobs)))
|
||||
}
|
||||
|
||||
// makeUnsignedTx is a utility method to construct a random blob transaction
|
||||
// without signing it.
|
||||
// makeUnsignedTxWithTestBlob is a utility method to construct a random blob transaction
|
||||
// with a specific test blob without signing it.
|
||||
func makeUnsignedTxWithTestBlob(nonce uint64, gasTipCap uint64, gasFeeCap uint64, blobFeeCap uint64, blobIdx int) *types.BlobTx {
|
||||
return &types.BlobTx{
|
||||
ChainID: uint256.MustFromBig(params.MainnetChainConfig.ChainID),
|
||||
|
|
@ -416,24 +423,40 @@ func verifyBlobRetrievals(t *testing.T, pool *BlobPool) {
|
|||
hashes = append(hashes, tx.vhashes...)
|
||||
}
|
||||
}
|
||||
blobs, _, proofs, err := pool.GetBlobs(hashes, types.BlobSidecarVersion0)
|
||||
blobs1, _, proofs1, err := pool.GetBlobs(hashes, types.BlobSidecarVersion0)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
blobs2, _, proofs2, err := pool.GetBlobs(hashes, types.BlobSidecarVersion1)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
// Cross validate what we received vs what we wanted
|
||||
if len(blobs) != len(hashes) || len(proofs) != len(hashes) {
|
||||
t.Errorf("retrieved blobs/proofs size mismatch: have %d/%d, want %d", len(blobs), len(proofs), len(hashes))
|
||||
if len(blobs1) != len(hashes) || len(proofs1) != len(hashes) {
|
||||
t.Errorf("retrieved blobs/proofs size mismatch: have %d/%d, want %d", len(blobs1), len(proofs1), len(hashes))
|
||||
return
|
||||
}
|
||||
if len(blobs2) != len(hashes) || len(proofs2) != len(hashes) {
|
||||
t.Errorf("retrieved blobs/proofs size mismatch: have %d/%d, want blobs %d, want proofs: %d", len(blobs2), len(proofs2), len(hashes), len(hashes))
|
||||
return
|
||||
}
|
||||
for i, hash := range hashes {
|
||||
// If an item is missing, but shouldn't, error
|
||||
if blobs[i] == nil || proofs[i] == nil {
|
||||
if blobs1[i] == nil || proofs1[i] == nil {
|
||||
t.Errorf("tracked blob retrieval failed: item %d, hash %x", i, hash)
|
||||
continue
|
||||
}
|
||||
if blobs2[i] == nil || proofs2[i] == nil {
|
||||
t.Errorf("tracked blob retrieval failed: item %d, hash %x", i, hash)
|
||||
continue
|
||||
}
|
||||
// Item retrieved, make sure it matches the expectation
|
||||
index := testBlobIndices[hash]
|
||||
if *blobs[i] != *testBlobs[index] || proofs[i][0] != testBlobProofs[index] {
|
||||
if *blobs1[i] != *testBlobs[index] || proofs1[i][0] != testBlobProofs[index] {
|
||||
t.Errorf("retrieved blob or proof mismatch: item %d, hash %x", i, hash)
|
||||
continue
|
||||
}
|
||||
if *blobs2[i] != *testBlobs[index] || !slices.Equal(proofs2[i], testBlobCellProofs[index]) {
|
||||
t.Errorf("retrieved blob or proof mismatch: item %d, hash %x", i, hash)
|
||||
continue
|
||||
}
|
||||
|
|
@ -1668,6 +1691,49 @@ func TestAdd(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
// Tests that adding the transactions with legacy sidecar and expect them to
|
||||
// be converted to new format correctly.
|
||||
func TestAddLegacyBlobTx(t *testing.T) {
|
||||
var (
|
||||
key1, _ = crypto.GenerateKey()
|
||||
key2, _ = crypto.GenerateKey()
|
||||
|
||||
addr1 = crypto.PubkeyToAddress(key1.PublicKey)
|
||||
addr2 = crypto.PubkeyToAddress(key2.PublicKey)
|
||||
)
|
||||
|
||||
statedb, _ := state.New(types.EmptyRootHash, state.NewDatabaseForTesting())
|
||||
statedb.AddBalance(addr1, uint256.NewInt(1_000_000_000), tracing.BalanceChangeUnspecified)
|
||||
statedb.AddBalance(addr2, uint256.NewInt(1_000_000_000), tracing.BalanceChangeUnspecified)
|
||||
statedb.Commit(0, true, false)
|
||||
|
||||
chain := &testBlockChain{
|
||||
config: params.MergedTestChainConfig,
|
||||
basefee: uint256.NewInt(1050),
|
||||
blobfee: uint256.NewInt(105),
|
||||
statedb: statedb,
|
||||
}
|
||||
pool := New(Config{Datadir: t.TempDir()}, chain, nil)
|
||||
if err := pool.Init(1, chain.CurrentBlock(), newReserver()); err != nil {
|
||||
t.Fatalf("failed to create blob pool: %v", err)
|
||||
}
|
||||
|
||||
// Attempt to add legacy blob transactions.
|
||||
var (
|
||||
tx1 = makeMultiBlobTx(0, 1, 1000, 100, 6, 0, key1, types.BlobSidecarVersion0)
|
||||
tx2 = makeMultiBlobTx(0, 1, 800, 70, 6, 6, key2, types.BlobSidecarVersion0)
|
||||
tx3 = makeMultiBlobTx(1, 1, 800, 70, 6, 12, key2, types.BlobSidecarVersion1)
|
||||
)
|
||||
errs := pool.Add([]*types.Transaction{tx1, tx2, tx3}, true)
|
||||
for _, err := range errs {
|
||||
if err != nil {
|
||||
t.Fatalf("failed to add tx: %v", err)
|
||||
}
|
||||
}
|
||||
verifyPoolInternals(t, pool)
|
||||
pool.Close()
|
||||
}
|
||||
|
||||
func TestGetBlobs(t *testing.T) {
|
||||
//log.SetDefault(log.NewLogger(log.NewTerminalHandlerWithLevel(os.Stderr, log.LevelTrace, true)))
|
||||
|
||||
|
|
@ -1750,10 +1816,10 @@ func TestGetBlobs(t *testing.T) {
|
|||
}
|
||||
|
||||
cases := []struct {
|
||||
start int
|
||||
limit int
|
||||
version byte
|
||||
expErr bool
|
||||
start int
|
||||
limit int
|
||||
fillRandom bool
|
||||
version byte
|
||||
}{
|
||||
{
|
||||
start: 0, limit: 6,
|
||||
|
|
@ -1763,6 +1829,14 @@ func TestGetBlobs(t *testing.T) {
|
|||
start: 0, limit: 6,
|
||||
version: types.BlobSidecarVersion1,
|
||||
},
|
||||
{
|
||||
start: 0, limit: 6, fillRandom: true,
|
||||
version: types.BlobSidecarVersion0,
|
||||
},
|
||||
{
|
||||
start: 0, limit: 6, fillRandom: true,
|
||||
version: types.BlobSidecarVersion1,
|
||||
},
|
||||
{
|
||||
start: 3, limit: 9,
|
||||
version: types.BlobSidecarVersion0,
|
||||
|
|
@ -1771,6 +1845,14 @@ func TestGetBlobs(t *testing.T) {
|
|||
start: 3, limit: 9,
|
||||
version: types.BlobSidecarVersion1,
|
||||
},
|
||||
{
|
||||
start: 3, limit: 9, fillRandom: true,
|
||||
version: types.BlobSidecarVersion0,
|
||||
},
|
||||
{
|
||||
start: 3, limit: 9, fillRandom: true,
|
||||
version: types.BlobSidecarVersion1,
|
||||
},
|
||||
{
|
||||
start: 3, limit: 15,
|
||||
version: types.BlobSidecarVersion0,
|
||||
|
|
@ -1779,6 +1861,14 @@ func TestGetBlobs(t *testing.T) {
|
|||
start: 3, limit: 15,
|
||||
version: types.BlobSidecarVersion1,
|
||||
},
|
||||
{
|
||||
start: 3, limit: 15, fillRandom: true,
|
||||
version: types.BlobSidecarVersion0,
|
||||
},
|
||||
{
|
||||
start: 3, limit: 15, fillRandom: true,
|
||||
version: types.BlobSidecarVersion1,
|
||||
},
|
||||
{
|
||||
start: 0, limit: 18,
|
||||
version: types.BlobSidecarVersion0,
|
||||
|
|
@ -1788,58 +1878,79 @@ func TestGetBlobs(t *testing.T) {
|
|||
version: types.BlobSidecarVersion1,
|
||||
},
|
||||
{
|
||||
start: 18, limit: 20,
|
||||
start: 0, limit: 18, fillRandom: true,
|
||||
version: types.BlobSidecarVersion0,
|
||||
expErr: true,
|
||||
},
|
||||
{
|
||||
start: 0, limit: 18, fillRandom: true,
|
||||
version: types.BlobSidecarVersion1,
|
||||
},
|
||||
}
|
||||
for i, c := range cases {
|
||||
var vhashes []common.Hash
|
||||
var (
|
||||
vhashes []common.Hash
|
||||
filled = make(map[int]struct{})
|
||||
)
|
||||
if c.fillRandom {
|
||||
filled[len(vhashes)] = struct{}{}
|
||||
vhashes = append(vhashes, testrand.Hash())
|
||||
}
|
||||
for j := c.start; j < c.limit; j++ {
|
||||
vhashes = append(vhashes, testBlobVHashes[j])
|
||||
if c.fillRandom && rand.Intn(2) == 0 {
|
||||
filled[len(vhashes)] = struct{}{}
|
||||
vhashes = append(vhashes, testrand.Hash())
|
||||
}
|
||||
}
|
||||
if c.fillRandom {
|
||||
filled[len(vhashes)] = struct{}{}
|
||||
vhashes = append(vhashes, testrand.Hash())
|
||||
}
|
||||
blobs, _, proofs, err := pool.GetBlobs(vhashes, c.version)
|
||||
if err != nil {
|
||||
t.Errorf("Unexpected error for case %d, %v", i, err)
|
||||
}
|
||||
|
||||
if c.expErr {
|
||||
if err == nil {
|
||||
t.Errorf("Unexpected return, want error for case %d", i)
|
||||
}
|
||||
} else {
|
||||
if err != nil {
|
||||
t.Errorf("Unexpected error for case %d, %v", i, err)
|
||||
}
|
||||
// Cross validate what we received vs what we wanted
|
||||
length := c.limit - c.start
|
||||
if len(blobs) != length || len(proofs) != length {
|
||||
t.Errorf("retrieved blobs/proofs size mismatch: have %d/%d, want %d", len(blobs), len(proofs), length)
|
||||
// Cross validate what we received vs what we wanted
|
||||
length := c.limit - c.start
|
||||
wantLen := length + len(filled)
|
||||
if len(blobs) != wantLen || len(proofs) != wantLen {
|
||||
t.Errorf("retrieved blobs/proofs size mismatch: have %d/%d, want %d", len(blobs), len(proofs), wantLen)
|
||||
continue
|
||||
}
|
||||
|
||||
var unknown int
|
||||
for j := 0; j < len(blobs); j++ {
|
||||
if _, exist := filled[j]; exist {
|
||||
if blobs[j] != nil || proofs[j] != nil {
|
||||
t.Errorf("Unexpected blob and proof, item %d", j)
|
||||
}
|
||||
unknown++
|
||||
continue
|
||||
}
|
||||
for j := 0; j < len(blobs); j++ {
|
||||
// If an item is missing, but shouldn't, error
|
||||
if blobs[j] == nil || proofs[j] == nil {
|
||||
t.Errorf("tracked blob retrieval failed: item %d, hash %x", j, vhashes[j])
|
||||
continue
|
||||
// If an item is missing, but shouldn't, error
|
||||
if blobs[j] == nil || proofs[j] == nil {
|
||||
t.Errorf("tracked blob retrieval failed: item %d, hash %x", j, vhashes[j])
|
||||
continue
|
||||
}
|
||||
// Item retrieved, make sure the blob matches the expectation
|
||||
if *blobs[j] != *testBlobs[c.start+j-unknown] {
|
||||
t.Errorf("retrieved blob mismatch: item %d, hash %x", j, vhashes[j])
|
||||
continue
|
||||
}
|
||||
// Item retrieved, make sure the proof matches the expectation
|
||||
if c.version == types.BlobSidecarVersion0 {
|
||||
if proofs[j][0] != testBlobProofs[c.start+j-unknown] {
|
||||
t.Errorf("retrieved proof mismatch: item %d, hash %x", j, vhashes[j])
|
||||
}
|
||||
// Item retrieved, make sure the blob matches the expectation
|
||||
if *blobs[j] != *testBlobs[c.start+j] {
|
||||
t.Errorf("retrieved blob mismatch: item %d, hash %x", j, vhashes[j])
|
||||
continue
|
||||
}
|
||||
// Item retrieved, make sure the proof matches the expectation
|
||||
if c.version == types.BlobSidecarVersion0 {
|
||||
if proofs[j][0] != testBlobProofs[c.start+j] {
|
||||
t.Errorf("retrieved proof mismatch: item %d, hash %x", j, vhashes[j])
|
||||
}
|
||||
} else {
|
||||
want, _ := kzg4844.ComputeCellProofs(blobs[j])
|
||||
if !reflect.DeepEqual(want, proofs[j]) {
|
||||
t.Errorf("retrieved proof mismatch: item %d, hash %x", j, vhashes[j])
|
||||
}
|
||||
} else {
|
||||
want, _ := kzg4844.ComputeCellProofs(blobs[j])
|
||||
if !reflect.DeepEqual(want, proofs[j]) {
|
||||
t.Errorf("retrieved proof mismatch: item %d, hash %x", j, vhashes[j])
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pool.Close()
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -514,26 +514,15 @@ func (pool *LegacyPool) Pending(filter txpool.PendingFilter) map[common.Address]
|
|||
pool.mu.Lock()
|
||||
defer pool.mu.Unlock()
|
||||
|
||||
// Convert the new uint256.Int types to the old big.Int ones used by the legacy pool
|
||||
var (
|
||||
minTipBig *big.Int
|
||||
baseFeeBig *big.Int
|
||||
)
|
||||
if filter.MinTip != nil {
|
||||
minTipBig = filter.MinTip.ToBig()
|
||||
}
|
||||
if filter.BaseFee != nil {
|
||||
baseFeeBig = filter.BaseFee.ToBig()
|
||||
}
|
||||
pending := make(map[common.Address][]*txpool.LazyTransaction, len(pool.pending))
|
||||
for addr, list := range pool.pending {
|
||||
txs := list.Flatten()
|
||||
|
||||
// If the miner requests tip enforcement, cap the lists now
|
||||
if minTipBig != nil || filter.GasLimitCap != 0 {
|
||||
if filter.MinTip != nil || filter.GasLimitCap != 0 {
|
||||
for i, tx := range txs {
|
||||
if minTipBig != nil {
|
||||
if tx.EffectiveGasTipIntCmp(minTipBig, baseFeeBig) < 0 {
|
||||
if filter.MinTip != nil {
|
||||
if tx.EffectiveGasTipIntCmp(filter.MinTip, filter.BaseFee) < 0 {
|
||||
txs = txs[:i]
|
||||
break
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2292,8 +2292,8 @@ func TestSetCodeTransactions(t *testing.T) {
|
|||
pending: 1,
|
||||
run: func(name string) {
|
||||
aa := common.Address{0xaa, 0xaa}
|
||||
statedb.SetCode(addrA, append(types.DelegationPrefix, aa.Bytes()...))
|
||||
statedb.SetCode(aa, []byte{byte(vm.ADDRESS), byte(vm.PUSH0), byte(vm.SSTORE)})
|
||||
statedb.SetCode(addrA, append(types.DelegationPrefix, aa.Bytes()...), tracing.CodeChangeUnspecified)
|
||||
statedb.SetCode(aa, []byte{byte(vm.ADDRESS), byte(vm.PUSH0), byte(vm.SSTORE)}, tracing.CodeChangeUnspecified)
|
||||
|
||||
// Send gapped transaction, it should be rejected.
|
||||
if err := pool.addRemoteSync(pricedTransaction(2, 100000, big.NewInt(1), keyA)); !errors.Is(err, ErrOutOfOrderTxFromDelegated) {
|
||||
|
|
@ -2317,7 +2317,7 @@ func TestSetCodeTransactions(t *testing.T) {
|
|||
}
|
||||
|
||||
// Reset the delegation, avoid leaking state into the other tests
|
||||
statedb.SetCode(addrA, nil)
|
||||
statedb.SetCode(addrA, nil, tracing.CodeChangeUnspecified)
|
||||
},
|
||||
},
|
||||
{
|
||||
|
|
@ -2583,7 +2583,7 @@ func TestSetCodeTransactionsReorg(t *testing.T) {
|
|||
}
|
||||
// Simulate the chain moving
|
||||
blockchain.statedb.SetNonce(addrA, 1, tracing.NonceChangeAuthorization)
|
||||
blockchain.statedb.SetCode(addrA, types.AddressToDelegation(auth.Address))
|
||||
blockchain.statedb.SetCode(addrA, types.AddressToDelegation(auth.Address), tracing.CodeChangeUnspecified)
|
||||
<-pool.requestReset(nil, nil)
|
||||
// Set an authorization for 0x00
|
||||
auth, _ = types.SignSetCode(keyA, types.SetCodeAuthorization{
|
||||
|
|
@ -2601,7 +2601,7 @@ func TestSetCodeTransactionsReorg(t *testing.T) {
|
|||
}
|
||||
// Simulate the chain moving
|
||||
blockchain.statedb.SetNonce(addrA, 2, tracing.NonceChangeAuthorization)
|
||||
blockchain.statedb.SetCode(addrA, nil)
|
||||
blockchain.statedb.SetCode(addrA, nil, tracing.CodeChangeUnspecified)
|
||||
<-pool.requestReset(nil, nil)
|
||||
// Now send two transactions from addrA
|
||||
if err := pool.addRemoteSync(pricedTransaction(2, 100000, big.NewInt(1000), keyA)); err != nil {
|
||||
|
|
|
|||
|
|
@ -475,7 +475,7 @@ func (l *list) subTotalCost(txs []*types.Transaction) {
|
|||
// then the heap is sorted based on the effective tip based on the given base fee.
|
||||
// If baseFee is nil then the sorting is based on gasFeeCap.
|
||||
type priceHeap struct {
|
||||
baseFee *big.Int // heap should always be re-sorted after baseFee is changed
|
||||
baseFee *uint256.Int // heap should always be re-sorted after baseFee is changed
|
||||
list []*types.Transaction
|
||||
}
|
||||
|
||||
|
|
@ -677,6 +677,10 @@ func (l *pricedList) Reheap() {
|
|||
// SetBaseFee updates the base fee and triggers a re-heap. Note that Removed is not
|
||||
// necessary to call right before SetBaseFee when processing a new block.
|
||||
func (l *pricedList) SetBaseFee(baseFee *big.Int) {
|
||||
l.urgent.baseFee = baseFee
|
||||
base := new(uint256.Int)
|
||||
if baseFee != nil {
|
||||
base.SetFromBig(baseFee)
|
||||
}
|
||||
l.urgent.baseFee = base
|
||||
l.Reheap()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,7 +22,6 @@ import (
|
|||
"math/big"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/consensus/misc/eip4844"
|
||||
"github.com/ethereum/go-ethereum/core"
|
||||
"github.com/ethereum/go-ethereum/core/state"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
|
|
@ -167,9 +166,8 @@ func validateBlobTx(tx *types.Transaction, head *types.Header, opts *ValidationO
|
|||
if len(hashes) == 0 {
|
||||
return errors.New("blobless blob transaction")
|
||||
}
|
||||
maxBlobs := eip4844.MaxBlobsPerBlock(opts.Config, head.Time)
|
||||
if len(hashes) > maxBlobs {
|
||||
return fmt.Errorf("too many blobs in transaction: have %d, permitted %d", len(hashes), maxBlobs)
|
||||
if len(hashes) > params.BlobTxMaxBlobs {
|
||||
return fmt.Errorf("too many blobs in transaction: have %d, permitted %d", len(hashes), params.BlobTxMaxBlobs)
|
||||
}
|
||||
if len(sidecar.Blobs) != len(hashes) {
|
||||
return fmt.Errorf("invalid number of %d blobs compared to %d blob hashes", len(sidecar.Blobs), len(hashes))
|
||||
|
|
|
|||
|
|
@ -45,4 +45,7 @@ var (
|
|||
|
||||
// EmptyVerkleHash is the known hash of an empty verkle trie.
|
||||
EmptyVerkleHash = common.Hash{}
|
||||
|
||||
// EmptyBinaryHash is the known hash of an empty binary trie.
|
||||
EmptyBinaryHash = common.Hash{}
|
||||
)
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@ import (
|
|||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/rlp"
|
||||
"github.com/holiman/uint256"
|
||||
)
|
||||
|
||||
var (
|
||||
|
|
@ -36,6 +37,7 @@ var (
|
|||
ErrInvalidTxType = errors.New("transaction type not valid in this context")
|
||||
ErrTxTypeNotSupported = errors.New("transaction type not supported")
|
||||
ErrGasFeeCapTooLow = errors.New("fee cap less than base fee")
|
||||
ErrUint256Overflow = errors.New("bigint overflow, too large for uint256")
|
||||
errShortTypedTx = errors.New("typed transaction too short")
|
||||
errInvalidYParity = errors.New("'yParity' field must be 0 or 1")
|
||||
errVYParityMismatch = errors.New("'v' and 'yParity' fields do not match")
|
||||
|
|
@ -357,54 +359,66 @@ func (tx *Transaction) GasTipCapIntCmp(other *big.Int) int {
|
|||
}
|
||||
|
||||
// EffectiveGasTip returns the effective miner gasTipCap for the given base fee.
|
||||
// Note: if the effective gasTipCap is negative, this method returns both error
|
||||
// the actual negative value, _and_ ErrGasFeeCapTooLow
|
||||
// Note: if the effective gasTipCap would be negative, this method
|
||||
// returns ErrGasFeeCapTooLow, and value is undefined.
|
||||
func (tx *Transaction) EffectiveGasTip(baseFee *big.Int) (*big.Int, error) {
|
||||
dst := new(big.Int)
|
||||
err := tx.calcEffectiveGasTip(dst, baseFee)
|
||||
return dst, err
|
||||
dst := new(uint256.Int)
|
||||
base := new(uint256.Int)
|
||||
if baseFee != nil {
|
||||
if base.SetFromBig(baseFee) {
|
||||
return nil, ErrUint256Overflow
|
||||
}
|
||||
}
|
||||
err := tx.calcEffectiveGasTip(dst, base)
|
||||
return dst.ToBig(), err
|
||||
}
|
||||
|
||||
// calcEffectiveGasTip calculates the effective gas tip of the transaction and
|
||||
// saves the result to dst.
|
||||
func (tx *Transaction) calcEffectiveGasTip(dst *big.Int, baseFee *big.Int) error {
|
||||
func (tx *Transaction) calcEffectiveGasTip(dst *uint256.Int, baseFee *uint256.Int) error {
|
||||
if baseFee == nil {
|
||||
dst.Set(tx.inner.gasTipCap())
|
||||
if dst.SetFromBig(tx.inner.gasTipCap()) {
|
||||
return ErrUint256Overflow
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
var err error
|
||||
gasFeeCap := tx.inner.gasFeeCap()
|
||||
if gasFeeCap.Cmp(baseFee) < 0 {
|
||||
if dst.SetFromBig(tx.inner.gasFeeCap()) {
|
||||
return ErrUint256Overflow
|
||||
}
|
||||
if dst.Cmp(baseFee) < 0 {
|
||||
err = ErrGasFeeCapTooLow
|
||||
}
|
||||
|
||||
dst.Sub(gasFeeCap, baseFee)
|
||||
gasTipCap := tx.inner.gasTipCap()
|
||||
dst.Sub(dst, baseFee)
|
||||
gasTipCap := new(uint256.Int)
|
||||
if gasTipCap.SetFromBig(tx.inner.gasTipCap()) {
|
||||
return ErrUint256Overflow
|
||||
}
|
||||
if gasTipCap.Cmp(dst) < 0 {
|
||||
dst.Set(gasTipCap)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// EffectiveGasTipCmp compares the effective gasTipCap of two transactions assuming the given base fee.
|
||||
func (tx *Transaction) EffectiveGasTipCmp(other *Transaction, baseFee *big.Int) int {
|
||||
func (tx *Transaction) EffectiveGasTipCmp(other *Transaction, baseFee *uint256.Int) int {
|
||||
if baseFee == nil {
|
||||
return tx.GasTipCapCmp(other)
|
||||
}
|
||||
// Use more efficient internal method.
|
||||
txTip, otherTip := new(big.Int), new(big.Int)
|
||||
txTip, otherTip := new(uint256.Int), new(uint256.Int)
|
||||
tx.calcEffectiveGasTip(txTip, baseFee)
|
||||
other.calcEffectiveGasTip(otherTip, baseFee)
|
||||
return txTip.Cmp(otherTip)
|
||||
}
|
||||
|
||||
// EffectiveGasTipIntCmp compares the effective gasTipCap of a transaction to the given gasTipCap.
|
||||
func (tx *Transaction) EffectiveGasTipIntCmp(other *big.Int, baseFee *big.Int) int {
|
||||
func (tx *Transaction) EffectiveGasTipIntCmp(other *uint256.Int, baseFee *uint256.Int) int {
|
||||
if baseFee == nil {
|
||||
return tx.GasTipCapIntCmp(other)
|
||||
return tx.GasTipCapIntCmp(other.ToBig())
|
||||
}
|
||||
txTip := new(big.Int)
|
||||
txTip := new(uint256.Int)
|
||||
tx.calcEffectiveGasTip(txTip, baseFee)
|
||||
return txTip.Cmp(other)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -31,6 +31,7 @@ import (
|
|||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
"github.com/ethereum/go-ethereum/rlp"
|
||||
"github.com/holiman/uint256"
|
||||
)
|
||||
|
||||
// The values in those tests are from the Transaction Tests
|
||||
|
|
@ -609,12 +610,12 @@ func BenchmarkEffectiveGasTip(b *testing.B) {
|
|||
Data: nil,
|
||||
}
|
||||
tx, _ := SignNewTx(key, signer, txdata)
|
||||
baseFee := big.NewInt(1000000000) // 1 gwei
|
||||
baseFee := uint256.NewInt(1000000000) // 1 gwei
|
||||
|
||||
b.Run("Original", func(b *testing.B) {
|
||||
b.ReportAllocs()
|
||||
for i := 0; i < b.N; i++ {
|
||||
_, err := tx.EffectiveGasTip(baseFee)
|
||||
_, err := tx.EffectiveGasTip(baseFee.ToBig())
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
|
|
@ -623,7 +624,7 @@ func BenchmarkEffectiveGasTip(b *testing.B) {
|
|||
|
||||
b.Run("IntoMethod", func(b *testing.B) {
|
||||
b.ReportAllocs()
|
||||
dst := new(big.Int)
|
||||
dst := new(uint256.Int)
|
||||
for i := 0; i < b.N; i++ {
|
||||
err := tx.calcEffectiveGasTip(dst, baseFee)
|
||||
if err != nil {
|
||||
|
|
@ -634,9 +635,6 @@ func BenchmarkEffectiveGasTip(b *testing.B) {
|
|||
}
|
||||
|
||||
func TestEffectiveGasTipInto(t *testing.T) {
|
||||
signer := LatestSigner(params.TestChainConfig)
|
||||
key, _ := crypto.GenerateKey()
|
||||
|
||||
testCases := []struct {
|
||||
tipCap int64
|
||||
feeCap int64
|
||||
|
|
@ -652,8 +650,26 @@ func TestEffectiveGasTipInto(t *testing.T) {
|
|||
{tipCap: 50, feeCap: 100, baseFee: nil}, // nil base fee
|
||||
}
|
||||
|
||||
// original, non-allocation golfed version
|
||||
orig := func(tx *Transaction, baseFee *big.Int) (*big.Int, error) {
|
||||
if baseFee == nil {
|
||||
return tx.GasTipCap(), nil
|
||||
}
|
||||
var err error
|
||||
gasFeeCap := tx.GasFeeCap()
|
||||
if gasFeeCap.Cmp(baseFee) < 0 {
|
||||
err = ErrGasFeeCapTooLow
|
||||
}
|
||||
gasFeeCap = gasFeeCap.Sub(gasFeeCap, baseFee)
|
||||
gasTipCap := tx.GasTipCap()
|
||||
if gasTipCap.Cmp(gasFeeCap) < 0 {
|
||||
return gasTipCap, err
|
||||
}
|
||||
return gasFeeCap, err
|
||||
}
|
||||
|
||||
for i, tc := range testCases {
|
||||
txdata := &DynamicFeeTx{
|
||||
tx := NewTx(&DynamicFeeTx{
|
||||
ChainID: big.NewInt(1),
|
||||
Nonce: 0,
|
||||
GasTipCap: big.NewInt(tc.tipCap),
|
||||
|
|
@ -662,27 +678,28 @@ func TestEffectiveGasTipInto(t *testing.T) {
|
|||
To: &common.Address{},
|
||||
Value: big.NewInt(0),
|
||||
Data: nil,
|
||||
}
|
||||
tx, _ := SignNewTx(key, signer, txdata)
|
||||
})
|
||||
|
||||
var baseFee *big.Int
|
||||
var baseFee2 *uint256.Int
|
||||
if tc.baseFee != nil {
|
||||
baseFee = big.NewInt(*tc.baseFee)
|
||||
baseFee2 = uint256.NewInt(uint64(*tc.baseFee))
|
||||
}
|
||||
|
||||
// Get result from original method
|
||||
orig, origErr := tx.EffectiveGasTip(baseFee)
|
||||
orig, origErr := orig(tx, baseFee)
|
||||
|
||||
// Get result from new method
|
||||
dst := new(big.Int)
|
||||
newErr := tx.calcEffectiveGasTip(dst, baseFee)
|
||||
dst := new(uint256.Int)
|
||||
newErr := tx.calcEffectiveGasTip(dst, baseFee2)
|
||||
|
||||
// Compare results
|
||||
if (origErr != nil) != (newErr != nil) {
|
||||
t.Fatalf("case %d: error mismatch: orig %v, new %v", i, origErr, newErr)
|
||||
}
|
||||
|
||||
if orig.Cmp(dst) != 0 {
|
||||
if origErr == nil && orig.Cmp(dst.ToBig()) != 0 {
|
||||
t.Fatalf("case %d: result mismatch: orig %v, new %v", i, orig, dst)
|
||||
}
|
||||
}
|
||||
|
|
@ -692,3 +709,28 @@ func TestEffectiveGasTipInto(t *testing.T) {
|
|||
func intPtr(i int64) *int64 {
|
||||
return &i
|
||||
}
|
||||
|
||||
func BenchmarkEffectiveGasTipCmp(b *testing.B) {
|
||||
signer := LatestSigner(params.TestChainConfig)
|
||||
key, _ := crypto.GenerateKey()
|
||||
txdata := &DynamicFeeTx{
|
||||
ChainID: big.NewInt(1),
|
||||
Nonce: 0,
|
||||
GasTipCap: big.NewInt(2000000000),
|
||||
GasFeeCap: big.NewInt(3000000000),
|
||||
Gas: 21000,
|
||||
To: &common.Address{},
|
||||
Value: big.NewInt(0),
|
||||
Data: nil,
|
||||
}
|
||||
tx, _ := SignNewTx(key, signer, txdata)
|
||||
other, _ := SignNewTx(key, signer, txdata)
|
||||
baseFee := uint256.NewInt(1000000000) // 1 gwei
|
||||
|
||||
b.Run("Original", func(b *testing.B) {
|
||||
b.ReportAllocs()
|
||||
for i := 0; i < b.N; i++ {
|
||||
tx.EffectiveGasTipCmp(other, baseFee)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
|
|||
|
|
@ -232,7 +232,7 @@ func TestProcessParentBlockHash(t *testing.T) {
|
|||
// etc
|
||||
checkBlockHashes := func(statedb *state.StateDB, isVerkle bool) {
|
||||
statedb.SetNonce(params.HistoryStorageAddress, 1, tracing.NonceChangeUnspecified)
|
||||
statedb.SetCode(params.HistoryStorageAddress, params.HistoryStorageCode)
|
||||
statedb.SetCode(params.HistoryStorageAddress, params.HistoryStorageCode, tracing.CodeChangeUnspecified)
|
||||
// Process n blocks, from 1 .. num
|
||||
var num = 2
|
||||
for i := 1; i <= num; i++ {
|
||||
|
|
@ -787,7 +787,7 @@ func TestProcessVerkleSelfDestructInSeparateTx(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
// TestProcessVerkleSelfDestructInSeparateTx controls the contents of the witness after
|
||||
// TestProcessVerkleSelfDestructInSameTx controls the contents of the witness after
|
||||
// a eip6780-compliant selfdestruct occurs.
|
||||
func TestProcessVerkleSelfDestructInSameTx(t *testing.T) {
|
||||
// The test txs were taken from a secondary testnet with chain id 69421
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@ import (
|
|||
"maps"
|
||||
"math"
|
||||
"math/big"
|
||||
"math/bits"
|
||||
|
||||
"github.com/consensys/gnark-crypto/ecc"
|
||||
bls12381 "github.com/consensys/gnark-crypto/ecc/bls12-381"
|
||||
|
|
@ -38,6 +39,7 @@ import (
|
|||
"github.com/ethereum/go-ethereum/crypto/kzg4844"
|
||||
"github.com/ethereum/go-ethereum/crypto/secp256r1"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
"github.com/holiman/uint256"
|
||||
"golang.org/x/crypto/ripemd160"
|
||||
)
|
||||
|
||||
|
|
@ -47,6 +49,7 @@ import (
|
|||
type PrecompiledContract interface {
|
||||
RequiredGas(input []byte) uint64 // RequiredPrice calculates the contract gas use
|
||||
Run(input []byte) ([]byte, error) // Run runs the precompiled contract
|
||||
Name() string
|
||||
}
|
||||
|
||||
// PrecompiledContracts contains the precompiled contracts supported at the given fork.
|
||||
|
|
@ -309,6 +312,10 @@ func (c *ecrecover) Run(input []byte) ([]byte, error) {
|
|||
return common.LeftPadBytes(crypto.Keccak256(pubKey[1:])[12:], 32), nil
|
||||
}
|
||||
|
||||
func (c *ecrecover) Name() string {
|
||||
return "ECREC"
|
||||
}
|
||||
|
||||
// SHA256 implemented as a native contract.
|
||||
type sha256hash struct{}
|
||||
|
||||
|
|
@ -324,6 +331,10 @@ func (c *sha256hash) Run(input []byte) ([]byte, error) {
|
|||
return h[:], nil
|
||||
}
|
||||
|
||||
func (c *sha256hash) Name() string {
|
||||
return "SHA256"
|
||||
}
|
||||
|
||||
// RIPEMD160 implemented as a native contract.
|
||||
type ripemd160hash struct{}
|
||||
|
||||
|
|
@ -340,6 +351,10 @@ func (c *ripemd160hash) Run(input []byte) ([]byte, error) {
|
|||
return common.LeftPadBytes(ripemd.Sum(nil), 32), nil
|
||||
}
|
||||
|
||||
func (c *ripemd160hash) Name() string {
|
||||
return "RIPEMD160"
|
||||
}
|
||||
|
||||
// data copy implemented as a native contract.
|
||||
type dataCopy struct{}
|
||||
|
||||
|
|
@ -354,6 +369,10 @@ func (c *dataCopy) Run(in []byte) ([]byte, error) {
|
|||
return common.CopyBytes(in), nil
|
||||
}
|
||||
|
||||
func (c *dataCopy) Name() string {
|
||||
return "ID"
|
||||
}
|
||||
|
||||
// bigModExp implements a native big integer exponential modular operation.
|
||||
type bigModExp struct {
|
||||
eip2565 bool
|
||||
|
|
@ -361,21 +380,7 @@ type bigModExp struct {
|
|||
eip7883 bool
|
||||
}
|
||||
|
||||
var (
|
||||
big1 = big.NewInt(1)
|
||||
big3 = big.NewInt(3)
|
||||
big7 = big.NewInt(7)
|
||||
big20 = big.NewInt(20)
|
||||
big32 = big.NewInt(32)
|
||||
big64 = big.NewInt(64)
|
||||
big96 = big.NewInt(96)
|
||||
big480 = big.NewInt(480)
|
||||
big1024 = big.NewInt(1024)
|
||||
big3072 = big.NewInt(3072)
|
||||
big199680 = big.NewInt(199680)
|
||||
)
|
||||
|
||||
// modexpMultComplexity implements bigModexp multComplexity formula, as defined in EIP-198
|
||||
// byzantiumMultComplexity implements the bigModexp multComplexity formula, as defined in EIP-198.
|
||||
//
|
||||
// def mult_complexity(x):
|
||||
// if x <= 64: return x ** 2
|
||||
|
|
@ -383,120 +388,215 @@ var (
|
|||
// else: return x ** 2 // 16 + 480 * x - 199680
|
||||
//
|
||||
// where is x is max(length_of_MODULUS, length_of_BASE)
|
||||
func modexpMultComplexity(x *big.Int) *big.Int {
|
||||
// returns MaxUint64 if an overflow occurred.
|
||||
func byzantiumMultComplexity(x uint64) uint64 {
|
||||
switch {
|
||||
case x.Cmp(big64) <= 0:
|
||||
x.Mul(x, x) // x ** 2
|
||||
case x.Cmp(big1024) <= 0:
|
||||
// (x ** 2 // 4 ) + ( 96 * x - 3072)
|
||||
x = new(big.Int).Add(
|
||||
new(big.Int).Rsh(new(big.Int).Mul(x, x), 2),
|
||||
new(big.Int).Sub(new(big.Int).Mul(big96, x), big3072),
|
||||
)
|
||||
case x <= 64:
|
||||
return x * x
|
||||
case x <= 1024:
|
||||
// x^2 / 4 + 96*x - 3072
|
||||
return x*x/4 + 96*x - 3072
|
||||
|
||||
default:
|
||||
// (x ** 2 // 16) + (480 * x - 199680)
|
||||
x = new(big.Int).Add(
|
||||
new(big.Int).Rsh(new(big.Int).Mul(x, x), 4),
|
||||
new(big.Int).Sub(new(big.Int).Mul(big480, x), big199680),
|
||||
)
|
||||
// For large x, use uint256 arithmetic to avoid overflow
|
||||
// x^2 / 16 + 480*x - 199680
|
||||
|
||||
// xSqr = x^2 / 16
|
||||
carry, xSqr := bits.Mul64(x, x)
|
||||
if carry != 0 {
|
||||
return math.MaxUint64
|
||||
}
|
||||
xSqr = xSqr >> 4
|
||||
|
||||
// Calculate 480 * x (can't overflow if x^2 didn't overflow)
|
||||
x480 := x * 480
|
||||
// Calculate 480 * x - 199680 (will not underflow, since x > 1024)
|
||||
x480 = x480 - 199680
|
||||
|
||||
// xSqr + x480
|
||||
sum, carry := bits.Add64(xSqr, x480, 0)
|
||||
if carry != 0 {
|
||||
return math.MaxUint64
|
||||
}
|
||||
return sum
|
||||
}
|
||||
}
|
||||
|
||||
// berlinMultComplexity implements the multiplication complexity formula for Berlin.
|
||||
//
|
||||
// def mult_complexity(x):
|
||||
//
|
||||
// ceiling(x/8)^2
|
||||
//
|
||||
// where is x is max(length_of_MODULUS, length_of_BASE)
|
||||
func berlinMultComplexity(x uint64) uint64 {
|
||||
// x = (x + 7) / 8
|
||||
x, carry := bits.Add64(x, 7, 0)
|
||||
if carry != 0 {
|
||||
return math.MaxUint64
|
||||
}
|
||||
x /= 8
|
||||
|
||||
// x^2
|
||||
carry, x = bits.Mul64(x, x)
|
||||
if carry != 0 {
|
||||
return math.MaxUint64
|
||||
}
|
||||
return x
|
||||
}
|
||||
|
||||
// osakaMultComplexity implements the multiplication complexity formula for Osaka.
|
||||
//
|
||||
// For x <= 32: returns 16
|
||||
// For x > 32: returns 2 * ceiling(x/8)^2
|
||||
func osakaMultComplexity(x uint64) uint64 {
|
||||
if x <= 32 {
|
||||
return 16
|
||||
}
|
||||
// For x > 32, return 2 * berlinMultComplexity(x)
|
||||
result := berlinMultComplexity(x)
|
||||
carry, result := bits.Mul64(result, 2)
|
||||
if carry != 0 {
|
||||
return math.MaxUint64
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// modexpIterationCount calculates the number of iterations for the modexp precompile.
|
||||
// This is the adjusted exponent length used in gas calculation.
|
||||
func modexpIterationCount(expLen uint64, expHead uint256.Int, multiplier uint64) uint64 {
|
||||
var iterationCount uint64
|
||||
|
||||
// For large exponents (expLen > 32), add (expLen - 32) * multiplier
|
||||
if expLen > 32 {
|
||||
iterationCount = (expLen - 32) * multiplier
|
||||
}
|
||||
|
||||
// Add the MSB position - 1 if expHead is non-zero
|
||||
if bitLen := expHead.BitLen(); bitLen > 0 {
|
||||
iterationCount += uint64(bitLen - 1)
|
||||
}
|
||||
|
||||
return max(iterationCount, 1)
|
||||
}
|
||||
|
||||
// byzantiumModexpGas calculates the gas cost for the modexp precompile using Byzantium rules.
|
||||
func byzantiumModexpGas(baseLen, expLen, modLen uint64, expHead uint256.Int) uint64 {
|
||||
const (
|
||||
multiplier = 8
|
||||
divisor = 20
|
||||
)
|
||||
|
||||
maxLen := max(baseLen, modLen)
|
||||
multComplexity := byzantiumMultComplexity(maxLen)
|
||||
if multComplexity == math.MaxUint64 {
|
||||
return math.MaxUint64
|
||||
}
|
||||
iterationCount := modexpIterationCount(expLen, expHead, multiplier)
|
||||
|
||||
// Calculate gas: (multComplexity * iterationCount) / divisor
|
||||
carry, gas := bits.Mul64(iterationCount, multComplexity)
|
||||
gas /= divisor
|
||||
if carry != 0 {
|
||||
return math.MaxUint64
|
||||
}
|
||||
return gas
|
||||
}
|
||||
|
||||
// berlinModexpGas calculates the gas cost for the modexp precompile using Berlin rules.
|
||||
func berlinModexpGas(baseLen, expLen, modLen uint64, expHead uint256.Int) uint64 {
|
||||
const (
|
||||
multiplier = 8
|
||||
divisor = 3
|
||||
minGas = 200
|
||||
)
|
||||
|
||||
maxLen := max(baseLen, modLen)
|
||||
multComplexity := berlinMultComplexity(maxLen)
|
||||
if multComplexity == math.MaxUint64 {
|
||||
return math.MaxUint64
|
||||
}
|
||||
iterationCount := modexpIterationCount(expLen, expHead, multiplier)
|
||||
|
||||
// Calculate gas: (multComplexity * iterationCount) / divisor
|
||||
carry, gas := bits.Mul64(iterationCount, multComplexity)
|
||||
gas /= divisor
|
||||
if carry != 0 {
|
||||
return math.MaxUint64
|
||||
}
|
||||
return max(gas, minGas)
|
||||
}
|
||||
|
||||
// osakaModexpGas calculates the gas cost for the modexp precompile using Osaka rules.
|
||||
func osakaModexpGas(baseLen, expLen, modLen uint64, expHead uint256.Int) uint64 {
|
||||
const (
|
||||
multiplier = 16
|
||||
divisor = 3
|
||||
minGas = 500
|
||||
)
|
||||
|
||||
maxLen := max(baseLen, modLen)
|
||||
multComplexity := osakaMultComplexity(maxLen)
|
||||
if multComplexity == math.MaxUint64 {
|
||||
return math.MaxUint64
|
||||
}
|
||||
iterationCount := modexpIterationCount(expLen, expHead, multiplier)
|
||||
|
||||
// Calculate gas: (multComplexity * iterationCount) / osakaDivisor
|
||||
carry, gas := bits.Mul64(iterationCount, multComplexity)
|
||||
if carry != 0 {
|
||||
return math.MaxUint64
|
||||
}
|
||||
return max(gas, minGas)
|
||||
}
|
||||
|
||||
// RequiredGas returns the gas required to execute the pre-compiled contract.
|
||||
func (c *bigModExp) RequiredGas(input []byte) uint64 {
|
||||
var (
|
||||
baseLen = new(big.Int).SetBytes(getData(input, 0, 32))
|
||||
expLen = new(big.Int).SetBytes(getData(input, 32, 32))
|
||||
modLen = new(big.Int).SetBytes(getData(input, 64, 32))
|
||||
)
|
||||
// Parse input lengths
|
||||
baseLenBig := new(uint256.Int).SetBytes(getData(input, 0, 32))
|
||||
expLenBig := new(uint256.Int).SetBytes(getData(input, 32, 32))
|
||||
modLenBig := new(uint256.Int).SetBytes(getData(input, 64, 32))
|
||||
|
||||
// Convert to uint64, capping at max value
|
||||
baseLen := baseLenBig.Uint64()
|
||||
if !baseLenBig.IsUint64() {
|
||||
baseLen = math.MaxUint64
|
||||
}
|
||||
expLen := expLenBig.Uint64()
|
||||
if !expLenBig.IsUint64() {
|
||||
expLen = math.MaxUint64
|
||||
}
|
||||
modLen := modLenBig.Uint64()
|
||||
if !modLenBig.IsUint64() {
|
||||
modLen = math.MaxUint64
|
||||
}
|
||||
|
||||
// Skip the header
|
||||
if len(input) > 96 {
|
||||
input = input[96:]
|
||||
} else {
|
||||
input = input[:0]
|
||||
}
|
||||
|
||||
// Retrieve the head 32 bytes of exp for the adjusted exponent length
|
||||
var expHead *big.Int
|
||||
if big.NewInt(int64(len(input))).Cmp(baseLen) <= 0 {
|
||||
expHead = new(big.Int)
|
||||
} else {
|
||||
if expLen.Cmp(big32) > 0 {
|
||||
expHead = new(big.Int).SetBytes(getData(input, baseLen.Uint64(), 32))
|
||||
var expHead uint256.Int
|
||||
if uint64(len(input)) > baseLen {
|
||||
if expLen > 32 {
|
||||
expHead.SetBytes(getData(input, baseLen, 32))
|
||||
} else {
|
||||
expHead = new(big.Int).SetBytes(getData(input, baseLen.Uint64(), expLen.Uint64()))
|
||||
// TODO: Check that if expLen < baseLen, then getData will return an empty slice
|
||||
expHead.SetBytes(getData(input, baseLen, expLen))
|
||||
}
|
||||
}
|
||||
// Calculate the adjusted exponent length
|
||||
var msb int
|
||||
if bitlen := expHead.BitLen(); bitlen > 0 {
|
||||
msb = bitlen - 1
|
||||
}
|
||||
adjExpLen := new(big.Int)
|
||||
if expLen.Cmp(big32) > 0 {
|
||||
adjExpLen.Sub(expLen, big32)
|
||||
if c.eip7883 {
|
||||
adjExpLen.Lsh(adjExpLen, 4)
|
||||
} else {
|
||||
adjExpLen.Lsh(adjExpLen, 3)
|
||||
}
|
||||
}
|
||||
adjExpLen.Add(adjExpLen, big.NewInt(int64(msb)))
|
||||
// Calculate the gas cost of the operation
|
||||
gas := new(big.Int)
|
||||
if modLen.Cmp(baseLen) < 0 {
|
||||
gas.Set(baseLen)
|
||||
|
||||
// Choose the appropriate gas calculation based on the EIP flags
|
||||
if c.eip7883 {
|
||||
return osakaModexpGas(baseLen, expLen, modLen, expHead)
|
||||
} else if c.eip2565 {
|
||||
return berlinModexpGas(baseLen, expLen, modLen, expHead)
|
||||
} else {
|
||||
gas.Set(modLen)
|
||||
return byzantiumModexpGas(baseLen, expLen, modLen, expHead)
|
||||
}
|
||||
|
||||
maxLenOver32 := gas.Cmp(big32) > 0
|
||||
if c.eip2565 {
|
||||
// EIP-2565 (Berlin fork) has three changes:
|
||||
//
|
||||
// 1. Different multComplexity (inlined here)
|
||||
// in EIP-2565 (https://eips.ethereum.org/EIPS/eip-2565):
|
||||
//
|
||||
// def mult_complexity(x):
|
||||
// ceiling(x/8)^2
|
||||
//
|
||||
// where is x is max(length_of_MODULUS, length_of_BASE)
|
||||
gas.Add(gas, big7)
|
||||
gas.Rsh(gas, 3)
|
||||
gas.Mul(gas, gas)
|
||||
|
||||
var minPrice uint64 = 200
|
||||
if c.eip7883 {
|
||||
minPrice = 500
|
||||
if maxLenOver32 {
|
||||
gas.Add(gas, gas)
|
||||
} else {
|
||||
gas = big.NewInt(16)
|
||||
}
|
||||
}
|
||||
|
||||
if adjExpLen.Cmp(big1) > 0 {
|
||||
gas.Mul(gas, adjExpLen)
|
||||
}
|
||||
// 2. Different divisor (`GQUADDIVISOR`) (3)
|
||||
if !c.eip7883 {
|
||||
gas.Div(gas, big3)
|
||||
}
|
||||
if gas.BitLen() > 64 {
|
||||
return math.MaxUint64
|
||||
}
|
||||
return max(minPrice, gas.Uint64())
|
||||
}
|
||||
|
||||
// Pre-Berlin logic.
|
||||
gas = modexpMultComplexity(gas)
|
||||
if adjExpLen.Cmp(big1) > 0 {
|
||||
gas.Mul(gas, adjExpLen)
|
||||
}
|
||||
gas.Div(gas, big20)
|
||||
if gas.BitLen() > 64 {
|
||||
return math.MaxUint64
|
||||
}
|
||||
return gas.Uint64()
|
||||
}
|
||||
|
||||
func (c *bigModExp) Run(input []byte) ([]byte, error) {
|
||||
|
|
@ -543,6 +643,10 @@ func (c *bigModExp) Run(input []byte) ([]byte, error) {
|
|||
return common.LeftPadBytes(v, int(modLen)), nil
|
||||
}
|
||||
|
||||
func (c *bigModExp) Name() string {
|
||||
return "MODEXP"
|
||||
}
|
||||
|
||||
// newCurvePoint unmarshals a binary blob into a bn256 elliptic curve point,
|
||||
// returning it, or an error if the point is invalid.
|
||||
func newCurvePoint(blob []byte) (*bn256.G1, error) {
|
||||
|
|
@ -592,6 +696,10 @@ func (c *bn256AddIstanbul) Run(input []byte) ([]byte, error) {
|
|||
return runBn256Add(input)
|
||||
}
|
||||
|
||||
func (c *bn256AddIstanbul) Name() string {
|
||||
return "BN254_ADD"
|
||||
}
|
||||
|
||||
// bn256AddByzantium implements a native elliptic curve point addition
|
||||
// conforming to Byzantium consensus rules.
|
||||
type bn256AddByzantium struct{}
|
||||
|
|
@ -605,6 +713,10 @@ func (c *bn256AddByzantium) Run(input []byte) ([]byte, error) {
|
|||
return runBn256Add(input)
|
||||
}
|
||||
|
||||
func (c *bn256AddByzantium) Name() string {
|
||||
return "BN254_ADD"
|
||||
}
|
||||
|
||||
// runBn256ScalarMul implements the Bn256ScalarMul precompile, referenced by
|
||||
// both Byzantium and Istanbul operations.
|
||||
func runBn256ScalarMul(input []byte) ([]byte, error) {
|
||||
|
|
@ -630,6 +742,10 @@ func (c *bn256ScalarMulIstanbul) Run(input []byte) ([]byte, error) {
|
|||
return runBn256ScalarMul(input)
|
||||
}
|
||||
|
||||
func (c *bn256ScalarMulIstanbul) Name() string {
|
||||
return "BN254_MUL"
|
||||
}
|
||||
|
||||
// bn256ScalarMulByzantium implements a native elliptic curve scalar
|
||||
// multiplication conforming to Byzantium consensus rules.
|
||||
type bn256ScalarMulByzantium struct{}
|
||||
|
|
@ -643,6 +759,10 @@ func (c *bn256ScalarMulByzantium) Run(input []byte) ([]byte, error) {
|
|||
return runBn256ScalarMul(input)
|
||||
}
|
||||
|
||||
func (c *bn256ScalarMulByzantium) Name() string {
|
||||
return "BN254_MUL"
|
||||
}
|
||||
|
||||
var (
|
||||
// true32Byte is returned if the bn256 pairing check succeeds.
|
||||
true32Byte = []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}
|
||||
|
|
@ -698,6 +818,10 @@ func (c *bn256PairingIstanbul) Run(input []byte) ([]byte, error) {
|
|||
return runBn256Pairing(input)
|
||||
}
|
||||
|
||||
func (c *bn256PairingIstanbul) Name() string {
|
||||
return "BN254_PAIRING"
|
||||
}
|
||||
|
||||
// bn256PairingByzantium implements a pairing pre-compile for the bn256 curve
|
||||
// conforming to Byzantium consensus rules.
|
||||
type bn256PairingByzantium struct{}
|
||||
|
|
@ -711,6 +835,10 @@ func (c *bn256PairingByzantium) Run(input []byte) ([]byte, error) {
|
|||
return runBn256Pairing(input)
|
||||
}
|
||||
|
||||
func (c *bn256PairingByzantium) Name() string {
|
||||
return "BN254_PAIRING"
|
||||
}
|
||||
|
||||
type blake2F struct{}
|
||||
|
||||
func (c *blake2F) RequiredGas(input []byte) uint64 {
|
||||
|
|
@ -772,6 +900,10 @@ func (c *blake2F) Run(input []byte) ([]byte, error) {
|
|||
return output, nil
|
||||
}
|
||||
|
||||
func (c *blake2F) Name() string {
|
||||
return "BLAKE2F"
|
||||
}
|
||||
|
||||
var (
|
||||
errBLS12381InvalidInputLength = errors.New("invalid input length")
|
||||
errBLS12381InvalidFieldElementTopBytes = errors.New("invalid field element top bytes")
|
||||
|
|
@ -815,6 +947,10 @@ func (c *bls12381G1Add) Run(input []byte) ([]byte, error) {
|
|||
return encodePointG1(p0), nil
|
||||
}
|
||||
|
||||
func (c *bls12381G1Add) Name() string {
|
||||
return "BLS12_G1ADD"
|
||||
}
|
||||
|
||||
// bls12381G1MultiExp implements EIP-2537 G1MultiExp precompile.
|
||||
type bls12381G1MultiExp struct{}
|
||||
|
||||
|
|
@ -875,6 +1011,10 @@ func (c *bls12381G1MultiExp) Run(input []byte) ([]byte, error) {
|
|||
return encodePointG1(r), nil
|
||||
}
|
||||
|
||||
func (c *bls12381G1MultiExp) Name() string {
|
||||
return "BLS12_G1MSM"
|
||||
}
|
||||
|
||||
// bls12381G2Add implements EIP-2537 G2Add precompile.
|
||||
type bls12381G2Add struct{}
|
||||
|
||||
|
|
@ -912,6 +1052,10 @@ func (c *bls12381G2Add) Run(input []byte) ([]byte, error) {
|
|||
return encodePointG2(r), nil
|
||||
}
|
||||
|
||||
func (c *bls12381G2Add) Name() string {
|
||||
return "BLS12_G2ADD"
|
||||
}
|
||||
|
||||
// bls12381G2MultiExp implements EIP-2537 G2MultiExp precompile.
|
||||
type bls12381G2MultiExp struct{}
|
||||
|
||||
|
|
@ -972,6 +1116,10 @@ func (c *bls12381G2MultiExp) Run(input []byte) ([]byte, error) {
|
|||
return encodePointG2(r), nil
|
||||
}
|
||||
|
||||
func (c *bls12381G2MultiExp) Name() string {
|
||||
return "BLS12_G2MSM"
|
||||
}
|
||||
|
||||
// bls12381Pairing implements EIP-2537 Pairing precompile.
|
||||
type bls12381Pairing struct{}
|
||||
|
||||
|
|
@ -1035,6 +1183,10 @@ func (c *bls12381Pairing) Run(input []byte) ([]byte, error) {
|
|||
return out, nil
|
||||
}
|
||||
|
||||
func (c *bls12381Pairing) Name() string {
|
||||
return "BLS12_PAIRING_CHECK"
|
||||
}
|
||||
|
||||
func decodePointG1(in []byte) (*bls12381.G1Affine, error) {
|
||||
if len(in) != 128 {
|
||||
return nil, errors.New("invalid g1 point length")
|
||||
|
|
@ -1153,6 +1305,10 @@ func (c *bls12381MapG1) Run(input []byte) ([]byte, error) {
|
|||
return encodePointG1(&r), nil
|
||||
}
|
||||
|
||||
func (c *bls12381MapG1) Name() string {
|
||||
return "BLS12_MAP_FP_TO_G1"
|
||||
}
|
||||
|
||||
// bls12381MapG2 implements EIP-2537 MapG2 precompile.
|
||||
type bls12381MapG2 struct{}
|
||||
|
||||
|
|
@ -1186,6 +1342,10 @@ func (c *bls12381MapG2) Run(input []byte) ([]byte, error) {
|
|||
return encodePointG2(&r), nil
|
||||
}
|
||||
|
||||
func (c *bls12381MapG2) Name() string {
|
||||
return "BLS12_MAP_FP2_TO_G2"
|
||||
}
|
||||
|
||||
// kzgPointEvaluation implements the EIP-4844 point evaluation precompile.
|
||||
type kzgPointEvaluation struct{}
|
||||
|
||||
|
|
@ -1242,6 +1402,10 @@ func (b *kzgPointEvaluation) Run(input []byte) ([]byte, error) {
|
|||
return common.Hex2Bytes(blobPrecompileReturnValue), nil
|
||||
}
|
||||
|
||||
func (b *kzgPointEvaluation) Name() string {
|
||||
return "KZG_POINT_EVALUATION"
|
||||
}
|
||||
|
||||
// kZGToVersionedHash implements kzg_to_versioned_hash from EIP-4844
|
||||
func kZGToVersionedHash(kzg kzg4844.Commitment) common.Hash {
|
||||
h := sha256.Sum256(kzg[:])
|
||||
|
|
@ -1277,3 +1441,7 @@ func (c *p256Verify) Run(input []byte) ([]byte, error) {
|
|||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (c *p256Verify) Name() string {
|
||||
return "P256VERIFY"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -601,7 +601,7 @@ func (evm *EVM) initNewContract(contract *Contract, address common.Address) ([]b
|
|||
}
|
||||
}
|
||||
|
||||
evm.StateDB.SetCode(address, ret)
|
||||
evm.StateDB.SetCode(address, ret, tracing.CodeChangeContractCreation)
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@ import (
|
|||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||
"github.com/ethereum/go-ethereum/core/state"
|
||||
"github.com/ethereum/go-ethereum/core/tracing"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
"github.com/holiman/uint256"
|
||||
|
|
@ -87,7 +88,7 @@ func TestEIP2200(t *testing.T) {
|
|||
|
||||
statedb, _ := state.New(types.EmptyRootHash, state.NewDatabaseForTesting())
|
||||
statedb.CreateAccount(address)
|
||||
statedb.SetCode(address, hexutil.MustDecode(tt.input))
|
||||
statedb.SetCode(address, hexutil.MustDecode(tt.input), tracing.CodeChangeUnspecified)
|
||||
statedb.SetState(address, common.Hash{}, common.BytesToHash([]byte{tt.original}))
|
||||
statedb.Finalise(true) // Push the state into the "original" slot
|
||||
|
||||
|
|
@ -139,7 +140,7 @@ func TestCreateGas(t *testing.T) {
|
|||
address := common.BytesToAddress([]byte("contract"))
|
||||
statedb, _ := state.New(types.EmptyRootHash, state.NewDatabaseForTesting())
|
||||
statedb.CreateAccount(address)
|
||||
statedb.SetCode(address, hexutil.MustDecode(tt.code))
|
||||
statedb.SetCode(address, hexutil.MustDecode(tt.code), tracing.CodeChangeUnspecified)
|
||||
statedb.Finalise(true)
|
||||
vmctx := BlockContext{
|
||||
CanTransfer: func(StateDB, common.Address, *uint256.Int) bool { return true },
|
||||
|
|
|
|||
|
|
@ -43,7 +43,7 @@ type StateDB interface {
|
|||
GetCode(common.Address) []byte
|
||||
|
||||
// SetCode sets the new code for the address, and returns the previous code, if any.
|
||||
SetCode(common.Address, []byte) []byte
|
||||
SetCode(common.Address, []byte, tracing.CodeChangeReason) []byte
|
||||
GetCodeSize(common.Address) int
|
||||
|
||||
AddRefund(uint64)
|
||||
|
|
|
|||
|
|
@ -33,6 +33,7 @@ type Config struct {
|
|||
ExtraEips []int // Additional EIPS that are to be enabled
|
||||
|
||||
StatelessSelfValidation bool // Generate execution witnesses and self-check against them (testing purpose)
|
||||
EnableWitnessStats bool // Whether trie access statistics collection is enabled
|
||||
}
|
||||
|
||||
// ScopeContext contains the things that are per-call, such as stack and memory,
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@ import (
|
|||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/core/state"
|
||||
"github.com/ethereum/go-ethereum/core/tracing"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
"github.com/holiman/uint256"
|
||||
|
|
@ -45,7 +46,7 @@ func TestLoopInterrupt(t *testing.T) {
|
|||
for i, tt := range loopInterruptTests {
|
||||
statedb, _ := state.New(types.EmptyRootHash, state.NewDatabaseForTesting())
|
||||
statedb.CreateAccount(address)
|
||||
statedb.SetCode(address, common.Hex2Bytes(tt))
|
||||
statedb.SetCode(address, common.Hex2Bytes(tt), tracing.CodeChangeUnspecified)
|
||||
statedb.Finalise(true)
|
||||
|
||||
evm := NewEVM(vmctx, statedb, params.AllEthashProtocolChanges, Config{})
|
||||
|
|
|
|||
|
|
@ -53,7 +53,7 @@ func (p *Program) add(op byte) *Program {
|
|||
return p
|
||||
}
|
||||
|
||||
// pushBig creates a PUSHX instruction and pushes the given val.
|
||||
// doPush creates a PUSHX instruction and pushes the given val.
|
||||
// - If the val is nil, it pushes zero
|
||||
// - If the val is bigger than 32 bytes, it panics
|
||||
func (p *Program) doPush(val *uint256.Int) {
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ import (
|
|||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/core/state"
|
||||
"github.com/ethereum/go-ethereum/core/tracing"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/core/vm"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
|
|
@ -139,7 +140,7 @@ func Execute(code, input []byte, cfg *Config) ([]byte, *state.StateDB, error) {
|
|||
cfg.State.Prepare(rules, cfg.Origin, cfg.Coinbase, &address, vm.ActivePrecompiles(rules), nil)
|
||||
cfg.State.CreateAccount(address)
|
||||
// set the receiver's (the executing contract) code for execution.
|
||||
cfg.State.SetCode(address, code)
|
||||
cfg.State.SetCode(address, code, tracing.CodeChangeUnspecified)
|
||||
// Call the code with the given configuration.
|
||||
ret, leftOverGas, err := vmenv.Call(
|
||||
cfg.Origin,
|
||||
|
|
|
|||
|
|
@ -114,7 +114,7 @@ func TestCall(t *testing.T) {
|
|||
byte(vm.PUSH1), 32,
|
||||
byte(vm.PUSH1), 0,
|
||||
byte(vm.RETURN),
|
||||
})
|
||||
}, tracing.CodeChangeUnspecified)
|
||||
|
||||
ret, _, err := Call(address, nil, &Config{State: state})
|
||||
if err != nil {
|
||||
|
|
@ -167,7 +167,7 @@ func benchmarkEVM_Create(bench *testing.B, code string) {
|
|||
)
|
||||
|
||||
statedb.CreateAccount(sender)
|
||||
statedb.SetCode(receiver, common.FromHex(code))
|
||||
statedb.SetCode(receiver, common.FromHex(code), tracing.CodeChangeUnspecified)
|
||||
runtimeConfig := Config{
|
||||
Origin: sender,
|
||||
State: statedb,
|
||||
|
|
@ -232,7 +232,7 @@ func BenchmarkEVM_SWAP1(b *testing.B) {
|
|||
|
||||
b.Run("10k", func(b *testing.B) {
|
||||
contractCode := swapContract(10_000)
|
||||
state.SetCode(contractAddr, contractCode)
|
||||
state.SetCode(contractAddr, contractCode, tracing.CodeChangeUnspecified)
|
||||
|
||||
for i := 0; i < b.N; i++ {
|
||||
_, _, err := Call(contractAddr, []byte{}, &Config{State: state})
|
||||
|
|
@ -263,7 +263,7 @@ func BenchmarkEVM_RETURN(b *testing.B) {
|
|||
b.ReportAllocs()
|
||||
|
||||
contractCode := returnContract(n)
|
||||
state.SetCode(contractAddr, contractCode)
|
||||
state.SetCode(contractAddr, contractCode, tracing.CodeChangeUnspecified)
|
||||
|
||||
for i := 0; i < b.N; i++ {
|
||||
ret, _, err := Call(contractAddr, []byte{}, &Config{State: state})
|
||||
|
|
@ -422,12 +422,12 @@ func benchmarkNonModifyingCode(gas uint64, code []byte, name string, tracerCode
|
|||
byte(vm.PUSH1), 0x00,
|
||||
byte(vm.PUSH1), 0x00,
|
||||
byte(vm.REVERT),
|
||||
})
|
||||
}, tracing.CodeChangeUnspecified)
|
||||
}
|
||||
|
||||
//cfg.State.CreateAccount(cfg.Origin)
|
||||
// set the receiver's (the executing contract) code for execution.
|
||||
cfg.State.SetCode(destination, code)
|
||||
cfg.State.SetCode(destination, code, tracing.CodeChangeUnspecified)
|
||||
Call(destination, nil, cfg)
|
||||
|
||||
b.Run(name, func(b *testing.B) {
|
||||
|
|
@ -772,12 +772,12 @@ func TestRuntimeJSTracer(t *testing.T) {
|
|||
for i, jsTracer := range jsTracers {
|
||||
for j, tc := range tests {
|
||||
statedb, _ := state.New(types.EmptyRootHash, state.NewDatabaseForTesting())
|
||||
statedb.SetCode(main, tc.code)
|
||||
statedb.SetCode(common.HexToAddress("0xbb"), calleeCode)
|
||||
statedb.SetCode(common.HexToAddress("0xcc"), calleeCode)
|
||||
statedb.SetCode(common.HexToAddress("0xdd"), calleeCode)
|
||||
statedb.SetCode(common.HexToAddress("0xee"), calleeCode)
|
||||
statedb.SetCode(common.HexToAddress("0xff"), suicideCode)
|
||||
statedb.SetCode(main, tc.code, tracing.CodeChangeUnspecified)
|
||||
statedb.SetCode(common.HexToAddress("0xbb"), calleeCode, tracing.CodeChangeUnspecified)
|
||||
statedb.SetCode(common.HexToAddress("0xcc"), calleeCode, tracing.CodeChangeUnspecified)
|
||||
statedb.SetCode(common.HexToAddress("0xdd"), calleeCode, tracing.CodeChangeUnspecified)
|
||||
statedb.SetCode(common.HexToAddress("0xee"), calleeCode, tracing.CodeChangeUnspecified)
|
||||
statedb.SetCode(common.HexToAddress("0xff"), suicideCode, tracing.CodeChangeUnspecified)
|
||||
|
||||
tracer, err := tracers.DefaultDirectory.New(jsTracer, new(tracers.Context), nil, params.MergedTestChainConfig)
|
||||
if err != nil {
|
||||
|
|
@ -862,8 +862,8 @@ func BenchmarkTracerStepVsCallFrame(b *testing.B) {
|
|||
// delegation designator incurs the correct amount of gas based on the tracer.
|
||||
func TestDelegatedAccountAccessCost(t *testing.T) {
|
||||
statedb, _ := state.New(types.EmptyRootHash, state.NewDatabaseForTesting())
|
||||
statedb.SetCode(common.HexToAddress("0xff"), types.AddressToDelegation(common.HexToAddress("0xaa")))
|
||||
statedb.SetCode(common.HexToAddress("0xaa"), program.New().Return(0, 0).Bytes())
|
||||
statedb.SetCode(common.HexToAddress("0xff"), types.AddressToDelegation(common.HexToAddress("0xaa")), tracing.CodeChangeUnspecified)
|
||||
statedb.SetCode(common.HexToAddress("0xaa"), program.New().Return(0, 0).Bytes(), tracing.CodeChangeUnspecified)
|
||||
|
||||
for i, tc := range []struct {
|
||||
code []byte
|
||||
|
|
|
|||
210
core/vm/testdata/precompiles/modexp_eip2565.json
vendored
210
core/vm/testdata/precompiles/modexp_eip2565.json
vendored
|
|
@ -117,5 +117,215 @@
|
|||
"Name": "nagydani-5-pow0x10001",
|
||||
"Gas": 87381,
|
||||
"NoBenchmark": false
|
||||
},
|
||||
{
|
||||
"Input": "000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000000000c1000000000000000000000000000000000000000000000000000000000000000cffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe000007d7d7d83828282348286877d7d827d407d797d7d7d7d7d7d7d7d7d7d7d5b00000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000021000000000000000000000000000000000000000000000000000000000000000cffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff4000007d7d7d83828282348286877d7d82",
|
||||
"Expected": "36a385a417859b5e178d3ab9",
|
||||
"Name": "marius-1-even",
|
||||
"Gas": 2057,
|
||||
"NoBenchmark": false
|
||||
},
|
||||
{
|
||||
"Input": "000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000d80000000000000000000000000000000000000000000000000000000000000010ffffffffffffffff76ffffffffffffff1cffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c76ec7c7c7c7ffffffffffffffc7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7ffffffffffffc7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c76ec7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7ffffffffff3f000000000000000000000000",
|
||||
"Expected": "c3745de81615f80088ffffffffffffff",
|
||||
"Name": "guido-1-even",
|
||||
"Gas": 2298,
|
||||
"NoBenchmark": false
|
||||
},
|
||||
{
|
||||
"Input": "000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000d80000000000000000000000000000000000000000000000000000000000000010e0060000a921212121212121ff0000212b212121ffff1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f00feffff212121212121ffffffff1fe1e0e0e01e1f1f169f1f1f1f490afcefffffffffffffffff82828282828282828282828282828282828282828200ffff28ff2b212121ffff1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1fffffffffff0afceffffff7ffffffffff7c8282828282a1828282828282828282828282828200ffff28ff2b212121ffff1f1f1f1f1f1fd11f1f1f1f1f1f1f1f1f1f1fffffffffffffffff21212121212121fb2121212121ffff1f1f1f1f1f1f1f1fffaf82828282828200ffff28ff2b21828200",
|
||||
"Expected": "458ef0af2549d46d24c89079499479e1",
|
||||
"Name": "guido-2-even",
|
||||
"Gas": 2300,
|
||||
"NoBenchmark": false
|
||||
},
|
||||
{
|
||||
"Input": "00000000000000000000000000000000000000000000000000000000000001e7000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000002cb0193585a48e18aad777e9c1b54221a0f58140392e4f091cd5f42b2e8644a9384fbd58ae1edec2477ebf7edbf7c0a3f8bd21d1890ee87646feab3c47be716f842cc3da9b940af312dc54450a960e3fc0b86e56abddd154068e10571a96fff6259431632bc15695c6c8679057e66c2c25c127e97e64ee5de6ea1fc0a4a0e431343fed1daafa072c238a45841da86a9806680bc9f298411173210790359209cd454b5af7b4d5688b4403924e5f863d97e2c5349e1a04b54fcf385b1e9d7714bab8fbf5835f6ff9ed575e77dff7af5cbb641db5d537933bae1fa6555d6c12d6fb31ca27b57771f4aebfbe0bf95e8990c0108ffe7cbdaf370be52cf3ade594543af75ad9329d2d11a402270b5b9a6bf4b83307506e118fca4862749d04e916fc7a039f0d13f2a02e0eedb800199ec95df15b4ccd8669b52586879624d51219e72102fad810b5909b1e372ddf33888fb9beb09b416e4164966edbabd89e4a286be36277fc576ed519a15643dac602e92b63d0b9121f0491da5b16ef793a967f096d80b6c81ecaaffad7e3f06a4a5ac2796f1ed9f68e6a0fd5cf191f0c5c2eec338952ff8d31abc68bf760febeb57e088995ba1d7726a2fdd6d8ca28a181378b8b4ab699bfd4b696739bbf17a9eb2df6251143046137fdbbfacac312ebf67a67da9741b596000000000000419a2917c61722b0713d3b00a2f0e1dd5aebbbe09615de424700eea3c3020fe6e9ea5de9fa1ace781df28b21f746d2ab61d0da496e08473c90ff7dfe25b43bcde76f4bafb82e0975bea75f5a0591dba80ba2fff80a07d8853bea5be13ab326ba70c57b153acc646151948d1cf061ca31b02d4719fac710e7c723ca44f5b1737824b7ccc74ba5bff980aabdbf267621cafc3d6dcc29d0ca9c16839a92ed34de136da7900aa3ee43d21aa57498981124357cf0ca9b86f9a8d3f9c604ca00c726e48f7a9945021ea6dfff92d6b2d6514693169ca133e993541bfa4c4c191de806aa80c48109bcfc9901eccfdeb2395ab75fe63c67de900829d00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
|
||||
"Expected": "1194e971c875bb45f030316793bc6916343335b18670dfad7a4646fba99749b30283b78b818836de7400ff1a68ddad1a2dd850ec0f227441e2d4d13f5ee4b5d7a856db0a9ad1f86987e1f117d70f9e6a1a2b8d083fa82653aa16f1773b6deb2ed8a1f9e7f3a5db121c4a0c91cb954e2ec53e63422efe86c7984d79cd0e7b5e3eb8ca4980551d63f302c7d72500a84baf12c82fc7bd9b5c2ab8b9c33baf1df28b2031c58a8b2928a42c9f456e98874e22fe13cf17aa5915b11bb108b6ae40842d434604ccddcb4f64324c67b2dde32e6cd759d964f17d9cdf0046cd0ed3588e1fc4b88f67a5d4f3a870aad1cba89ead265d6ad327c8ea7ff54fe4b5e7dbe87c5c59c468543eab3675751111bfa1d6c51daf789d41dc21fd8ba9e05490f881a973a3c1567ff3129a49aa6658cf06f0a79530a7256ce5a07c2a77b4306383d538866bba376d90621c4f82d1f5f32304ee2b7170805d42418fc6967642e5648d8c64fe9c0fdff2d7c114a47add7767c8fccb8808de8c3c6e1a8880c05e16fafc1513fded8eba222dcaaa809bdb36999cc27ab8d0055009e9690e8a35b859df865dc510d25c7812d8eebbb35607ad595573f0fabd1b57970a2bc113ac6f0ca01e985032b9c2c139316ac099ed1632d2bc0fcc341343d303db2a9c3cf2ad572c6c43084b08d458bf822e92da16079f39cdb0dd10ef47f87ecae404117fc72660372cee9ea42266e7f8d973e7f6b09930ca5f96e04976bf23b9d356bbd2429597b04b7663e0e1a1228f4dfda3b854233e4888dc60c6886c1e0e8aec1705f681027b1e0b3017337557f107ef5cd272df5fd31dfec2bdffe163a8369895ffe124c0aa0ee00ca0fe1db4d5cf37b4af0e49bd73a89d88ced3d88f8e6f00d8e61ad09946d0e72cd3e25bc688a021a83758b5023daae7c269a6cbbd447aba5da7629b75801e1654ce85b8e21ccb9865654f8662e538625d75fea31000000000000000000000000000000000000000000000",
|
||||
"Name": "guido-3-even",
|
||||
"Gas": 5400,
|
||||
"NoBenchmark": false
|
||||
},
|
||||
{
|
||||
"Input": "00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000181000000000000000000000000000000000000000000000000000000000000000801ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff2cffffffffffffffffffffffffffffffffffffffffffffffffffffffff3b10000000006c01ffffffffffffffffffffffffffffffffffffffffffffffdffffb97ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff3bffffffffffffffffffffffffffffffffffffffffffffffffffffffffebafd93b37ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc5bb6affffffff3fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff2a",
|
||||
"Expected": "0000000000000001",
|
||||
"Name": "guido-4-even",
|
||||
"Gas": 1026,
|
||||
"NoBenchmark": false
|
||||
},
|
||||
{
|
||||
"Input": "00000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000c0e8e77626586f73b955364c7b4bbf0bb7f7685ebd40e852b164633a4acbd3244c51f81bcdfc324a0dff2b5bec9d92e21cbebc4d5e29d3a3d30de3e03fbeab8d7f2ee5f854d076701c8753d72779187e404f9b2fb705c495137d78551250314a463ef5a213fe22de1cea28d60f518364ff95fe0b73660793e3efcfbe31bda68aeccc21cc9477a6aea5df8cae73422b700c47e54d892691e099167e77befc94780a920ae4155769cd69c30626f054134b5f003772473f57f84837402df6d166e66303f01681d2220bfea4bb888a5543db8c0916274ddb1ea93b144c042c01d8164c954b27ac388a17c8e9a7ba12a968f288f3308d6fe7bcdf28e685e9a2e00d8be1af19726b7662016d6404f9336493ad633777feb88c9d02a1d2428e566ac38f42c0bb66d2962ec349088ce0d03b35bf27f6114414ef558c87ad8e543754a352f7dffcaca429690688595ab1d1b349d9295b480a82f43ac5c9112fe40720545cc78501cd8b42f3605212fe06a835c9cbc0328e07e94aedb2ac11f6d6649e7fcd8c43",
|
||||
"Expected": "120cf297dbe810911c7d060e109e03699ccefa00a257d296c5a14b4180776c5f7c0d7f1cd789c694807689729af267b53f00373f395dee264a3daba11fcac1fa8875aee0950acd8fa656f1fc58077a7549d794dd160506ecea1acc9c0cda13795749c94f9973b683ce2162866e8d6b5b1165a4c7fa4234964d394d6ec4e0113698b89d173e24a962dd7a41a1819b0fef188ef64e7ee264595dce0d76fbc3ba42d5de833b143c8744366effede8bc8197e8f747ff8cdbc0bf1a93560bec960ca9",
|
||||
"Name": "marcin-1-base-heavy",
|
||||
"Gas": 200,
|
||||
"NoBenchmark": false
|
||||
},
|
||||
{
|
||||
"Input": "0000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000005100000000000000000000000000000000000000000000000000000000000000080001020304050607ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0001020304050607",
|
||||
"Expected": "0000000000000000",
|
||||
"Name": "marcin-1-exp-heavy",
|
||||
"Gas": 215,
|
||||
"NoBenchmark": false
|
||||
},
|
||||
{
|
||||
"Input": "000000000000000000000000000000000000000000000000000000000000002800000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000028e8e77626586f73b955364c7b4bbf0bb7f7685ebd40e852b164633a4acbd3244c000102030405060701fffffff01681d2220bfea4bb888a5543db8c0916274ddb1ea93b144c042c01d8164c950001020304050607",
|
||||
"Expected": "1abce71dc2205cce4eb6934397a88136f94641342e283cbcd30e929e85605c6718ed67f475192ffd",
|
||||
"Name": "marcin-1-balanced",
|
||||
"Gas": 200,
|
||||
"NoBenchmark": false
|
||||
},
|
||||
{
|
||||
"Input": "000000000000000000000000000000000000000000000000000000000000019800000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000198e8e77626586f73b955364c7b4bbf0bb7f7685ebd40e852b164633a4acbd3244c51f81bcdfc324a0dff2b5bec9d92e21cbebc4d5e29d3a3d30de3e03fbeab8d7f2ee5f854d076701c8753d72779187e404f9b2fb705c495137d78551250314a463ef5a213fe22de1cea28d60f518364ff95fe0b73660793e3efcfbe31bda68aeccc21cc9477a6aea5df8cae73422b700c47e54d892691e099167e77befc94780a920ae4155769cd69c30626f054134b5f003772473f57f84837402df6d166e663c29021b0e084f7dc16f6ec88cc597f1aea9f8e0b9501e0f7a546805d2a20eeda0bf080aeb3ed7ea6f9174d804bd242f0b31ff1ea24800344abb580cd87f61ca75a013a87733553966400242399dee3760877fead2cd87287747155e47a854acb50fe07922f57ae3b4553201bfd7c11aca85e1541f91db8e62dca9c418dc5feae086c9487350539c884510044efce5e3f2aaffca4215c12b9044506375097fecd9b22e2ef46f01f1af8aff742aebf96bdcaf55a341600971dc62555376b9e98a8000102030405060708090a0b0c0d0e0f101112131415161703f01681d2220bfea4bb888a5543db8c0916274ddb1ea93b144c042c01d8164c954b27ac388a17c8e9a7ba12a968f288f3308d6fe7bcdf28e685e9a2e00d8be1af19726b7662016d6404f9336493ad633777feb88c9d02a1d2428e566ac38f42c0bb66d2962ec349088ce0d03b35bf27f6114414ef558c87ad8e543754a352f7dffcaca429690688595ab1d1b349d9295b480a82f43ac5c9112fe40720545cc78501cd8b42f3605212fe06a835c9cbc0328e07e94aedb2ac11f6d6649e7fcd8c43ddce2bd0cdc6c22c4dcd345735040d5bfe3f09b7c61362089f728e2222db96cab2f2c2ccf43574f9e119f4860fd0f1b6036a43ad9db8a428ea09a4ee385112f3fe9c6656ea2cec604cbb5a9227526653bfa7035e4ae80010b1ba16a76608d5dde0a62bc019e9047b5ec05b1005fd017366130a4ba555e7be654561ee3f539c93cb2c9988fca71bf0ad9c4a426b924641a28e1e4adb93609bfa5b2bc81714cbba1110208b86d7b87be28bdf63a62e33ae81dbcc43de9192bd192c40e85faab539000102030405060708090a0b0c0d0e0f1011121314151617",
|
||||
"Expected": "817d05c4d540ace3f250fd082e2deb8c2097410fcfe4ce40862cfa015b7f62a7bb72ec1af2915cad66294447b45d177fe759eb80370b0dbd3c0e1c448d54db81eadc11e40c19e394d066a5c019c443798a98d4afd116fc220593d42fbf191b6af0ae75410badb641187ba24a0b968f742a75e2822853f137151d9ea972fd1f36b7c7cc4e71355ecf50648aec094b864cfae9316c0a7be3ddb8ab2d0050b2a029ee956c2366d49430c8f889f29ab514aea8e5b8dec40ba1b49432e30aee32ec45e96dd548205a79d8f8f918eed46acb2115c59086b1011b1d2b093cea723535c3d95efc8e51a7da43b80586d69eb7f213dfb06f7a8e789a9472392bd224411b50f8ca6f2862bcd63431912d1ff99c8d76408245da9e4bea649f0eb930b32922f2d0a345a206160be44d418f1a6c74bd49c4618392ef9350b264a461dfc684c7343211e27675b027054f1cb3d4a5b1a066d3a3ea2eed9caf13251d8be936818f15274e8e3d7539b7c5f216cef327a270fd2a886fbf679c163fb5806249f2c74da5ee0e3ffe9ad1fde2634b29b35da6da6d184ab6ae70199229",
|
||||
"Name": "marcin-2-base-heavy",
|
||||
"Gas": 867,
|
||||
"NoBenchmark": false
|
||||
},
|
||||
{
|
||||
"Input": "000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000500000000000000000000000000000000000000000000000000000000000000010000102030405060708090a0b0c0d0e0fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000102030405060708090a0b0c0d0e0f",
|
||||
"Expected": "00000000000000000000000000000000",
|
||||
"Name": "marcin-2-exp-heavy",
|
||||
"Gas": 852,
|
||||
"NoBenchmark": false
|
||||
},
|
||||
{
|
||||
"Input": "000000000000000000000000000000000000000000000000000000000000003800000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000038e8e77626586f73b955364c7b4bbf0bb7f7685ebd40e852b164633a4acbd3244c000102030405060708090a0b0c0d0e0f10111213141516172bfffffffffffffff01681d2220bfea4bb888a5543db8c0916274ddb1ea93b144c042c01d8164c95000102030405060708090a0b0c0d0e0f1011121314151617",
|
||||
"Expected": "86bef3367fc7117c8a6b825cadebe80f3e94c321dda73e9e240b98188a1d5c071c60a195097c8d1fb85ce03a2e1b6964846edee5aa2c3f46",
|
||||
"Name": "marcin-2-balanced",
|
||||
"Gas": 996,
|
||||
"NoBenchmark": false
|
||||
},
|
||||
{
|
||||
"Input": "000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000020e8e77626586f73b955364c7b4bbf0bb7f7685ebd40e852b164633a4acbd3244cfffffffffffffffffffffffffffffffff01681d2220bfea4bb888a5543db8c0916274ddb1ea93b144c042c01d8164c95",
|
||||
"Expected": "4004762c491606a5132134da6086284f74cc8e14b08f18b90fc09f31bca3d78f",
|
||||
"Name": "marcin-3-base-heavy",
|
||||
"Gas": 677,
|
||||
"NoBenchmark": false
|
||||
},
|
||||
{
|
||||
"Input": "000000000000000000000000000000000000000000000000000000000000001800000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000018000102030405060708090a0b0c0d0e0f1011121314151617ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000102030405060708090a0b0c0d0e0f1011121314151617",
|
||||
"Expected": "000000000000000000000000000000000000000000000000",
|
||||
"Name": "marcin-3-exp-heavy",
|
||||
"Gas": 765,
|
||||
"NoBenchmark": false
|
||||
},
|
||||
{
|
||||
"Input": "000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000020e8e77626586f73b955364c7b4bbf0bb7f7685ebd40e852b164633a4acbd3244cfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01681d2220bfea4bb888a5543db8c0916274ddb1ea93b144c042c01d8164c95",
|
||||
"Expected": "2d3feee20d394af68dd6744b86a8aca6a4a0b7f01bbcd3c3eec768245ca6acee",
|
||||
"Name": "marcin-3-balanced",
|
||||
"Gas": 1360,
|
||||
"NoBenchmark": false
|
||||
},
|
||||
{
|
||||
"Input": "00000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000051000000000000000000000000000000000000000000000000000000000000000800ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00",
|
||||
"Expected": "00ffffffffffffff",
|
||||
"Name": "mod-8-exp-648",
|
||||
"Gas": 215,
|
||||
"NoBenchmark": false
|
||||
},
|
||||
{
|
||||
"Input": "00000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000070000000000000000000000000000000000000000000000000000000000000000800ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00",
|
||||
"Expected": "00ffffffffffffff",
|
||||
"Name": "mod-8-exp-896",
|
||||
"Gas": 298,
|
||||
"NoBenchmark": false
|
||||
},
|
||||
{
|
||||
"Input": "00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000002000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00",
|
||||
"Expected": "00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
|
||||
"Name": "mod-32-exp-32",
|
||||
"Gas": 200,
|
||||
"NoBenchmark": false
|
||||
},
|
||||
{
|
||||
"Input": "00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000005000000000000000000000000000000000000000000000000000000000000002000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00",
|
||||
"Expected": "00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
|
||||
"Name": "mod-32-exp-36",
|
||||
"Gas": 200,
|
||||
"NoBenchmark": false
|
||||
},
|
||||
{
|
||||
"Input": "00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000005000000000000000000000000000000000000000000000000000000000000002000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00",
|
||||
"Expected": "00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
|
||||
"Name": "mod-32-exp-40",
|
||||
"Gas": 208,
|
||||
"NoBenchmark": false
|
||||
},
|
||||
{
|
||||
"Input": "00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000002000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00",
|
||||
"Expected": "00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
|
||||
"Name": "mod-32-exp-64",
|
||||
"Gas": 336,
|
||||
"NoBenchmark": false
|
||||
},
|
||||
{
|
||||
"Input": "00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000009000000000000000000000000000000000000000000000000000000000000002000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00",
|
||||
"Expected": "00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
|
||||
"Name": "mod-32-exp-65",
|
||||
"Gas": 341,
|
||||
"NoBenchmark": false
|
||||
},
|
||||
{
|
||||
"Input": "00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00",
|
||||
"Expected": "00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
|
||||
"Name": "mod-32-exp-128",
|
||||
"Gas": 677,
|
||||
"NoBenchmark": false
|
||||
},
|
||||
{
|
||||
"Input": "00000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000010000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00",
|
||||
"Expected": "02fd01000000000000000000000000000000fefffffffffffd03ff0000000000000000000000000000000000000000000000fefffffffffffd03ff0000000000000000000000000000000000000000000000fefffffffffffd03ff0000000000000000000000000000000000000000000000fefffffffffffd03ff0000000000000000000000000000000000000000000000fefffffffffffd03ff0000000000000000000000000000000000000000000000fefffffffffffd03ff0000000000000000000000000000000000000000000000fefffffffffffd03ff0000000000000000000000000000000000000000000000fefffffffffffd03feffffffffff",
|
||||
"Name": "mod-256-exp-2",
|
||||
"Gas": 341,
|
||||
"NoBenchmark": false
|
||||
},
|
||||
{
|
||||
"Input": "00000000000000000000000000000000000000000000000000000000000001080000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000010800ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffff00",
|
||||
"Expected": "0100fefffffffffffff710f80000000006f108000000000000feffffffffffff0100fefffffffffffef90ff80000000105f008ffffffffff02fdffffffffffff0100feffffffffff00f80ff80000010101f407fffffffd06fc0000000000fd020000feffffffff00fff80ff8000101fa08f207fffffb0afa0000000001fb03000000feffffff00fffff80ff80102f80405f207fff90ef80000000002f90400000000feffff00fffffff80ff903f6050005f207f712f60000000003f7050000000000feff00fffffffff810fcf406000005f1fd16f40000000004f506000000000000fe00fffffffffff915ea0700000005e522f20000000005f306ffffffffffffffffffffffffff",
|
||||
"Name": "mod-264-exp-2",
|
||||
"Gas": 363,
|
||||
"NoBenchmark": false
|
||||
},
|
||||
{
|
||||
"Input": "00000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000040000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00",
|
||||
"Expected": "00fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe02fefffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe02fefffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe02fefffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe02fefffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe02fefffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe02fefffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe02fefffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe02fefffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe02fefffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe02fefffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe02fefffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe02fefffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe02fefffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe02fefffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe02fefffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe02fefffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe02fefffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe02fefffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe02fefffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe02fefffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe02fefffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe02fefffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe02fefffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe02fefffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe02fefffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe02fefffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe02fefffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe02fefffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe02fefffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe02fefffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe02feffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
|
||||
"Name": "mod-1024-exp-2",
|
||||
"Gas": 5461,
|
||||
"NoBenchmark": false
|
||||
},
|
||||
{
|
||||
"Input": "000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000700000000000000000000000000000000000000000000000000000000000000008ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
|
||||
"Expected": "2a02d5f86c2375ff",
|
||||
"Name": "pawel-1-exp-heavy",
|
||||
"Gas": 298,
|
||||
"NoBenchmark": false
|
||||
},
|
||||
{
|
||||
"Input": "000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000280000000000000000000000000000000000000000000000000000000000000010ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
|
||||
"Expected": "823ef7dc60d6d9616756c48f69b7c4ff",
|
||||
"Name": "pawel-2-exp-heavy",
|
||||
"Gas": 425,
|
||||
"NoBenchmark": false
|
||||
},
|
||||
{
|
||||
"Input": "000000000000000000000000000000000000000000000000000000000000001800000000000000000000000000000000000000000000000000000000000000150000000000000000000000000000000000000000000000000000000000000018ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
|
||||
"Expected": "c817dd5aa60a41948eed409706c2aa97be3000d4da0261ff",
|
||||
"Name": "pawel-3-exp-heavy",
|
||||
"Gas": 501,
|
||||
"NoBenchmark": false
|
||||
},
|
||||
{
|
||||
"Input": "0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000020ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
|
||||
"Expected": "2defaca0137d6edacbbd5d36d6ed70cbf8a998ffb19fc270d45a18d37e0f35ff",
|
||||
"Name": "pawel-4-exp-heavy",
|
||||
"Gas": 506,
|
||||
"NoBenchmark": false
|
||||
},
|
||||
{
|
||||
"Input": "000000000000000000000000000000000000000000000000000000000000001700000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000017bffffffffffffffffffffffffffffffffffffffffffffbffffffffffffffffff7ffffffffffffffffffffffffffffffffffffffffffe",
|
||||
"Expected": "200f14de1d474710c1c979920452e0ffc2ac6f618afba5",
|
||||
"Name": "mod_vul_pawel_3_exp_8",
|
||||
"Gas": 200,
|
||||
"NoBenchmark": false
|
||||
}
|
||||
]
|
||||
210
core/vm/testdata/precompiles/modexp_eip7883.json
vendored
210
core/vm/testdata/precompiles/modexp_eip7883.json
vendored
|
|
@ -103,5 +103,215 @@
|
|||
"Name": "nagydani-5-pow0x10001",
|
||||
"Gas": 524288,
|
||||
"NoBenchmark": false
|
||||
},
|
||||
{
|
||||
"Input": "000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000000000c1000000000000000000000000000000000000000000000000000000000000000cffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe000007d7d7d83828282348286877d7d827d407d797d7d7d7d7d7d7d7d7d7d7d5b00000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000021000000000000000000000000000000000000000000000000000000000000000cffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff4000007d7d7d83828282348286877d7d82",
|
||||
"Expected": "36a385a417859b5e178d3ab9",
|
||||
"Name": "marius-1-even",
|
||||
"Gas": 45296,
|
||||
"NoBenchmark": false
|
||||
},
|
||||
{
|
||||
"Input": "000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000d80000000000000000000000000000000000000000000000000000000000000010ffffffffffffffff76ffffffffffffff1cffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c76ec7c7c7c7ffffffffffffffc7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7ffffffffffffc7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c76ec7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7ffffffffff3f000000000000000000000000",
|
||||
"Expected": "c3745de81615f80088ffffffffffffff",
|
||||
"Name": "guido-1-even",
|
||||
"Gas": 51136,
|
||||
"NoBenchmark": false
|
||||
},
|
||||
{
|
||||
"Input": "000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000d80000000000000000000000000000000000000000000000000000000000000010e0060000a921212121212121ff0000212b212121ffff1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f00feffff212121212121ffffffff1fe1e0e0e01e1f1f169f1f1f1f490afcefffffffffffffffff82828282828282828282828282828282828282828200ffff28ff2b212121ffff1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1fffffffffff0afceffffff7ffffffffff7c8282828282a1828282828282828282828282828200ffff28ff2b212121ffff1f1f1f1f1f1fd11f1f1f1f1f1f1f1f1f1f1fffffffffffffffff21212121212121fb2121212121ffff1f1f1f1f1f1f1f1fffaf82828282828200ffff28ff2b21828200",
|
||||
"Expected": "458ef0af2549d46d24c89079499479e1",
|
||||
"Name": "guido-2-even",
|
||||
"Gas": 51152,
|
||||
"NoBenchmark": false
|
||||
},
|
||||
{
|
||||
"Input": "00000000000000000000000000000000000000000000000000000000000001e7000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000002cb0193585a48e18aad777e9c1b54221a0f58140392e4f091cd5f42b2e8644a9384fbd58ae1edec2477ebf7edbf7c0a3f8bd21d1890ee87646feab3c47be716f842cc3da9b940af312dc54450a960e3fc0b86e56abddd154068e10571a96fff6259431632bc15695c6c8679057e66c2c25c127e97e64ee5de6ea1fc0a4a0e431343fed1daafa072c238a45841da86a9806680bc9f298411173210790359209cd454b5af7b4d5688b4403924e5f863d97e2c5349e1a04b54fcf385b1e9d7714bab8fbf5835f6ff9ed575e77dff7af5cbb641db5d537933bae1fa6555d6c12d6fb31ca27b57771f4aebfbe0bf95e8990c0108ffe7cbdaf370be52cf3ade594543af75ad9329d2d11a402270b5b9a6bf4b83307506e118fca4862749d04e916fc7a039f0d13f2a02e0eedb800199ec95df15b4ccd8669b52586879624d51219e72102fad810b5909b1e372ddf33888fb9beb09b416e4164966edbabd89e4a286be36277fc576ed519a15643dac602e92b63d0b9121f0491da5b16ef793a967f096d80b6c81ecaaffad7e3f06a4a5ac2796f1ed9f68e6a0fd5cf191f0c5c2eec338952ff8d31abc68bf760febeb57e088995ba1d7726a2fdd6d8ca28a181378b8b4ab699bfd4b696739bbf17a9eb2df6251143046137fdbbfacac312ebf67a67da9741b596000000000000419a2917c61722b0713d3b00a2f0e1dd5aebbbe09615de424700eea3c3020fe6e9ea5de9fa1ace781df28b21f746d2ab61d0da496e08473c90ff7dfe25b43bcde76f4bafb82e0975bea75f5a0591dba80ba2fff80a07d8853bea5be13ab326ba70c57b153acc646151948d1cf061ca31b02d4719fac710e7c723ca44f5b1737824b7ccc74ba5bff980aabdbf267621cafc3d6dcc29d0ca9c16839a92ed34de136da7900aa3ee43d21aa57498981124357cf0ca9b86f9a8d3f9c604ca00c726e48f7a9945021ea6dfff92d6b2d6514693169ca133e993541bfa4c4c191de806aa80c48109bcfc9901eccfdeb2395ab75fe63c67de900829d00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
|
||||
"Expected": "1194e971c875bb45f030316793bc6916343335b18670dfad7a4646fba99749b30283b78b818836de7400ff1a68ddad1a2dd850ec0f227441e2d4d13f5ee4b5d7a856db0a9ad1f86987e1f117d70f9e6a1a2b8d083fa82653aa16f1773b6deb2ed8a1f9e7f3a5db121c4a0c91cb954e2ec53e63422efe86c7984d79cd0e7b5e3eb8ca4980551d63f302c7d72500a84baf12c82fc7bd9b5c2ab8b9c33baf1df28b2031c58a8b2928a42c9f456e98874e22fe13cf17aa5915b11bb108b6ae40842d434604ccddcb4f64324c67b2dde32e6cd759d964f17d9cdf0046cd0ed3588e1fc4b88f67a5d4f3a870aad1cba89ead265d6ad327c8ea7ff54fe4b5e7dbe87c5c59c468543eab3675751111bfa1d6c51daf789d41dc21fd8ba9e05490f881a973a3c1567ff3129a49aa6658cf06f0a79530a7256ce5a07c2a77b4306383d538866bba376d90621c4f82d1f5f32304ee2b7170805d42418fc6967642e5648d8c64fe9c0fdff2d7c114a47add7767c8fccb8808de8c3c6e1a8880c05e16fafc1513fded8eba222dcaaa809bdb36999cc27ab8d0055009e9690e8a35b859df865dc510d25c7812d8eebbb35607ad595573f0fabd1b57970a2bc113ac6f0ca01e985032b9c2c139316ac099ed1632d2bc0fcc341343d303db2a9c3cf2ad572c6c43084b08d458bf822e92da16079f39cdb0dd10ef47f87ecae404117fc72660372cee9ea42266e7f8d973e7f6b09930ca5f96e04976bf23b9d356bbd2429597b04b7663e0e1a1228f4dfda3b854233e4888dc60c6886c1e0e8aec1705f681027b1e0b3017337557f107ef5cd272df5fd31dfec2bdffe163a8369895ffe124c0aa0ee00ca0fe1db4d5cf37b4af0e49bd73a89d88ced3d88f8e6f00d8e61ad09946d0e72cd3e25bc688a021a83758b5023daae7c269a6cbbd447aba5da7629b75801e1654ce85b8e21ccb9865654f8662e538625d75fea31000000000000000000000000000000000000000000000",
|
||||
"Name": "guido-3-even",
|
||||
"Gas": 32400,
|
||||
"NoBenchmark": false
|
||||
},
|
||||
{
|
||||
"Input": "00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000181000000000000000000000000000000000000000000000000000000000000000801ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff2cffffffffffffffffffffffffffffffffffffffffffffffffffffffff3b10000000006c01ffffffffffffffffffffffffffffffffffffffffffffffdffffb97ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff3bffffffffffffffffffffffffffffffffffffffffffffffffffffffffebafd93b37ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc5bb6affffffff3fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff2a",
|
||||
"Expected": "0000000000000001",
|
||||
"Name": "guido-4-even",
|
||||
"Gas": 94448,
|
||||
"NoBenchmark": false
|
||||
},
|
||||
{
|
||||
"Input": "00000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000c0e8e77626586f73b955364c7b4bbf0bb7f7685ebd40e852b164633a4acbd3244c51f81bcdfc324a0dff2b5bec9d92e21cbebc4d5e29d3a3d30de3e03fbeab8d7f2ee5f854d076701c8753d72779187e404f9b2fb705c495137d78551250314a463ef5a213fe22de1cea28d60f518364ff95fe0b73660793e3efcfbe31bda68aeccc21cc9477a6aea5df8cae73422b700c47e54d892691e099167e77befc94780a920ae4155769cd69c30626f054134b5f003772473f57f84837402df6d166e66303f01681d2220bfea4bb888a5543db8c0916274ddb1ea93b144c042c01d8164c954b27ac388a17c8e9a7ba12a968f288f3308d6fe7bcdf28e685e9a2e00d8be1af19726b7662016d6404f9336493ad633777feb88c9d02a1d2428e566ac38f42c0bb66d2962ec349088ce0d03b35bf27f6114414ef558c87ad8e543754a352f7dffcaca429690688595ab1d1b349d9295b480a82f43ac5c9112fe40720545cc78501cd8b42f3605212fe06a835c9cbc0328e07e94aedb2ac11f6d6649e7fcd8c43",
|
||||
"Expected": "120cf297dbe810911c7d060e109e03699ccefa00a257d296c5a14b4180776c5f7c0d7f1cd789c694807689729af267b53f00373f395dee264a3daba11fcac1fa8875aee0950acd8fa656f1fc58077a7549d794dd160506ecea1acc9c0cda13795749c94f9973b683ce2162866e8d6b5b1165a4c7fa4234964d394d6ec4e0113698b89d173e24a962dd7a41a1819b0fef188ef64e7ee264595dce0d76fbc3ba42d5de833b143c8744366effede8bc8197e8f747ff8cdbc0bf1a93560bec960ca9",
|
||||
"Name": "marcin-1-base-heavy",
|
||||
"Gas": 1152,
|
||||
"NoBenchmark": false
|
||||
},
|
||||
{
|
||||
"Input": "0000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000005100000000000000000000000000000000000000000000000000000000000000080001020304050607ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0001020304050607",
|
||||
"Expected": "0000000000000000",
|
||||
"Name": "marcin-1-exp-heavy",
|
||||
"Gas": 16624,
|
||||
"NoBenchmark": false
|
||||
},
|
||||
{
|
||||
"Input": "000000000000000000000000000000000000000000000000000000000000002800000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000028e8e77626586f73b955364c7b4bbf0bb7f7685ebd40e852b164633a4acbd3244c000102030405060701fffffff01681d2220bfea4bb888a5543db8c0916274ddb1ea93b144c042c01d8164c950001020304050607",
|
||||
"Expected": "1abce71dc2205cce4eb6934397a88136f94641342e283cbcd30e929e85605c6718ed67f475192ffd",
|
||||
"Name": "marcin-1-balanced",
|
||||
"Gas": 1200,
|
||||
"NoBenchmark": false
|
||||
},
|
||||
{
|
||||
"Input": "000000000000000000000000000000000000000000000000000000000000019800000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000198e8e77626586f73b955364c7b4bbf0bb7f7685ebd40e852b164633a4acbd3244c51f81bcdfc324a0dff2b5bec9d92e21cbebc4d5e29d3a3d30de3e03fbeab8d7f2ee5f854d076701c8753d72779187e404f9b2fb705c495137d78551250314a463ef5a213fe22de1cea28d60f518364ff95fe0b73660793e3efcfbe31bda68aeccc21cc9477a6aea5df8cae73422b700c47e54d892691e099167e77befc94780a920ae4155769cd69c30626f054134b5f003772473f57f84837402df6d166e663c29021b0e084f7dc16f6ec88cc597f1aea9f8e0b9501e0f7a546805d2a20eeda0bf080aeb3ed7ea6f9174d804bd242f0b31ff1ea24800344abb580cd87f61ca75a013a87733553966400242399dee3760877fead2cd87287747155e47a854acb50fe07922f57ae3b4553201bfd7c11aca85e1541f91db8e62dca9c418dc5feae086c9487350539c884510044efce5e3f2aaffca4215c12b9044506375097fecd9b22e2ef46f01f1af8aff742aebf96bdcaf55a341600971dc62555376b9e98a8000102030405060708090a0b0c0d0e0f101112131415161703f01681d2220bfea4bb888a5543db8c0916274ddb1ea93b144c042c01d8164c954b27ac388a17c8e9a7ba12a968f288f3308d6fe7bcdf28e685e9a2e00d8be1af19726b7662016d6404f9336493ad633777feb88c9d02a1d2428e566ac38f42c0bb66d2962ec349088ce0d03b35bf27f6114414ef558c87ad8e543754a352f7dffcaca429690688595ab1d1b349d9295b480a82f43ac5c9112fe40720545cc78501cd8b42f3605212fe06a835c9cbc0328e07e94aedb2ac11f6d6649e7fcd8c43ddce2bd0cdc6c22c4dcd345735040d5bfe3f09b7c61362089f728e2222db96cab2f2c2ccf43574f9e119f4860fd0f1b6036a43ad9db8a428ea09a4ee385112f3fe9c6656ea2cec604cbb5a9227526653bfa7035e4ae80010b1ba16a76608d5dde0a62bc019e9047b5ec05b1005fd017366130a4ba555e7be654561ee3f539c93cb2c9988fca71bf0ad9c4a426b924641a28e1e4adb93609bfa5b2bc81714cbba1110208b86d7b87be28bdf63a62e33ae81dbcc43de9192bd192c40e85faab539000102030405060708090a0b0c0d0e0f1011121314151617",
|
||||
"Expected": "817d05c4d540ace3f250fd082e2deb8c2097410fcfe4ce40862cfa015b7f62a7bb72ec1af2915cad66294447b45d177fe759eb80370b0dbd3c0e1c448d54db81eadc11e40c19e394d066a5c019c443798a98d4afd116fc220593d42fbf191b6af0ae75410badb641187ba24a0b968f742a75e2822853f137151d9ea972fd1f36b7c7cc4e71355ecf50648aec094b864cfae9316c0a7be3ddb8ab2d0050b2a029ee956c2366d49430c8f889f29ab514aea8e5b8dec40ba1b49432e30aee32ec45e96dd548205a79d8f8f918eed46acb2115c59086b1011b1d2b093cea723535c3d95efc8e51a7da43b80586d69eb7f213dfb06f7a8e789a9472392bd224411b50f8ca6f2862bcd63431912d1ff99c8d76408245da9e4bea649f0eb930b32922f2d0a345a206160be44d418f1a6c74bd49c4618392ef9350b264a461dfc684c7343211e27675b027054f1cb3d4a5b1a066d3a3ea2eed9caf13251d8be936818f15274e8e3d7539b7c5f216cef327a270fd2a886fbf679c163fb5806249f2c74da5ee0e3ffe9ad1fde2634b29b35da6da6d184ab6ae70199229",
|
||||
"Name": "marcin-2-base-heavy",
|
||||
"Gas": 5202,
|
||||
"NoBenchmark": false
|
||||
},
|
||||
{
|
||||
"Input": "000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000500000000000000000000000000000000000000000000000000000000000000010000102030405060708090a0b0c0d0e0fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000102030405060708090a0b0c0d0e0f",
|
||||
"Expected": "00000000000000000000000000000000",
|
||||
"Name": "marcin-2-exp-heavy",
|
||||
"Gas": 16368,
|
||||
"NoBenchmark": false
|
||||
},
|
||||
{
|
||||
"Input": "000000000000000000000000000000000000000000000000000000000000003800000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000038e8e77626586f73b955364c7b4bbf0bb7f7685ebd40e852b164633a4acbd3244c000102030405060708090a0b0c0d0e0f10111213141516172bfffffffffffffff01681d2220bfea4bb888a5543db8c0916274ddb1ea93b144c042c01d8164c95000102030405060708090a0b0c0d0e0f1011121314151617",
|
||||
"Expected": "86bef3367fc7117c8a6b825cadebe80f3e94c321dda73e9e240b98188a1d5c071c60a195097c8d1fb85ce03a2e1b6964846edee5aa2c3f46",
|
||||
"Name": "marcin-2-balanced",
|
||||
"Gas": 5978,
|
||||
"NoBenchmark": false
|
||||
},
|
||||
{
|
||||
"Input": "000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000020e8e77626586f73b955364c7b4bbf0bb7f7685ebd40e852b164633a4acbd3244cfffffffffffffffffffffffffffffffff01681d2220bfea4bb888a5543db8c0916274ddb1ea93b144c042c01d8164c95",
|
||||
"Expected": "4004762c491606a5132134da6086284f74cc8e14b08f18b90fc09f31bca3d78f",
|
||||
"Name": "marcin-3-base-heavy",
|
||||
"Gas": 2032,
|
||||
"NoBenchmark": false
|
||||
},
|
||||
{
|
||||
"Input": "000000000000000000000000000000000000000000000000000000000000001800000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000018000102030405060708090a0b0c0d0e0f1011121314151617ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000102030405060708090a0b0c0d0e0f1011121314151617",
|
||||
"Expected": "000000000000000000000000000000000000000000000000",
|
||||
"Name": "marcin-3-exp-heavy",
|
||||
"Gas": 4080,
|
||||
"NoBenchmark": false
|
||||
},
|
||||
{
|
||||
"Input": "000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000020e8e77626586f73b955364c7b4bbf0bb7f7685ebd40e852b164633a4acbd3244cfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01681d2220bfea4bb888a5543db8c0916274ddb1ea93b144c042c01d8164c95",
|
||||
"Expected": "2d3feee20d394af68dd6744b86a8aca6a4a0b7f01bbcd3c3eec768245ca6acee",
|
||||
"Name": "marcin-3-balanced",
|
||||
"Gas": 4080,
|
||||
"NoBenchmark": false
|
||||
},
|
||||
{
|
||||
"Input": "00000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000051000000000000000000000000000000000000000000000000000000000000000800ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00",
|
||||
"Expected": "00ffffffffffffff",
|
||||
"Name": "mod-8-exp-648",
|
||||
"Gas": 16624,
|
||||
"NoBenchmark": false
|
||||
},
|
||||
{
|
||||
"Input": "00000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000070000000000000000000000000000000000000000000000000000000000000000800ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00",
|
||||
"Expected": "00ffffffffffffff",
|
||||
"Name": "mod-8-exp-896",
|
||||
"Gas": 24560,
|
||||
"NoBenchmark": false
|
||||
},
|
||||
{
|
||||
"Input": "00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000002000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00",
|
||||
"Expected": "00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
|
||||
"Name": "mod-32-exp-32",
|
||||
"Gas": 500,
|
||||
"NoBenchmark": false
|
||||
},
|
||||
{
|
||||
"Input": "00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000005000000000000000000000000000000000000000000000000000000000000002000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00",
|
||||
"Expected": "00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
|
||||
"Name": "mod-32-exp-36",
|
||||
"Gas": 560,
|
||||
"NoBenchmark": false
|
||||
},
|
||||
{
|
||||
"Input": "00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000005000000000000000000000000000000000000000000000000000000000000002000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00",
|
||||
"Expected": "00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
|
||||
"Name": "mod-32-exp-40",
|
||||
"Gas": 624,
|
||||
"NoBenchmark": false
|
||||
},
|
||||
{
|
||||
"Input": "00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000002000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00",
|
||||
"Expected": "00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
|
||||
"Name": "mod-32-exp-64",
|
||||
"Gas": 1008,
|
||||
"NoBenchmark": false
|
||||
},
|
||||
{
|
||||
"Input": "00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000009000000000000000000000000000000000000000000000000000000000000002000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00",
|
||||
"Expected": "00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
|
||||
"Name": "mod-32-exp-65",
|
||||
"Gas": 1024,
|
||||
"NoBenchmark": false
|
||||
},
|
||||
{
|
||||
"Input": "00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00",
|
||||
"Expected": "00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
|
||||
"Name": "mod-32-exp-128",
|
||||
"Gas": 2032,
|
||||
"NoBenchmark": false
|
||||
},
|
||||
{
|
||||
"Input": "00000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000010000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00",
|
||||
"Expected": "02fd01000000000000000000000000000000fefffffffffffd03ff0000000000000000000000000000000000000000000000fefffffffffffd03ff0000000000000000000000000000000000000000000000fefffffffffffd03ff0000000000000000000000000000000000000000000000fefffffffffffd03ff0000000000000000000000000000000000000000000000fefffffffffffd03ff0000000000000000000000000000000000000000000000fefffffffffffd03ff0000000000000000000000000000000000000000000000fefffffffffffd03ff0000000000000000000000000000000000000000000000fefffffffffffd03feffffffffff",
|
||||
"Name": "mod-256-exp-2",
|
||||
"Gas": 2048,
|
||||
"NoBenchmark": false
|
||||
},
|
||||
{
|
||||
"Input": "00000000000000000000000000000000000000000000000000000000000001080000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000010800ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffff00",
|
||||
"Expected": "0100fefffffffffffff710f80000000006f108000000000000feffffffffffff0100fefffffffffffef90ff80000000105f008ffffffffff02fdffffffffffff0100feffffffffff00f80ff80000010101f407fffffffd06fc0000000000fd020000feffffffff00fff80ff8000101fa08f207fffffb0afa0000000001fb03000000feffffff00fffff80ff80102f80405f207fff90ef80000000002f90400000000feffff00fffffff80ff903f6050005f207f712f60000000003f7050000000000feff00fffffffff810fcf406000005f1fd16f40000000004f506000000000000fe00fffffffffff915ea0700000005e522f20000000005f306ffffffffffffffffffffffffff",
|
||||
"Name": "mod-264-exp-2",
|
||||
"Gas": 2178,
|
||||
"NoBenchmark": false
|
||||
},
|
||||
{
|
||||
"Input": "00000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000040000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00",
|
||||
"Expected": "00fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe02fefffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe02fefffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe02fefffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe02fefffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe02fefffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe02fefffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe02fefffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe02fefffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe02fefffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe02fefffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe02fefffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe02fefffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe02fefffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe02fefffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe02fefffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe02fefffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe02fefffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe02fefffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe02fefffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe02fefffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe02fefffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe02fefffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe02fefffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe02fefffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe02fefffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe02fefffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe02fefffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe02fefffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe02fefffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe02fefffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe02feffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
|
||||
"Name": "mod-1024-exp-2",
|
||||
"Gas": 32768,
|
||||
"NoBenchmark": false
|
||||
},
|
||||
{
|
||||
"Input": "000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000700000000000000000000000000000000000000000000000000000000000000008ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
|
||||
"Expected": "2a02d5f86c2375ff",
|
||||
"Name": "pawel-1-exp-heavy",
|
||||
"Gas": 24560,
|
||||
"NoBenchmark": false
|
||||
},
|
||||
{
|
||||
"Input": "000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000280000000000000000000000000000000000000000000000000000000000000010ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
|
||||
"Expected": "823ef7dc60d6d9616756c48f69b7c4ff",
|
||||
"Name": "pawel-2-exp-heavy",
|
||||
"Gas": 6128,
|
||||
"NoBenchmark": false
|
||||
},
|
||||
{
|
||||
"Input": "000000000000000000000000000000000000000000000000000000000000001800000000000000000000000000000000000000000000000000000000000000150000000000000000000000000000000000000000000000000000000000000018ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
|
||||
"Expected": "c817dd5aa60a41948eed409706c2aa97be3000d4da0261ff",
|
||||
"Name": "pawel-3-exp-heavy",
|
||||
"Gas": 2672,
|
||||
"NoBenchmark": false
|
||||
},
|
||||
{
|
||||
"Input": "0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000020ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
|
||||
"Expected": "2defaca0137d6edacbbd5d36d6ed70cbf8a998ffb19fc270d45a18d37e0f35ff",
|
||||
"Name": "pawel-4-exp-heavy",
|
||||
"Gas": 1520,
|
||||
"NoBenchmark": false
|
||||
},
|
||||
{
|
||||
"Input": "000000000000000000000000000000000000000000000000000000000000001700000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000017bffffffffffffffffffffffffffffffffffffffffffffbffffffffffffffffff7ffffffffffffffffffffffffffffffffffffffffffe",
|
||||
"Expected": "200f14de1d474710c1c979920452e0ffc2ac6f618afba5",
|
||||
"Name": "mod_vul_pawel_3_exp_8",
|
||||
"Gas": 1008,
|
||||
"NoBenchmark": false
|
||||
}
|
||||
]
|
||||
|
|
|
|||
|
|
@ -150,7 +150,7 @@ func ckzgComputeCellProofs(blob *Blob) ([]Proof, error) {
|
|||
return p, nil
|
||||
}
|
||||
|
||||
// ckzgVerifyCellProofs verifies that the blob data corresponds to the provided commitment.
|
||||
// ckzgVerifyCellProofBatch verifies that the blob data corresponds to the provided commitment.
|
||||
func ckzgVerifyCellProofBatch(blobs []Blob, commitments []Commitment, cellProofs []Proof) error {
|
||||
ckzgIniter.Do(ckzgInit)
|
||||
var (
|
||||
|
|
|
|||
|
|
@ -115,7 +115,7 @@ func gokzgComputeCellProofs(blob *Blob) ([]Proof, error) {
|
|||
return p, nil
|
||||
}
|
||||
|
||||
// gokzgVerifyCellProofs verifies that the blob data corresponds to the provided commitment.
|
||||
// gokzgVerifyCellProofBatch verifies that the blob data corresponds to the provided commitment.
|
||||
func gokzgVerifyCellProofBatch(blobs []Blob, commitments []Commitment, cellProofs []Proof) error {
|
||||
gokzgIniter.Do(gokzgInit)
|
||||
|
||||
|
|
|
|||
|
|
@ -443,3 +443,51 @@ func (api *DebugAPI) GetTrieFlushInterval() (string, error) {
|
|||
}
|
||||
return api.eth.blockchain.GetTrieFlushInterval().String(), nil
|
||||
}
|
||||
|
||||
// StateSize returns the current state size statistics from the state size tracker.
|
||||
// Returns an error if the state size tracker is not initialized or if stats are not ready.
|
||||
func (api *DebugAPI) StateSize(blockHashOrNumber *rpc.BlockNumberOrHash) (interface{}, error) {
|
||||
sizer := api.eth.blockchain.StateSizer()
|
||||
if sizer == nil {
|
||||
return nil, errors.New("state size tracker is not enabled")
|
||||
}
|
||||
var (
|
||||
err error
|
||||
stats *state.SizeStats
|
||||
)
|
||||
if blockHashOrNumber == nil {
|
||||
stats, err = sizer.Query(nil)
|
||||
} else {
|
||||
header, herr := api.eth.APIBackend.HeaderByNumberOrHash(context.Background(), *blockHashOrNumber)
|
||||
if herr != nil || header == nil {
|
||||
return nil, fmt.Errorf("block %s is unknown", blockHashOrNumber)
|
||||
}
|
||||
stats, err = sizer.Query(&header.Root)
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if stats == nil {
|
||||
var s string
|
||||
if blockHashOrNumber == nil {
|
||||
s = "chain head"
|
||||
} else {
|
||||
s = blockHashOrNumber.String()
|
||||
}
|
||||
return nil, fmt.Errorf("state size of %s is not available", s)
|
||||
}
|
||||
return map[string]interface{}{
|
||||
"stateRoot": stats.StateRoot,
|
||||
"blockNumber": hexutil.Uint64(stats.BlockNumber),
|
||||
"accounts": hexutil.Uint64(stats.Accounts),
|
||||
"accountBytes": hexutil.Uint64(stats.AccountBytes),
|
||||
"storages": hexutil.Uint64(stats.Storages),
|
||||
"storageBytes": hexutil.Uint64(stats.StorageBytes),
|
||||
"accountTrienodes": hexutil.Uint64(stats.AccountTrienodes),
|
||||
"accountTrienodeBytes": hexutil.Uint64(stats.AccountTrienodeBytes),
|
||||
"storageTrienodes": hexutil.Uint64(stats.StorageTrienodes),
|
||||
"storageTrienodeBytes": hexutil.Uint64(stats.StorageTrienodeBytes),
|
||||
"contractCodes": hexutil.Uint64(stats.ContractCodes),
|
||||
"contractCodeBytes": hexutil.Uint64(stats.ContractCodeBytes),
|
||||
}, nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -240,6 +240,7 @@ func New(stack *node.Node, config *ethconfig.Config) (*Ethereum, error) {
|
|||
// - DATADIR/triedb/merkle.journal
|
||||
// - DATADIR/triedb/verkle.journal
|
||||
TrieJournalDirectory: stack.ResolvePath("triedb"),
|
||||
StateSizeTracking: config.EnableStateSizeTracking,
|
||||
}
|
||||
)
|
||||
if config.VMTrace != "" {
|
||||
|
|
|
|||
|
|
@ -20,10 +20,12 @@ package catalyst
|
|||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
"unicode"
|
||||
|
||||
"github.com/ethereum/go-ethereum/beacon/engine"
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
|
|
@ -80,47 +82,6 @@ const (
|
|||
beaconUpdateWarnFrequency = 5 * time.Minute
|
||||
)
|
||||
|
||||
// All methods provided over the engine endpoint.
|
||||
var caps = []string{
|
||||
"engine_forkchoiceUpdatedV1",
|
||||
"engine_forkchoiceUpdatedV2",
|
||||
"engine_forkchoiceUpdatedV3",
|
||||
"engine_forkchoiceUpdatedV3P11",
|
||||
"engine_forkchoiceUpdatedWithWitnessV1",
|
||||
"engine_forkchoiceUpdatedWithWitnessV2",
|
||||
"engine_forkchoiceUpdatedWithWitnessV3",
|
||||
"engine_forkchoiceUpdatedWithWitnessV3P11",
|
||||
"engine_exchangeTransitionConfigurationV1",
|
||||
"engine_getPayloadV1",
|
||||
"engine_getPayloadV2",
|
||||
"engine_getPayloadV3",
|
||||
"engine_getPayloadV4",
|
||||
"engine_getPayloadV4P11",
|
||||
"engine_getPayloadV5",
|
||||
"engine_getBlobsV1",
|
||||
"engine_getBlobsV2",
|
||||
"engine_newPayloadV1",
|
||||
"engine_newPayloadV2",
|
||||
"engine_newPayloadV3",
|
||||
"engine_newPayloadV4",
|
||||
"engine_newPayloadV4P11",
|
||||
"engine_newPayloadWithWitnessV1",
|
||||
"engine_newPayloadWithWitnessV2",
|
||||
"engine_newPayloadWithWitnessV3",
|
||||
"engine_newPayloadWithWitnessV4",
|
||||
"engine_newPayloadWithWitnessV4P11",
|
||||
"engine_executeStatelessPayloadV1",
|
||||
"engine_executeStatelessPayloadV2",
|
||||
"engine_executeStatelessPayloadV3",
|
||||
"engine_executeStatelessPayloadV4",
|
||||
"engine_executeStatelessPayloadV4P11",
|
||||
"engine_getPayloadBodiesByHashV1",
|
||||
"engine_getPayloadBodiesByHashV2",
|
||||
"engine_getPayloadBodiesByRangeV1",
|
||||
"engine_getPayloadBodiesByRangeV2",
|
||||
"engine_getClientVersionV1",
|
||||
}
|
||||
|
||||
var (
|
||||
// Number of blobs requested via getBlobsV2
|
||||
getBlobsRequestedCounter = metrics.NewRegisteredCounter("engine/getblobs/requested", nil)
|
||||
|
|
@ -534,6 +495,26 @@ func (api *ConsensusAPI) getPayload(payloadID engine.PayloadID, full bool) (*eng
|
|||
}
|
||||
|
||||
// GetBlobsV1 returns a blob from the transaction pool.
|
||||
//
|
||||
// Specification:
|
||||
//
|
||||
// Given an array of blob versioned hashes client software MUST respond with an
|
||||
// array of BlobAndProofV1 objects with matching versioned hashes, respecting the
|
||||
// order of versioned hashes in the input array.
|
||||
//
|
||||
// Client software MUST place responses in the order given in the request, using
|
||||
// null for any missing blobs. For instance:
|
||||
//
|
||||
// if the request is [A_versioned_hash, B_versioned_hash, C_versioned_hash] and
|
||||
// client software has data for blobs A and C, but doesn't have data for B, the
|
||||
// response MUST be [A, null, C].
|
||||
//
|
||||
// Client software MUST support request sizes of at least 128 blob versioned hashes.
|
||||
// The client MUST return -38004: Too large request error if the number of requested
|
||||
// blobs is too large.
|
||||
//
|
||||
// Client software MAY return an array of all null entries if syncing or otherwise
|
||||
// unable to serve blob pool data.
|
||||
func (api *ConsensusAPI) GetBlobsV1(hashes []common.Hash) ([]*engine.BlobAndProofV1, error) {
|
||||
if len(hashes) > 128 {
|
||||
return nil, engine.TooLargeRequest.With(fmt.Errorf("requested blob count too large: %v", len(hashes)))
|
||||
|
|
@ -544,6 +525,10 @@ func (api *ConsensusAPI) GetBlobsV1(hashes []common.Hash) ([]*engine.BlobAndProo
|
|||
}
|
||||
res := make([]*engine.BlobAndProofV1, len(hashes))
|
||||
for i := 0; i < len(blobs); i++ {
|
||||
// Skip the non-existing blob
|
||||
if blobs[i] == nil {
|
||||
continue
|
||||
}
|
||||
res[i] = &engine.BlobAndProofV1{
|
||||
Blob: blobs[i][:],
|
||||
Proof: proofs[i][0][:],
|
||||
|
|
@ -553,6 +538,33 @@ func (api *ConsensusAPI) GetBlobsV1(hashes []common.Hash) ([]*engine.BlobAndProo
|
|||
}
|
||||
|
||||
// GetBlobsV2 returns a blob from the transaction pool.
|
||||
//
|
||||
// Specification:
|
||||
// Refer to the specification for engine_getBlobsV1 with changes of the following:
|
||||
//
|
||||
// Given an array of blob versioned hashes client software MUST respond with an
|
||||
// array of BlobAndProofV2 objects with matching versioned hashes, respecting
|
||||
// the order of versioned hashes in the input array.
|
||||
//
|
||||
// Client software MUST return null in case of any missing or older version blobs.
|
||||
// For instance,
|
||||
//
|
||||
// - if the request is [A_versioned_hash, B_versioned_hash, C_versioned_hash] and
|
||||
// client software has data for blobs A and C, but doesn't have data for B, the
|
||||
// response MUST be null.
|
||||
//
|
||||
// - if the request is [A_versioned_hash_for_blob_with_blob_proof], the response
|
||||
// MUST be null as well.
|
||||
//
|
||||
// Note, geth internally make the conversion from old version to new one, so the
|
||||
// data will be returned normally.
|
||||
//
|
||||
// Client software MUST support request sizes of at least 128 blob versioned
|
||||
// hashes. The client MUST return -38004: Too large request error if the number
|
||||
// of requested blobs is too large.
|
||||
//
|
||||
// Client software MUST return null if syncing or otherwise unable to serve
|
||||
// blob pool data.
|
||||
func (api *ConsensusAPI) GetBlobsV2(hashes []common.Hash) ([]*engine.BlobAndProofV2, error) {
|
||||
if len(hashes) > 128 {
|
||||
return nil, engine.TooLargeRequest.With(fmt.Errorf("requested blob count too large: %v", len(hashes)))
|
||||
|
|
@ -566,12 +578,21 @@ func (api *ConsensusAPI) GetBlobsV2(hashes []common.Hash) ([]*engine.BlobAndProo
|
|||
getBlobsV2RequestMiss.Inc(1)
|
||||
return nil, nil
|
||||
}
|
||||
getBlobsV2RequestHit.Inc(1)
|
||||
|
||||
blobs, _, proofs, err := api.eth.BlobTxPool().GetBlobs(hashes, types.BlobSidecarVersion1)
|
||||
if err != nil {
|
||||
return nil, engine.InvalidParams.With(err)
|
||||
}
|
||||
|
||||
// To comply with API spec, check again that we really got all data needed
|
||||
for _, blob := range blobs {
|
||||
if blob == nil {
|
||||
getBlobsV2RequestMiss.Inc(1)
|
||||
return nil, nil
|
||||
}
|
||||
}
|
||||
getBlobsV2RequestHit.Inc(1)
|
||||
|
||||
res := make([]*engine.BlobAndProofV2, len(hashes))
|
||||
for i := 0; i < len(blobs); i++ {
|
||||
var cellProofs []hexutil.Bytes
|
||||
|
|
@ -987,6 +1008,15 @@ func (api *ConsensusAPI) checkFork(timestamp uint64, forks ...forks.Fork) bool {
|
|||
|
||||
// ExchangeCapabilities returns the current methods provided by this node.
|
||||
func (api *ConsensusAPI) ExchangeCapabilities([]string) []string {
|
||||
valueT := reflect.TypeOf(api)
|
||||
caps := make([]string, 0, valueT.NumMethod())
|
||||
for i := 0; i < valueT.NumMethod(); i++ {
|
||||
name := []rune(valueT.Method(i).Name)
|
||||
if string(name) == "ExchangeCapabilities" {
|
||||
continue
|
||||
}
|
||||
caps = append(caps, "engine_"+string(unicode.ToLower(name[0]))+string(name[1:]))
|
||||
}
|
||||
return caps
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -19,7 +19,9 @@ package catalyst
|
|||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"crypto/ecdsa"
|
||||
crand "crypto/rand"
|
||||
"crypto/sha256"
|
||||
"errors"
|
||||
"fmt"
|
||||
"math/big"
|
||||
|
|
@ -40,6 +42,7 @@ import (
|
|||
"github.com/ethereum/go-ethereum/crypto/kzg4844"
|
||||
"github.com/ethereum/go-ethereum/eth"
|
||||
"github.com/ethereum/go-ethereum/eth/ethconfig"
|
||||
"github.com/ethereum/go-ethereum/internal/testrand"
|
||||
"github.com/ethereum/go-ethereum/internal/version"
|
||||
"github.com/ethereum/go-ethereum/miner"
|
||||
"github.com/ethereum/go-ethereum/node"
|
||||
|
|
@ -47,6 +50,7 @@ import (
|
|||
"github.com/ethereum/go-ethereum/params"
|
||||
"github.com/ethereum/go-ethereum/rpc"
|
||||
"github.com/ethereum/go-ethereum/trie"
|
||||
"github.com/holiman/uint256"
|
||||
)
|
||||
|
||||
var (
|
||||
|
|
@ -112,7 +116,7 @@ func TestEth2AssembleBlock(t *testing.T) {
|
|||
n, ethservice := startEthService(t, genesis, blocks)
|
||||
defer n.Close()
|
||||
|
||||
api := NewConsensusAPI(ethservice)
|
||||
api := newConsensusAPIWithoutHeartbeat(ethservice)
|
||||
signer := types.NewEIP155Signer(ethservice.BlockChain().Config().ChainID)
|
||||
tx, err := types.SignTx(types.NewTransaction(uint64(10), blocks[9].Coinbase(), big.NewInt(1000), params.TxGas, big.NewInt(params.InitialBaseFee), nil), signer, testKey)
|
||||
if err != nil {
|
||||
|
|
@ -151,7 +155,7 @@ func TestEth2AssembleBlockWithAnotherBlocksTxs(t *testing.T) {
|
|||
n, ethservice := startEthService(t, genesis, blocks[:9])
|
||||
defer n.Close()
|
||||
|
||||
api := NewConsensusAPI(ethservice)
|
||||
api := newConsensusAPIWithoutHeartbeat(ethservice)
|
||||
|
||||
// Put the 10th block's tx in the pool and produce a new block
|
||||
txs := blocks[9].Transactions()
|
||||
|
|
@ -173,7 +177,7 @@ func TestEth2PrepareAndGetPayload(t *testing.T) {
|
|||
n, ethservice := startEthService(t, genesis, blocks[:9])
|
||||
defer n.Close()
|
||||
|
||||
api := NewConsensusAPI(ethservice)
|
||||
api := newConsensusAPIWithoutHeartbeat(ethservice)
|
||||
|
||||
// Put the 10th block's tx in the pool and produce a new block
|
||||
txs := blocks[9].Transactions()
|
||||
|
|
@ -238,8 +242,9 @@ func TestInvalidPayloadTimestamp(t *testing.T) {
|
|||
genesis, preMergeBlocks := generateMergeChain(10, false)
|
||||
n, ethservice := startEthService(t, genesis, preMergeBlocks)
|
||||
defer n.Close()
|
||||
|
||||
var (
|
||||
api = NewConsensusAPI(ethservice)
|
||||
api = newConsensusAPIWithoutHeartbeat(ethservice)
|
||||
parent = ethservice.BlockChain().CurrentBlock()
|
||||
)
|
||||
tests := []struct {
|
||||
|
|
@ -281,7 +286,7 @@ func TestEth2NewBlock(t *testing.T) {
|
|||
defer n.Close()
|
||||
|
||||
var (
|
||||
api = NewConsensusAPI(ethservice)
|
||||
api = newConsensusAPIWithoutHeartbeat(ethservice)
|
||||
parent = preMergeBlocks[len(preMergeBlocks)-1]
|
||||
|
||||
// This EVM code generates a log when the contract is created.
|
||||
|
|
@ -434,8 +439,14 @@ func startEthService(t *testing.T, genesis *core.Genesis, blocks []*types.Block)
|
|||
t.Fatal("can't create node:", err)
|
||||
}
|
||||
|
||||
mcfg := miner.DefaultConfig
|
||||
ethcfg := ðconfig.Config{Genesis: genesis, SyncMode: ethconfig.FullSync, TrieTimeout: time.Minute, TrieDirtyCache: 256, TrieCleanCache: 256, Miner: mcfg}
|
||||
ethcfg := ðconfig.Config{
|
||||
Genesis: genesis,
|
||||
SyncMode: ethconfig.FullSync,
|
||||
TrieTimeout: time.Minute,
|
||||
TrieDirtyCache: 256,
|
||||
TrieCleanCache: 256,
|
||||
Miner: miner.DefaultConfig,
|
||||
}
|
||||
ethservice, err := eth.New(n, ethcfg)
|
||||
if err != nil {
|
||||
t.Fatal("can't create eth service:", err)
|
||||
|
|
@ -459,6 +470,7 @@ func TestFullAPI(t *testing.T) {
|
|||
genesis, preMergeBlocks := generateMergeChain(10, false)
|
||||
n, ethservice := startEthService(t, genesis, preMergeBlocks)
|
||||
defer n.Close()
|
||||
|
||||
var (
|
||||
parent = ethservice.BlockChain().CurrentBlock()
|
||||
// This EVM code generates a log when the contract is created.
|
||||
|
|
@ -476,7 +488,7 @@ func TestFullAPI(t *testing.T) {
|
|||
}
|
||||
|
||||
func setupBlocks(t *testing.T, ethservice *eth.Ethereum, n int, parent *types.Header, callback func(parent *types.Header), withdrawals [][]*types.Withdrawal, beaconRoots []common.Hash, proposerPubkeys []common.Pubkey) []*types.Header {
|
||||
api := NewConsensusAPI(ethservice)
|
||||
api := newConsensusAPIWithoutHeartbeat(ethservice)
|
||||
var blocks []*types.Header
|
||||
for i := 0; i < n; i++ {
|
||||
callback(parent)
|
||||
|
|
@ -528,7 +540,7 @@ func TestExchangeTransitionConfig(t *testing.T) {
|
|||
defer n.Close()
|
||||
|
||||
// invalid ttd
|
||||
api := NewConsensusAPI(ethservice)
|
||||
api := newConsensusAPIWithoutHeartbeat(ethservice)
|
||||
config := engine.TransitionConfigurationV1{
|
||||
TerminalTotalDifficulty: (*hexutil.Big)(big.NewInt(0)),
|
||||
TerminalBlockHash: common.Hash{},
|
||||
|
|
@ -589,7 +601,7 @@ func TestNewPayloadOnInvalidChain(t *testing.T) {
|
|||
defer n.Close()
|
||||
|
||||
var (
|
||||
api = NewConsensusAPI(ethservice)
|
||||
api = newConsensusAPIWithoutHeartbeat(ethservice)
|
||||
parent = ethservice.BlockChain().CurrentBlock()
|
||||
signer = types.LatestSigner(ethservice.BlockChain().Config())
|
||||
// This EVM code generates a log when the contract is created.
|
||||
|
|
@ -693,7 +705,7 @@ func TestEmptyBlocks(t *testing.T) {
|
|||
defer n.Close()
|
||||
|
||||
commonAncestor := ethservice.BlockChain().CurrentBlock()
|
||||
api := NewConsensusAPI(ethservice)
|
||||
api := newConsensusAPIWithoutHeartbeat(ethservice)
|
||||
|
||||
// Setup 10 blocks on the canonical chain
|
||||
setupBlocks(t, ethservice, 10, commonAncestor, func(parent *types.Header) {}, nil, nil, nil)
|
||||
|
|
@ -820,8 +832,8 @@ func TestTrickRemoteBlockCache(t *testing.T) {
|
|||
}
|
||||
nodeA.Server().AddPeer(nodeB.Server().Self())
|
||||
nodeB.Server().AddPeer(nodeA.Server().Self())
|
||||
apiA := NewConsensusAPI(ethserviceA)
|
||||
apiB := NewConsensusAPI(ethserviceB)
|
||||
apiA := newConsensusAPIWithoutHeartbeat(ethserviceA)
|
||||
apiB := newConsensusAPIWithoutHeartbeat(ethserviceB)
|
||||
|
||||
commonAncestor := ethserviceA.BlockChain().CurrentBlock()
|
||||
|
||||
|
|
@ -878,7 +890,7 @@ func TestInvalidBloom(t *testing.T) {
|
|||
defer n.Close()
|
||||
|
||||
commonAncestor := ethservice.BlockChain().CurrentBlock()
|
||||
api := NewConsensusAPI(ethservice)
|
||||
api := newConsensusAPIWithoutHeartbeat(ethservice)
|
||||
|
||||
// Setup 10 blocks on the canonical chain
|
||||
setupBlocks(t, ethservice, 10, commonAncestor, func(parent *types.Header) {}, nil, nil, nil)
|
||||
|
|
@ -904,7 +916,7 @@ func TestSimultaneousNewBlock(t *testing.T) {
|
|||
defer n.Close()
|
||||
|
||||
var (
|
||||
api = NewConsensusAPI(ethservice)
|
||||
api = newConsensusAPIWithoutHeartbeat(ethservice)
|
||||
parent = preMergeBlocks[len(preMergeBlocks)-1]
|
||||
)
|
||||
for i := 0; i < 10; i++ {
|
||||
|
|
@ -994,7 +1006,7 @@ func TestWithdrawals(t *testing.T) {
|
|||
n, ethservice := startEthService(t, genesis, blocks)
|
||||
defer n.Close()
|
||||
|
||||
api := NewConsensusAPI(ethservice)
|
||||
api := newConsensusAPIWithoutHeartbeat(ethservice)
|
||||
|
||||
// 10: Build Shanghai block with no withdrawals.
|
||||
parent := ethservice.BlockChain().CurrentHeader()
|
||||
|
|
@ -1111,7 +1123,7 @@ func TestNilWithdrawals(t *testing.T) {
|
|||
n, ethservice := startEthService(t, genesis, blocks)
|
||||
defer n.Close()
|
||||
|
||||
api := NewConsensusAPI(ethservice)
|
||||
api := newConsensusAPIWithoutHeartbeat(ethservice)
|
||||
parent := ethservice.BlockChain().CurrentHeader()
|
||||
aa := common.Address{0xaa}
|
||||
|
||||
|
|
@ -1307,7 +1319,7 @@ func allBodies(blocks []*types.Block) []*types.Body {
|
|||
|
||||
func TestGetBlockBodiesByHash(t *testing.T) {
|
||||
node, eth, blocks := setupBodies(t)
|
||||
api := NewConsensusAPI(eth)
|
||||
api := newConsensusAPIWithoutHeartbeat(eth)
|
||||
defer node.Close()
|
||||
|
||||
tests := []struct {
|
||||
|
|
@ -1363,7 +1375,7 @@ func TestGetBlockBodiesByHash(t *testing.T) {
|
|||
|
||||
func TestGetBlockBodiesByRange(t *testing.T) {
|
||||
node, eth, blocks := setupBodies(t)
|
||||
api := NewConsensusAPI(eth)
|
||||
api := newConsensusAPIWithoutHeartbeat(eth)
|
||||
defer node.Close()
|
||||
|
||||
tests := []struct {
|
||||
|
|
@ -1444,7 +1456,7 @@ func TestGetBlockBodiesByRange(t *testing.T) {
|
|||
|
||||
func TestGetBlockBodiesByRangeInvalidParams(t *testing.T) {
|
||||
node, eth, _ := setupBodies(t)
|
||||
api := NewConsensusAPI(eth)
|
||||
api := newConsensusAPIWithoutHeartbeat(eth)
|
||||
defer node.Close()
|
||||
tests := []struct {
|
||||
start hexutil.Uint64
|
||||
|
|
@ -1556,7 +1568,7 @@ func TestParentBeaconBlockRoot(t *testing.T) {
|
|||
n, ethservice := startEthService(t, genesis, blocks)
|
||||
defer n.Close()
|
||||
|
||||
api := NewConsensusAPI(ethservice)
|
||||
api := newConsensusAPIWithoutHeartbeat(ethservice)
|
||||
|
||||
// 11: Build Shanghai block with no withdrawals.
|
||||
parent := ethservice.BlockChain().CurrentHeader()
|
||||
|
|
@ -1639,7 +1651,7 @@ func TestWitnessCreationAndConsumption(t *testing.T) {
|
|||
n, ethservice := startEthService(t, genesis, blocks[:9])
|
||||
defer n.Close()
|
||||
|
||||
api := NewConsensusAPI(ethservice)
|
||||
api := newConsensusAPIWithoutHeartbeat(ethservice)
|
||||
|
||||
// Put the 10th block's tx in the pool and produce a new block
|
||||
txs := blocks[9].Transactions()
|
||||
|
|
@ -1731,7 +1743,7 @@ func TestGetClientVersion(t *testing.T) {
|
|||
n, ethservice := startEthService(t, genesis, preMergeBlocks)
|
||||
defer n.Close()
|
||||
|
||||
api := NewConsensusAPI(ethservice)
|
||||
api := newConsensusAPIWithoutHeartbeat(ethservice)
|
||||
info := engine.ClientVersionV1{
|
||||
Code: "TT",
|
||||
Name: "test",
|
||||
|
|
@ -1805,3 +1817,245 @@ func TestValidateRequests(t *testing.T) {
|
|||
})
|
||||
}
|
||||
}
|
||||
|
||||
var (
|
||||
testBlobs []*kzg4844.Blob
|
||||
testBlobCommits []kzg4844.Commitment
|
||||
testBlobProofs []kzg4844.Proof
|
||||
testBlobCellProofs [][]kzg4844.Proof
|
||||
testBlobVHashes [][32]byte
|
||||
)
|
||||
|
||||
func init() {
|
||||
for i := 0; i < 6; i++ {
|
||||
testBlob := &kzg4844.Blob{byte(i)}
|
||||
testBlobs = append(testBlobs, testBlob)
|
||||
|
||||
testBlobCommit, _ := kzg4844.BlobToCommitment(testBlob)
|
||||
testBlobCommits = append(testBlobCommits, testBlobCommit)
|
||||
|
||||
testBlobProof, _ := kzg4844.ComputeBlobProof(testBlob, testBlobCommit)
|
||||
testBlobProofs = append(testBlobProofs, testBlobProof)
|
||||
|
||||
testBlobCellProof, _ := kzg4844.ComputeCellProofs(testBlob)
|
||||
testBlobCellProofs = append(testBlobCellProofs, testBlobCellProof)
|
||||
|
||||
testBlobVHash := kzg4844.CalcBlobHashV1(sha256.New(), &testBlobCommit)
|
||||
testBlobVHashes = append(testBlobVHashes, testBlobVHash)
|
||||
}
|
||||
}
|
||||
|
||||
// makeMultiBlobTx is a utility method to construct a random blob tx with
|
||||
// certain number of blobs in its sidecar.
|
||||
func makeMultiBlobTx(chainConfig *params.ChainConfig, nonce uint64, blobCount int, blobOffset int, key *ecdsa.PrivateKey, version byte) *types.Transaction {
|
||||
var (
|
||||
blobs []kzg4844.Blob
|
||||
blobHashes []common.Hash
|
||||
commitments []kzg4844.Commitment
|
||||
proofs []kzg4844.Proof
|
||||
)
|
||||
for i := 0; i < blobCount; i++ {
|
||||
blobs = append(blobs, *testBlobs[blobOffset+i])
|
||||
commitments = append(commitments, testBlobCommits[blobOffset+i])
|
||||
if version == types.BlobSidecarVersion0 {
|
||||
proofs = append(proofs, testBlobProofs[blobOffset+i])
|
||||
} else {
|
||||
cellProofs, _ := kzg4844.ComputeCellProofs(testBlobs[blobOffset+i])
|
||||
proofs = append(proofs, cellProofs...)
|
||||
}
|
||||
blobHashes = append(blobHashes, testBlobVHashes[blobOffset+i])
|
||||
}
|
||||
blobtx := &types.BlobTx{
|
||||
ChainID: uint256.MustFromBig(chainConfig.ChainID),
|
||||
Nonce: nonce,
|
||||
GasTipCap: uint256.NewInt(1),
|
||||
GasFeeCap: uint256.NewInt(1000),
|
||||
Gas: 21000,
|
||||
BlobFeeCap: uint256.NewInt(1000),
|
||||
BlobHashes: blobHashes,
|
||||
Value: uint256.NewInt(100),
|
||||
Sidecar: types.NewBlobTxSidecar(version, blobs, commitments, proofs),
|
||||
}
|
||||
return types.MustSignNewTx(key, types.LatestSigner(chainConfig), blobtx)
|
||||
}
|
||||
|
||||
func newGetBlobEnv(t *testing.T, version byte) (*node.Node, *ConsensusAPI) {
|
||||
var (
|
||||
// Create a database pre-initialize with a genesis block
|
||||
config = *params.MergedTestChainConfig
|
||||
|
||||
key1, _ = crypto.GenerateKey()
|
||||
key2, _ = crypto.GenerateKey()
|
||||
key3, _ = crypto.GenerateKey()
|
||||
|
||||
addr1 = crypto.PubkeyToAddress(key1.PublicKey)
|
||||
addr2 = crypto.PubkeyToAddress(key2.PublicKey)
|
||||
addr3 = crypto.PubkeyToAddress(key3.PublicKey)
|
||||
)
|
||||
// Disable Osaka fork for GetBlobsV1
|
||||
if version == 0 {
|
||||
config.OsakaTime = nil
|
||||
}
|
||||
gspec := &core.Genesis{
|
||||
Config: &config,
|
||||
Alloc: types.GenesisAlloc{
|
||||
testAddr: {Balance: testBalance},
|
||||
addr1: {Balance: testBalance},
|
||||
addr2: {Balance: testBalance},
|
||||
addr3: {Balance: testBalance},
|
||||
},
|
||||
Difficulty: common.Big0,
|
||||
}
|
||||
n, ethServ := startEthService(t, gspec, nil)
|
||||
|
||||
// fill blob txs into the pool
|
||||
tx1 := makeMultiBlobTx(&config, 0, 2, 0, key1, version) // blob[0, 2)
|
||||
tx2 := makeMultiBlobTx(&config, 0, 2, 2, key2, version) // blob[2, 4)
|
||||
tx3 := makeMultiBlobTx(&config, 0, 2, 4, key3, version) // blob[4, 6)
|
||||
ethServ.TxPool().Add([]*types.Transaction{tx1, tx2, tx3}, true)
|
||||
|
||||
api := newConsensusAPIWithoutHeartbeat(ethServ)
|
||||
return n, api
|
||||
}
|
||||
|
||||
func TestGetBlobsV1(t *testing.T) {
|
||||
n, api := newGetBlobEnv(t, 0)
|
||||
defer n.Close()
|
||||
|
||||
suites := []struct {
|
||||
start int
|
||||
limit int
|
||||
fillRandom bool
|
||||
}{
|
||||
{
|
||||
start: 0, limit: 1,
|
||||
},
|
||||
{
|
||||
start: 0, limit: 1, fillRandom: true,
|
||||
},
|
||||
{
|
||||
start: 0, limit: 2,
|
||||
},
|
||||
{
|
||||
start: 0, limit: 2, fillRandom: true,
|
||||
},
|
||||
{
|
||||
start: 1, limit: 3,
|
||||
},
|
||||
{
|
||||
start: 1, limit: 3, fillRandom: true,
|
||||
},
|
||||
{
|
||||
start: 0, limit: 6,
|
||||
},
|
||||
{
|
||||
start: 0, limit: 6, fillRandom: true,
|
||||
},
|
||||
{
|
||||
start: 1, limit: 5,
|
||||
},
|
||||
{
|
||||
start: 1, limit: 5, fillRandom: true,
|
||||
},
|
||||
}
|
||||
for i, suite := range suites {
|
||||
// Fill the request for retrieving blobs
|
||||
var (
|
||||
vhashes []common.Hash
|
||||
expect []*engine.BlobAndProofV1
|
||||
)
|
||||
// fill missing blob at the beginning
|
||||
if suite.fillRandom {
|
||||
vhashes = append(vhashes, testrand.Hash())
|
||||
expect = append(expect, nil)
|
||||
}
|
||||
for j := suite.start; j < suite.limit; j++ {
|
||||
vhashes = append(vhashes, testBlobVHashes[j])
|
||||
expect = append(expect, &engine.BlobAndProofV1{
|
||||
Blob: testBlobs[j][:],
|
||||
Proof: testBlobProofs[j][:],
|
||||
})
|
||||
|
||||
// fill missing blobs in the middle
|
||||
if suite.fillRandom && rand.Intn(2) == 0 {
|
||||
vhashes = append(vhashes, testrand.Hash())
|
||||
expect = append(expect, nil)
|
||||
}
|
||||
}
|
||||
// fill missing blobs at the end
|
||||
if suite.fillRandom {
|
||||
vhashes = append(vhashes, testrand.Hash())
|
||||
expect = append(expect, nil)
|
||||
}
|
||||
result, err := api.GetBlobsV1(vhashes)
|
||||
if err != nil {
|
||||
t.Errorf("Unexpected error for case %d, %v", i, err)
|
||||
}
|
||||
if !reflect.DeepEqual(result, expect) {
|
||||
t.Fatalf("Unexpected result for case %d", i)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetBlobsV2(t *testing.T) {
|
||||
n, api := newGetBlobEnv(t, 1)
|
||||
defer n.Close()
|
||||
|
||||
suites := []struct {
|
||||
start int
|
||||
limit int
|
||||
fillRandom bool
|
||||
}{
|
||||
{
|
||||
start: 0, limit: 1,
|
||||
},
|
||||
{
|
||||
start: 0, limit: 2,
|
||||
},
|
||||
{
|
||||
start: 1, limit: 3,
|
||||
},
|
||||
{
|
||||
start: 0, limit: 6,
|
||||
},
|
||||
{
|
||||
start: 1, limit: 5,
|
||||
},
|
||||
{
|
||||
start: 0, limit: 6, fillRandom: true,
|
||||
},
|
||||
}
|
||||
for i, suite := range suites {
|
||||
// Fill the request for retrieving blobs
|
||||
var (
|
||||
vhashes []common.Hash
|
||||
expect []*engine.BlobAndProofV2
|
||||
)
|
||||
// fill missing blob
|
||||
if suite.fillRandom {
|
||||
vhashes = append(vhashes, testrand.Hash())
|
||||
}
|
||||
for j := suite.start; j < suite.limit; j++ {
|
||||
vhashes = append(vhashes, testBlobVHashes[j])
|
||||
var cellProofs []hexutil.Bytes
|
||||
for _, proof := range testBlobCellProofs[j] {
|
||||
cellProofs = append(cellProofs, proof[:])
|
||||
}
|
||||
expect = append(expect, &engine.BlobAndProofV2{
|
||||
Blob: testBlobs[j][:],
|
||||
CellProofs: cellProofs,
|
||||
})
|
||||
}
|
||||
result, err := api.GetBlobsV2(vhashes)
|
||||
if err != nil {
|
||||
t.Errorf("Unexpected error for case %d, %v", i, err)
|
||||
}
|
||||
// null is responded if any blob is missing
|
||||
if suite.fillRandom {
|
||||
expect = nil
|
||||
}
|
||||
if !reflect.DeepEqual(result, expect) {
|
||||
t.Fatalf("Unexpected result for case %d", i)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -144,6 +144,9 @@ type Config struct {
|
|||
// Enables tracking of SHA3 preimages in the VM
|
||||
EnablePreimageRecording bool
|
||||
|
||||
// Enables tracking of state size
|
||||
EnableStateSizeTracking bool
|
||||
|
||||
// Enables VM tracing
|
||||
VMTrace string
|
||||
VMTraceJsonConfig string
|
||||
|
|
|
|||
|
|
@ -49,6 +49,7 @@ func (c Config) MarshalTOML() (interface{}, error) {
|
|||
BlobPool blobpool.Config
|
||||
GPO gasprice.Config
|
||||
EnablePreimageRecording bool
|
||||
EnableStateSizeTracking bool
|
||||
VMTrace string
|
||||
VMTraceJsonConfig string
|
||||
RPCGasCap uint64
|
||||
|
|
@ -90,6 +91,7 @@ func (c Config) MarshalTOML() (interface{}, error) {
|
|||
enc.BlobPool = c.BlobPool
|
||||
enc.GPO = c.GPO
|
||||
enc.EnablePreimageRecording = c.EnablePreimageRecording
|
||||
enc.EnableStateSizeTracking = c.EnableStateSizeTracking
|
||||
enc.VMTrace = c.VMTrace
|
||||
enc.VMTraceJsonConfig = c.VMTraceJsonConfig
|
||||
enc.RPCGasCap = c.RPCGasCap
|
||||
|
|
@ -135,6 +137,7 @@ func (c *Config) UnmarshalTOML(unmarshal func(interface{}) error) error {
|
|||
BlobPool *blobpool.Config
|
||||
GPO *gasprice.Config
|
||||
EnablePreimageRecording *bool
|
||||
EnableStateSizeTracking *bool
|
||||
VMTrace *string
|
||||
VMTraceJsonConfig *string
|
||||
RPCGasCap *uint64
|
||||
|
|
@ -243,6 +246,9 @@ func (c *Config) UnmarshalTOML(unmarshal func(interface{}) error) error {
|
|||
if dec.EnablePreimageRecording != nil {
|
||||
c.EnablePreimageRecording = *dec.EnablePreimageRecording
|
||||
}
|
||||
if dec.EnableStateSizeTracking != nil {
|
||||
c.EnableStateSizeTracking = *dec.EnableStateSizeTracking
|
||||
}
|
||||
if dec.VMTrace != nil {
|
||||
c.VMTrace = *dec.VMTrace
|
||||
}
|
||||
|
|
|
|||
124
eth/handler.go
124
eth/handler.go
|
|
@ -17,21 +17,22 @@
|
|||
package eth
|
||||
|
||||
import (
|
||||
"cmp"
|
||||
crand "crypto/rand"
|
||||
"errors"
|
||||
"maps"
|
||||
"math"
|
||||
"math/big"
|
||||
"slices"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"github.com/dchest/siphash"
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/core"
|
||||
"github.com/ethereum/go-ethereum/core/rawdb"
|
||||
"github.com/ethereum/go-ethereum/core/txpool"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/eth/downloader"
|
||||
"github.com/ethereum/go-ethereum/eth/ethconfig"
|
||||
"github.com/ethereum/go-ethereum/eth/fetcher"
|
||||
|
|
@ -119,9 +120,10 @@ type handler struct {
|
|||
chain *core.BlockChain
|
||||
maxPeers int
|
||||
|
||||
downloader *downloader.Downloader
|
||||
txFetcher *fetcher.TxFetcher
|
||||
peers *peerSet
|
||||
downloader *downloader.Downloader
|
||||
txFetcher *fetcher.TxFetcher
|
||||
peers *peerSet
|
||||
txBroadcastKey [16]byte
|
||||
|
||||
eventMux *event.TypeMux
|
||||
txsCh chan core.NewTxsEvent
|
||||
|
|
@ -153,6 +155,7 @@ func newHandler(config *handlerConfig) (*handler, error) {
|
|||
txpool: config.TxPool,
|
||||
chain: config.Chain,
|
||||
peers: newPeerSet(),
|
||||
txBroadcastKey: newBroadcastChoiceKey(),
|
||||
requiredBlocks: config.RequiredBlocks,
|
||||
quitSync: make(chan struct{}),
|
||||
handlerDoneCh: make(chan struct{}),
|
||||
|
|
@ -480,58 +483,40 @@ func (h *handler) BroadcastTransactions(txs types.Transactions) {
|
|||
|
||||
txset = make(map[*ethPeer][]common.Hash) // Set peer->hash to transfer directly
|
||||
annos = make(map[*ethPeer][]common.Hash) // Set peer->hash to announce
|
||||
)
|
||||
// Broadcast transactions to a batch of peers not knowing about it
|
||||
direct := big.NewInt(int64(math.Sqrt(float64(h.peers.len())))) // Approximate number of peers to broadcast to
|
||||
if direct.BitLen() == 0 {
|
||||
direct = big.NewInt(1)
|
||||
}
|
||||
total := new(big.Int).Exp(direct, big.NewInt(2), nil) // Stabilise total peer count a bit based on sqrt peers
|
||||
|
||||
var (
|
||||
signer = types.LatestSigner(h.chain.Config()) // Don't care about chain status, we just need *a* sender
|
||||
hasher = crypto.NewKeccakState()
|
||||
hash = make([]byte, 32)
|
||||
signer = types.LatestSigner(h.chain.Config())
|
||||
choice = newBroadcastChoice(h.nodeID, h.txBroadcastKey)
|
||||
peers = h.peers.all()
|
||||
)
|
||||
|
||||
for _, tx := range txs {
|
||||
var maybeDirect bool
|
||||
var directSet map[*ethPeer]struct{}
|
||||
switch {
|
||||
case tx.Type() == types.BlobTxType:
|
||||
blobTxs++
|
||||
case tx.Size() > txMaxBroadcastSize:
|
||||
largeTxs++
|
||||
default:
|
||||
maybeDirect = true
|
||||
// Get transaction sender address. Here we can ignore any error
|
||||
// since we're just interested in any value.
|
||||
txSender, _ := types.Sender(signer, tx)
|
||||
directSet = choice.choosePeers(peers, txSender)
|
||||
}
|
||||
// Send the transaction (if it's small enough) directly to a subset of
|
||||
// the peers that have not received it yet, ensuring that the flow of
|
||||
// transactions is grouped by account to (try and) avoid nonce gaps.
|
||||
//
|
||||
// To do this, we hash the local enode IW with together with a peer's
|
||||
// enode ID together with the transaction sender and broadcast if
|
||||
// `sha(self, peer, sender) mod peers < sqrt(peers)`.
|
||||
for _, peer := range h.peers.peersWithoutTransaction(tx.Hash()) {
|
||||
var broadcast bool
|
||||
if maybeDirect {
|
||||
hasher.Reset()
|
||||
hasher.Write(h.nodeID.Bytes())
|
||||
hasher.Write(peer.Node().ID().Bytes())
|
||||
|
||||
from, _ := types.Sender(signer, tx) // Ignore error, we only use the addr as a propagation target splitter
|
||||
hasher.Write(from.Bytes())
|
||||
|
||||
hasher.Read(hash)
|
||||
if new(big.Int).Mod(new(big.Int).SetBytes(hash), total).Cmp(direct) < 0 {
|
||||
broadcast = true
|
||||
}
|
||||
for _, peer := range peers {
|
||||
if peer.KnownTransaction(tx.Hash()) {
|
||||
continue
|
||||
}
|
||||
if broadcast {
|
||||
if _, ok := directSet[peer]; ok {
|
||||
// Send direct.
|
||||
txset[peer] = append(txset[peer], tx.Hash())
|
||||
} else {
|
||||
// Send announcement.
|
||||
annos[peer] = append(annos[peer], tx.Hash())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for peer, hashes := range txset {
|
||||
directCount += len(hashes)
|
||||
peer.AsyncSendTransactions(hashes)
|
||||
|
|
@ -594,7 +579,7 @@ func newBlockRangeState(chain *core.BlockChain, typeMux *event.TypeMux) *blockRa
|
|||
return st
|
||||
}
|
||||
|
||||
// blockRangeBroadcastLoop announces changes in locally-available block range to peers.
|
||||
// blockRangeLoop announces changes in locally-available block range to peers.
|
||||
// The range to announce is the range that is available in the store, so it's not just
|
||||
// about imported blocks.
|
||||
func (h *handler) blockRangeLoop(st *blockRangeState) {
|
||||
|
|
@ -696,3 +681,62 @@ func (st *blockRangeState) stop() {
|
|||
func (st *blockRangeState) currentRange() eth.BlockRangeUpdatePacket {
|
||||
return *st.next.Load()
|
||||
}
|
||||
|
||||
// broadcastChoice implements a deterministic random choice of peers. This is designed
|
||||
// specifically for choosing which peer receives a direct broadcast of a transaction.
|
||||
//
|
||||
// The choice is made based on the involved p2p node IDs and the transaction sender,
|
||||
// ensuring that the flow of transactions is grouped by account to (try and) avoid nonce
|
||||
// gaps.
|
||||
type broadcastChoice struct {
|
||||
self enode.ID
|
||||
key [16]byte
|
||||
buffer map[*ethPeer]struct{}
|
||||
tmp []broadcastPeer
|
||||
}
|
||||
|
||||
type broadcastPeer struct {
|
||||
p *ethPeer
|
||||
score uint64
|
||||
}
|
||||
|
||||
func newBroadcastChoiceKey() (k [16]byte) {
|
||||
crand.Read(k[:])
|
||||
return k
|
||||
}
|
||||
|
||||
func newBroadcastChoice(self enode.ID, key [16]byte) *broadcastChoice {
|
||||
return &broadcastChoice{
|
||||
self: self,
|
||||
key: key,
|
||||
buffer: make(map[*ethPeer]struct{}),
|
||||
}
|
||||
}
|
||||
|
||||
// choosePeers selects the peers that will receive a direct transaction broadcast message.
|
||||
// Note the return value will only stay valid until the next call to choosePeers.
|
||||
func (bc *broadcastChoice) choosePeers(peers []*ethPeer, txSender common.Address) map[*ethPeer]struct{} {
|
||||
// Compute randomized scores.
|
||||
bc.tmp = slices.Grow(bc.tmp[:0], len(peers))[:len(peers)]
|
||||
hash := siphash.New(bc.key[:])
|
||||
for i, peer := range peers {
|
||||
hash.Reset()
|
||||
hash.Write(bc.self[:])
|
||||
hash.Write(peer.Peer.Peer.ID().Bytes())
|
||||
hash.Write(txSender[:])
|
||||
bc.tmp[i] = broadcastPeer{peer, hash.Sum64()}
|
||||
}
|
||||
|
||||
// Sort by score.
|
||||
slices.SortFunc(bc.tmp, func(a, b broadcastPeer) int {
|
||||
return cmp.Compare(a.score, b.score)
|
||||
})
|
||||
|
||||
// Take top n.
|
||||
clear(bc.buffer)
|
||||
n := int(math.Ceil(math.Sqrt(float64(len(bc.tmp)))))
|
||||
for i := range n {
|
||||
bc.buffer[bc.tmp[i].p] = struct{}{}
|
||||
}
|
||||
return bc.buffer
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,9 +17,12 @@
|
|||
package eth
|
||||
|
||||
import (
|
||||
"maps"
|
||||
"math/big"
|
||||
"math/rand"
|
||||
"sort"
|
||||
"sync"
|
||||
"testing"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/consensus/ethash"
|
||||
|
|
@ -29,8 +32,11 @@ import (
|
|||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/eth/ethconfig"
|
||||
"github.com/ethereum/go-ethereum/eth/protocols/eth"
|
||||
"github.com/ethereum/go-ethereum/ethdb"
|
||||
"github.com/ethereum/go-ethereum/event"
|
||||
"github.com/ethereum/go-ethereum/p2p"
|
||||
"github.com/ethereum/go-ethereum/p2p/enode"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
"github.com/ethereum/go-ethereum/rlp"
|
||||
"github.com/holiman/uint256"
|
||||
|
|
@ -212,3 +218,102 @@ func (b *testHandler) close() {
|
|||
b.handler.Stop()
|
||||
b.chain.Stop()
|
||||
}
|
||||
|
||||
func TestBroadcastChoice(t *testing.T) {
|
||||
self := enode.HexID("1111111111111111111111111111111111111111111111111111111111111111")
|
||||
choice49 := newBroadcastChoice(self, [16]byte{1})
|
||||
choice50 := newBroadcastChoice(self, [16]byte{1})
|
||||
|
||||
// Create test peers and random tx sender addresses.
|
||||
rand := rand.New(rand.NewSource(33))
|
||||
txsenders := make([]common.Address, 400)
|
||||
for i := range txsenders {
|
||||
rand.Read(txsenders[i][:])
|
||||
}
|
||||
peers := createTestPeers(rand, 50)
|
||||
defer closePeers(peers)
|
||||
|
||||
// Evaluate choice49 first.
|
||||
expectedCount := 7 // sqrt(49)
|
||||
var chosen49 = make([]map[*ethPeer]struct{}, len(txsenders))
|
||||
for i, txSender := range txsenders {
|
||||
set := choice49.choosePeers(peers[:49], txSender)
|
||||
chosen49[i] = maps.Clone(set)
|
||||
|
||||
// Sanity check choices. Here we check that the function selects different peers
|
||||
// for different transaction senders.
|
||||
if len(set) != expectedCount {
|
||||
t.Fatalf("choice49 produced wrong count %d, want %d", len(set), expectedCount)
|
||||
}
|
||||
if i > 0 && maps.Equal(set, chosen49[i-1]) {
|
||||
t.Errorf("choice49 for tx %d is equal to tx %d", i, i-1)
|
||||
}
|
||||
}
|
||||
|
||||
// Evaluate choice50 for the same peers and transactions. It should always yield more
|
||||
// peers than choice49, and the chosen set should be a superset of choice49's.
|
||||
for i, txSender := range txsenders {
|
||||
set := choice50.choosePeers(peers[:50], txSender)
|
||||
if len(set) < len(chosen49[i]) {
|
||||
t.Errorf("for tx %d, choice50 has less peers than choice49", i)
|
||||
}
|
||||
for p := range chosen49[i] {
|
||||
if _, ok := set[p]; !ok {
|
||||
t.Errorf("for tx %d, choice50 did not choose peer %v, but choice49 did", i, p.ID())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkBroadcastChoice(b *testing.B) {
|
||||
b.Run("50", func(b *testing.B) {
|
||||
benchmarkBroadcastChoice(b, 50)
|
||||
})
|
||||
b.Run("200", func(b *testing.B) {
|
||||
benchmarkBroadcastChoice(b, 200)
|
||||
})
|
||||
b.Run("500", func(b *testing.B) {
|
||||
benchmarkBroadcastChoice(b, 500)
|
||||
})
|
||||
}
|
||||
|
||||
// This measures the overhead of sending one transaction to N peers.
|
||||
func benchmarkBroadcastChoice(b *testing.B, npeers int) {
|
||||
rand := rand.New(rand.NewSource(33))
|
||||
peers := createTestPeers(rand, npeers)
|
||||
defer closePeers(peers)
|
||||
|
||||
txsenders := make([]common.Address, b.N)
|
||||
for i := range txsenders {
|
||||
rand.Read(txsenders[i][:])
|
||||
}
|
||||
|
||||
self := enode.HexID("1111111111111111111111111111111111111111111111111111111111111111")
|
||||
choice := newBroadcastChoice(self, [16]byte{1})
|
||||
|
||||
b.ResetTimer()
|
||||
for i := range b.N {
|
||||
set := choice.choosePeers(peers, txsenders[i])
|
||||
if len(set) == 0 {
|
||||
b.Fatal("empty result")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func createTestPeers(rand *rand.Rand, n int) []*ethPeer {
|
||||
peers := make([]*ethPeer, n)
|
||||
for i := range peers {
|
||||
var id enode.ID
|
||||
rand.Read(id[:])
|
||||
p2pPeer := p2p.NewPeer(id, "test", nil)
|
||||
ep := eth.NewPeer(eth.ETH69, p2pPeer, nil, nil)
|
||||
peers[i] = ðPeer{Peer: ep}
|
||||
}
|
||||
return peers
|
||||
}
|
||||
|
||||
func closePeers(peers []*ethPeer) {
|
||||
for _, p := range peers {
|
||||
p.Close()
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,9 +19,10 @@ package eth
|
|||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"maps"
|
||||
"slices"
|
||||
"sync"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/eth/protocols/eth"
|
||||
"github.com/ethereum/go-ethereum/eth/protocols/snap"
|
||||
"github.com/ethereum/go-ethereum/p2p"
|
||||
|
|
@ -191,19 +192,12 @@ func (ps *peerSet) peer(id string) *ethPeer {
|
|||
return ps.peers[id]
|
||||
}
|
||||
|
||||
// peersWithoutTransaction retrieves a list of peers that do not have a given
|
||||
// transaction in their set of known hashes.
|
||||
func (ps *peerSet) peersWithoutTransaction(hash common.Hash) []*ethPeer {
|
||||
// all returns all current peers.
|
||||
func (ps *peerSet) all() []*ethPeer {
|
||||
ps.lock.RLock()
|
||||
defer ps.lock.RUnlock()
|
||||
|
||||
list := make([]*ethPeer, 0, len(ps.peers))
|
||||
for _, p := range ps.peers {
|
||||
if !p.KnownTransaction(hash) {
|
||||
list = append(list, p)
|
||||
}
|
||||
}
|
||||
return list
|
||||
return slices.Collect(maps.Values(ps.peers))
|
||||
}
|
||||
|
||||
// len returns if the current number of `eth` peers in the set. Since the `snap`
|
||||
|
|
|
|||
|
|
@ -88,9 +88,17 @@ func testSnapSyncDisabling(t *testing.T, ethVer uint, snapVer uint) {
|
|||
if err := empty.handler.downloader.BeaconSync(ethconfig.SnapSync, full.chain.CurrentBlock(), nil); err != nil {
|
||||
t.Fatal("sync failed:", err)
|
||||
}
|
||||
time.Sleep(time.Second * 5) // Downloader internally has to wait a timer (3s) to be expired before exiting
|
||||
|
||||
if empty.handler.snapSync.Load() {
|
||||
t.Fatalf("snap sync not disabled after successful synchronisation")
|
||||
// Downloader internally has to wait for a timer (3s) to be expired before
|
||||
// exiting. Poll after to determine if sync is disabled.
|
||||
time.Sleep(time.Second * 3)
|
||||
for timeout := time.After(time.Second); ; {
|
||||
select {
|
||||
case <-timeout:
|
||||
t.Fatalf("snap sync not disabled after successful synchronisation")
|
||||
case <-time.After(100 * time.Millisecond):
|
||||
if !empty.handler.snapSync.Load() {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -89,6 +89,7 @@ func (s *Syncer) run() {
|
|||
target *types.Header
|
||||
ticker = time.NewTicker(time.Second * 5)
|
||||
)
|
||||
defer ticker.Stop()
|
||||
for {
|
||||
select {
|
||||
case req := <-s.request:
|
||||
|
|
|
|||
|
|
@ -527,7 +527,7 @@ func TestTraceTransaction(t *testing.T) {
|
|||
b.AddTx(tx)
|
||||
target = tx.Hash()
|
||||
})
|
||||
defer backend.chain.Stop()
|
||||
defer backend.teardown()
|
||||
api := NewAPI(backend)
|
||||
result, err := api.TraceTransaction(context.Background(), target, nil)
|
||||
if err != nil {
|
||||
|
|
@ -584,7 +584,7 @@ func TestTraceBlock(t *testing.T) {
|
|||
b.AddTx(tx)
|
||||
txHash = tx.Hash()
|
||||
})
|
||||
defer backend.chain.Stop()
|
||||
defer backend.teardown()
|
||||
api := NewAPI(backend)
|
||||
|
||||
var testSuite = []struct {
|
||||
|
|
@ -681,7 +681,7 @@ func TestTracingWithOverrides(t *testing.T) {
|
|||
signer, accounts[0].key)
|
||||
b.AddTx(tx)
|
||||
})
|
||||
defer backend.chain.Stop()
|
||||
defer backend.teardown()
|
||||
api := NewAPI(backend)
|
||||
randomAccounts := newAccounts(3)
|
||||
type res struct {
|
||||
|
|
@ -1105,6 +1105,7 @@ func TestTraceChain(t *testing.T) {
|
|||
nonce += 1
|
||||
}
|
||||
})
|
||||
defer backend.teardown()
|
||||
backend.refHook = func() { ref.Add(1) }
|
||||
backend.relHook = func() { rel.Add(1) }
|
||||
api := NewAPI(backend)
|
||||
|
|
@ -1212,7 +1213,7 @@ func TestTraceBlockWithBasefee(t *testing.T) {
|
|||
txHash = tx.Hash()
|
||||
baseFee.Set(b.BaseFee())
|
||||
})
|
||||
defer backend.chain.Stop()
|
||||
defer backend.teardown()
|
||||
api := NewAPI(backend)
|
||||
|
||||
var testSuite = []struct {
|
||||
|
|
@ -1298,7 +1299,7 @@ func TestStandardTraceBlockToFile(t *testing.T) {
|
|||
b.AddTx(tx)
|
||||
txHashs = append(txHashs, tx.Hash())
|
||||
})
|
||||
defer backend.chain.Stop()
|
||||
defer backend.teardown()
|
||||
|
||||
var testSuite = []struct {
|
||||
blockNumber rpc.BlockNumber
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue