mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-07-23 13:16:42 +00:00
commit
b8ea5f66d0
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://goreportcard.com/report/github.com/ethereum/go-ethereum)
|
||||||
[](https://app.travis-ci.com/github/ethereum/go-ethereum)
|
[](https://app.travis-ci.com/github/ethereum/go-ethereum)
|
||||||
[](https://discord.gg/nthXNEv)
|
[](https://discord.gg/nthXNEv)
|
||||||
|
[](https://x.com/go_ethereum)
|
||||||
|
|
||||||
Automated builds are available for stable releases and the unstable master branch. Binary
|
Automated builds are available for stable releases and the unstable master branch. Binary
|
||||||
archives are published at https://geth.ethereum.org/downloads/.
|
archives are published at https://geth.ethereum.org/downloads/.
|
||||||
|
|
|
||||||
|
|
@ -183,7 +183,7 @@ var (
|
||||||
// Solidity: {{.Original.String}}
|
// Solidity: {{.Original.String}}
|
||||||
func ({{ decapitalise $contract.Type}} *{{$contract.Type}}) Unpack{{.Normalized.Name}}Event(log *types.Log) (*{{$contract.Type}}{{.Normalized.Name}}, error) {
|
func ({{ decapitalise $contract.Type}} *{{$contract.Type}}) Unpack{{.Normalized.Name}}Event(log *types.Log) (*{{$contract.Type}}{{.Normalized.Name}}, error) {
|
||||||
event := "{{.Original.Name}}"
|
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")
|
return nil, errors.New("event signature mismatch")
|
||||||
}
|
}
|
||||||
out := new({{$contract.Type}}{{.Normalized.Name}})
|
out := new({{$contract.Type}}{{.Normalized.Name}})
|
||||||
|
|
|
||||||
|
|
@ -360,7 +360,7 @@ func (CrowdsaleFundTransfer) ContractEventName() string {
|
||||||
// Solidity: event FundTransfer(address backer, uint256 amount, bool isContribution)
|
// Solidity: event FundTransfer(address backer, uint256 amount, bool isContribution)
|
||||||
func (crowdsale *Crowdsale) UnpackFundTransferEvent(log *types.Log) (*CrowdsaleFundTransfer, error) {
|
func (crowdsale *Crowdsale) UnpackFundTransferEvent(log *types.Log) (*CrowdsaleFundTransfer, error) {
|
||||||
event := "FundTransfer"
|
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")
|
return nil, errors.New("event signature mismatch")
|
||||||
}
|
}
|
||||||
out := new(CrowdsaleFundTransfer)
|
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)
|
// Solidity: event ChangeOfRules(uint256 minimumQuorum, uint256 debatingPeriodInMinutes, int256 majorityMargin)
|
||||||
func (dAO *DAO) UnpackChangeOfRulesEvent(log *types.Log) (*DAOChangeOfRules, error) {
|
func (dAO *DAO) UnpackChangeOfRulesEvent(log *types.Log) (*DAOChangeOfRules, error) {
|
||||||
event := "ChangeOfRules"
|
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")
|
return nil, errors.New("event signature mismatch")
|
||||||
}
|
}
|
||||||
out := new(DAOChangeOfRules)
|
out := new(DAOChangeOfRules)
|
||||||
|
|
@ -648,7 +648,7 @@ func (DAOMembershipChanged) ContractEventName() string {
|
||||||
// Solidity: event MembershipChanged(address member, bool isMember)
|
// Solidity: event MembershipChanged(address member, bool isMember)
|
||||||
func (dAO *DAO) UnpackMembershipChangedEvent(log *types.Log) (*DAOMembershipChanged, error) {
|
func (dAO *DAO) UnpackMembershipChangedEvent(log *types.Log) (*DAOMembershipChanged, error) {
|
||||||
event := "MembershipChanged"
|
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")
|
return nil, errors.New("event signature mismatch")
|
||||||
}
|
}
|
||||||
out := new(DAOMembershipChanged)
|
out := new(DAOMembershipChanged)
|
||||||
|
|
@ -692,7 +692,7 @@ func (DAOProposalAdded) ContractEventName() string {
|
||||||
// Solidity: event ProposalAdded(uint256 proposalID, address recipient, uint256 amount, string description)
|
// Solidity: event ProposalAdded(uint256 proposalID, address recipient, uint256 amount, string description)
|
||||||
func (dAO *DAO) UnpackProposalAddedEvent(log *types.Log) (*DAOProposalAdded, error) {
|
func (dAO *DAO) UnpackProposalAddedEvent(log *types.Log) (*DAOProposalAdded, error) {
|
||||||
event := "ProposalAdded"
|
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")
|
return nil, errors.New("event signature mismatch")
|
||||||
}
|
}
|
||||||
out := new(DAOProposalAdded)
|
out := new(DAOProposalAdded)
|
||||||
|
|
@ -736,7 +736,7 @@ func (DAOProposalTallied) ContractEventName() string {
|
||||||
// Solidity: event ProposalTallied(uint256 proposalID, int256 result, uint256 quorum, bool active)
|
// Solidity: event ProposalTallied(uint256 proposalID, int256 result, uint256 quorum, bool active)
|
||||||
func (dAO *DAO) UnpackProposalTalliedEvent(log *types.Log) (*DAOProposalTallied, error) {
|
func (dAO *DAO) UnpackProposalTalliedEvent(log *types.Log) (*DAOProposalTallied, error) {
|
||||||
event := "ProposalTallied"
|
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")
|
return nil, errors.New("event signature mismatch")
|
||||||
}
|
}
|
||||||
out := new(DAOProposalTallied)
|
out := new(DAOProposalTallied)
|
||||||
|
|
@ -780,7 +780,7 @@ func (DAOVoted) ContractEventName() string {
|
||||||
// Solidity: event Voted(uint256 proposalID, bool position, address voter, string justification)
|
// Solidity: event Voted(uint256 proposalID, bool position, address voter, string justification)
|
||||||
func (dAO *DAO) UnpackVotedEvent(log *types.Log) (*DAOVoted, error) {
|
func (dAO *DAO) UnpackVotedEvent(log *types.Log) (*DAOVoted, error) {
|
||||||
event := "Voted"
|
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")
|
return nil, errors.New("event signature mismatch")
|
||||||
}
|
}
|
||||||
out := new(DAOVoted)
|
out := new(DAOVoted)
|
||||||
|
|
|
||||||
|
|
@ -72,7 +72,7 @@ func (EventCheckerDynamic) ContractEventName() string {
|
||||||
// Solidity: event dynamic(string indexed idxStr, bytes indexed idxDat, string str, bytes dat)
|
// Solidity: event dynamic(string indexed idxStr, bytes indexed idxDat, string str, bytes dat)
|
||||||
func (eventChecker *EventChecker) UnpackDynamicEvent(log *types.Log) (*EventCheckerDynamic, error) {
|
func (eventChecker *EventChecker) UnpackDynamicEvent(log *types.Log) (*EventCheckerDynamic, error) {
|
||||||
event := "dynamic"
|
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")
|
return nil, errors.New("event signature mismatch")
|
||||||
}
|
}
|
||||||
out := new(EventCheckerDynamic)
|
out := new(EventCheckerDynamic)
|
||||||
|
|
@ -112,7 +112,7 @@ func (EventCheckerEmpty) ContractEventName() string {
|
||||||
// Solidity: event empty()
|
// Solidity: event empty()
|
||||||
func (eventChecker *EventChecker) UnpackEmptyEvent(log *types.Log) (*EventCheckerEmpty, error) {
|
func (eventChecker *EventChecker) UnpackEmptyEvent(log *types.Log) (*EventCheckerEmpty, error) {
|
||||||
event := "empty"
|
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")
|
return nil, errors.New("event signature mismatch")
|
||||||
}
|
}
|
||||||
out := new(EventCheckerEmpty)
|
out := new(EventCheckerEmpty)
|
||||||
|
|
@ -154,7 +154,7 @@ func (EventCheckerIndexed) ContractEventName() string {
|
||||||
// Solidity: event indexed(address indexed addr, int256 indexed num)
|
// Solidity: event indexed(address indexed addr, int256 indexed num)
|
||||||
func (eventChecker *EventChecker) UnpackIndexedEvent(log *types.Log) (*EventCheckerIndexed, error) {
|
func (eventChecker *EventChecker) UnpackIndexedEvent(log *types.Log) (*EventCheckerIndexed, error) {
|
||||||
event := "indexed"
|
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")
|
return nil, errors.New("event signature mismatch")
|
||||||
}
|
}
|
||||||
out := new(EventCheckerIndexed)
|
out := new(EventCheckerIndexed)
|
||||||
|
|
@ -196,7 +196,7 @@ func (EventCheckerMixed) ContractEventName() string {
|
||||||
// Solidity: event mixed(address indexed addr, int256 num)
|
// Solidity: event mixed(address indexed addr, int256 num)
|
||||||
func (eventChecker *EventChecker) UnpackMixedEvent(log *types.Log) (*EventCheckerMixed, error) {
|
func (eventChecker *EventChecker) UnpackMixedEvent(log *types.Log) (*EventCheckerMixed, error) {
|
||||||
event := "mixed"
|
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")
|
return nil, errors.New("event signature mismatch")
|
||||||
}
|
}
|
||||||
out := new(EventCheckerMixed)
|
out := new(EventCheckerMixed)
|
||||||
|
|
@ -238,7 +238,7 @@ func (EventCheckerUnnamed) ContractEventName() string {
|
||||||
// Solidity: event unnamed(uint256 indexed arg0, uint256 indexed arg1)
|
// Solidity: event unnamed(uint256 indexed arg0, uint256 indexed arg1)
|
||||||
func (eventChecker *EventChecker) UnpackUnnamedEvent(log *types.Log) (*EventCheckerUnnamed, error) {
|
func (eventChecker *EventChecker) UnpackUnnamedEvent(log *types.Log) (*EventCheckerUnnamed, error) {
|
||||||
event := "unnamed"
|
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")
|
return nil, errors.New("event signature mismatch")
|
||||||
}
|
}
|
||||||
out := new(EventCheckerUnnamed)
|
out := new(EventCheckerUnnamed)
|
||||||
|
|
|
||||||
|
|
@ -134,7 +134,7 @@ func (NameConflictLog) ContractEventName() string {
|
||||||
// Solidity: event log(int256 msg, int256 _msg)
|
// Solidity: event log(int256 msg, int256 _msg)
|
||||||
func (nameConflict *NameConflict) UnpackLogEvent(log *types.Log) (*NameConflictLog, error) {
|
func (nameConflict *NameConflict) UnpackLogEvent(log *types.Log) (*NameConflictLog, error) {
|
||||||
event := "log"
|
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")
|
return nil, errors.New("event signature mismatch")
|
||||||
}
|
}
|
||||||
out := new(NameConflictLog)
|
out := new(NameConflictLog)
|
||||||
|
|
|
||||||
|
|
@ -136,7 +136,7 @@ func (NumericMethodNameE1TestEvent) ContractEventName() string {
|
||||||
// Solidity: event _1TestEvent(address _param)
|
// Solidity: event _1TestEvent(address _param)
|
||||||
func (numericMethodName *NumericMethodName) UnpackE1TestEventEvent(log *types.Log) (*NumericMethodNameE1TestEvent, error) {
|
func (numericMethodName *NumericMethodName) UnpackE1TestEventEvent(log *types.Log) (*NumericMethodNameE1TestEvent, error) {
|
||||||
event := "_1TestEvent"
|
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")
|
return nil, errors.New("event signature mismatch")
|
||||||
}
|
}
|
||||||
out := new(NumericMethodNameE1TestEvent)
|
out := new(NumericMethodNameE1TestEvent)
|
||||||
|
|
|
||||||
|
|
@ -114,7 +114,7 @@ func (OverloadBar) ContractEventName() string {
|
||||||
// Solidity: event bar(uint256 i)
|
// Solidity: event bar(uint256 i)
|
||||||
func (overload *Overload) UnpackBarEvent(log *types.Log) (*OverloadBar, error) {
|
func (overload *Overload) UnpackBarEvent(log *types.Log) (*OverloadBar, error) {
|
||||||
event := "bar"
|
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")
|
return nil, errors.New("event signature mismatch")
|
||||||
}
|
}
|
||||||
out := new(OverloadBar)
|
out := new(OverloadBar)
|
||||||
|
|
@ -156,7 +156,7 @@ func (OverloadBar0) ContractEventName() string {
|
||||||
// Solidity: event bar(uint256 i, uint256 j)
|
// Solidity: event bar(uint256 i, uint256 j)
|
||||||
func (overload *Overload) UnpackBar0Event(log *types.Log) (*OverloadBar0, error) {
|
func (overload *Overload) UnpackBar0Event(log *types.Log) (*OverloadBar0, error) {
|
||||||
event := "bar0"
|
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")
|
return nil, errors.New("event signature mismatch")
|
||||||
}
|
}
|
||||||
out := new(OverloadBar0)
|
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)
|
// Solidity: event Transfer(address indexed from, address indexed to, uint256 value)
|
||||||
func (token *Token) UnpackTransferEvent(log *types.Log) (*TokenTransfer, error) {
|
func (token *Token) UnpackTransferEvent(log *types.Log) (*TokenTransfer, error) {
|
||||||
event := "Transfer"
|
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")
|
return nil, errors.New("event signature mismatch")
|
||||||
}
|
}
|
||||||
out := new(TokenTransfer)
|
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)
|
// 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) {
|
func (tuple *Tuple) UnpackTupleEventEvent(log *types.Log) (*TupleTupleEvent, error) {
|
||||||
event := "TupleEvent"
|
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")
|
return nil, errors.New("event signature mismatch")
|
||||||
}
|
}
|
||||||
out := new(TupleTupleEvent)
|
out := new(TupleTupleEvent)
|
||||||
|
|
@ -234,7 +234,7 @@ func (TupleTupleEvent2) ContractEventName() string {
|
||||||
// Solidity: event TupleEvent2((uint8,uint8)[] arg0)
|
// Solidity: event TupleEvent2((uint8,uint8)[] arg0)
|
||||||
func (tuple *Tuple) UnpackTupleEvent2Event(log *types.Log) (*TupleTupleEvent2, error) {
|
func (tuple *Tuple) UnpackTupleEvent2Event(log *types.Log) (*TupleTupleEvent2, error) {
|
||||||
event := "TupleEvent2"
|
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")
|
return nil, errors.New("event signature mismatch")
|
||||||
}
|
}
|
||||||
out := new(TupleTupleEvent2)
|
out := new(TupleTupleEvent2)
|
||||||
|
|
|
||||||
|
|
@ -276,7 +276,7 @@ func (DBInsert) ContractEventName() string {
|
||||||
// Solidity: event Insert(uint256 key, uint256 value, uint256 length)
|
// Solidity: event Insert(uint256 key, uint256 value, uint256 length)
|
||||||
func (dB *DB) UnpackInsertEvent(log *types.Log) (*DBInsert, error) {
|
func (dB *DB) UnpackInsertEvent(log *types.Log) (*DBInsert, error) {
|
||||||
event := "Insert"
|
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")
|
return nil, errors.New("event signature mismatch")
|
||||||
}
|
}
|
||||||
out := new(DBInsert)
|
out := new(DBInsert)
|
||||||
|
|
@ -318,7 +318,7 @@ func (DBKeyedInsert) ContractEventName() string {
|
||||||
// Solidity: event KeyedInsert(uint256 indexed key, uint256 value)
|
// Solidity: event KeyedInsert(uint256 indexed key, uint256 value)
|
||||||
func (dB *DB) UnpackKeyedInsertEvent(log *types.Log) (*DBKeyedInsert, error) {
|
func (dB *DB) UnpackKeyedInsertEvent(log *types.Log) (*DBKeyedInsert, error) {
|
||||||
event := "KeyedInsert"
|
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")
|
return nil, errors.New("event signature mismatch")
|
||||||
}
|
}
|
||||||
out := new(DBKeyedInsert)
|
out := new(DBKeyedInsert)
|
||||||
|
|
|
||||||
|
|
@ -115,7 +115,7 @@ func (CBasic1) ContractEventName() string {
|
||||||
// Solidity: event basic1(uint256 indexed id, uint256 data)
|
// Solidity: event basic1(uint256 indexed id, uint256 data)
|
||||||
func (c *C) UnpackBasic1Event(log *types.Log) (*CBasic1, error) {
|
func (c *C) UnpackBasic1Event(log *types.Log) (*CBasic1, error) {
|
||||||
event := "basic1"
|
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")
|
return nil, errors.New("event signature mismatch")
|
||||||
}
|
}
|
||||||
out := new(CBasic1)
|
out := new(CBasic1)
|
||||||
|
|
@ -157,7 +157,7 @@ func (CBasic2) ContractEventName() string {
|
||||||
// Solidity: event basic2(bool indexed flag, uint256 data)
|
// Solidity: event basic2(bool indexed flag, uint256 data)
|
||||||
func (c *C) UnpackBasic2Event(log *types.Log) (*CBasic2, error) {
|
func (c *C) UnpackBasic2Event(log *types.Log) (*CBasic2, error) {
|
||||||
event := "basic2"
|
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")
|
return nil, errors.New("event signature mismatch")
|
||||||
}
|
}
|
||||||
out := new(CBasic2)
|
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")
|
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
|
continue
|
||||||
}
|
}
|
||||||
pairing := w.Hub.pairing(w)
|
pairing := w.Hub.pairing(w)
|
||||||
|
if pairing == nil {
|
||||||
|
w.lock.Unlock()
|
||||||
|
reqc <- struct{}{}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
// Device lock obtained, derive the next batch of accounts
|
// Device lock obtained, derive the next batch of accounts
|
||||||
var (
|
var (
|
||||||
|
|
@ -631,13 +636,13 @@ func (w *Wallet) Derive(path accounts.DerivationPath, pin bool) (accounts.Accoun
|
||||||
}
|
}
|
||||||
|
|
||||||
if pin {
|
if pin {
|
||||||
pairing := w.Hub.pairing(w)
|
if pairing := w.Hub.pairing(w); pairing != nil {
|
||||||
pairing.Accounts[account.Address] = path
|
pairing.Accounts[account.Address] = path
|
||||||
if err := w.Hub.setPairing(w, pairing); err != nil {
|
if err := w.Hub.setPairing(w, pairing); err != nil {
|
||||||
return accounts.Account{}, err
|
return accounts.Account{}, err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return account, nil
|
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
|
// 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.
|
// not found, attempts to parse the derivation path from the account's URL.
|
||||||
func (w *Wallet) findAccountPath(account accounts.Account) (accounts.DerivationPath, error) {
|
func (w *Wallet) findAccountPath(account accounts.Account) (accounts.DerivationPath, error) {
|
||||||
pairing := w.Hub.pairing(w)
|
if pairing := w.Hub.pairing(w); pairing != nil {
|
||||||
if path, ok := pairing.Accounts[account.Address]; ok {
|
if path, ok := pairing.Accounts[account.Address]; ok {
|
||||||
return path, nil
|
return path, nil
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Look for the path in the URL
|
// Look for the path in the URL
|
||||||
if account.URL.Scheme != w.Hub.scheme {
|
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)
|
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
|
return common.Address{}, nil, accounts.ErrWalletClosed
|
||||||
}
|
}
|
||||||
// Ensure the wallet is capable of signing the given transaction
|
// 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
|
//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])
|
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()
|
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
|
// 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
|
// module that originally initiated the request. It also ensures that no events
|
||||||
// related to a server are returned before EvRegistered or after EvUnregistered.
|
// related to a server are returned before EvRegistered or after EvUnregistered.
|
||||||
|
|
|
||||||
|
|
@ -124,6 +124,7 @@ var (
|
||||||
"jammy", // 22.04, EOL: 04/2032
|
"jammy", // 22.04, EOL: 04/2032
|
||||||
"noble", // 24.04, EOL: 04/2034
|
"noble", // 24.04, EOL: 04/2034
|
||||||
"oracular", // 24.10, EOL: 07/2025
|
"oracular", // 24.10, EOL: 07/2025
|
||||||
|
"plucky", // 25.04, EOL: 01/2026
|
||||||
}
|
}
|
||||||
|
|
||||||
// This is where the tests should be unpacked.
|
// 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.MetricsInfluxDBTokenFlag,
|
||||||
utils.MetricsInfluxDBBucketFlag,
|
utils.MetricsInfluxDBBucketFlag,
|
||||||
utils.MetricsInfluxDBOrganizationFlag,
|
utils.MetricsInfluxDBOrganizationFlag,
|
||||||
|
utils.StateSizeTrackingFlag,
|
||||||
utils.TxLookupLimitFlag,
|
utils.TxLookupLimitFlag,
|
||||||
utils.VMTraceFlag,
|
utils.VMTraceFlag,
|
||||||
utils.VMTraceJsonConfigFlag,
|
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
|
// 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
|
// https://github.com/ethereum/go-ethereum/pull/25523 for why the triedb cannot be
|
||||||
// re-created when using PBSS.
|
// 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()
|
defer triedb.Close()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -651,7 +652,7 @@ func dump(ctx *cli.Context) error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
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()
|
defer triedb.Close()
|
||||||
|
|
||||||
state, err := state.New(root, state.NewDatabase(triedb, nil))
|
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)
|
db := utils.MakeChainDatabase(ctx, stack, true)
|
||||||
defer db.Close()
|
defer db.Close()
|
||||||
|
|
||||||
triedb := utils.MakeTrieDatabase(ctx, db, false, true, false)
|
triedb := utils.MakeTrieDatabase(ctx, stack, db, false, true, false)
|
||||||
defer triedb.Close()
|
defer triedb.Close()
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
|
@ -859,7 +859,7 @@ func inspectHistory(ctx *cli.Context) error {
|
||||||
db := utils.MakeChainDatabase(ctx, stack, true)
|
db := utils.MakeChainDatabase(ctx, stack, true)
|
||||||
defer db.Close()
|
defer db.Close()
|
||||||
|
|
||||||
triedb := utils.MakeTrieDatabase(ctx, db, false, false, false)
|
triedb := utils.MakeTrieDatabase(ctx, stack, db, false, false, false)
|
||||||
defer triedb.Close()
|
defer triedb.Close()
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
|
|
||||||
|
|
@ -200,6 +200,7 @@ var (
|
||||||
utils.MetricsInfluxDBTokenFlag,
|
utils.MetricsInfluxDBTokenFlag,
|
||||||
utils.MetricsInfluxDBBucketFlag,
|
utils.MetricsInfluxDBBucketFlag,
|
||||||
utils.MetricsInfluxDBOrganizationFlag,
|
utils.MetricsInfluxDBOrganizationFlag,
|
||||||
|
utils.StateSizeTrackingFlag,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -217,7 +217,7 @@ func verifyState(ctx *cli.Context) error {
|
||||||
log.Error("Failed to load head block")
|
log.Error("Failed to load head block")
|
||||||
return errors.New("no 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()
|
defer triedb.Close()
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
|
@ -282,7 +282,7 @@ func traverseState(ctx *cli.Context) error {
|
||||||
chaindb := utils.MakeChainDatabase(ctx, stack, true)
|
chaindb := utils.MakeChainDatabase(ctx, stack, true)
|
||||||
defer chaindb.Close()
|
defer chaindb.Close()
|
||||||
|
|
||||||
triedb := utils.MakeTrieDatabase(ctx, chaindb, false, true, false)
|
triedb := utils.MakeTrieDatabase(ctx, stack, chaindb, false, true, false)
|
||||||
defer triedb.Close()
|
defer triedb.Close()
|
||||||
|
|
||||||
headBlock := rawdb.ReadHeadBlock(chaindb)
|
headBlock := rawdb.ReadHeadBlock(chaindb)
|
||||||
|
|
@ -391,7 +391,7 @@ func traverseRawState(ctx *cli.Context) error {
|
||||||
chaindb := utils.MakeChainDatabase(ctx, stack, true)
|
chaindb := utils.MakeChainDatabase(ctx, stack, true)
|
||||||
defer chaindb.Close()
|
defer chaindb.Close()
|
||||||
|
|
||||||
triedb := utils.MakeTrieDatabase(ctx, chaindb, false, true, false)
|
triedb := utils.MakeTrieDatabase(ctx, stack, chaindb, false, true, false)
|
||||||
defer triedb.Close()
|
defer triedb.Close()
|
||||||
|
|
||||||
headBlock := rawdb.ReadHeadBlock(chaindb)
|
headBlock := rawdb.ReadHeadBlock(chaindb)
|
||||||
|
|
@ -558,7 +558,7 @@ func dumpState(ctx *cli.Context) error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
triedb := utils.MakeTrieDatabase(ctx, db, false, true, false)
|
triedb := utils.MakeTrieDatabase(ctx, stack, db, false, true, false)
|
||||||
defer triedb.Close()
|
defer triedb.Close()
|
||||||
|
|
||||||
snapConfig := snapshot.Config{
|
snapConfig := snapshot.Config{
|
||||||
|
|
@ -640,7 +640,7 @@ func snapshotExportPreimages(ctx *cli.Context) error {
|
||||||
chaindb := utils.MakeChainDatabase(ctx, stack, true)
|
chaindb := utils.MakeChainDatabase(ctx, stack, true)
|
||||||
defer chaindb.Close()
|
defer chaindb.Close()
|
||||||
|
|
||||||
triedb := utils.MakeTrieDatabase(ctx, chaindb, false, true, false)
|
triedb := utils.MakeTrieDatabase(ctx, stack, chaindb, false, true, false)
|
||||||
defer triedb.Close()
|
defer triedb.Close()
|
||||||
|
|
||||||
var root common.Hash
|
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.
|
// transmit the same tx but with correct sidecar from the good peer.
|
||||||
|
|
||||||
var req *eth.GetPooledTransactionsPacket
|
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 {
|
if err != nil {
|
||||||
errc <- fmt.Errorf("reading pooled tx request failed: %v", err)
|
errc <- fmt.Errorf("reading pooled tx request failed: %v", err)
|
||||||
return
|
return
|
||||||
|
|
|
||||||
|
|
@ -425,7 +425,7 @@ func MakePreState(db ethdb.Database, accounts types.GenesisAlloc) *state.StateDB
|
||||||
sdb := state.NewDatabase(tdb, nil)
|
sdb := state.NewDatabase(tdb, nil)
|
||||||
statedb, _ := state.New(types.EmptyRootHash, sdb)
|
statedb, _ := state.New(types.EmptyRootHash, sdb)
|
||||||
for addr, a := range accounts {
|
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.SetNonce(addr, a.Nonce, tracing.NonceChangeGenesis)
|
||||||
statedb.SetBalance(addr, uint256.MustFromBig(a.Balance), tracing.BalanceIncreaseGenesisBalance)
|
statedb.SetBalance(addr, uint256.MustFromBig(a.Balance), tracing.BalanceIncreaseGenesisBalance)
|
||||||
for k, v := range a.Storage {
|
for k, v := range a.Storage {
|
||||||
|
|
|
||||||
|
|
@ -322,7 +322,7 @@ func runCmd(ctx *cli.Context) error {
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if len(code) > 0 {
|
if len(code) > 0 {
|
||||||
prestate.SetCode(receiver, code)
|
prestate.SetCode(receiver, code, tracing.CodeChangeUnspecified)
|
||||||
}
|
}
|
||||||
execFunc = func() ([]byte, uint64, error) {
|
execFunc = func() ([]byte, uint64, error) {
|
||||||
// don't mutate the state!
|
// don't mutate the state!
|
||||||
|
|
|
||||||
|
|
@ -281,6 +281,12 @@ var (
|
||||||
Usage: "Scheme to use for storing ethereum state ('hash' or 'path')",
|
Usage: "Scheme to use for storing ethereum state ('hash' or 'path')",
|
||||||
Category: flags.StateCategory,
|
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{
|
StateHistoryFlag = &cli.Uint64Flag{
|
||||||
Name: "history.state",
|
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)",
|
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)
|
cfg.EthDiscoveryURLs = SplitAndTrim(urls)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if ctx.Bool(StateSizeTrackingFlag.Name) {
|
||||||
|
cfg.EnableStateSizeTracking = true
|
||||||
|
}
|
||||||
// Override any default configs for hard coded networks.
|
// Override any default configs for hard coded networks.
|
||||||
switch {
|
switch {
|
||||||
case ctx.Bool(MainnetFlag.Name):
|
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/merkle.journal
|
||||||
// - DATADIR/triedb/verkle.journal
|
// - DATADIR/triedb/verkle.journal
|
||||||
TrieJournalDirectory: stack.ResolvePath("triedb"),
|
TrieJournalDirectory: stack.ResolvePath("triedb"),
|
||||||
|
|
||||||
|
// Enable state size tracking if enabled
|
||||||
|
StateSizeTracking: ctx.Bool(StateSizeTrackingFlag.Name),
|
||||||
}
|
}
|
||||||
if options.ArchiveMode && !options.Preimages {
|
if options.ArchiveMode && !options.Preimages {
|
||||||
options.Preimages = true
|
options.Preimages = true
|
||||||
|
|
@ -2284,7 +2296,7 @@ func MakeConsolePreloads(ctx *cli.Context) []string {
|
||||||
}
|
}
|
||||||
|
|
||||||
// MakeTrieDatabase constructs a trie database based on the configured scheme.
|
// 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{
|
config := &triedb.Config{
|
||||||
Preimages: preimage,
|
Preimages: preimage,
|
||||||
IsVerkle: isVerkle,
|
IsVerkle: isVerkle,
|
||||||
|
|
@ -2300,10 +2312,13 @@ func MakeTrieDatabase(ctx *cli.Context, disk ethdb.Database, preimage bool, read
|
||||||
config.HashDB = hashdb.Defaults
|
config.HashDB = hashdb.Defaults
|
||||||
return triedb.NewDatabase(disk, config)
|
return triedb.NewDatabase(disk, config)
|
||||||
}
|
}
|
||||||
|
var pathConfig pathdb.Config
|
||||||
if readOnly {
|
if readOnly {
|
||||||
config.PathDB = pathdb.ReadOnly
|
pathConfig = *pathdb.ReadOnly
|
||||||
} else {
|
} else {
|
||||||
config.PathDB = pathdb.Defaults
|
pathConfig = *pathdb.Defaults
|
||||||
}
|
}
|
||||||
|
pathConfig.JournalDirectory = stack.ResolvePath("triedb")
|
||||||
|
config.PathDB = &pathConfig
|
||||||
return triedb.NewDatabase(disk, config)
|
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))
|
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) {
|
func writeErrors(errorFile string, errors []*filterQuery) {
|
||||||
file, err := os.Create(errorFile)
|
file, err := os.Create(errorFile)
|
||||||
if err != nil {
|
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
|
TrieNoAsyncFlush bool // Whether the asynchronous buffer flushing is disallowed
|
||||||
TrieJournalDirectory string // Directory path to the journal used for persisting trie data across node restarts
|
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
|
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
|
||||||
StateScheme string // Scheme used to store ethereum states and merkle tree nodes on top
|
ArchiveMode bool // Whether to enable the archive mode
|
||||||
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
|
// State snapshot related options
|
||||||
SnapshotLimit int // Memory allowance (MB) to use for caching snapshot entries in memory
|
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 zero, all transactions of the entire chain will be indexed.
|
||||||
// If the value is -1, indexing is disabled.
|
// If the value is -1, indexing is disabled.
|
||||||
TxLookupLimit int64
|
TxLookupLimit int64
|
||||||
|
|
||||||
|
// StateSizeTracking indicates whether the state size tracking is enabled.
|
||||||
|
StateSizeTracking bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// DefaultConfig returns the default config.
|
// DefaultConfig returns the default config.
|
||||||
|
|
@ -330,6 +336,7 @@ type BlockChain struct {
|
||||||
prefetcher Prefetcher
|
prefetcher Prefetcher
|
||||||
processor Processor // Block transaction processor interface
|
processor Processor // Block transaction processor interface
|
||||||
logger *tracing.Hooks
|
logger *tracing.Hooks
|
||||||
|
stateSizer *state.SizeTracker // State size tracking
|
||||||
|
|
||||||
lastForkReadyAlert time.Time // Last time there was a fork readiness print out
|
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 {
|
if bc.cfg.TxLookupLimit >= 0 {
|
||||||
bc.txIndexer = newTxIndexer(uint64(bc.cfg.TxLookupLimit), bc)
|
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
|
return bc, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1249,6 +1267,10 @@ func (bc *BlockChain) stopWithoutSaving() {
|
||||||
// Signal shutdown to all goroutines.
|
// Signal shutdown to all goroutines.
|
||||||
bc.InterruptInsert(true)
|
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.
|
// 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
|
// 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)
|
log.Crit("Failed to write block into disk", "err", err)
|
||||||
}
|
}
|
||||||
// Commit all cached state changes into underlying memory database.
|
// 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 {
|
if err != nil {
|
||||||
return err
|
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
|
// If node is running in path mode, skip explicit gc operation
|
||||||
// which is unnecessary in this mode.
|
// which is unnecessary in this mode.
|
||||||
if bc.triedb.Scheme() == rawdb.PathScheme {
|
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
|
// If we are past Byzantium, enable prefetching to pull in trie node paths
|
||||||
// while processing transactions. Before Byzantium the prefetcher is mostly
|
// while processing transactions. Before Byzantium the prefetcher is mostly
|
||||||
// useless due to the intermediate root hashing after each transaction.
|
// 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()) {
|
if bc.chainConfig.IsByzantium(block.Number()) {
|
||||||
// Generate witnesses either if we're self-testing, or if it's the
|
// Generate witnesses either if we're self-testing, or if it's the
|
||||||
// only block being inserted. A bit crude, but witnesses are huge,
|
// 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 {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
if bc.cfg.VmConfig.EnableWitnessStats {
|
||||||
|
witnessStats = stateless.NewWitnessStats()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
statedb.StartPrefetcher("chain", witness)
|
statedb.StartPrefetcher("chain", witness, witnessStats)
|
||||||
defer statedb.StopPrefetcher()
|
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())
|
return nil, fmt.Errorf("stateless self-validation receipt root mismatch (cross: %x local: %x)", crossReceiptRoot, block.ReceiptHash())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
xvtime := time.Since(xvstart)
|
xvtime := time.Since(xvstart)
|
||||||
proctime := time.Since(startTime) // processing + validation + cross validation
|
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 {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
// Report the collected witness statistics
|
||||||
|
if witnessStats != nil {
|
||||||
|
witnessStats.ReportMetrics()
|
||||||
|
}
|
||||||
|
|
||||||
// Update the metrics touched during block commit
|
// Update the metrics touched during block commit
|
||||||
accountCommitTimer.Update(statedb.AccountCommits) // Account commits are complete, we can mark them
|
accountCommitTimer.Update(statedb.AccountCommits) // Account commits are complete, we can mark them
|
||||||
storageCommitTimer.Update(statedb.StorageCommits) // Storage 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 {
|
func (bc *BlockChain) GetTrieFlushInterval() time.Duration {
|
||||||
return time.Duration(bc.flushInterval.Load())
|
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)
|
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.
|
// current targetView.
|
||||||
func (f *FilterMaps) indexerLoop() {
|
func (f *FilterMaps) indexerLoop() {
|
||||||
defer f.closeWg.Done()
|
defer f.closeWg.Done()
|
||||||
|
|
@ -221,7 +221,7 @@ func (f *FilterMaps) processSingleEvent(blocking bool) bool {
|
||||||
return true
|
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) {
|
func (f *FilterMaps) setTarget(target targetUpdate) {
|
||||||
f.targetView = target.targetView
|
f.targetView = target.targetView
|
||||||
f.historyCutoff = target.historyCutoff
|
f.historyCutoff = target.historyCutoff
|
||||||
|
|
|
||||||
|
|
@ -153,7 +153,7 @@ func hashAlloc(ga *types.GenesisAlloc, isVerkle bool) (common.Hash, error) {
|
||||||
if account.Balance != nil {
|
if account.Balance != nil {
|
||||||
statedb.AddBalance(addr, uint256.MustFromBig(account.Balance), tracing.BalanceIncreaseGenesisBalance)
|
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)
|
statedb.SetNonce(addr, account.Nonce, tracing.NonceChangeGenesis)
|
||||||
for key, value := range account.Storage {
|
for key, value := range account.Storage {
|
||||||
statedb.SetState(addr, key, value)
|
statedb.SetState(addr, key, value)
|
||||||
|
|
@ -179,7 +179,7 @@ func flushAlloc(ga *types.GenesisAlloc, triedb *triedb.Database) (common.Hash, e
|
||||||
// already captures the allocations.
|
// already captures the allocations.
|
||||||
statedb.AddBalance(addr, uint256.MustFromBig(account.Balance), tracing.BalanceIncreaseGenesisBalance)
|
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)
|
statedb.SetNonce(addr, account.Nonce, tracing.NonceChangeGenesis)
|
||||||
for key, value := range account.Storage {
|
for key, value := range account.Storage {
|
||||||
statedb.SetState(addr, key, value)
|
statedb.SetState(addr, key, value)
|
||||||
|
|
|
||||||
|
|
@ -174,16 +174,3 @@ func UpdateUncleanShutdownMarker(db ethdb.KeyValueStore) {
|
||||||
log.Warn("Failed to write unclean-shutdown marker", "err", err)
|
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.
|
// ReadPersistentStateID retrieves the id of the persistent state from the database.
|
||||||
func ReadPersistentStateID(db ethdb.KeyValueReader) uint64 {
|
func ReadPersistentStateID(db ethdb.KeyValueReader) uint64 {
|
||||||
data, _ := db.Get(persistentStateIDKey)
|
data, _ := db.Get(persistentStateIDKey)
|
||||||
|
|
|
||||||
|
|
@ -48,12 +48,16 @@ func basicRead(t *testing.T, newFn func(kinds []string) ethdb.AncientStore) {
|
||||||
)
|
)
|
||||||
defer db.Close()
|
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++ {
|
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
|
return nil
|
||||||
})
|
}); err != nil {
|
||||||
|
t.Fatalf("Failed to write ancient data %v", err)
|
||||||
|
}
|
||||||
db.TruncateTail(10)
|
db.TruncateTail(10)
|
||||||
db.TruncateHead(90)
|
db.TruncateHead(90)
|
||||||
|
|
||||||
|
|
@ -109,12 +113,16 @@ func batchRead(t *testing.T, newFn func(kinds []string) ethdb.AncientStore) {
|
||||||
)
|
)
|
||||||
defer db.Close()
|
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++ {
|
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
|
return nil
|
||||||
})
|
}); err != nil {
|
||||||
|
t.Fatalf("Failed to write ancient data %v", err)
|
||||||
|
}
|
||||||
db.TruncateTail(10)
|
db.TruncateTail(10)
|
||||||
db.TruncateHead(90)
|
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
|
// The ancient write to tables should be aligned
|
||||||
_, err := db.ModifyAncients(func(op ethdb.AncientWriteOp) error {
|
_, err := db.ModifyAncients(func(op ethdb.AncientWriteOp) error {
|
||||||
for i := 0; i < 100; i++ {
|
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
|
return nil
|
||||||
})
|
})
|
||||||
|
|
@ -200,8 +210,12 @@ func basicWrite(t *testing.T, newFn func(kinds []string) ethdb.AncientStore) {
|
||||||
// Test normal ancient write
|
// Test normal ancient write
|
||||||
size, err := db.ModifyAncients(func(op ethdb.AncientWriteOp) error {
|
size, err := db.ModifyAncients(func(op ethdb.AncientWriteOp) error {
|
||||||
for i := 0; i < 100; i++ {
|
for i := 0; i < 100; i++ {
|
||||||
op.AppendRaw("a", uint64(i), dataA[i])
|
if err := op.AppendRaw("a", uint64(i), dataA[i]); err != nil {
|
||||||
op.AppendRaw("b", uint64(i), dataB[i])
|
return err
|
||||||
|
}
|
||||||
|
if err := op.AppendRaw("b", uint64(i), dataB[i]); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
|
|
@ -217,8 +231,12 @@ func basicWrite(t *testing.T, newFn func(kinds []string) ethdb.AncientStore) {
|
||||||
db.TruncateHead(90)
|
db.TruncateHead(90)
|
||||||
_, err = db.ModifyAncients(func(op ethdb.AncientWriteOp) error {
|
_, err = db.ModifyAncients(func(op ethdb.AncientWriteOp) error {
|
||||||
for i := 90; i < 100; i++ {
|
for i := 90; i < 100; i++ {
|
||||||
op.AppendRaw("a", uint64(i), dataA[i])
|
if err := op.AppendRaw("a", uint64(i), dataA[i]); err != nil {
|
||||||
op.AppendRaw("b", uint64(i), dataB[i])
|
return err
|
||||||
|
}
|
||||||
|
if err := op.AppendRaw("b", uint64(i), dataB[i]); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
|
|
@ -227,11 +245,15 @@ func basicWrite(t *testing.T, newFn func(kinds []string) ethdb.AncientStore) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Write should work after truncating everything
|
// Write should work after truncating everything
|
||||||
db.TruncateTail(0)
|
db.TruncateHead(0)
|
||||||
_, err = db.ModifyAncients(func(op ethdb.AncientWriteOp) error {
|
_, err = db.ModifyAncients(func(op ethdb.AncientWriteOp) error {
|
||||||
for i := 0; i < 100; i++ {
|
for i := 0; i < 100; i++ {
|
||||||
op.AppendRaw("a", uint64(i), dataA[i])
|
if err := op.AppendRaw("a", uint64(i), dataA[i]); err != nil {
|
||||||
op.AppendRaw("b", uint64(i), dataB[i])
|
return err
|
||||||
|
}
|
||||||
|
if err := op.AppendRaw("b", uint64(i), dataB[i]); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
|
|
@ -245,14 +267,18 @@ func nonMutable(t *testing.T, newFn func(kinds []string) ethdb.AncientStore) {
|
||||||
defer db.Close()
|
defer db.Close()
|
||||||
|
|
||||||
// We write 100 zero-bytes to the freezer and immediately mutate the slice
|
// 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)
|
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 {
|
for i := range data {
|
||||||
data[i] = 0xff
|
data[i] = 0xff
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
})
|
}); err != nil {
|
||||||
|
t.Fatalf("Failed to write ancient data %v", err)
|
||||||
|
}
|
||||||
// Now read it.
|
// Now read it.
|
||||||
data, err := db.Ancient("a", uint64(0))
|
data, err := db.Ancient("a", uint64(0))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -275,23 +301,31 @@ func TestResettableAncientSuite(t *testing.T, newFn func(kinds []string) ethdb.R
|
||||||
)
|
)
|
||||||
defer db.Close()
|
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++ {
|
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
|
return nil
|
||||||
})
|
}); err != nil {
|
||||||
|
t.Fatalf("Failed to write ancient data %v", err)
|
||||||
|
}
|
||||||
db.TruncateTail(10)
|
db.TruncateTail(10)
|
||||||
db.TruncateHead(90)
|
db.TruncateHead(90)
|
||||||
|
|
||||||
// Ancient write should work after resetting
|
// Ancient write should work after resetting
|
||||||
db.Reset()
|
db.Reset()
|
||||||
db.ModifyAncients(func(op ethdb.AncientWriteOp) error {
|
if _, err := db.ModifyAncients(func(op ethdb.AncientWriteOp) error {
|
||||||
for i := 0; i < 100; i++ {
|
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
|
return nil
|
||||||
})
|
}); err != nil {
|
||||||
|
t.Fatalf("Failed to write ancient data %v", err)
|
||||||
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -18,13 +18,17 @@ package rawdb
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"maps"
|
"maps"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"runtime"
|
||||||
"slices"
|
"slices"
|
||||||
"strings"
|
"strings"
|
||||||
|
"sync"
|
||||||
|
"sync/atomic"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
|
@ -32,7 +36,7 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/ethdb"
|
"github.com/ethereum/go-ethereum/ethdb"
|
||||||
"github.com/ethereum/go-ethereum/ethdb/memorydb"
|
"github.com/ethereum/go-ethereum/ethdb/memorydb"
|
||||||
"github.com/ethereum/go-ethereum/log"
|
"github.com/ethereum/go-ethereum/log"
|
||||||
"github.com/olekukonko/tablewriter"
|
"golang.org/x/sync/errgroup"
|
||||||
)
|
)
|
||||||
|
|
||||||
var ErrDeleteRangeInterrupted = errors.New("safe delete range operation interrupted")
|
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))
|
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 {
|
type stat struct {
|
||||||
size common.StorageSize
|
size uint64
|
||||||
count counter
|
count uint64
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add size to the stat and increase the counter by 1
|
func (s *stat) empty() bool {
|
||||||
func (s *stat) Add(size common.StorageSize) {
|
return atomic.LoadUint64(&s.count) == 0
|
||||||
s.size += size
|
|
||||||
s.count++
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *stat) Size() string {
|
func (s *stat) add(size common.StorageSize) {
|
||||||
return s.size.String()
|
atomic.AddUint64(&s.size, uint64(size))
|
||||||
|
atomic.AddUint64(&s.count, 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *stat) Count() string {
|
func (s *stat) sizeString() string {
|
||||||
return s.count.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
|
// InspectDatabase traverses the entire database and checks the size
|
||||||
// of all different categories of data.
|
// of all different categories of data.
|
||||||
func InspectDatabase(db ethdb.Database, keyPrefix, keyStart []byte) error {
|
func InspectDatabase(db ethdb.Database, keyPrefix, keyStart []byte) error {
|
||||||
it := db.NewIterator(keyPrefix, keyStart)
|
|
||||||
defer it.Release()
|
|
||||||
|
|
||||||
var (
|
var (
|
||||||
count int64
|
start = time.Now()
|
||||||
start = time.Now()
|
count atomic.Int64
|
||||||
logged = time.Now()
|
total atomic.Uint64
|
||||||
|
|
||||||
// Key-value store statistics
|
// Key-value store statistics
|
||||||
headers stat
|
headers stat
|
||||||
|
|
@ -428,144 +432,200 @@ func InspectDatabase(db ethdb.Database, keyPrefix, keyStart []byte) error {
|
||||||
metadata stat
|
metadata stat
|
||||||
unaccounted stat
|
unaccounted stat
|
||||||
|
|
||||||
// Totals
|
|
||||||
total common.StorageSize
|
|
||||||
|
|
||||||
// This map tracks example keys for unaccounted data.
|
// This map tracks example keys for unaccounted data.
|
||||||
// For each unique two-byte prefix, the first unaccounted key encountered
|
// For each unique two-byte prefix, the first unaccounted key encountered
|
||||||
// by the iterator will be stored.
|
// by the iterator will be stored.
|
||||||
unaccountedKeys = make(map[[2]byte][]byte)
|
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
|
inspectRange := func(ctx context.Context, r byte) error {
|
||||||
case bytes.HasPrefix(key, filterMapRowPrefix) && len(key) <= len(filterMapRowPrefix)+9:
|
var s []byte
|
||||||
filterMapRows.Add(size)
|
if len(keyStart) > 0 {
|
||||||
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 {
|
switch {
|
||||||
case IsAccountTrieNode(remain):
|
case r < keyStart[0]:
|
||||||
verkleTries.Add(size)
|
return nil
|
||||||
case bytes.HasPrefix(remain, stateIDPrefix) && len(remain) == len(stateIDPrefix)+common.HashLength:
|
case r == keyStart[0]:
|
||||||
verkleStateLookups.Add(size)
|
s = keyStart[1:]
|
||||||
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:
|
default:
|
||||||
unaccounted.Add(size)
|
// entire key range is included for inspection
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
it := db.NewIterator(append(keyPrefix, r), s)
|
||||||
|
defer it.Release()
|
||||||
|
|
||||||
// Metadata keys
|
for it.Next() {
|
||||||
case slices.ContainsFunc(knownMetadataKeys, func(x []byte) bool { return bytes.Equal(x, key) }):
|
var (
|
||||||
metadata.Add(size)
|
key = it.Key()
|
||||||
|
size = common.StorageSize(len(key) + len(it.Value()))
|
||||||
|
)
|
||||||
|
total.Add(uint64(size))
|
||||||
|
count.Add(1)
|
||||||
|
|
||||||
default:
|
switch {
|
||||||
unaccounted.Add(size)
|
case bytes.HasPrefix(key, headerPrefix) && len(key) == (len(headerPrefix)+8+common.HashLength):
|
||||||
if len(key) >= 2 {
|
headers.add(size)
|
||||||
prefix := [2]byte(key[:2])
|
case bytes.HasPrefix(key, blockBodyPrefix) && len(key) == (len(blockBodyPrefix)+8+common.HashLength):
|
||||||
if _, ok := unaccountedKeys[prefix]; !ok {
|
bodies.add(size)
|
||||||
unaccountedKeys[prefix] = bytes.Clone(key)
|
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 {
|
return it.Error()
|
||||||
log.Info("Inspecting database", "count", count, "elapsed", common.PrettyDuration(time.Since(start)))
|
|
||||||
logged = time.Now()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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.
|
// Display the database statistic of key-value store.
|
||||||
stats := [][]string{
|
stats := [][]string{
|
||||||
{"Key-Value store", "Headers", headers.Size(), headers.Count()},
|
{"Key-Value store", "Headers", headers.sizeString(), headers.countString()},
|
||||||
{"Key-Value store", "Bodies", bodies.Size(), bodies.Count()},
|
{"Key-Value store", "Bodies", bodies.sizeString(), bodies.countString()},
|
||||||
{"Key-Value store", "Receipt lists", receipts.Size(), receipts.Count()},
|
{"Key-Value store", "Receipt lists", receipts.sizeString(), receipts.countString()},
|
||||||
{"Key-Value store", "Difficulties (deprecated)", tds.Size(), tds.Count()},
|
{"Key-Value store", "Difficulties (deprecated)", tds.sizeString(), tds.countString()},
|
||||||
{"Key-Value store", "Block number->hash", numHashPairings.Size(), numHashPairings.Count()},
|
{"Key-Value store", "Block number->hash", numHashPairings.sizeString(), numHashPairings.countString()},
|
||||||
{"Key-Value store", "Block hash->number", hashNumPairings.Size(), hashNumPairings.Count()},
|
{"Key-Value store", "Block hash->number", hashNumPairings.sizeString(), hashNumPairings.countString()},
|
||||||
{"Key-Value store", "Transaction index", txLookups.Size(), txLookups.Count()},
|
{"Key-Value store", "Transaction index", txLookups.sizeString(), txLookups.countString()},
|
||||||
{"Key-Value store", "Log index filter-map rows", filterMapRows.Size(), filterMapRows.Count()},
|
{"Key-Value store", "Log index filter-map rows", filterMapRows.sizeString(), filterMapRows.countString()},
|
||||||
{"Key-Value store", "Log index last-block-of-map", filterMapLastBlock.Size(), filterMapLastBlock.Count()},
|
{"Key-Value store", "Log index last-block-of-map", filterMapLastBlock.sizeString(), filterMapLastBlock.countString()},
|
||||||
{"Key-Value store", "Log index block-lv", filterMapBlockLV.Size(), filterMapBlockLV.Count()},
|
{"Key-Value store", "Log index block-lv", filterMapBlockLV.sizeString(), filterMapBlockLV.countString()},
|
||||||
{"Key-Value store", "Log bloombits (deprecated)", bloomBits.Size(), bloomBits.Count()},
|
{"Key-Value store", "Log bloombits (deprecated)", bloomBits.sizeString(), bloomBits.countString()},
|
||||||
{"Key-Value store", "Contract codes", codes.Size(), codes.Count()},
|
{"Key-Value store", "Contract codes", codes.sizeString(), codes.countString()},
|
||||||
{"Key-Value store", "Hash trie nodes", legacyTries.Size(), legacyTries.Count()},
|
{"Key-Value store", "Hash trie nodes", legacyTries.sizeString(), legacyTries.countString()},
|
||||||
{"Key-Value store", "Path trie state lookups", stateLookups.Size(), stateLookups.Count()},
|
{"Key-Value store", "Path trie state lookups", stateLookups.sizeString(), stateLookups.countString()},
|
||||||
{"Key-Value store", "Path trie account nodes", accountTries.Size(), accountTries.Count()},
|
{"Key-Value store", "Path trie account nodes", accountTries.sizeString(), accountTries.countString()},
|
||||||
{"Key-Value store", "Path trie storage nodes", storageTries.Size(), storageTries.Count()},
|
{"Key-Value store", "Path trie storage nodes", storageTries.sizeString(), storageTries.countString()},
|
||||||
{"Key-Value store", "Path state history indexes", stateIndex.Size(), stateIndex.Count()},
|
{"Key-Value store", "Path state history indexes", stateIndex.sizeString(), stateIndex.countString()},
|
||||||
{"Key-Value store", "Verkle trie nodes", verkleTries.Size(), verkleTries.Count()},
|
{"Key-Value store", "Verkle trie nodes", verkleTries.sizeString(), verkleTries.countString()},
|
||||||
{"Key-Value store", "Verkle trie state lookups", verkleStateLookups.Size(), verkleStateLookups.Count()},
|
{"Key-Value store", "Verkle trie state lookups", verkleStateLookups.sizeString(), verkleStateLookups.countString()},
|
||||||
{"Key-Value store", "Trie preimages", preimages.Size(), preimages.Count()},
|
{"Key-Value store", "Trie preimages", preimages.sizeString(), preimages.countString()},
|
||||||
{"Key-Value store", "Account snapshot", accountSnaps.Size(), accountSnaps.Count()},
|
{"Key-Value store", "Account snapshot", accountSnaps.sizeString(), accountSnaps.countString()},
|
||||||
{"Key-Value store", "Storage snapshot", storageSnaps.Size(), storageSnaps.Count()},
|
{"Key-Value store", "Storage snapshot", storageSnaps.sizeString(), storageSnaps.countString()},
|
||||||
{"Key-Value store", "Beacon sync headers", beaconHeaders.Size(), beaconHeaders.Count()},
|
{"Key-Value store", "Beacon sync headers", beaconHeaders.sizeString(), beaconHeaders.countString()},
|
||||||
{"Key-Value store", "Clique snapshots", cliqueSnaps.Size(), cliqueSnaps.Count()},
|
{"Key-Value store", "Clique snapshots", cliqueSnaps.sizeString(), cliqueSnaps.countString()},
|
||||||
{"Key-Value store", "Singleton metadata", metadata.Size(), metadata.Count()},
|
{"Key-Value store", "Singleton metadata", metadata.sizeString(), metadata.countString()},
|
||||||
}
|
}
|
||||||
|
|
||||||
// Inspect all registered append-only file store then.
|
// Inspect all registered append-only file store then.
|
||||||
ancients, err := inspectFreezers(db)
|
ancients, err := inspectFreezers(db)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -580,16 +640,17 @@ func InspectDatabase(db ethdb.Database, keyPrefix, keyStart []byte) error {
|
||||||
fmt.Sprintf("%d", ancient.count()),
|
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.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.AppendBulk(stats)
|
||||||
table.Render()
|
table.Render()
|
||||||
|
|
||||||
if unaccounted.size > 0 {
|
if !unaccounted.empty() {
|
||||||
log.Error("Database contains unaccounted data", "size", unaccounted.size, "count", unaccounted.count)
|
log.Error("Database contains unaccounted data", "size", unaccounted.sizeString(), "count", unaccounted.countString())
|
||||||
for _, e := range slices.SortedFunc(maps.Values(unaccountedKeys), bytes.Compare) {
|
for _, e := range slices.SortedFunc(maps.Values(unaccountedKeys), bytes.Compare) {
|
||||||
log.Error(fmt.Sprintf(" example key: %x", e))
|
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
|
uncleanShutdownKey = []byte("unclean-shutdown") // config prefix for the db
|
||||||
|
|
||||||
// transitionStatusKey tracks the eth2 transition status.
|
// transitionStatusKey tracks the eth2 transition status.
|
||||||
transitionStatusKey = []byte("eth2-transition")
|
transitionStatusKey = []byte("eth2-transition") // deprecated
|
||||||
|
|
||||||
// snapSyncStatusFlagKey flags that status of snap sync.
|
// snapSyncStatusFlagKey flags that status of snap sync.
|
||||||
snapSyncStatusFlagKey = []byte("SnapSyncStatus")
|
snapSyncStatusFlagKey = []byte("SnapSyncStatus")
|
||||||
|
|
@ -384,21 +384,48 @@ func accountHistoryIndexKey(addressHash common.Hash) []byte {
|
||||||
|
|
||||||
// storageHistoryIndexKey = StateHistoryStorageMetadataPrefix + addressHash + storageHash
|
// storageHistoryIndexKey = StateHistoryStorageMetadataPrefix + addressHash + storageHash
|
||||||
func storageHistoryIndexKey(addressHash common.Hash, storageHash common.Hash) []byte {
|
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
|
// accountHistoryIndexBlockKey = StateHistoryAccountBlockPrefix + addressHash + blockID
|
||||||
func accountHistoryIndexBlockKey(addressHash common.Hash, blockID uint32) []byte {
|
func accountHistoryIndexBlockKey(addressHash common.Hash, blockID uint32) []byte {
|
||||||
var buf [4]byte
|
var buf4 [4]byte
|
||||||
binary.BigEndian.PutUint32(buf[:], blockID)
|
binary.BigEndian.PutUint32(buf4[:], blockID)
|
||||||
return append(append(StateHistoryAccountBlockPrefix, addressHash.Bytes()...), buf[:]...)
|
|
||||||
|
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
|
// storageHistoryIndexBlockKey = StateHistoryStorageBlockPrefix + addressHash + storageHash + blockID
|
||||||
func storageHistoryIndexBlockKey(addressHash common.Hash, storageHash common.Hash, blockID uint32) []byte {
|
func storageHistoryIndexBlockKey(addressHash common.Hash, storageHash common.Hash, blockID uint32) []byte {
|
||||||
var buf [4]byte
|
var buf4 [4]byte
|
||||||
binary.BigEndian.PutUint32(buf[:], blockID)
|
binary.BigEndian.PutUint32(buf4[:], blockID)
|
||||||
return append(append(append(StateHistoryStorageBlockPrefix, addressHash.Bytes()...), storageHash.Bytes()...), buf[:]...)
|
|
||||||
|
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
|
// transitionStateKey = transitionStatusKey + hash
|
||||||
|
|
|
||||||
|
|
@ -81,11 +81,19 @@ type Trie interface {
|
||||||
// be returned.
|
// be returned.
|
||||||
GetAccount(address common.Address) (*types.StateAccount, error)
|
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
|
// 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,
|
// must not be modified by the caller. If a node was not found in the database,
|
||||||
// a trie.MissingNodeError is returned.
|
// a trie.MissingNodeError is returned.
|
||||||
GetStorage(addr common.Address, key []byte) ([]byte, error)
|
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
|
// UpdateAccount abstracts an account write to the trie. It encodes the
|
||||||
// provided account object with associated algorithm and then updates it
|
// provided account object with associated algorithm and then updates it
|
||||||
// in the trie with provided address.
|
// 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.
|
// Witness returns a set containing all trie nodes that have been accessed.
|
||||||
// The returned map could be nil if the witness is empty.
|
// 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
|
// 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
|
// 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
|
var eta time.Duration // Realistically will never remain uninited
|
||||||
if done := binary.BigEndian.Uint64(key[:8]); done > 0 {
|
if done := binary.BigEndian.Uint64(key[:8]); done > 0 {
|
||||||
var (
|
left := math.MaxUint64 - binary.BigEndian.Uint64(key[:8])
|
||||||
left = math.MaxUint64 - binary.BigEndian.Uint64(key[:8])
|
eta = common.CalculateETA(done, left, time.Since(pstart))
|
||||||
speed = done/uint64(time.Since(pstart)/time.Millisecond+1) + 1 // +1s to avoid division by zero
|
|
||||||
)
|
|
||||||
eta = time.Duration(left/speed) * time.Millisecond
|
|
||||||
}
|
}
|
||||||
if time.Since(logged) > 8*time.Second {
|
if time.Since(logged) > 8*time.Second {
|
||||||
log.Info("Pruning state data", "nodes", count, "skipped", skipped, "size", size,
|
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
|
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.
|
// returned if the associated trie specified by root is not existent.
|
||||||
func newTrieReader(root common.Hash, db *triedb.Database, cache *utils.PointCache) (*trieReader, error) {
|
func newTrieReader(root common.Hash, db *triedb.Database, cache *utils.PointCache) (*trieReader, error) {
|
||||||
var (
|
var (
|
||||||
|
|
@ -253,7 +253,7 @@ func newTrieReader(root common.Hash, db *triedb.Database, cache *utils.PointCach
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
tr = trie.NewTransitionTree(mpt, tr.(*trie.VerkleTrie), false)
|
tr = trie.NewTransitionTrie(mpt, tr.(*trie.VerkleTrie), false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if err != nil {
|
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 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 {
|
if done := binary.BigEndian.Uint64(stat.head[:8]) / stat.accounts; done > 0 {
|
||||||
var (
|
var (
|
||||||
left = (math.MaxUint64 - binary.BigEndian.Uint64(stat.head[:8])) / stat.accounts
|
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 = common.CalculateETA(done, left, time.Since(stat.start))
|
||||||
eta = time.Duration(left/speed) * time.Millisecond
|
|
||||||
)
|
)
|
||||||
// If there are large contract crawls in progress, estimate their finish time
|
// If there are large contract crawls in progress, estimate their finish time
|
||||||
for acc, head := range stat.slotsHead {
|
for acc, head := range stat.slotsHead {
|
||||||
start := stat.slotsStart[acc]
|
start := stat.slotsStart[acc]
|
||||||
if done := binary.BigEndian.Uint64(head[:8]); done > 0 {
|
if done := binary.BigEndian.Uint64(head[:8]); done > 0 {
|
||||||
var (
|
left := math.MaxUint64 - binary.BigEndian.Uint64(head[:8])
|
||||||
left = math.MaxUint64 - binary.BigEndian.Uint64(head[:8])
|
|
||||||
speed = done/uint64(time.Since(start)/time.Millisecond+1) + 1 // +1s to avoid division by zero
|
|
||||||
)
|
|
||||||
// Override the ETA if larger than the largest until now
|
// 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
|
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
|
journal *journal
|
||||||
|
|
||||||
// State witness if cross validation is needed
|
// State witness if cross validation is needed
|
||||||
witness *stateless.Witness
|
witness *stateless.Witness
|
||||||
|
witnessStats *stateless.WitnessStats
|
||||||
|
|
||||||
// Measurements gathered during execution for debugging purposes
|
// Measurements gathered during execution for debugging purposes
|
||||||
AccountReads time.Duration
|
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
|
// 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
|
// state trie concurrently while the state is mutated so that when we reach the
|
||||||
// commit phase, most of the needed data is already hot.
|
// 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
|
// Terminate any previously running prefetcher
|
||||||
s.StopPrefetcher()
|
s.StopPrefetcher()
|
||||||
|
|
||||||
// Enable witness collection if requested
|
// Enable witness collection if requested
|
||||||
s.witness = witness
|
s.witness = witness
|
||||||
|
s.witnessStats = witnessStats
|
||||||
|
|
||||||
// With the switch to the Proof-of-Stake consensus algorithm, block production
|
// With the switch to the Proof-of-Stake consensus algorithm, block production
|
||||||
// rewards are now handled at the consensus layer. Consequently, a block may
|
// 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)
|
stateObject := s.getOrNewStateObject(addr)
|
||||||
if stateObject != nil {
|
if stateObject != nil {
|
||||||
return stateObject.SetCode(crypto.Keccak256Hash(code), code)
|
return stateObject.SetCode(crypto.Keccak256Hash(code), code)
|
||||||
|
|
@ -858,9 +860,17 @@ func (s *StateDB) IntermediateRoot(deleteEmptyObjects bool) common.Hash {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if trie := obj.getPrefetchedTrie(); trie != nil {
|
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 {
|
} 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
|
// Pull in only-read and non-destructed trie witnesses
|
||||||
|
|
@ -874,9 +884,17 @@ func (s *StateDB) IntermediateRoot(deleteEmptyObjects bool) common.Hash {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if trie := obj.getPrefetchedTrie(); trie != nil {
|
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 {
|
} 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 witness building is enabled, gather the account trie witness
|
||||||
if s.witness != nil {
|
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
|
return hash
|
||||||
}
|
}
|
||||||
|
|
@ -1133,7 +1155,7 @@ func (s *StateDB) GetTrie() Trie {
|
||||||
|
|
||||||
// commit gathers the state mutations accumulated along with the associated
|
// commit gathers the state mutations accumulated along with the associated
|
||||||
// trie changes, resetting all internal flags with the new state as the base.
|
// 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.
|
// Short circuit in case any database failure occurred earlier.
|
||||||
if s.dbErr != nil {
|
if s.dbErr != nil {
|
||||||
return nil, fmt.Errorf("commit aborted due to earlier error: %v", s.dbErr)
|
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
|
origin := s.originalRoot
|
||||||
s.originalRoot = root
|
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
|
// commitAndFlush is a wrapper of commit which also commits the state mutations
|
||||||
// to the configured data stores.
|
// to the configured data stores.
|
||||||
func (s *StateDB) commitAndFlush(block uint64, deleteEmptyObjects bool, noStorageWiping bool) (*stateUpdate, error) {
|
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 {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
@ -1356,6 +1378,16 @@ func (s *StateDB) Commit(block uint64, deleteEmptyObjects bool, noStorageWiping
|
||||||
return ret.root, nil
|
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.
|
// Prepare handles the preparatory steps for executing a state transition with.
|
||||||
// This method must be invoked before state transition.
|
// 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)
|
code := make([]byte, 16)
|
||||||
binary.BigEndian.PutUint64(code, uint64(a.args[0]))
|
binary.BigEndian.PutUint64(code, uint64(a.args[0]))
|
||||||
binary.BigEndian.PutUint64(code[8:], uint64(a.args[1]))
|
binary.BigEndian.PutUint64(code[8:], uint64(a.args[1]))
|
||||||
s.SetCode(addr, code)
|
s.SetCode(addr, code, tracing.CodeChangeUnspecified)
|
||||||
},
|
},
|
||||||
args: make([]int64, 2),
|
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 {
|
func (s *hookedStateDB) SetCode(address common.Address, code []byte, reason tracing.CodeChangeReason) []byte {
|
||||||
prev := s.inner.SetCode(address, code)
|
prev := s.inner.SetCode(address, code, reason)
|
||||||
if s.hooks.OnCodeChange != nil {
|
if s.hooks.OnCodeChangeV2 != nil || s.hooks.OnCodeChange != nil {
|
||||||
prevHash := types.EmptyCodeHash
|
prevHash := types.EmptyCodeHash
|
||||||
if len(prev) != 0 {
|
if len(prev) != 0 {
|
||||||
prevHash = crypto.Keccak256Hash(prev)
|
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
|
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)
|
s.hooks.OnBalanceChange(address, prev.ToBig(), new(big.Int), tracing.BalanceDecreaseSelfdestruct)
|
||||||
}
|
}
|
||||||
|
|
||||||
if s.hooks.OnCodeChange != nil && len(prevCode) > 0 {
|
if len(prevCode) > 0 {
|
||||||
s.hooks.OnCodeChange(address, prevCodeHash, prevCode, types.EmptyCodeHash, nil)
|
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
|
return prev
|
||||||
|
|
@ -242,12 +252,16 @@ func (s *hookedStateDB) SelfDestruct6780(address common.Address) (uint256.Int, b
|
||||||
|
|
||||||
prev, changed := s.inner.SelfDestruct6780(address)
|
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)
|
s.hooks.OnBalanceChange(address, prev.ToBig(), new(big.Int), tracing.BalanceDecreaseSelfdestruct)
|
||||||
}
|
}
|
||||||
|
|
||||||
if s.hooks.OnCodeChange != nil && changed && len(prevCode) > 0 {
|
if changed && len(prevCode) > 0 {
|
||||||
s.hooks.OnCodeChange(address, prevCodeHash, prevCode, types.EmptyCodeHash, nil)
|
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
|
return prev, changed
|
||||||
|
|
|
||||||
|
|
@ -114,7 +114,7 @@ func TestHooks(t *testing.T) {
|
||||||
sdb.AddBalance(common.Address{0xaa}, uint256.NewInt(100), tracing.BalanceChangeUnspecified)
|
sdb.AddBalance(common.Address{0xaa}, uint256.NewInt(100), tracing.BalanceChangeUnspecified)
|
||||||
sdb.SubBalance(common.Address{0xaa}, uint256.NewInt(50), tracing.BalanceChangeTransfer)
|
sdb.SubBalance(common.Address{0xaa}, uint256.NewInt(50), tracing.BalanceChangeTransfer)
|
||||||
sdb.SetNonce(common.Address{0xaa}, 1337, tracing.NonceChangeGenesis)
|
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("0x11"))
|
||||||
sdb.SetState(common.Address{0xaa}, common.HexToHash("0x01"), common.HexToHash("0x22"))
|
sdb.SetState(common.Address{0xaa}, common.HexToHash("0x01"), common.HexToHash("0x22"))
|
||||||
sdb.SetTransientState(common.Address{0xaa}, common.HexToHash("0x02"), common.HexToHash("0x01"))
|
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}))
|
state.SetState(addr, common.BytesToHash([]byte{i, i, i}), common.BytesToHash([]byte{i, i, i, i}))
|
||||||
}
|
}
|
||||||
if i%3 == 0 {
|
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})
|
state.SetState(addr, common.Hash{i, i, i, tweak}, common.Hash{i, i, i, i, tweak})
|
||||||
}
|
}
|
||||||
if i%3 == 0 {
|
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)
|
code := make([]byte, 16)
|
||||||
binary.BigEndian.PutUint64(code, uint64(a.args[0]))
|
binary.BigEndian.PutUint64(code, uint64(a.args[0]))
|
||||||
binary.BigEndian.PutUint64(code[8:], uint64(a.args[1]))
|
binary.BigEndian.PutUint64(code[8:], uint64(a.args[1]))
|
||||||
s.SetCode(addr, code)
|
s.SetCode(addr, code, tracing.CodeChangeUnspecified)
|
||||||
},
|
},
|
||||||
args: make([]int64, 2),
|
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
|
// which would cause a difference in state when unrolling
|
||||||
// the journal. (CreateContact assumes created was false prior to
|
// the journal. (CreateContact assumes created was false prior to
|
||||||
// invocation, and the journal rollback sets it to false).
|
// 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")
|
sval := common.HexToHash("bbb")
|
||||||
|
|
||||||
state.SetBalance(addr, uint256.NewInt(42), tracing.BalanceChangeUnspecified) // Change the account trie
|
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
|
state.SetState(addr, skey, sval) // Change the storage trie
|
||||||
|
|
||||||
if balance := state.GetBalance(addr); balance.Cmp(uint256.NewInt(42)) != 0 {
|
if balance := state.GetBalance(addr); balance.Cmp(uint256.NewInt(42)) != 0 {
|
||||||
|
|
@ -804,7 +804,7 @@ func TestCopyCopyCommitCopy(t *testing.T) {
|
||||||
sval := common.HexToHash("bbb")
|
sval := common.HexToHash("bbb")
|
||||||
|
|
||||||
state.SetBalance(addr, uint256.NewInt(42), tracing.BalanceChangeUnspecified) // Change the account trie
|
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
|
state.SetState(addr, skey, sval) // Change the storage trie
|
||||||
|
|
||||||
if balance := state.GetBalance(addr); balance.Cmp(uint256.NewInt(42)) != 0 {
|
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")
|
sval1, sval2 := common.HexToHash("b1"), common.HexToHash("b2")
|
||||||
|
|
||||||
state.SetBalance(addr, uint256.NewInt(42), tracing.BalanceChangeUnspecified) // Change the account trie
|
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
|
state.SetState(addr, skey1, sval1) // Change the storage trie
|
||||||
|
|
||||||
if balance := state.GetBalance(addr); balance.Cmp(uint256.NewInt(42)) != 0 {
|
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"))
|
addr := common.BytesToAddress([]byte("so"))
|
||||||
{
|
{
|
||||||
state.SetBalance(addr, uint256.NewInt(1), tracing.BalanceChangeUnspecified)
|
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"))
|
a2 := common.BytesToAddress([]byte("another"))
|
||||||
state.SetBalance(a2, uint256.NewInt(100), tracing.BalanceChangeUnspecified)
|
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)
|
root, _ = state.Commit(0, false, false)
|
||||||
t.Logf("root: %x", root)
|
t.Logf("root: %x", root)
|
||||||
// force-flush
|
// force-flush
|
||||||
|
|
|
||||||
|
|
@ -64,8 +64,10 @@ type accountUpdate struct {
|
||||||
// execution. It contains information about mutated contract codes, accounts,
|
// execution. It contains information about mutated contract codes, accounts,
|
||||||
// and storage slots, along with their original values.
|
// and storage slots, along with their original values.
|
||||||
type stateUpdate struct {
|
type stateUpdate struct {
|
||||||
originRoot common.Hash // hash of the state before applying mutation
|
originRoot common.Hash // hash of the state before applying mutation
|
||||||
root common.Hash // hash of the state after 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
|
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
|
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
|
// 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.
|
// 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 (
|
var (
|
||||||
accounts = make(map[common.Hash][]byte)
|
accounts = make(map[common.Hash][]byte)
|
||||||
accountsOrigin = make(map[common.Address][]byte)
|
accountsOrigin = make(map[common.Address][]byte)
|
||||||
|
|
@ -164,6 +166,7 @@ func newStateUpdate(rawStorageKey bool, originRoot common.Hash, root common.Hash
|
||||||
return &stateUpdate{
|
return &stateUpdate{
|
||||||
originRoot: originRoot,
|
originRoot: originRoot,
|
||||||
root: root,
|
root: root,
|
||||||
|
blockNumber: blockNumber,
|
||||||
accounts: accounts,
|
accounts: accounts,
|
||||||
accountsOrigin: accountsOrigin,
|
accountsOrigin: accountsOrigin,
|
||||||
storages: storages,
|
storages: storages,
|
||||||
|
|
|
||||||
|
|
@ -388,6 +388,10 @@ func (sf *subfetcher) loop() {
|
||||||
sf.tasks = nil
|
sf.tasks = nil
|
||||||
sf.lock.Unlock()
|
sf.lock.Unlock()
|
||||||
|
|
||||||
|
var (
|
||||||
|
addresses []common.Address
|
||||||
|
slots [][]byte
|
||||||
|
)
|
||||||
for _, task := range tasks {
|
for _, task := range tasks {
|
||||||
if task.addr != nil {
|
if task.addr != nil {
|
||||||
key := *task.addr
|
key := *task.addr
|
||||||
|
|
@ -400,6 +404,7 @@ func (sf *subfetcher) loop() {
|
||||||
sf.dupsCross++
|
sf.dupsCross++
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
sf.seenReadAddr[key] = struct{}{}
|
||||||
} else {
|
} else {
|
||||||
if _, ok := sf.seenReadAddr[key]; ok {
|
if _, ok := sf.seenReadAddr[key]; ok {
|
||||||
sf.dupsCross++
|
sf.dupsCross++
|
||||||
|
|
@ -409,7 +414,9 @@ func (sf *subfetcher) loop() {
|
||||||
sf.dupsWrite++
|
sf.dupsWrite++
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
sf.seenWriteAddr[key] = struct{}{}
|
||||||
}
|
}
|
||||||
|
addresses = append(addresses, *task.addr)
|
||||||
} else {
|
} else {
|
||||||
key := *task.slot
|
key := *task.slot
|
||||||
if task.read {
|
if task.read {
|
||||||
|
|
@ -421,6 +428,7 @@ func (sf *subfetcher) loop() {
|
||||||
sf.dupsCross++
|
sf.dupsCross++
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
sf.seenReadSlot[key] = struct{}{}
|
||||||
} else {
|
} else {
|
||||||
if _, ok := sf.seenReadSlot[key]; ok {
|
if _, ok := sf.seenReadSlot[key]; ok {
|
||||||
sf.dupsCross++
|
sf.dupsCross++
|
||||||
|
|
@ -430,25 +438,19 @@ func (sf *subfetcher) loop() {
|
||||||
sf.dupsWrite++
|
sf.dupsWrite++
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
sf.seenWriteSlot[key] = struct{}{}
|
||||||
}
|
}
|
||||||
|
slots = append(slots, key.Bytes())
|
||||||
}
|
}
|
||||||
if task.addr != nil {
|
}
|
||||||
sf.trie.GetAccount(*task.addr)
|
if len(addresses) != 0 {
|
||||||
} else {
|
if err := sf.trie.PrefetchAccount(addresses); err != nil {
|
||||||
sf.trie.GetStorage(sf.addr, (*task.slot)[:])
|
log.Error("Failed to prefetch accounts", "err", err)
|
||||||
}
|
}
|
||||||
if task.read {
|
}
|
||||||
if task.addr != nil {
|
if len(slots) != 0 {
|
||||||
sf.seenReadAddr[*task.addr] = struct{}{}
|
if err := sf.trie.PrefetchStorage(sf.addr, slots); err != nil {
|
||||||
} else {
|
log.Error("Failed to prefetch storage", "err", err)
|
||||||
sf.seenReadSlot[*task.slot] = struct{}{}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if task.addr != nil {
|
|
||||||
sf.seenWriteAddr[*task.addr] = struct{}{}
|
|
||||||
} else {
|
|
||||||
sf.seenWriteSlot[*task.slot] = struct{}{}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -39,7 +39,7 @@ func filledStateDB() *StateDB {
|
||||||
sval := common.HexToHash("bbb")
|
sval := common.HexToHash("bbb")
|
||||||
|
|
||||||
state.SetBalance(addr, uint256.NewInt(42), tracing.BalanceChangeUnspecified) // Change the account trie
|
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
|
state.SetState(addr, skey, sval) // Change the storage trie
|
||||||
for i := 0; i < 100; i++ {
|
for i := 0; i < 100; i++ {
|
||||||
sk := common.BigToHash(big.NewInt(int64(i)))
|
sk := common.BigToHash(big.NewInt(int64(i)))
|
||||||
|
|
@ -81,7 +81,7 @@ func TestVerklePrefetcher(t *testing.T) {
|
||||||
sval := testrand.Hash()
|
sval := testrand.Hash()
|
||||||
|
|
||||||
state.SetBalance(addr, uint256.NewInt(42), tracing.BalanceChangeUnspecified) // Change the account trie
|
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
|
state.SetState(addr, skey, sval) // Change the storage trie
|
||||||
root, _ := state.Commit(0, true, false)
|
root, _ := state.Commit(0, true, false)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -111,12 +111,6 @@ func (p *statePrefetcher) Prefetch(block *types.Block, statedb *state.StateDB, c
|
||||||
fails.Add(1)
|
fails.Add(1)
|
||||||
return nil // Ugh, something went horribly wrong, bail out
|
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
|
return nil
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -110,15 +110,15 @@ func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg
|
||||||
requests = [][]byte{}
|
requests = [][]byte{}
|
||||||
// EIP-6110
|
// EIP-6110
|
||||||
if err := ParseDepositLogs(&requests, allLogs, p.config); err != nil {
|
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
|
// EIP-7002
|
||||||
if err := ProcessWithdrawalQueue(&requests, evm); err != nil {
|
if err := ProcessWithdrawalQueue(&requests, evm); err != nil {
|
||||||
return nil, err
|
return nil, fmt.Errorf("failed to process withdrawal queue: %w", err)
|
||||||
}
|
}
|
||||||
// EIP-7251
|
// EIP-7251
|
||||||
if err := ProcessConsolidationQueue(&requests, evm); err != nil {
|
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)
|
st.state.SetNonce(authority, auth.Nonce+1, tracing.NonceChangeAuthorization)
|
||||||
if auth.Address == (common.Address{}) {
|
if auth.Address == (common.Address{}) {
|
||||||
// Delegation to zero address means clear.
|
// Delegation to zero address means clear.
|
||||||
st.state.SetCode(authority, nil)
|
st.state.SetCode(authority, nil, tracing.CodeChangeAuthorizationClear)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Otherwise install delegation to auth.Address.
|
// 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
|
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)
|
headers = append(headers, parent)
|
||||||
}
|
}
|
||||||
// Create the wtness with a reconstructed gutted out block
|
// Create the witness with a reconstructed gutted out block
|
||||||
return &Witness{
|
return &Witness{
|
||||||
context: context,
|
context: context,
|
||||||
Headers: headers,
|
Headers: headers,
|
||||||
|
|
@ -88,14 +88,16 @@ func (w *Witness) AddCode(code []byte) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// AddState inserts a batch of MPT trie nodes into the witness.
|
// 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 {
|
if len(nodes) == 0 {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
w.lock.Lock()
|
w.lock.Lock()
|
||||||
defer w.lock.Unlock()
|
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
|
// 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]
|
## [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.
|
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
|
### Deprecated methods
|
||||||
|
|
@ -23,8 +44,13 @@ The tracing interface has been extended with backwards-compatible changes to sup
|
||||||
|
|
||||||
### Modified types
|
### 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.
|
- `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
|
### State journaling
|
||||||
|
|
||||||
|
|
@ -49,6 +75,26 @@ The state changes that are covered by the journaling library are:
|
||||||
- `OnCodeChange`
|
- `OnCodeChange`
|
||||||
- `OnStorageChange`
|
- `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)
|
## [v1.14.9](https://github.com/ethereum/go-ethereum/releases/tag/v1.14.9)
|
||||||
|
|
||||||
### Modified types
|
### 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.
|
- `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.
|
- `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]
|
## [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.
|
- `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.
|
- `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
|
[unreleased]: https://github.com/ethereum/go-ethereum/compare/v1.16.3...master
|
||||||
[v1.14.0]: https://github.com/ethereum/go-ethereum/releases/tag/v1.14.0
|
[v1.15.4]: https://github.com/ethereum/go-ethereum/releases/tag/v1.15.4
|
||||||
[v1.14.3]: https://github.com/ethereum/go-ethereum/releases/tag/v1.14.3
|
[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.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 is called when the code of an account changes.
|
||||||
CodeChangeHook = func(addr common.Address, prevCodeHash common.Hash, prevCode []byte, codeHash common.Hash, code []byte)
|
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 is called when the storage of an account changes.
|
||||||
StorageChangeHook = func(addr common.Address, slot common.Hash, prev, new common.Hash)
|
StorageChangeHook = func(addr common.Address, slot common.Hash, prev, new common.Hash)
|
||||||
|
|
||||||
|
|
@ -211,6 +214,7 @@ type Hooks struct {
|
||||||
OnNonceChange NonceChangeHook
|
OnNonceChange NonceChangeHook
|
||||||
OnNonceChangeV2 NonceChangeHookV2
|
OnNonceChangeV2 NonceChangeHookV2
|
||||||
OnCodeChange CodeChangeHook
|
OnCodeChange CodeChangeHook
|
||||||
|
OnCodeChangeV2 CodeChangeHookV2
|
||||||
OnStorageChange StorageChangeHook
|
OnStorageChange StorageChangeHook
|
||||||
OnLog LogHook
|
OnLog LogHook
|
||||||
// Block hash read
|
// Block hash read
|
||||||
|
|
@ -372,3 +376,31 @@ const (
|
||||||
// It is only emitted when the tracer has opted in to use the journaling wrapper (WrapWithJournal).
|
// It is only emitted when the tracer has opted in to use the journaling wrapper (WrapWithJournal).
|
||||||
NonceChangeRevert NonceChangeReason = 6
|
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")
|
return nil, errors.New("wrapping nil tracer")
|
||||||
}
|
}
|
||||||
// No state change to journal, return the wrapped hooks as is
|
// 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
|
return hooks, nil
|
||||||
}
|
}
|
||||||
if hooks.OnNonceChange != nil && hooks.OnNonceChangeV2 != nil {
|
if hooks.OnNonceChange != nil && hooks.OnNonceChangeV2 != nil {
|
||||||
return nil, errors.New("cannot have both OnNonceChange and OnNonceChangeV2")
|
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
|
// Create a new Hooks instance and copy all hooks
|
||||||
wrapped := *hooks
|
wrapped := *hooks
|
||||||
|
|
@ -72,6 +75,9 @@ func WrapWithJournal(hooks *Hooks) (*Hooks, error) {
|
||||||
if hooks.OnCodeChange != nil {
|
if hooks.OnCodeChange != nil {
|
||||||
wrapped.OnCodeChange = j.OnCodeChange
|
wrapped.OnCodeChange = j.OnCodeChange
|
||||||
}
|
}
|
||||||
|
if hooks.OnCodeChangeV2 != nil {
|
||||||
|
wrapped.OnCodeChangeV2 = j.OnCodeChangeV2
|
||||||
|
}
|
||||||
if hooks.OnStorageChange != nil {
|
if hooks.OnStorageChange != nil {
|
||||||
wrapped.OnStorageChange = j.OnStorageChange
|
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) {
|
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})
|
j.entries = append(j.entries, storageChange{addr: addr, slot: slot, prev: prev, new: new})
|
||||||
if j.hooks.OnStorageChange != nil {
|
if j.hooks.OnStorageChange != nil {
|
||||||
|
|
@ -225,7 +244,9 @@ func (n nonceChange) revert(hooks *Hooks) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c codeChange) 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)
|
hooks.OnCodeChange(c.addr, c.newCodeHash, c.newCode, c.prevCodeHash, c.prevCode)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,7 @@ import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
)
|
)
|
||||||
|
|
||||||
type testTracer struct {
|
type testTracer struct {
|
||||||
|
|
@ -56,6 +57,11 @@ func (t *testTracer) OnCodeChange(addr common.Address, prevCodeHash common.Hash,
|
||||||
t.code = code
|
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) {
|
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)
|
t.t.Logf("OnStorageCodeChange(%v, %v, %v -> %v)", addr, slot, prev, new)
|
||||||
if t.storage == nil {
|
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) {
|
func TestAllHooksCalled(t *testing.T) {
|
||||||
tracer := newTracerAllHooks()
|
tracer := newTracerAllHooks()
|
||||||
hooks := tracer.hooks()
|
hooks := tracer.hooks()
|
||||||
|
|
@ -298,6 +325,7 @@ func newTracerAllHooks() *tracerAllHooks {
|
||||||
t.hooksCalled[hooksType.Field(i).Name] = false
|
t.hooksCalled[hooksType.Field(i).Name] = false
|
||||||
}
|
}
|
||||||
delete(t.hooksCalled, "OnNonceChange")
|
delete(t.hooksCalled, "OnNonceChange")
|
||||||
|
delete(t.hooksCalled, "OnCodeChange")
|
||||||
return t
|
return t
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -322,7 +350,7 @@ func (t *tracerAllHooks) hooks() *Hooks {
|
||||||
hooksValue := reflect.ValueOf(h).Elem()
|
hooksValue := reflect.ValueOf(h).Elem()
|
||||||
for i := 0; i < hooksValue.NumField(); i++ {
|
for i := 0; i < hooksValue.NumField(); i++ {
|
||||||
field := hooksValue.Type().Field(i)
|
field := hooksValue.Type().Field(i)
|
||||||
if field.Name == "OnNonceChange" {
|
if field.Name == "OnNonceChange" || field.Name == "OnCodeChange" {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
hookMethod := reflect.MakeFunc(field.Type, func(args []reflect.Value) []reflect.Value {
|
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.
|
// 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
|
// This is a utility method for the engine API, enabling consensus clients to
|
||||||
// retrieve blobs from the pools directly instead of the network.
|
// 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) {
|
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 {
|
if _, ok := filled[vhash]; ok {
|
||||||
continue
|
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()
|
p.lock.RLock()
|
||||||
txID, exists := p.lookup.storeidOfBlob(vhash)
|
txID, exists := p.lookup.storeidOfBlob(vhash)
|
||||||
p.lock.RUnlock()
|
p.lock.RUnlock()
|
||||||
if !exists {
|
if !exists {
|
||||||
return nil, nil, nil, fmt.Errorf("blob with vhash %x is not found", vhash)
|
continue
|
||||||
}
|
}
|
||||||
data, err := p.store.Get(txID)
|
data, err := p.store.Get(txID)
|
||||||
if err != nil {
|
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
|
// Decode the blob transaction
|
||||||
tx := new(types.Transaction)
|
tx := new(types.Transaction)
|
||||||
if err := rlp.DecodeBytes(data, tx); err != nil {
|
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()
|
sidecar := tx.BlobTxSidecar()
|
||||||
if sidecar == nil {
|
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
|
// Traverse the blobs in the transaction
|
||||||
for i, hash := range tx.BlobHashes() {
|
for i, hash := range tx.BlobHashes() {
|
||||||
|
|
@ -1397,6 +1408,31 @@ func (p *BlobPool) AvailableBlobs(vhashes []common.Hash) int {
|
||||||
return available
|
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
|
// Add inserts a set of blob transactions into the pool if they pass validation (both
|
||||||
// consensus validity and pool restrictions).
|
// 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.
|
// related to the add is finished. Only use this during tests for determinism.
|
||||||
func (p *BlobPool) Add(txs []*types.Transaction, sync bool) []error {
|
func (p *BlobPool) Add(txs []*types.Transaction, sync bool) []error {
|
||||||
var (
|
var (
|
||||||
|
errs []error
|
||||||
adds = make([]*types.Transaction, 0, len(txs))
|
adds = make([]*types.Transaction, 0, len(txs))
|
||||||
errs = make([]error, len(txs))
|
|
||||||
)
|
)
|
||||||
|
txs, errs = p.convertSidecar(txs)
|
||||||
for i, tx := range txs {
|
for i, tx := range txs {
|
||||||
|
if errs[i] != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
errs[i] = p.add(tx)
|
errs[i] = p.add(tx)
|
||||||
if errs[i] == nil {
|
if errs[i] == nil {
|
||||||
adds = append(adds, tx.WithoutBlobTxSidecar())
|
adds = append(adds, tx.WithoutBlobTxSidecar())
|
||||||
|
|
|
||||||
|
|
@ -24,9 +24,11 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"math"
|
"math"
|
||||||
"math/big"
|
"math/big"
|
||||||
|
"math/rand"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"reflect"
|
"reflect"
|
||||||
|
"slices"
|
||||||
"sync"
|
"sync"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
|
@ -40,6 +42,7 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
"github.com/ethereum/go-ethereum/crypto"
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
"github.com/ethereum/go-ethereum/crypto/kzg4844"
|
"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/params"
|
||||||
"github.com/ethereum/go-ethereum/rlp"
|
"github.com/ethereum/go-ethereum/rlp"
|
||||||
"github.com/holiman/billy"
|
"github.com/holiman/billy"
|
||||||
|
|
@ -47,11 +50,12 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
testBlobs []*kzg4844.Blob
|
testBlobs []*kzg4844.Blob
|
||||||
testBlobCommits []kzg4844.Commitment
|
testBlobCommits []kzg4844.Commitment
|
||||||
testBlobProofs []kzg4844.Proof
|
testBlobProofs []kzg4844.Proof
|
||||||
testBlobVHashes [][32]byte
|
testBlobCellProofs [][]kzg4844.Proof
|
||||||
testBlobIndices = make(map[[32]byte]int)
|
testBlobVHashes [][32]byte
|
||||||
|
testBlobIndices = make(map[[32]byte]int)
|
||||||
)
|
)
|
||||||
|
|
||||||
const testMaxBlobsPerBlock = 6
|
const testMaxBlobsPerBlock = 6
|
||||||
|
|
@ -67,6 +71,9 @@ func init() {
|
||||||
testBlobProof, _ := kzg4844.ComputeBlobProof(testBlob, testBlobCommit)
|
testBlobProof, _ := kzg4844.ComputeBlobProof(testBlob, testBlobCommit)
|
||||||
testBlobProofs = append(testBlobProofs, testBlobProof)
|
testBlobProofs = append(testBlobProofs, testBlobProof)
|
||||||
|
|
||||||
|
testBlobCellProof, _ := kzg4844.ComputeCellProofs(testBlob)
|
||||||
|
testBlobCellProofs = append(testBlobCellProofs, testBlobCellProof)
|
||||||
|
|
||||||
testBlobVHash := kzg4844.CalcBlobHashV1(sha256.New(), &testBlobCommit)
|
testBlobVHash := kzg4844.CalcBlobHashV1(sha256.New(), &testBlobCommit)
|
||||||
testBlobIndices[testBlobVHash] = len(testBlobVHashes)
|
testBlobIndices[testBlobVHash] = len(testBlobVHashes)
|
||||||
testBlobVHashes = append(testBlobVHashes, testBlobVHash)
|
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)))
|
return makeUnsignedTxWithTestBlob(nonce, gasTipCap, gasFeeCap, blobFeeCap, rnd.Intn(len(testBlobs)))
|
||||||
}
|
}
|
||||||
|
|
||||||
// makeUnsignedTx is a utility method to construct a random blob transaction
|
// makeUnsignedTxWithTestBlob is a utility method to construct a random blob transaction
|
||||||
// without signing it.
|
// with a specific test blob without signing it.
|
||||||
func makeUnsignedTxWithTestBlob(nonce uint64, gasTipCap uint64, gasFeeCap uint64, blobFeeCap uint64, blobIdx int) *types.BlobTx {
|
func makeUnsignedTxWithTestBlob(nonce uint64, gasTipCap uint64, gasFeeCap uint64, blobFeeCap uint64, blobIdx int) *types.BlobTx {
|
||||||
return &types.BlobTx{
|
return &types.BlobTx{
|
||||||
ChainID: uint256.MustFromBig(params.MainnetChainConfig.ChainID),
|
ChainID: uint256.MustFromBig(params.MainnetChainConfig.ChainID),
|
||||||
|
|
@ -416,24 +423,40 @@ func verifyBlobRetrievals(t *testing.T, pool *BlobPool) {
|
||||||
hashes = append(hashes, tx.vhashes...)
|
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 {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
// Cross validate what we received vs what we wanted
|
// Cross validate what we received vs what we wanted
|
||||||
if len(blobs) != len(hashes) || 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(blobs), len(proofs), 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
|
return
|
||||||
}
|
}
|
||||||
for i, hash := range hashes {
|
for i, hash := range hashes {
|
||||||
// If an item is missing, but shouldn't, error
|
// 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)
|
t.Errorf("tracked blob retrieval failed: item %d, hash %x", i, hash)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
// Item retrieved, make sure it matches the expectation
|
// Item retrieved, make sure it matches the expectation
|
||||||
index := testBlobIndices[hash]
|
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)
|
t.Errorf("retrieved blob or proof mismatch: item %d, hash %x", i, hash)
|
||||||
continue
|
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) {
|
func TestGetBlobs(t *testing.T) {
|
||||||
//log.SetDefault(log.NewLogger(log.NewTerminalHandlerWithLevel(os.Stderr, log.LevelTrace, true)))
|
//log.SetDefault(log.NewLogger(log.NewTerminalHandlerWithLevel(os.Stderr, log.LevelTrace, true)))
|
||||||
|
|
||||||
|
|
@ -1750,10 +1816,10 @@ func TestGetBlobs(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
cases := []struct {
|
cases := []struct {
|
||||||
start int
|
start int
|
||||||
limit int
|
limit int
|
||||||
version byte
|
fillRandom bool
|
||||||
expErr bool
|
version byte
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
start: 0, limit: 6,
|
start: 0, limit: 6,
|
||||||
|
|
@ -1763,6 +1829,14 @@ func TestGetBlobs(t *testing.T) {
|
||||||
start: 0, limit: 6,
|
start: 0, limit: 6,
|
||||||
version: types.BlobSidecarVersion1,
|
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,
|
start: 3, limit: 9,
|
||||||
version: types.BlobSidecarVersion0,
|
version: types.BlobSidecarVersion0,
|
||||||
|
|
@ -1771,6 +1845,14 @@ func TestGetBlobs(t *testing.T) {
|
||||||
start: 3, limit: 9,
|
start: 3, limit: 9,
|
||||||
version: types.BlobSidecarVersion1,
|
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,
|
start: 3, limit: 15,
|
||||||
version: types.BlobSidecarVersion0,
|
version: types.BlobSidecarVersion0,
|
||||||
|
|
@ -1779,6 +1861,14 @@ func TestGetBlobs(t *testing.T) {
|
||||||
start: 3, limit: 15,
|
start: 3, limit: 15,
|
||||||
version: types.BlobSidecarVersion1,
|
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,
|
start: 0, limit: 18,
|
||||||
version: types.BlobSidecarVersion0,
|
version: types.BlobSidecarVersion0,
|
||||||
|
|
@ -1788,58 +1878,79 @@ func TestGetBlobs(t *testing.T) {
|
||||||
version: types.BlobSidecarVersion1,
|
version: types.BlobSidecarVersion1,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
start: 18, limit: 20,
|
start: 0, limit: 18, fillRandom: true,
|
||||||
version: types.BlobSidecarVersion0,
|
version: types.BlobSidecarVersion0,
|
||||||
expErr: true,
|
},
|
||||||
|
{
|
||||||
|
start: 0, limit: 18, fillRandom: true,
|
||||||
|
version: types.BlobSidecarVersion1,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
for i, c := range cases {
|
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++ {
|
for j := c.start; j < c.limit; j++ {
|
||||||
vhashes = append(vhashes, testBlobVHashes[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)
|
blobs, _, proofs, err := pool.GetBlobs(vhashes, c.version)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("Unexpected error for case %d, %v", i, err)
|
||||||
|
}
|
||||||
|
|
||||||
if c.expErr {
|
// Cross validate what we received vs what we wanted
|
||||||
if err == nil {
|
length := c.limit - c.start
|
||||||
t.Errorf("Unexpected return, want error for case %d", i)
|
wantLen := length + len(filled)
|
||||||
}
|
if len(blobs) != wantLen || len(proofs) != wantLen {
|
||||||
} else {
|
t.Errorf("retrieved blobs/proofs size mismatch: have %d/%d, want %d", len(blobs), len(proofs), wantLen)
|
||||||
if err != nil {
|
continue
|
||||||
t.Errorf("Unexpected error for case %d, %v", i, err)
|
}
|
||||||
}
|
|
||||||
// Cross validate what we received vs what we wanted
|
var unknown int
|
||||||
length := c.limit - c.start
|
for j := 0; j < len(blobs); j++ {
|
||||||
if len(blobs) != length || len(proofs) != length {
|
if _, exist := filled[j]; exist {
|
||||||
t.Errorf("retrieved blobs/proofs size mismatch: have %d/%d, want %d", len(blobs), len(proofs), length)
|
if blobs[j] != nil || proofs[j] != nil {
|
||||||
|
t.Errorf("Unexpected blob and proof, item %d", j)
|
||||||
|
}
|
||||||
|
unknown++
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
for j := 0; j < len(blobs); j++ {
|
// If an item is missing, but shouldn't, error
|
||||||
// If an item is missing, but shouldn't, error
|
if blobs[j] == nil || proofs[j] == nil {
|
||||||
if blobs[j] == nil || proofs[j] == nil {
|
t.Errorf("tracked blob retrieval failed: item %d, hash %x", j, vhashes[j])
|
||||||
t.Errorf("tracked blob retrieval failed: item %d, hash %x", j, vhashes[j])
|
continue
|
||||||
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
|
} else {
|
||||||
if *blobs[j] != *testBlobs[c.start+j] {
|
want, _ := kzg4844.ComputeCellProofs(blobs[j])
|
||||||
t.Errorf("retrieved blob mismatch: item %d, hash %x", j, vhashes[j])
|
if !reflect.DeepEqual(want, proofs[j]) {
|
||||||
continue
|
t.Errorf("retrieved proof mismatch: item %d, hash %x", j, vhashes[j])
|
||||||
}
|
|
||||||
// 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])
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pool.Close()
|
pool.Close()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -514,26 +514,15 @@ func (pool *LegacyPool) Pending(filter txpool.PendingFilter) map[common.Address]
|
||||||
pool.mu.Lock()
|
pool.mu.Lock()
|
||||||
defer pool.mu.Unlock()
|
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))
|
pending := make(map[common.Address][]*txpool.LazyTransaction, len(pool.pending))
|
||||||
for addr, list := range pool.pending {
|
for addr, list := range pool.pending {
|
||||||
txs := list.Flatten()
|
txs := list.Flatten()
|
||||||
|
|
||||||
// If the miner requests tip enforcement, cap the lists now
|
// 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 {
|
for i, tx := range txs {
|
||||||
if minTipBig != nil {
|
if filter.MinTip != nil {
|
||||||
if tx.EffectiveGasTipIntCmp(minTipBig, baseFeeBig) < 0 {
|
if tx.EffectiveGasTipIntCmp(filter.MinTip, filter.BaseFee) < 0 {
|
||||||
txs = txs[:i]
|
txs = txs[:i]
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2292,8 +2292,8 @@ func TestSetCodeTransactions(t *testing.T) {
|
||||||
pending: 1,
|
pending: 1,
|
||||||
run: func(name string) {
|
run: func(name string) {
|
||||||
aa := common.Address{0xaa, 0xaa}
|
aa := common.Address{0xaa, 0xaa}
|
||||||
statedb.SetCode(addrA, append(types.DelegationPrefix, aa.Bytes()...))
|
statedb.SetCode(addrA, append(types.DelegationPrefix, aa.Bytes()...), tracing.CodeChangeUnspecified)
|
||||||
statedb.SetCode(aa, []byte{byte(vm.ADDRESS), byte(vm.PUSH0), byte(vm.SSTORE)})
|
statedb.SetCode(aa, []byte{byte(vm.ADDRESS), byte(vm.PUSH0), byte(vm.SSTORE)}, tracing.CodeChangeUnspecified)
|
||||||
|
|
||||||
// Send gapped transaction, it should be rejected.
|
// Send gapped transaction, it should be rejected.
|
||||||
if err := pool.addRemoteSync(pricedTransaction(2, 100000, big.NewInt(1), keyA)); !errors.Is(err, ErrOutOfOrderTxFromDelegated) {
|
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
|
// 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
|
// Simulate the chain moving
|
||||||
blockchain.statedb.SetNonce(addrA, 1, tracing.NonceChangeAuthorization)
|
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)
|
<-pool.requestReset(nil, nil)
|
||||||
// Set an authorization for 0x00
|
// Set an authorization for 0x00
|
||||||
auth, _ = types.SignSetCode(keyA, types.SetCodeAuthorization{
|
auth, _ = types.SignSetCode(keyA, types.SetCodeAuthorization{
|
||||||
|
|
@ -2601,7 +2601,7 @@ func TestSetCodeTransactionsReorg(t *testing.T) {
|
||||||
}
|
}
|
||||||
// Simulate the chain moving
|
// Simulate the chain moving
|
||||||
blockchain.statedb.SetNonce(addrA, 2, tracing.NonceChangeAuthorization)
|
blockchain.statedb.SetNonce(addrA, 2, tracing.NonceChangeAuthorization)
|
||||||
blockchain.statedb.SetCode(addrA, nil)
|
blockchain.statedb.SetCode(addrA, nil, tracing.CodeChangeUnspecified)
|
||||||
<-pool.requestReset(nil, nil)
|
<-pool.requestReset(nil, nil)
|
||||||
// Now send two transactions from addrA
|
// Now send two transactions from addrA
|
||||||
if err := pool.addRemoteSync(pricedTransaction(2, 100000, big.NewInt(1000), keyA)); err != nil {
|
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.
|
// 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.
|
// If baseFee is nil then the sorting is based on gasFeeCap.
|
||||||
type priceHeap struct {
|
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
|
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
|
// 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.
|
// necessary to call right before SetBaseFee when processing a new block.
|
||||||
func (l *pricedList) SetBaseFee(baseFee *big.Int) {
|
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()
|
l.Reheap()
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,6 @@ import (
|
||||||
"math/big"
|
"math/big"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"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"
|
||||||
"github.com/ethereum/go-ethereum/core/state"
|
"github.com/ethereum/go-ethereum/core/state"
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"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 {
|
if len(hashes) == 0 {
|
||||||
return errors.New("blobless blob transaction")
|
return errors.New("blobless blob transaction")
|
||||||
}
|
}
|
||||||
maxBlobs := eip4844.MaxBlobsPerBlock(opts.Config, head.Time)
|
if len(hashes) > params.BlobTxMaxBlobs {
|
||||||
if len(hashes) > maxBlobs {
|
return fmt.Errorf("too many blobs in transaction: have %d, permitted %d", len(hashes), params.BlobTxMaxBlobs)
|
||||||
return fmt.Errorf("too many blobs in transaction: have %d, permitted %d", len(hashes), maxBlobs)
|
|
||||||
}
|
}
|
||||||
if len(sidecar.Blobs) != len(hashes) {
|
if len(sidecar.Blobs) != len(hashes) {
|
||||||
return fmt.Errorf("invalid number of %d blobs compared to %d blob hashes", 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 is the known hash of an empty verkle trie.
|
||||||
EmptyVerkleHash = common.Hash{}
|
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/common"
|
||||||
"github.com/ethereum/go-ethereum/crypto"
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
"github.com/ethereum/go-ethereum/rlp"
|
"github.com/ethereum/go-ethereum/rlp"
|
||||||
|
"github.com/holiman/uint256"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
|
@ -36,6 +37,7 @@ var (
|
||||||
ErrInvalidTxType = errors.New("transaction type not valid in this context")
|
ErrInvalidTxType = errors.New("transaction type not valid in this context")
|
||||||
ErrTxTypeNotSupported = errors.New("transaction type not supported")
|
ErrTxTypeNotSupported = errors.New("transaction type not supported")
|
||||||
ErrGasFeeCapTooLow = errors.New("fee cap less than base fee")
|
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")
|
errShortTypedTx = errors.New("typed transaction too short")
|
||||||
errInvalidYParity = errors.New("'yParity' field must be 0 or 1")
|
errInvalidYParity = errors.New("'yParity' field must be 0 or 1")
|
||||||
errVYParityMismatch = errors.New("'v' and 'yParity' fields do not match")
|
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.
|
// EffectiveGasTip returns the effective miner gasTipCap for the given base fee.
|
||||||
// Note: if the effective gasTipCap is negative, this method returns both error
|
// Note: if the effective gasTipCap would be negative, this method
|
||||||
// the actual negative value, _and_ ErrGasFeeCapTooLow
|
// returns ErrGasFeeCapTooLow, and value is undefined.
|
||||||
func (tx *Transaction) EffectiveGasTip(baseFee *big.Int) (*big.Int, error) {
|
func (tx *Transaction) EffectiveGasTip(baseFee *big.Int) (*big.Int, error) {
|
||||||
dst := new(big.Int)
|
dst := new(uint256.Int)
|
||||||
err := tx.calcEffectiveGasTip(dst, baseFee)
|
base := new(uint256.Int)
|
||||||
return dst, err
|
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
|
// calcEffectiveGasTip calculates the effective gas tip of the transaction and
|
||||||
// saves the result to dst.
|
// 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 {
|
if baseFee == nil {
|
||||||
dst.Set(tx.inner.gasTipCap())
|
if dst.SetFromBig(tx.inner.gasTipCap()) {
|
||||||
|
return ErrUint256Overflow
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
var err error
|
var err error
|
||||||
gasFeeCap := tx.inner.gasFeeCap()
|
if dst.SetFromBig(tx.inner.gasFeeCap()) {
|
||||||
if gasFeeCap.Cmp(baseFee) < 0 {
|
return ErrUint256Overflow
|
||||||
|
}
|
||||||
|
if dst.Cmp(baseFee) < 0 {
|
||||||
err = ErrGasFeeCapTooLow
|
err = ErrGasFeeCapTooLow
|
||||||
}
|
}
|
||||||
|
|
||||||
dst.Sub(gasFeeCap, baseFee)
|
dst.Sub(dst, baseFee)
|
||||||
gasTipCap := tx.inner.gasTipCap()
|
gasTipCap := new(uint256.Int)
|
||||||
|
if gasTipCap.SetFromBig(tx.inner.gasTipCap()) {
|
||||||
|
return ErrUint256Overflow
|
||||||
|
}
|
||||||
if gasTipCap.Cmp(dst) < 0 {
|
if gasTipCap.Cmp(dst) < 0 {
|
||||||
dst.Set(gasTipCap)
|
dst.Set(gasTipCap)
|
||||||
}
|
}
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// EffectiveGasTipCmp compares the effective gasTipCap of two transactions assuming the given base fee.
|
func (tx *Transaction) EffectiveGasTipCmp(other *Transaction, baseFee *uint256.Int) int {
|
||||||
func (tx *Transaction) EffectiveGasTipCmp(other *Transaction, baseFee *big.Int) int {
|
|
||||||
if baseFee == nil {
|
if baseFee == nil {
|
||||||
return tx.GasTipCapCmp(other)
|
return tx.GasTipCapCmp(other)
|
||||||
}
|
}
|
||||||
// Use more efficient internal method.
|
// 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)
|
tx.calcEffectiveGasTip(txTip, baseFee)
|
||||||
other.calcEffectiveGasTip(otherTip, baseFee)
|
other.calcEffectiveGasTip(otherTip, baseFee)
|
||||||
return txTip.Cmp(otherTip)
|
return txTip.Cmp(otherTip)
|
||||||
}
|
}
|
||||||
|
|
||||||
// EffectiveGasTipIntCmp compares the effective gasTipCap of a transaction to the given gasTipCap.
|
// 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 {
|
if baseFee == nil {
|
||||||
return tx.GasTipCapIntCmp(other)
|
return tx.GasTipCapIntCmp(other.ToBig())
|
||||||
}
|
}
|
||||||
txTip := new(big.Int)
|
txTip := new(uint256.Int)
|
||||||
tx.calcEffectiveGasTip(txTip, baseFee)
|
tx.calcEffectiveGasTip(txTip, baseFee)
|
||||||
return txTip.Cmp(other)
|
return txTip.Cmp(other)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -31,6 +31,7 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/crypto"
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
"github.com/ethereum/go-ethereum/params"
|
"github.com/ethereum/go-ethereum/params"
|
||||||
"github.com/ethereum/go-ethereum/rlp"
|
"github.com/ethereum/go-ethereum/rlp"
|
||||||
|
"github.com/holiman/uint256"
|
||||||
)
|
)
|
||||||
|
|
||||||
// The values in those tests are from the Transaction Tests
|
// The values in those tests are from the Transaction Tests
|
||||||
|
|
@ -609,12 +610,12 @@ func BenchmarkEffectiveGasTip(b *testing.B) {
|
||||||
Data: nil,
|
Data: nil,
|
||||||
}
|
}
|
||||||
tx, _ := SignNewTx(key, signer, txdata)
|
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.Run("Original", func(b *testing.B) {
|
||||||
b.ReportAllocs()
|
b.ReportAllocs()
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
_, err := tx.EffectiveGasTip(baseFee)
|
_, err := tx.EffectiveGasTip(baseFee.ToBig())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
b.Fatal(err)
|
b.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
@ -623,7 +624,7 @@ func BenchmarkEffectiveGasTip(b *testing.B) {
|
||||||
|
|
||||||
b.Run("IntoMethod", func(b *testing.B) {
|
b.Run("IntoMethod", func(b *testing.B) {
|
||||||
b.ReportAllocs()
|
b.ReportAllocs()
|
||||||
dst := new(big.Int)
|
dst := new(uint256.Int)
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
err := tx.calcEffectiveGasTip(dst, baseFee)
|
err := tx.calcEffectiveGasTip(dst, baseFee)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -634,9 +635,6 @@ func BenchmarkEffectiveGasTip(b *testing.B) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestEffectiveGasTipInto(t *testing.T) {
|
func TestEffectiveGasTipInto(t *testing.T) {
|
||||||
signer := LatestSigner(params.TestChainConfig)
|
|
||||||
key, _ := crypto.GenerateKey()
|
|
||||||
|
|
||||||
testCases := []struct {
|
testCases := []struct {
|
||||||
tipCap int64
|
tipCap int64
|
||||||
feeCap int64
|
feeCap int64
|
||||||
|
|
@ -652,8 +650,26 @@ func TestEffectiveGasTipInto(t *testing.T) {
|
||||||
{tipCap: 50, feeCap: 100, baseFee: nil}, // nil base fee
|
{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 {
|
for i, tc := range testCases {
|
||||||
txdata := &DynamicFeeTx{
|
tx := NewTx(&DynamicFeeTx{
|
||||||
ChainID: big.NewInt(1),
|
ChainID: big.NewInt(1),
|
||||||
Nonce: 0,
|
Nonce: 0,
|
||||||
GasTipCap: big.NewInt(tc.tipCap),
|
GasTipCap: big.NewInt(tc.tipCap),
|
||||||
|
|
@ -662,27 +678,28 @@ func TestEffectiveGasTipInto(t *testing.T) {
|
||||||
To: &common.Address{},
|
To: &common.Address{},
|
||||||
Value: big.NewInt(0),
|
Value: big.NewInt(0),
|
||||||
Data: nil,
|
Data: nil,
|
||||||
}
|
})
|
||||||
tx, _ := SignNewTx(key, signer, txdata)
|
|
||||||
|
|
||||||
var baseFee *big.Int
|
var baseFee *big.Int
|
||||||
|
var baseFee2 *uint256.Int
|
||||||
if tc.baseFee != nil {
|
if tc.baseFee != nil {
|
||||||
baseFee = big.NewInt(*tc.baseFee)
|
baseFee = big.NewInt(*tc.baseFee)
|
||||||
|
baseFee2 = uint256.NewInt(uint64(*tc.baseFee))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get result from original method
|
// Get result from original method
|
||||||
orig, origErr := tx.EffectiveGasTip(baseFee)
|
orig, origErr := orig(tx, baseFee)
|
||||||
|
|
||||||
// Get result from new method
|
// Get result from new method
|
||||||
dst := new(big.Int)
|
dst := new(uint256.Int)
|
||||||
newErr := tx.calcEffectiveGasTip(dst, baseFee)
|
newErr := tx.calcEffectiveGasTip(dst, baseFee2)
|
||||||
|
|
||||||
// Compare results
|
// Compare results
|
||||||
if (origErr != nil) != (newErr != nil) {
|
if (origErr != nil) != (newErr != nil) {
|
||||||
t.Fatalf("case %d: error mismatch: orig %v, new %v", i, origErr, newErr)
|
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)
|
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 {
|
func intPtr(i int64) *int64 {
|
||||||
return &i
|
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
|
// etc
|
||||||
checkBlockHashes := func(statedb *state.StateDB, isVerkle bool) {
|
checkBlockHashes := func(statedb *state.StateDB, isVerkle bool) {
|
||||||
statedb.SetNonce(params.HistoryStorageAddress, 1, tracing.NonceChangeUnspecified)
|
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
|
// Process n blocks, from 1 .. num
|
||||||
var num = 2
|
var num = 2
|
||||||
for i := 1; i <= num; i++ {
|
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.
|
// a eip6780-compliant selfdestruct occurs.
|
||||||
func TestProcessVerkleSelfDestructInSameTx(t *testing.T) {
|
func TestProcessVerkleSelfDestructInSameTx(t *testing.T) {
|
||||||
// The test txs were taken from a secondary testnet with chain id 69421
|
// The test txs were taken from a secondary testnet with chain id 69421
|
||||||
|
|
|
||||||
|
|
@ -24,6 +24,7 @@ import (
|
||||||
"maps"
|
"maps"
|
||||||
"math"
|
"math"
|
||||||
"math/big"
|
"math/big"
|
||||||
|
"math/bits"
|
||||||
|
|
||||||
"github.com/consensys/gnark-crypto/ecc"
|
"github.com/consensys/gnark-crypto/ecc"
|
||||||
bls12381 "github.com/consensys/gnark-crypto/ecc/bls12-381"
|
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/kzg4844"
|
||||||
"github.com/ethereum/go-ethereum/crypto/secp256r1"
|
"github.com/ethereum/go-ethereum/crypto/secp256r1"
|
||||||
"github.com/ethereum/go-ethereum/params"
|
"github.com/ethereum/go-ethereum/params"
|
||||||
|
"github.com/holiman/uint256"
|
||||||
"golang.org/x/crypto/ripemd160"
|
"golang.org/x/crypto/ripemd160"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -47,6 +49,7 @@ import (
|
||||||
type PrecompiledContract interface {
|
type PrecompiledContract interface {
|
||||||
RequiredGas(input []byte) uint64 // RequiredPrice calculates the contract gas use
|
RequiredGas(input []byte) uint64 // RequiredPrice calculates the contract gas use
|
||||||
Run(input []byte) ([]byte, error) // Run runs the precompiled contract
|
Run(input []byte) ([]byte, error) // Run runs the precompiled contract
|
||||||
|
Name() string
|
||||||
}
|
}
|
||||||
|
|
||||||
// PrecompiledContracts contains the precompiled contracts supported at the given fork.
|
// 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
|
return common.LeftPadBytes(crypto.Keccak256(pubKey[1:])[12:], 32), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *ecrecover) Name() string {
|
||||||
|
return "ECREC"
|
||||||
|
}
|
||||||
|
|
||||||
// SHA256 implemented as a native contract.
|
// SHA256 implemented as a native contract.
|
||||||
type sha256hash struct{}
|
type sha256hash struct{}
|
||||||
|
|
||||||
|
|
@ -324,6 +331,10 @@ func (c *sha256hash) Run(input []byte) ([]byte, error) {
|
||||||
return h[:], nil
|
return h[:], nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *sha256hash) Name() string {
|
||||||
|
return "SHA256"
|
||||||
|
}
|
||||||
|
|
||||||
// RIPEMD160 implemented as a native contract.
|
// RIPEMD160 implemented as a native contract.
|
||||||
type ripemd160hash struct{}
|
type ripemd160hash struct{}
|
||||||
|
|
||||||
|
|
@ -340,6 +351,10 @@ func (c *ripemd160hash) Run(input []byte) ([]byte, error) {
|
||||||
return common.LeftPadBytes(ripemd.Sum(nil), 32), nil
|
return common.LeftPadBytes(ripemd.Sum(nil), 32), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *ripemd160hash) Name() string {
|
||||||
|
return "RIPEMD160"
|
||||||
|
}
|
||||||
|
|
||||||
// data copy implemented as a native contract.
|
// data copy implemented as a native contract.
|
||||||
type dataCopy struct{}
|
type dataCopy struct{}
|
||||||
|
|
||||||
|
|
@ -354,6 +369,10 @@ func (c *dataCopy) Run(in []byte) ([]byte, error) {
|
||||||
return common.CopyBytes(in), nil
|
return common.CopyBytes(in), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *dataCopy) Name() string {
|
||||||
|
return "ID"
|
||||||
|
}
|
||||||
|
|
||||||
// bigModExp implements a native big integer exponential modular operation.
|
// bigModExp implements a native big integer exponential modular operation.
|
||||||
type bigModExp struct {
|
type bigModExp struct {
|
||||||
eip2565 bool
|
eip2565 bool
|
||||||
|
|
@ -361,21 +380,7 @@ type bigModExp struct {
|
||||||
eip7883 bool
|
eip7883 bool
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
// byzantiumMultComplexity implements the bigModexp multComplexity formula, as defined in EIP-198.
|
||||||
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
|
|
||||||
//
|
//
|
||||||
// def mult_complexity(x):
|
// def mult_complexity(x):
|
||||||
// if x <= 64: return x ** 2
|
// if x <= 64: return x ** 2
|
||||||
|
|
@ -383,120 +388,215 @@ var (
|
||||||
// else: return x ** 2 // 16 + 480 * x - 199680
|
// else: return x ** 2 // 16 + 480 * x - 199680
|
||||||
//
|
//
|
||||||
// where is x is max(length_of_MODULUS, length_of_BASE)
|
// 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 {
|
switch {
|
||||||
case x.Cmp(big64) <= 0:
|
case x <= 64:
|
||||||
x.Mul(x, x) // x ** 2
|
return x * x
|
||||||
case x.Cmp(big1024) <= 0:
|
case x <= 1024:
|
||||||
// (x ** 2 // 4 ) + ( 96 * x - 3072)
|
// x^2 / 4 + 96*x - 3072
|
||||||
x = new(big.Int).Add(
|
return x*x/4 + 96*x - 3072
|
||||||
new(big.Int).Rsh(new(big.Int).Mul(x, x), 2),
|
|
||||||
new(big.Int).Sub(new(big.Int).Mul(big96, x), big3072),
|
|
||||||
)
|
|
||||||
default:
|
default:
|
||||||
// (x ** 2 // 16) + (480 * x - 199680)
|
// For large x, use uint256 arithmetic to avoid overflow
|
||||||
x = new(big.Int).Add(
|
// x^2 / 16 + 480*x - 199680
|
||||||
new(big.Int).Rsh(new(big.Int).Mul(x, x), 4),
|
|
||||||
new(big.Int).Sub(new(big.Int).Mul(big480, x), big199680),
|
// 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
|
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.
|
// RequiredGas returns the gas required to execute the pre-compiled contract.
|
||||||
func (c *bigModExp) RequiredGas(input []byte) uint64 {
|
func (c *bigModExp) RequiredGas(input []byte) uint64 {
|
||||||
var (
|
// Parse input lengths
|
||||||
baseLen = new(big.Int).SetBytes(getData(input, 0, 32))
|
baseLenBig := new(uint256.Int).SetBytes(getData(input, 0, 32))
|
||||||
expLen = new(big.Int).SetBytes(getData(input, 32, 32))
|
expLenBig := new(uint256.Int).SetBytes(getData(input, 32, 32))
|
||||||
modLen = new(big.Int).SetBytes(getData(input, 64, 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 {
|
if len(input) > 96 {
|
||||||
input = input[96:]
|
input = input[96:]
|
||||||
} else {
|
} else {
|
||||||
input = input[:0]
|
input = input[:0]
|
||||||
}
|
}
|
||||||
|
|
||||||
// Retrieve the head 32 bytes of exp for the adjusted exponent length
|
// Retrieve the head 32 bytes of exp for the adjusted exponent length
|
||||||
var expHead *big.Int
|
var expHead uint256.Int
|
||||||
if big.NewInt(int64(len(input))).Cmp(baseLen) <= 0 {
|
if uint64(len(input)) > baseLen {
|
||||||
expHead = new(big.Int)
|
if expLen > 32 {
|
||||||
} else {
|
expHead.SetBytes(getData(input, baseLen, 32))
|
||||||
if expLen.Cmp(big32) > 0 {
|
|
||||||
expHead = new(big.Int).SetBytes(getData(input, baseLen.Uint64(), 32))
|
|
||||||
} else {
|
} 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
|
// Choose the appropriate gas calculation based on the EIP flags
|
||||||
if bitlen := expHead.BitLen(); bitlen > 0 {
|
if c.eip7883 {
|
||||||
msb = bitlen - 1
|
return osakaModexpGas(baseLen, expLen, modLen, expHead)
|
||||||
}
|
} else if c.eip2565 {
|
||||||
adjExpLen := new(big.Int)
|
return berlinModexpGas(baseLen, expLen, modLen, expHead)
|
||||||
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)
|
|
||||||
} else {
|
} 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) {
|
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
|
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,
|
// newCurvePoint unmarshals a binary blob into a bn256 elliptic curve point,
|
||||||
// returning it, or an error if the point is invalid.
|
// returning it, or an error if the point is invalid.
|
||||||
func newCurvePoint(blob []byte) (*bn256.G1, error) {
|
func newCurvePoint(blob []byte) (*bn256.G1, error) {
|
||||||
|
|
@ -592,6 +696,10 @@ func (c *bn256AddIstanbul) Run(input []byte) ([]byte, error) {
|
||||||
return runBn256Add(input)
|
return runBn256Add(input)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *bn256AddIstanbul) Name() string {
|
||||||
|
return "BN254_ADD"
|
||||||
|
}
|
||||||
|
|
||||||
// bn256AddByzantium implements a native elliptic curve point addition
|
// bn256AddByzantium implements a native elliptic curve point addition
|
||||||
// conforming to Byzantium consensus rules.
|
// conforming to Byzantium consensus rules.
|
||||||
type bn256AddByzantium struct{}
|
type bn256AddByzantium struct{}
|
||||||
|
|
@ -605,6 +713,10 @@ func (c *bn256AddByzantium) Run(input []byte) ([]byte, error) {
|
||||||
return runBn256Add(input)
|
return runBn256Add(input)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *bn256AddByzantium) Name() string {
|
||||||
|
return "BN254_ADD"
|
||||||
|
}
|
||||||
|
|
||||||
// runBn256ScalarMul implements the Bn256ScalarMul precompile, referenced by
|
// runBn256ScalarMul implements the Bn256ScalarMul precompile, referenced by
|
||||||
// both Byzantium and Istanbul operations.
|
// both Byzantium and Istanbul operations.
|
||||||
func runBn256ScalarMul(input []byte) ([]byte, error) {
|
func runBn256ScalarMul(input []byte) ([]byte, error) {
|
||||||
|
|
@ -630,6 +742,10 @@ func (c *bn256ScalarMulIstanbul) Run(input []byte) ([]byte, error) {
|
||||||
return runBn256ScalarMul(input)
|
return runBn256ScalarMul(input)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *bn256ScalarMulIstanbul) Name() string {
|
||||||
|
return "BN254_MUL"
|
||||||
|
}
|
||||||
|
|
||||||
// bn256ScalarMulByzantium implements a native elliptic curve scalar
|
// bn256ScalarMulByzantium implements a native elliptic curve scalar
|
||||||
// multiplication conforming to Byzantium consensus rules.
|
// multiplication conforming to Byzantium consensus rules.
|
||||||
type bn256ScalarMulByzantium struct{}
|
type bn256ScalarMulByzantium struct{}
|
||||||
|
|
@ -643,6 +759,10 @@ func (c *bn256ScalarMulByzantium) Run(input []byte) ([]byte, error) {
|
||||||
return runBn256ScalarMul(input)
|
return runBn256ScalarMul(input)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *bn256ScalarMulByzantium) Name() string {
|
||||||
|
return "BN254_MUL"
|
||||||
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
// true32Byte is returned if the bn256 pairing check succeeds.
|
// 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}
|
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)
|
return runBn256Pairing(input)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *bn256PairingIstanbul) Name() string {
|
||||||
|
return "BN254_PAIRING"
|
||||||
|
}
|
||||||
|
|
||||||
// bn256PairingByzantium implements a pairing pre-compile for the bn256 curve
|
// bn256PairingByzantium implements a pairing pre-compile for the bn256 curve
|
||||||
// conforming to Byzantium consensus rules.
|
// conforming to Byzantium consensus rules.
|
||||||
type bn256PairingByzantium struct{}
|
type bn256PairingByzantium struct{}
|
||||||
|
|
@ -711,6 +835,10 @@ func (c *bn256PairingByzantium) Run(input []byte) ([]byte, error) {
|
||||||
return runBn256Pairing(input)
|
return runBn256Pairing(input)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *bn256PairingByzantium) Name() string {
|
||||||
|
return "BN254_PAIRING"
|
||||||
|
}
|
||||||
|
|
||||||
type blake2F struct{}
|
type blake2F struct{}
|
||||||
|
|
||||||
func (c *blake2F) RequiredGas(input []byte) uint64 {
|
func (c *blake2F) RequiredGas(input []byte) uint64 {
|
||||||
|
|
@ -772,6 +900,10 @@ func (c *blake2F) Run(input []byte) ([]byte, error) {
|
||||||
return output, nil
|
return output, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *blake2F) Name() string {
|
||||||
|
return "BLAKE2F"
|
||||||
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
errBLS12381InvalidInputLength = errors.New("invalid input length")
|
errBLS12381InvalidInputLength = errors.New("invalid input length")
|
||||||
errBLS12381InvalidFieldElementTopBytes = errors.New("invalid field element top bytes")
|
errBLS12381InvalidFieldElementTopBytes = errors.New("invalid field element top bytes")
|
||||||
|
|
@ -815,6 +947,10 @@ func (c *bls12381G1Add) Run(input []byte) ([]byte, error) {
|
||||||
return encodePointG1(p0), nil
|
return encodePointG1(p0), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *bls12381G1Add) Name() string {
|
||||||
|
return "BLS12_G1ADD"
|
||||||
|
}
|
||||||
|
|
||||||
// bls12381G1MultiExp implements EIP-2537 G1MultiExp precompile.
|
// bls12381G1MultiExp implements EIP-2537 G1MultiExp precompile.
|
||||||
type bls12381G1MultiExp struct{}
|
type bls12381G1MultiExp struct{}
|
||||||
|
|
||||||
|
|
@ -875,6 +1011,10 @@ func (c *bls12381G1MultiExp) Run(input []byte) ([]byte, error) {
|
||||||
return encodePointG1(r), nil
|
return encodePointG1(r), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *bls12381G1MultiExp) Name() string {
|
||||||
|
return "BLS12_G1MSM"
|
||||||
|
}
|
||||||
|
|
||||||
// bls12381G2Add implements EIP-2537 G2Add precompile.
|
// bls12381G2Add implements EIP-2537 G2Add precompile.
|
||||||
type bls12381G2Add struct{}
|
type bls12381G2Add struct{}
|
||||||
|
|
||||||
|
|
@ -912,6 +1052,10 @@ func (c *bls12381G2Add) Run(input []byte) ([]byte, error) {
|
||||||
return encodePointG2(r), nil
|
return encodePointG2(r), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *bls12381G2Add) Name() string {
|
||||||
|
return "BLS12_G2ADD"
|
||||||
|
}
|
||||||
|
|
||||||
// bls12381G2MultiExp implements EIP-2537 G2MultiExp precompile.
|
// bls12381G2MultiExp implements EIP-2537 G2MultiExp precompile.
|
||||||
type bls12381G2MultiExp struct{}
|
type bls12381G2MultiExp struct{}
|
||||||
|
|
||||||
|
|
@ -972,6 +1116,10 @@ func (c *bls12381G2MultiExp) Run(input []byte) ([]byte, error) {
|
||||||
return encodePointG2(r), nil
|
return encodePointG2(r), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *bls12381G2MultiExp) Name() string {
|
||||||
|
return "BLS12_G2MSM"
|
||||||
|
}
|
||||||
|
|
||||||
// bls12381Pairing implements EIP-2537 Pairing precompile.
|
// bls12381Pairing implements EIP-2537 Pairing precompile.
|
||||||
type bls12381Pairing struct{}
|
type bls12381Pairing struct{}
|
||||||
|
|
||||||
|
|
@ -1035,6 +1183,10 @@ func (c *bls12381Pairing) Run(input []byte) ([]byte, error) {
|
||||||
return out, nil
|
return out, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *bls12381Pairing) Name() string {
|
||||||
|
return "BLS12_PAIRING_CHECK"
|
||||||
|
}
|
||||||
|
|
||||||
func decodePointG1(in []byte) (*bls12381.G1Affine, error) {
|
func decodePointG1(in []byte) (*bls12381.G1Affine, error) {
|
||||||
if len(in) != 128 {
|
if len(in) != 128 {
|
||||||
return nil, errors.New("invalid g1 point length")
|
return nil, errors.New("invalid g1 point length")
|
||||||
|
|
@ -1153,6 +1305,10 @@ func (c *bls12381MapG1) Run(input []byte) ([]byte, error) {
|
||||||
return encodePointG1(&r), nil
|
return encodePointG1(&r), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *bls12381MapG1) Name() string {
|
||||||
|
return "BLS12_MAP_FP_TO_G1"
|
||||||
|
}
|
||||||
|
|
||||||
// bls12381MapG2 implements EIP-2537 MapG2 precompile.
|
// bls12381MapG2 implements EIP-2537 MapG2 precompile.
|
||||||
type bls12381MapG2 struct{}
|
type bls12381MapG2 struct{}
|
||||||
|
|
||||||
|
|
@ -1186,6 +1342,10 @@ func (c *bls12381MapG2) Run(input []byte) ([]byte, error) {
|
||||||
return encodePointG2(&r), nil
|
return encodePointG2(&r), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *bls12381MapG2) Name() string {
|
||||||
|
return "BLS12_MAP_FP2_TO_G2"
|
||||||
|
}
|
||||||
|
|
||||||
// kzgPointEvaluation implements the EIP-4844 point evaluation precompile.
|
// kzgPointEvaluation implements the EIP-4844 point evaluation precompile.
|
||||||
type kzgPointEvaluation struct{}
|
type kzgPointEvaluation struct{}
|
||||||
|
|
||||||
|
|
@ -1242,6 +1402,10 @@ func (b *kzgPointEvaluation) Run(input []byte) ([]byte, error) {
|
||||||
return common.Hex2Bytes(blobPrecompileReturnValue), nil
|
return common.Hex2Bytes(blobPrecompileReturnValue), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (b *kzgPointEvaluation) Name() string {
|
||||||
|
return "KZG_POINT_EVALUATION"
|
||||||
|
}
|
||||||
|
|
||||||
// kZGToVersionedHash implements kzg_to_versioned_hash from EIP-4844
|
// kZGToVersionedHash implements kzg_to_versioned_hash from EIP-4844
|
||||||
func kZGToVersionedHash(kzg kzg4844.Commitment) common.Hash {
|
func kZGToVersionedHash(kzg kzg4844.Commitment) common.Hash {
|
||||||
h := sha256.Sum256(kzg[:])
|
h := sha256.Sum256(kzg[:])
|
||||||
|
|
@ -1277,3 +1441,7 @@ func (c *p256Verify) Run(input []byte) ([]byte, error) {
|
||||||
}
|
}
|
||||||
return nil, nil
|
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
|
return ret, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -27,6 +27,7 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||||
"github.com/ethereum/go-ethereum/core/state"
|
"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/types"
|
||||||
"github.com/ethereum/go-ethereum/params"
|
"github.com/ethereum/go-ethereum/params"
|
||||||
"github.com/holiman/uint256"
|
"github.com/holiman/uint256"
|
||||||
|
|
@ -87,7 +88,7 @@ func TestEIP2200(t *testing.T) {
|
||||||
|
|
||||||
statedb, _ := state.New(types.EmptyRootHash, state.NewDatabaseForTesting())
|
statedb, _ := state.New(types.EmptyRootHash, state.NewDatabaseForTesting())
|
||||||
statedb.CreateAccount(address)
|
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.SetState(address, common.Hash{}, common.BytesToHash([]byte{tt.original}))
|
||||||
statedb.Finalise(true) // Push the state into the "original" slot
|
statedb.Finalise(true) // Push the state into the "original" slot
|
||||||
|
|
||||||
|
|
@ -139,7 +140,7 @@ func TestCreateGas(t *testing.T) {
|
||||||
address := common.BytesToAddress([]byte("contract"))
|
address := common.BytesToAddress([]byte("contract"))
|
||||||
statedb, _ := state.New(types.EmptyRootHash, state.NewDatabaseForTesting())
|
statedb, _ := state.New(types.EmptyRootHash, state.NewDatabaseForTesting())
|
||||||
statedb.CreateAccount(address)
|
statedb.CreateAccount(address)
|
||||||
statedb.SetCode(address, hexutil.MustDecode(tt.code))
|
statedb.SetCode(address, hexutil.MustDecode(tt.code), tracing.CodeChangeUnspecified)
|
||||||
statedb.Finalise(true)
|
statedb.Finalise(true)
|
||||||
vmctx := BlockContext{
|
vmctx := BlockContext{
|
||||||
CanTransfer: func(StateDB, common.Address, *uint256.Int) bool { return true },
|
CanTransfer: func(StateDB, common.Address, *uint256.Int) bool { return true },
|
||||||
|
|
|
||||||
|
|
@ -43,7 +43,7 @@ type StateDB interface {
|
||||||
GetCode(common.Address) []byte
|
GetCode(common.Address) []byte
|
||||||
|
|
||||||
// SetCode sets the new code for the address, and returns the previous code, if any.
|
// 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
|
GetCodeSize(common.Address) int
|
||||||
|
|
||||||
AddRefund(uint64)
|
AddRefund(uint64)
|
||||||
|
|
|
||||||
|
|
@ -33,6 +33,7 @@ type Config struct {
|
||||||
ExtraEips []int // Additional EIPS that are to be enabled
|
ExtraEips []int // Additional EIPS that are to be enabled
|
||||||
|
|
||||||
StatelessSelfValidation bool // Generate execution witnesses and self-check against them (testing purpose)
|
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,
|
// 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/common"
|
||||||
"github.com/ethereum/go-ethereum/core/state"
|
"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/types"
|
||||||
"github.com/ethereum/go-ethereum/params"
|
"github.com/ethereum/go-ethereum/params"
|
||||||
"github.com/holiman/uint256"
|
"github.com/holiman/uint256"
|
||||||
|
|
@ -45,7 +46,7 @@ func TestLoopInterrupt(t *testing.T) {
|
||||||
for i, tt := range loopInterruptTests {
|
for i, tt := range loopInterruptTests {
|
||||||
statedb, _ := state.New(types.EmptyRootHash, state.NewDatabaseForTesting())
|
statedb, _ := state.New(types.EmptyRootHash, state.NewDatabaseForTesting())
|
||||||
statedb.CreateAccount(address)
|
statedb.CreateAccount(address)
|
||||||
statedb.SetCode(address, common.Hex2Bytes(tt))
|
statedb.SetCode(address, common.Hex2Bytes(tt), tracing.CodeChangeUnspecified)
|
||||||
statedb.Finalise(true)
|
statedb.Finalise(true)
|
||||||
|
|
||||||
evm := NewEVM(vmctx, statedb, params.AllEthashProtocolChanges, Config{})
|
evm := NewEVM(vmctx, statedb, params.AllEthashProtocolChanges, Config{})
|
||||||
|
|
|
||||||
|
|
@ -53,7 +53,7 @@ func (p *Program) add(op byte) *Program {
|
||||||
return p
|
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 nil, it pushes zero
|
||||||
// - If the val is bigger than 32 bytes, it panics
|
// - If the val is bigger than 32 bytes, it panics
|
||||||
func (p *Program) doPush(val *uint256.Int) {
|
func (p *Program) doPush(val *uint256.Int) {
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,7 @@ import (
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/core/state"
|
"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/types"
|
||||||
"github.com/ethereum/go-ethereum/core/vm"
|
"github.com/ethereum/go-ethereum/core/vm"
|
||||||
"github.com/ethereum/go-ethereum/crypto"
|
"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.Prepare(rules, cfg.Origin, cfg.Coinbase, &address, vm.ActivePrecompiles(rules), nil)
|
||||||
cfg.State.CreateAccount(address)
|
cfg.State.CreateAccount(address)
|
||||||
// set the receiver's (the executing contract) code for execution.
|
// 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.
|
// Call the code with the given configuration.
|
||||||
ret, leftOverGas, err := vmenv.Call(
|
ret, leftOverGas, err := vmenv.Call(
|
||||||
cfg.Origin,
|
cfg.Origin,
|
||||||
|
|
|
||||||
|
|
@ -114,7 +114,7 @@ func TestCall(t *testing.T) {
|
||||||
byte(vm.PUSH1), 32,
|
byte(vm.PUSH1), 32,
|
||||||
byte(vm.PUSH1), 0,
|
byte(vm.PUSH1), 0,
|
||||||
byte(vm.RETURN),
|
byte(vm.RETURN),
|
||||||
})
|
}, tracing.CodeChangeUnspecified)
|
||||||
|
|
||||||
ret, _, err := Call(address, nil, &Config{State: state})
|
ret, _, err := Call(address, nil, &Config{State: state})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -167,7 +167,7 @@ func benchmarkEVM_Create(bench *testing.B, code string) {
|
||||||
)
|
)
|
||||||
|
|
||||||
statedb.CreateAccount(sender)
|
statedb.CreateAccount(sender)
|
||||||
statedb.SetCode(receiver, common.FromHex(code))
|
statedb.SetCode(receiver, common.FromHex(code), tracing.CodeChangeUnspecified)
|
||||||
runtimeConfig := Config{
|
runtimeConfig := Config{
|
||||||
Origin: sender,
|
Origin: sender,
|
||||||
State: statedb,
|
State: statedb,
|
||||||
|
|
@ -232,7 +232,7 @@ func BenchmarkEVM_SWAP1(b *testing.B) {
|
||||||
|
|
||||||
b.Run("10k", func(b *testing.B) {
|
b.Run("10k", func(b *testing.B) {
|
||||||
contractCode := swapContract(10_000)
|
contractCode := swapContract(10_000)
|
||||||
state.SetCode(contractAddr, contractCode)
|
state.SetCode(contractAddr, contractCode, tracing.CodeChangeUnspecified)
|
||||||
|
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
_, _, err := Call(contractAddr, []byte{}, &Config{State: state})
|
_, _, err := Call(contractAddr, []byte{}, &Config{State: state})
|
||||||
|
|
@ -263,7 +263,7 @@ func BenchmarkEVM_RETURN(b *testing.B) {
|
||||||
b.ReportAllocs()
|
b.ReportAllocs()
|
||||||
|
|
||||||
contractCode := returnContract(n)
|
contractCode := returnContract(n)
|
||||||
state.SetCode(contractAddr, contractCode)
|
state.SetCode(contractAddr, contractCode, tracing.CodeChangeUnspecified)
|
||||||
|
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
ret, _, err := Call(contractAddr, []byte{}, &Config{State: state})
|
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.PUSH1), 0x00,
|
byte(vm.PUSH1), 0x00,
|
||||||
byte(vm.REVERT),
|
byte(vm.REVERT),
|
||||||
})
|
}, tracing.CodeChangeUnspecified)
|
||||||
}
|
}
|
||||||
|
|
||||||
//cfg.State.CreateAccount(cfg.Origin)
|
//cfg.State.CreateAccount(cfg.Origin)
|
||||||
// set the receiver's (the executing contract) code for execution.
|
// 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)
|
Call(destination, nil, cfg)
|
||||||
|
|
||||||
b.Run(name, func(b *testing.B) {
|
b.Run(name, func(b *testing.B) {
|
||||||
|
|
@ -772,12 +772,12 @@ func TestRuntimeJSTracer(t *testing.T) {
|
||||||
for i, jsTracer := range jsTracers {
|
for i, jsTracer := range jsTracers {
|
||||||
for j, tc := range tests {
|
for j, tc := range tests {
|
||||||
statedb, _ := state.New(types.EmptyRootHash, state.NewDatabaseForTesting())
|
statedb, _ := state.New(types.EmptyRootHash, state.NewDatabaseForTesting())
|
||||||
statedb.SetCode(main, tc.code)
|
statedb.SetCode(main, tc.code, tracing.CodeChangeUnspecified)
|
||||||
statedb.SetCode(common.HexToAddress("0xbb"), calleeCode)
|
statedb.SetCode(common.HexToAddress("0xbb"), calleeCode, tracing.CodeChangeUnspecified)
|
||||||
statedb.SetCode(common.HexToAddress("0xcc"), calleeCode)
|
statedb.SetCode(common.HexToAddress("0xcc"), calleeCode, tracing.CodeChangeUnspecified)
|
||||||
statedb.SetCode(common.HexToAddress("0xdd"), calleeCode)
|
statedb.SetCode(common.HexToAddress("0xdd"), calleeCode, tracing.CodeChangeUnspecified)
|
||||||
statedb.SetCode(common.HexToAddress("0xee"), calleeCode)
|
statedb.SetCode(common.HexToAddress("0xee"), calleeCode, tracing.CodeChangeUnspecified)
|
||||||
statedb.SetCode(common.HexToAddress("0xff"), suicideCode)
|
statedb.SetCode(common.HexToAddress("0xff"), suicideCode, tracing.CodeChangeUnspecified)
|
||||||
|
|
||||||
tracer, err := tracers.DefaultDirectory.New(jsTracer, new(tracers.Context), nil, params.MergedTestChainConfig)
|
tracer, err := tracers.DefaultDirectory.New(jsTracer, new(tracers.Context), nil, params.MergedTestChainConfig)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -862,8 +862,8 @@ func BenchmarkTracerStepVsCallFrame(b *testing.B) {
|
||||||
// delegation designator incurs the correct amount of gas based on the tracer.
|
// delegation designator incurs the correct amount of gas based on the tracer.
|
||||||
func TestDelegatedAccountAccessCost(t *testing.T) {
|
func TestDelegatedAccountAccessCost(t *testing.T) {
|
||||||
statedb, _ := state.New(types.EmptyRootHash, state.NewDatabaseForTesting())
|
statedb, _ := state.New(types.EmptyRootHash, state.NewDatabaseForTesting())
|
||||||
statedb.SetCode(common.HexToAddress("0xff"), types.AddressToDelegation(common.HexToAddress("0xaa")))
|
statedb.SetCode(common.HexToAddress("0xff"), types.AddressToDelegation(common.HexToAddress("0xaa")), tracing.CodeChangeUnspecified)
|
||||||
statedb.SetCode(common.HexToAddress("0xaa"), program.New().Return(0, 0).Bytes())
|
statedb.SetCode(common.HexToAddress("0xaa"), program.New().Return(0, 0).Bytes(), tracing.CodeChangeUnspecified)
|
||||||
|
|
||||||
for i, tc := range []struct {
|
for i, tc := range []struct {
|
||||||
code []byte
|
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",
|
"Name": "nagydani-5-pow0x10001",
|
||||||
"Gas": 87381,
|
"Gas": 87381,
|
||||||
"NoBenchmark": false
|
"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",
|
"Name": "nagydani-5-pow0x10001",
|
||||||
"Gas": 524288,
|
"Gas": 524288,
|
||||||
"NoBenchmark": false
|
"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
|
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 {
|
func ckzgVerifyCellProofBatch(blobs []Blob, commitments []Commitment, cellProofs []Proof) error {
|
||||||
ckzgIniter.Do(ckzgInit)
|
ckzgIniter.Do(ckzgInit)
|
||||||
var (
|
var (
|
||||||
|
|
|
||||||
|
|
@ -115,7 +115,7 @@ func gokzgComputeCellProofs(blob *Blob) ([]Proof, error) {
|
||||||
return p, nil
|
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 {
|
func gokzgVerifyCellProofBatch(blobs []Blob, commitments []Commitment, cellProofs []Proof) error {
|
||||||
gokzgIniter.Do(gokzgInit)
|
gokzgIniter.Do(gokzgInit)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -443,3 +443,51 @@ func (api *DebugAPI) GetTrieFlushInterval() (string, error) {
|
||||||
}
|
}
|
||||||
return api.eth.blockchain.GetTrieFlushInterval().String(), nil
|
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/merkle.journal
|
||||||
// - DATADIR/triedb/verkle.journal
|
// - DATADIR/triedb/verkle.journal
|
||||||
TrieJournalDirectory: stack.ResolvePath("triedb"),
|
TrieJournalDirectory: stack.ResolvePath("triedb"),
|
||||||
|
StateSizeTracking: config.EnableStateSizeTracking,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
if config.VMTrace != "" {
|
if config.VMTrace != "" {
|
||||||
|
|
|
||||||
|
|
@ -20,10 +20,12 @@ package catalyst
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"reflect"
|
||||||
"strconv"
|
"strconv"
|
||||||
"sync"
|
"sync"
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
"time"
|
"time"
|
||||||
|
"unicode"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/beacon/engine"
|
"github.com/ethereum/go-ethereum/beacon/engine"
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
|
@ -80,47 +82,6 @@ const (
|
||||||
beaconUpdateWarnFrequency = 5 * time.Minute
|
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 (
|
var (
|
||||||
// Number of blobs requested via getBlobsV2
|
// Number of blobs requested via getBlobsV2
|
||||||
getBlobsRequestedCounter = metrics.NewRegisteredCounter("engine/getblobs/requested", nil)
|
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.
|
// 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) {
|
func (api *ConsensusAPI) GetBlobsV1(hashes []common.Hash) ([]*engine.BlobAndProofV1, error) {
|
||||||
if len(hashes) > 128 {
|
if len(hashes) > 128 {
|
||||||
return nil, engine.TooLargeRequest.With(fmt.Errorf("requested blob count too large: %v", len(hashes)))
|
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))
|
res := make([]*engine.BlobAndProofV1, len(hashes))
|
||||||
for i := 0; i < len(blobs); i++ {
|
for i := 0; i < len(blobs); i++ {
|
||||||
|
// Skip the non-existing blob
|
||||||
|
if blobs[i] == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
res[i] = &engine.BlobAndProofV1{
|
res[i] = &engine.BlobAndProofV1{
|
||||||
Blob: blobs[i][:],
|
Blob: blobs[i][:],
|
||||||
Proof: proofs[i][0][:],
|
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.
|
// 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) {
|
func (api *ConsensusAPI) GetBlobsV2(hashes []common.Hash) ([]*engine.BlobAndProofV2, error) {
|
||||||
if len(hashes) > 128 {
|
if len(hashes) > 128 {
|
||||||
return nil, engine.TooLargeRequest.With(fmt.Errorf("requested blob count too large: %v", len(hashes)))
|
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)
|
getBlobsV2RequestMiss.Inc(1)
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
getBlobsV2RequestHit.Inc(1)
|
|
||||||
|
|
||||||
blobs, _, proofs, err := api.eth.BlobTxPool().GetBlobs(hashes, types.BlobSidecarVersion1)
|
blobs, _, proofs, err := api.eth.BlobTxPool().GetBlobs(hashes, types.BlobSidecarVersion1)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, engine.InvalidParams.With(err)
|
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))
|
res := make([]*engine.BlobAndProofV2, len(hashes))
|
||||||
for i := 0; i < len(blobs); i++ {
|
for i := 0; i < len(blobs); i++ {
|
||||||
var cellProofs []hexutil.Bytes
|
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.
|
// ExchangeCapabilities returns the current methods provided by this node.
|
||||||
func (api *ConsensusAPI) ExchangeCapabilities([]string) []string {
|
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
|
return caps
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,9 @@ package catalyst
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"context"
|
"context"
|
||||||
|
"crypto/ecdsa"
|
||||||
crand "crypto/rand"
|
crand "crypto/rand"
|
||||||
|
"crypto/sha256"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"math/big"
|
"math/big"
|
||||||
|
|
@ -40,6 +42,7 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/crypto/kzg4844"
|
"github.com/ethereum/go-ethereum/crypto/kzg4844"
|
||||||
"github.com/ethereum/go-ethereum/eth"
|
"github.com/ethereum/go-ethereum/eth"
|
||||||
"github.com/ethereum/go-ethereum/eth/ethconfig"
|
"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/internal/version"
|
||||||
"github.com/ethereum/go-ethereum/miner"
|
"github.com/ethereum/go-ethereum/miner"
|
||||||
"github.com/ethereum/go-ethereum/node"
|
"github.com/ethereum/go-ethereum/node"
|
||||||
|
|
@ -47,6 +50,7 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/params"
|
"github.com/ethereum/go-ethereum/params"
|
||||||
"github.com/ethereum/go-ethereum/rpc"
|
"github.com/ethereum/go-ethereum/rpc"
|
||||||
"github.com/ethereum/go-ethereum/trie"
|
"github.com/ethereum/go-ethereum/trie"
|
||||||
|
"github.com/holiman/uint256"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
|
@ -112,7 +116,7 @@ func TestEth2AssembleBlock(t *testing.T) {
|
||||||
n, ethservice := startEthService(t, genesis, blocks)
|
n, ethservice := startEthService(t, genesis, blocks)
|
||||||
defer n.Close()
|
defer n.Close()
|
||||||
|
|
||||||
api := NewConsensusAPI(ethservice)
|
api := newConsensusAPIWithoutHeartbeat(ethservice)
|
||||||
signer := types.NewEIP155Signer(ethservice.BlockChain().Config().ChainID)
|
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)
|
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 {
|
if err != nil {
|
||||||
|
|
@ -151,7 +155,7 @@ func TestEth2AssembleBlockWithAnotherBlocksTxs(t *testing.T) {
|
||||||
n, ethservice := startEthService(t, genesis, blocks[:9])
|
n, ethservice := startEthService(t, genesis, blocks[:9])
|
||||||
defer n.Close()
|
defer n.Close()
|
||||||
|
|
||||||
api := NewConsensusAPI(ethservice)
|
api := newConsensusAPIWithoutHeartbeat(ethservice)
|
||||||
|
|
||||||
// Put the 10th block's tx in the pool and produce a new block
|
// Put the 10th block's tx in the pool and produce a new block
|
||||||
txs := blocks[9].Transactions()
|
txs := blocks[9].Transactions()
|
||||||
|
|
@ -173,7 +177,7 @@ func TestEth2PrepareAndGetPayload(t *testing.T) {
|
||||||
n, ethservice := startEthService(t, genesis, blocks[:9])
|
n, ethservice := startEthService(t, genesis, blocks[:9])
|
||||||
defer n.Close()
|
defer n.Close()
|
||||||
|
|
||||||
api := NewConsensusAPI(ethservice)
|
api := newConsensusAPIWithoutHeartbeat(ethservice)
|
||||||
|
|
||||||
// Put the 10th block's tx in the pool and produce a new block
|
// Put the 10th block's tx in the pool and produce a new block
|
||||||
txs := blocks[9].Transactions()
|
txs := blocks[9].Transactions()
|
||||||
|
|
@ -238,8 +242,9 @@ func TestInvalidPayloadTimestamp(t *testing.T) {
|
||||||
genesis, preMergeBlocks := generateMergeChain(10, false)
|
genesis, preMergeBlocks := generateMergeChain(10, false)
|
||||||
n, ethservice := startEthService(t, genesis, preMergeBlocks)
|
n, ethservice := startEthService(t, genesis, preMergeBlocks)
|
||||||
defer n.Close()
|
defer n.Close()
|
||||||
|
|
||||||
var (
|
var (
|
||||||
api = NewConsensusAPI(ethservice)
|
api = newConsensusAPIWithoutHeartbeat(ethservice)
|
||||||
parent = ethservice.BlockChain().CurrentBlock()
|
parent = ethservice.BlockChain().CurrentBlock()
|
||||||
)
|
)
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
|
|
@ -281,7 +286,7 @@ func TestEth2NewBlock(t *testing.T) {
|
||||||
defer n.Close()
|
defer n.Close()
|
||||||
|
|
||||||
var (
|
var (
|
||||||
api = NewConsensusAPI(ethservice)
|
api = newConsensusAPIWithoutHeartbeat(ethservice)
|
||||||
parent = preMergeBlocks[len(preMergeBlocks)-1]
|
parent = preMergeBlocks[len(preMergeBlocks)-1]
|
||||||
|
|
||||||
// This EVM code generates a log when the contract is created.
|
// 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)
|
t.Fatal("can't create node:", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
mcfg := miner.DefaultConfig
|
ethcfg := ðconfig.Config{
|
||||||
ethcfg := ðconfig.Config{Genesis: genesis, SyncMode: ethconfig.FullSync, TrieTimeout: time.Minute, TrieDirtyCache: 256, TrieCleanCache: 256, Miner: mcfg}
|
Genesis: genesis,
|
||||||
|
SyncMode: ethconfig.FullSync,
|
||||||
|
TrieTimeout: time.Minute,
|
||||||
|
TrieDirtyCache: 256,
|
||||||
|
TrieCleanCache: 256,
|
||||||
|
Miner: miner.DefaultConfig,
|
||||||
|
}
|
||||||
ethservice, err := eth.New(n, ethcfg)
|
ethservice, err := eth.New(n, ethcfg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal("can't create eth service:", err)
|
t.Fatal("can't create eth service:", err)
|
||||||
|
|
@ -459,6 +470,7 @@ func TestFullAPI(t *testing.T) {
|
||||||
genesis, preMergeBlocks := generateMergeChain(10, false)
|
genesis, preMergeBlocks := generateMergeChain(10, false)
|
||||||
n, ethservice := startEthService(t, genesis, preMergeBlocks)
|
n, ethservice := startEthService(t, genesis, preMergeBlocks)
|
||||||
defer n.Close()
|
defer n.Close()
|
||||||
|
|
||||||
var (
|
var (
|
||||||
parent = ethservice.BlockChain().CurrentBlock()
|
parent = ethservice.BlockChain().CurrentBlock()
|
||||||
// This EVM code generates a log when the contract is created.
|
// 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 {
|
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
|
var blocks []*types.Header
|
||||||
for i := 0; i < n; i++ {
|
for i := 0; i < n; i++ {
|
||||||
callback(parent)
|
callback(parent)
|
||||||
|
|
@ -528,7 +540,7 @@ func TestExchangeTransitionConfig(t *testing.T) {
|
||||||
defer n.Close()
|
defer n.Close()
|
||||||
|
|
||||||
// invalid ttd
|
// invalid ttd
|
||||||
api := NewConsensusAPI(ethservice)
|
api := newConsensusAPIWithoutHeartbeat(ethservice)
|
||||||
config := engine.TransitionConfigurationV1{
|
config := engine.TransitionConfigurationV1{
|
||||||
TerminalTotalDifficulty: (*hexutil.Big)(big.NewInt(0)),
|
TerminalTotalDifficulty: (*hexutil.Big)(big.NewInt(0)),
|
||||||
TerminalBlockHash: common.Hash{},
|
TerminalBlockHash: common.Hash{},
|
||||||
|
|
@ -589,7 +601,7 @@ func TestNewPayloadOnInvalidChain(t *testing.T) {
|
||||||
defer n.Close()
|
defer n.Close()
|
||||||
|
|
||||||
var (
|
var (
|
||||||
api = NewConsensusAPI(ethservice)
|
api = newConsensusAPIWithoutHeartbeat(ethservice)
|
||||||
parent = ethservice.BlockChain().CurrentBlock()
|
parent = ethservice.BlockChain().CurrentBlock()
|
||||||
signer = types.LatestSigner(ethservice.BlockChain().Config())
|
signer = types.LatestSigner(ethservice.BlockChain().Config())
|
||||||
// This EVM code generates a log when the contract is created.
|
// This EVM code generates a log when the contract is created.
|
||||||
|
|
@ -693,7 +705,7 @@ func TestEmptyBlocks(t *testing.T) {
|
||||||
defer n.Close()
|
defer n.Close()
|
||||||
|
|
||||||
commonAncestor := ethservice.BlockChain().CurrentBlock()
|
commonAncestor := ethservice.BlockChain().CurrentBlock()
|
||||||
api := NewConsensusAPI(ethservice)
|
api := newConsensusAPIWithoutHeartbeat(ethservice)
|
||||||
|
|
||||||
// Setup 10 blocks on the canonical chain
|
// Setup 10 blocks on the canonical chain
|
||||||
setupBlocks(t, ethservice, 10, commonAncestor, func(parent *types.Header) {}, nil, nil, nil)
|
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())
|
nodeA.Server().AddPeer(nodeB.Server().Self())
|
||||||
nodeB.Server().AddPeer(nodeA.Server().Self())
|
nodeB.Server().AddPeer(nodeA.Server().Self())
|
||||||
apiA := NewConsensusAPI(ethserviceA)
|
apiA := newConsensusAPIWithoutHeartbeat(ethserviceA)
|
||||||
apiB := NewConsensusAPI(ethserviceB)
|
apiB := newConsensusAPIWithoutHeartbeat(ethserviceB)
|
||||||
|
|
||||||
commonAncestor := ethserviceA.BlockChain().CurrentBlock()
|
commonAncestor := ethserviceA.BlockChain().CurrentBlock()
|
||||||
|
|
||||||
|
|
@ -878,7 +890,7 @@ func TestInvalidBloom(t *testing.T) {
|
||||||
defer n.Close()
|
defer n.Close()
|
||||||
|
|
||||||
commonAncestor := ethservice.BlockChain().CurrentBlock()
|
commonAncestor := ethservice.BlockChain().CurrentBlock()
|
||||||
api := NewConsensusAPI(ethservice)
|
api := newConsensusAPIWithoutHeartbeat(ethservice)
|
||||||
|
|
||||||
// Setup 10 blocks on the canonical chain
|
// Setup 10 blocks on the canonical chain
|
||||||
setupBlocks(t, ethservice, 10, commonAncestor, func(parent *types.Header) {}, nil, nil, nil)
|
setupBlocks(t, ethservice, 10, commonAncestor, func(parent *types.Header) {}, nil, nil, nil)
|
||||||
|
|
@ -904,7 +916,7 @@ func TestSimultaneousNewBlock(t *testing.T) {
|
||||||
defer n.Close()
|
defer n.Close()
|
||||||
|
|
||||||
var (
|
var (
|
||||||
api = NewConsensusAPI(ethservice)
|
api = newConsensusAPIWithoutHeartbeat(ethservice)
|
||||||
parent = preMergeBlocks[len(preMergeBlocks)-1]
|
parent = preMergeBlocks[len(preMergeBlocks)-1]
|
||||||
)
|
)
|
||||||
for i := 0; i < 10; i++ {
|
for i := 0; i < 10; i++ {
|
||||||
|
|
@ -994,7 +1006,7 @@ func TestWithdrawals(t *testing.T) {
|
||||||
n, ethservice := startEthService(t, genesis, blocks)
|
n, ethservice := startEthService(t, genesis, blocks)
|
||||||
defer n.Close()
|
defer n.Close()
|
||||||
|
|
||||||
api := NewConsensusAPI(ethservice)
|
api := newConsensusAPIWithoutHeartbeat(ethservice)
|
||||||
|
|
||||||
// 10: Build Shanghai block with no withdrawals.
|
// 10: Build Shanghai block with no withdrawals.
|
||||||
parent := ethservice.BlockChain().CurrentHeader()
|
parent := ethservice.BlockChain().CurrentHeader()
|
||||||
|
|
@ -1111,7 +1123,7 @@ func TestNilWithdrawals(t *testing.T) {
|
||||||
n, ethservice := startEthService(t, genesis, blocks)
|
n, ethservice := startEthService(t, genesis, blocks)
|
||||||
defer n.Close()
|
defer n.Close()
|
||||||
|
|
||||||
api := NewConsensusAPI(ethservice)
|
api := newConsensusAPIWithoutHeartbeat(ethservice)
|
||||||
parent := ethservice.BlockChain().CurrentHeader()
|
parent := ethservice.BlockChain().CurrentHeader()
|
||||||
aa := common.Address{0xaa}
|
aa := common.Address{0xaa}
|
||||||
|
|
||||||
|
|
@ -1307,7 +1319,7 @@ func allBodies(blocks []*types.Block) []*types.Body {
|
||||||
|
|
||||||
func TestGetBlockBodiesByHash(t *testing.T) {
|
func TestGetBlockBodiesByHash(t *testing.T) {
|
||||||
node, eth, blocks := setupBodies(t)
|
node, eth, blocks := setupBodies(t)
|
||||||
api := NewConsensusAPI(eth)
|
api := newConsensusAPIWithoutHeartbeat(eth)
|
||||||
defer node.Close()
|
defer node.Close()
|
||||||
|
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
|
|
@ -1363,7 +1375,7 @@ func TestGetBlockBodiesByHash(t *testing.T) {
|
||||||
|
|
||||||
func TestGetBlockBodiesByRange(t *testing.T) {
|
func TestGetBlockBodiesByRange(t *testing.T) {
|
||||||
node, eth, blocks := setupBodies(t)
|
node, eth, blocks := setupBodies(t)
|
||||||
api := NewConsensusAPI(eth)
|
api := newConsensusAPIWithoutHeartbeat(eth)
|
||||||
defer node.Close()
|
defer node.Close()
|
||||||
|
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
|
|
@ -1444,7 +1456,7 @@ func TestGetBlockBodiesByRange(t *testing.T) {
|
||||||
|
|
||||||
func TestGetBlockBodiesByRangeInvalidParams(t *testing.T) {
|
func TestGetBlockBodiesByRangeInvalidParams(t *testing.T) {
|
||||||
node, eth, _ := setupBodies(t)
|
node, eth, _ := setupBodies(t)
|
||||||
api := NewConsensusAPI(eth)
|
api := newConsensusAPIWithoutHeartbeat(eth)
|
||||||
defer node.Close()
|
defer node.Close()
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
start hexutil.Uint64
|
start hexutil.Uint64
|
||||||
|
|
@ -1556,7 +1568,7 @@ func TestParentBeaconBlockRoot(t *testing.T) {
|
||||||
n, ethservice := startEthService(t, genesis, blocks)
|
n, ethservice := startEthService(t, genesis, blocks)
|
||||||
defer n.Close()
|
defer n.Close()
|
||||||
|
|
||||||
api := NewConsensusAPI(ethservice)
|
api := newConsensusAPIWithoutHeartbeat(ethservice)
|
||||||
|
|
||||||
// 11: Build Shanghai block with no withdrawals.
|
// 11: Build Shanghai block with no withdrawals.
|
||||||
parent := ethservice.BlockChain().CurrentHeader()
|
parent := ethservice.BlockChain().CurrentHeader()
|
||||||
|
|
@ -1639,7 +1651,7 @@ func TestWitnessCreationAndConsumption(t *testing.T) {
|
||||||
n, ethservice := startEthService(t, genesis, blocks[:9])
|
n, ethservice := startEthService(t, genesis, blocks[:9])
|
||||||
defer n.Close()
|
defer n.Close()
|
||||||
|
|
||||||
api := NewConsensusAPI(ethservice)
|
api := newConsensusAPIWithoutHeartbeat(ethservice)
|
||||||
|
|
||||||
// Put the 10th block's tx in the pool and produce a new block
|
// Put the 10th block's tx in the pool and produce a new block
|
||||||
txs := blocks[9].Transactions()
|
txs := blocks[9].Transactions()
|
||||||
|
|
@ -1731,7 +1743,7 @@ func TestGetClientVersion(t *testing.T) {
|
||||||
n, ethservice := startEthService(t, genesis, preMergeBlocks)
|
n, ethservice := startEthService(t, genesis, preMergeBlocks)
|
||||||
defer n.Close()
|
defer n.Close()
|
||||||
|
|
||||||
api := NewConsensusAPI(ethservice)
|
api := newConsensusAPIWithoutHeartbeat(ethservice)
|
||||||
info := engine.ClientVersionV1{
|
info := engine.ClientVersionV1{
|
||||||
Code: "TT",
|
Code: "TT",
|
||||||
Name: "test",
|
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
|
// Enables tracking of SHA3 preimages in the VM
|
||||||
EnablePreimageRecording bool
|
EnablePreimageRecording bool
|
||||||
|
|
||||||
|
// Enables tracking of state size
|
||||||
|
EnableStateSizeTracking bool
|
||||||
|
|
||||||
// Enables VM tracing
|
// Enables VM tracing
|
||||||
VMTrace string
|
VMTrace string
|
||||||
VMTraceJsonConfig string
|
VMTraceJsonConfig string
|
||||||
|
|
|
||||||
|
|
@ -49,6 +49,7 @@ func (c Config) MarshalTOML() (interface{}, error) {
|
||||||
BlobPool blobpool.Config
|
BlobPool blobpool.Config
|
||||||
GPO gasprice.Config
|
GPO gasprice.Config
|
||||||
EnablePreimageRecording bool
|
EnablePreimageRecording bool
|
||||||
|
EnableStateSizeTracking bool
|
||||||
VMTrace string
|
VMTrace string
|
||||||
VMTraceJsonConfig string
|
VMTraceJsonConfig string
|
||||||
RPCGasCap uint64
|
RPCGasCap uint64
|
||||||
|
|
@ -90,6 +91,7 @@ func (c Config) MarshalTOML() (interface{}, error) {
|
||||||
enc.BlobPool = c.BlobPool
|
enc.BlobPool = c.BlobPool
|
||||||
enc.GPO = c.GPO
|
enc.GPO = c.GPO
|
||||||
enc.EnablePreimageRecording = c.EnablePreimageRecording
|
enc.EnablePreimageRecording = c.EnablePreimageRecording
|
||||||
|
enc.EnableStateSizeTracking = c.EnableStateSizeTracking
|
||||||
enc.VMTrace = c.VMTrace
|
enc.VMTrace = c.VMTrace
|
||||||
enc.VMTraceJsonConfig = c.VMTraceJsonConfig
|
enc.VMTraceJsonConfig = c.VMTraceJsonConfig
|
||||||
enc.RPCGasCap = c.RPCGasCap
|
enc.RPCGasCap = c.RPCGasCap
|
||||||
|
|
@ -135,6 +137,7 @@ func (c *Config) UnmarshalTOML(unmarshal func(interface{}) error) error {
|
||||||
BlobPool *blobpool.Config
|
BlobPool *blobpool.Config
|
||||||
GPO *gasprice.Config
|
GPO *gasprice.Config
|
||||||
EnablePreimageRecording *bool
|
EnablePreimageRecording *bool
|
||||||
|
EnableStateSizeTracking *bool
|
||||||
VMTrace *string
|
VMTrace *string
|
||||||
VMTraceJsonConfig *string
|
VMTraceJsonConfig *string
|
||||||
RPCGasCap *uint64
|
RPCGasCap *uint64
|
||||||
|
|
@ -243,6 +246,9 @@ func (c *Config) UnmarshalTOML(unmarshal func(interface{}) error) error {
|
||||||
if dec.EnablePreimageRecording != nil {
|
if dec.EnablePreimageRecording != nil {
|
||||||
c.EnablePreimageRecording = *dec.EnablePreimageRecording
|
c.EnablePreimageRecording = *dec.EnablePreimageRecording
|
||||||
}
|
}
|
||||||
|
if dec.EnableStateSizeTracking != nil {
|
||||||
|
c.EnableStateSizeTracking = *dec.EnableStateSizeTracking
|
||||||
|
}
|
||||||
if dec.VMTrace != nil {
|
if dec.VMTrace != nil {
|
||||||
c.VMTrace = *dec.VMTrace
|
c.VMTrace = *dec.VMTrace
|
||||||
}
|
}
|
||||||
|
|
|
||||||
124
eth/handler.go
124
eth/handler.go
|
|
@ -17,21 +17,22 @@
|
||||||
package eth
|
package eth
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"cmp"
|
||||||
|
crand "crypto/rand"
|
||||||
"errors"
|
"errors"
|
||||||
"maps"
|
"maps"
|
||||||
"math"
|
"math"
|
||||||
"math/big"
|
|
||||||
"slices"
|
"slices"
|
||||||
"sync"
|
"sync"
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/dchest/siphash"
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/core"
|
"github.com/ethereum/go-ethereum/core"
|
||||||
"github.com/ethereum/go-ethereum/core/rawdb"
|
"github.com/ethereum/go-ethereum/core/rawdb"
|
||||||
"github.com/ethereum/go-ethereum/core/txpool"
|
"github.com/ethereum/go-ethereum/core/txpool"
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"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/downloader"
|
||||||
"github.com/ethereum/go-ethereum/eth/ethconfig"
|
"github.com/ethereum/go-ethereum/eth/ethconfig"
|
||||||
"github.com/ethereum/go-ethereum/eth/fetcher"
|
"github.com/ethereum/go-ethereum/eth/fetcher"
|
||||||
|
|
@ -119,9 +120,10 @@ type handler struct {
|
||||||
chain *core.BlockChain
|
chain *core.BlockChain
|
||||||
maxPeers int
|
maxPeers int
|
||||||
|
|
||||||
downloader *downloader.Downloader
|
downloader *downloader.Downloader
|
||||||
txFetcher *fetcher.TxFetcher
|
txFetcher *fetcher.TxFetcher
|
||||||
peers *peerSet
|
peers *peerSet
|
||||||
|
txBroadcastKey [16]byte
|
||||||
|
|
||||||
eventMux *event.TypeMux
|
eventMux *event.TypeMux
|
||||||
txsCh chan core.NewTxsEvent
|
txsCh chan core.NewTxsEvent
|
||||||
|
|
@ -153,6 +155,7 @@ func newHandler(config *handlerConfig) (*handler, error) {
|
||||||
txpool: config.TxPool,
|
txpool: config.TxPool,
|
||||||
chain: config.Chain,
|
chain: config.Chain,
|
||||||
peers: newPeerSet(),
|
peers: newPeerSet(),
|
||||||
|
txBroadcastKey: newBroadcastChoiceKey(),
|
||||||
requiredBlocks: config.RequiredBlocks,
|
requiredBlocks: config.RequiredBlocks,
|
||||||
quitSync: make(chan struct{}),
|
quitSync: make(chan struct{}),
|
||||||
handlerDoneCh: 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
|
txset = make(map[*ethPeer][]common.Hash) // Set peer->hash to transfer directly
|
||||||
annos = make(map[*ethPeer][]common.Hash) // Set peer->hash to announce
|
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())
|
||||||
signer = types.LatestSigner(h.chain.Config()) // Don't care about chain status, we just need *a* sender
|
choice = newBroadcastChoice(h.nodeID, h.txBroadcastKey)
|
||||||
hasher = crypto.NewKeccakState()
|
peers = h.peers.all()
|
||||||
hash = make([]byte, 32)
|
|
||||||
)
|
)
|
||||||
|
|
||||||
for _, tx := range txs {
|
for _, tx := range txs {
|
||||||
var maybeDirect bool
|
var directSet map[*ethPeer]struct{}
|
||||||
switch {
|
switch {
|
||||||
case tx.Type() == types.BlobTxType:
|
case tx.Type() == types.BlobTxType:
|
||||||
blobTxs++
|
blobTxs++
|
||||||
case tx.Size() > txMaxBroadcastSize:
|
case tx.Size() > txMaxBroadcastSize:
|
||||||
largeTxs++
|
largeTxs++
|
||||||
default:
|
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
|
for _, peer := range peers {
|
||||||
hasher.Write(from.Bytes())
|
if peer.KnownTransaction(tx.Hash()) {
|
||||||
|
continue
|
||||||
hasher.Read(hash)
|
|
||||||
if new(big.Int).Mod(new(big.Int).SetBytes(hash), total).Cmp(direct) < 0 {
|
|
||||||
broadcast = true
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if broadcast {
|
if _, ok := directSet[peer]; ok {
|
||||||
|
// Send direct.
|
||||||
txset[peer] = append(txset[peer], tx.Hash())
|
txset[peer] = append(txset[peer], tx.Hash())
|
||||||
} else {
|
} else {
|
||||||
|
// Send announcement.
|
||||||
annos[peer] = append(annos[peer], tx.Hash())
|
annos[peer] = append(annos[peer], tx.Hash())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for peer, hashes := range txset {
|
for peer, hashes := range txset {
|
||||||
directCount += len(hashes)
|
directCount += len(hashes)
|
||||||
peer.AsyncSendTransactions(hashes)
|
peer.AsyncSendTransactions(hashes)
|
||||||
|
|
@ -594,7 +579,7 @@ func newBlockRangeState(chain *core.BlockChain, typeMux *event.TypeMux) *blockRa
|
||||||
return st
|
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
|
// The range to announce is the range that is available in the store, so it's not just
|
||||||
// about imported blocks.
|
// about imported blocks.
|
||||||
func (h *handler) blockRangeLoop(st *blockRangeState) {
|
func (h *handler) blockRangeLoop(st *blockRangeState) {
|
||||||
|
|
@ -696,3 +681,62 @@ func (st *blockRangeState) stop() {
|
||||||
func (st *blockRangeState) currentRange() eth.BlockRangeUpdatePacket {
|
func (st *blockRangeState) currentRange() eth.BlockRangeUpdatePacket {
|
||||||
return *st.next.Load()
|
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
|
package eth
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"maps"
|
||||||
"math/big"
|
"math/big"
|
||||||
|
"math/rand"
|
||||||
"sort"
|
"sort"
|
||||||
"sync"
|
"sync"
|
||||||
|
"testing"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/consensus/ethash"
|
"github.com/ethereum/go-ethereum/consensus/ethash"
|
||||||
|
|
@ -29,8 +32,11 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
"github.com/ethereum/go-ethereum/crypto"
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
"github.com/ethereum/go-ethereum/eth/ethconfig"
|
"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/ethdb"
|
||||||
"github.com/ethereum/go-ethereum/event"
|
"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/params"
|
||||||
"github.com/ethereum/go-ethereum/rlp"
|
"github.com/ethereum/go-ethereum/rlp"
|
||||||
"github.com/holiman/uint256"
|
"github.com/holiman/uint256"
|
||||||
|
|
@ -212,3 +218,102 @@ func (b *testHandler) close() {
|
||||||
b.handler.Stop()
|
b.handler.Stop()
|
||||||
b.chain.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 (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"maps"
|
||||||
|
"slices"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
|
||||||
"github.com/ethereum/go-ethereum/eth/protocols/eth"
|
"github.com/ethereum/go-ethereum/eth/protocols/eth"
|
||||||
"github.com/ethereum/go-ethereum/eth/protocols/snap"
|
"github.com/ethereum/go-ethereum/eth/protocols/snap"
|
||||||
"github.com/ethereum/go-ethereum/p2p"
|
"github.com/ethereum/go-ethereum/p2p"
|
||||||
|
|
@ -191,19 +192,12 @@ func (ps *peerSet) peer(id string) *ethPeer {
|
||||||
return ps.peers[id]
|
return ps.peers[id]
|
||||||
}
|
}
|
||||||
|
|
||||||
// peersWithoutTransaction retrieves a list of peers that do not have a given
|
// all returns all current peers.
|
||||||
// transaction in their set of known hashes.
|
func (ps *peerSet) all() []*ethPeer {
|
||||||
func (ps *peerSet) peersWithoutTransaction(hash common.Hash) []*ethPeer {
|
|
||||||
ps.lock.RLock()
|
ps.lock.RLock()
|
||||||
defer ps.lock.RUnlock()
|
defer ps.lock.RUnlock()
|
||||||
|
|
||||||
list := make([]*ethPeer, 0, len(ps.peers))
|
return slices.Collect(maps.Values(ps.peers))
|
||||||
for _, p := range ps.peers {
|
|
||||||
if !p.KnownTransaction(hash) {
|
|
||||||
list = append(list, p)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return list
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// len returns if the current number of `eth` peers in the set. Since the `snap`
|
// 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 {
|
if err := empty.handler.downloader.BeaconSync(ethconfig.SnapSync, full.chain.CurrentBlock(), nil); err != nil {
|
||||||
t.Fatal("sync failed:", err)
|
t.Fatal("sync failed:", err)
|
||||||
}
|
}
|
||||||
time.Sleep(time.Second * 5) // Downloader internally has to wait a timer (3s) to be expired before exiting
|
// Downloader internally has to wait for a timer (3s) to be expired before
|
||||||
|
// exiting. Poll after to determine if sync is disabled.
|
||||||
if empty.handler.snapSync.Load() {
|
time.Sleep(time.Second * 3)
|
||||||
t.Fatalf("snap sync not disabled after successful synchronisation")
|
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
|
target *types.Header
|
||||||
ticker = time.NewTicker(time.Second * 5)
|
ticker = time.NewTicker(time.Second * 5)
|
||||||
)
|
)
|
||||||
|
defer ticker.Stop()
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
case req := <-s.request:
|
case req := <-s.request:
|
||||||
|
|
|
||||||
|
|
@ -527,7 +527,7 @@ func TestTraceTransaction(t *testing.T) {
|
||||||
b.AddTx(tx)
|
b.AddTx(tx)
|
||||||
target = tx.Hash()
|
target = tx.Hash()
|
||||||
})
|
})
|
||||||
defer backend.chain.Stop()
|
defer backend.teardown()
|
||||||
api := NewAPI(backend)
|
api := NewAPI(backend)
|
||||||
result, err := api.TraceTransaction(context.Background(), target, nil)
|
result, err := api.TraceTransaction(context.Background(), target, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -584,7 +584,7 @@ func TestTraceBlock(t *testing.T) {
|
||||||
b.AddTx(tx)
|
b.AddTx(tx)
|
||||||
txHash = tx.Hash()
|
txHash = tx.Hash()
|
||||||
})
|
})
|
||||||
defer backend.chain.Stop()
|
defer backend.teardown()
|
||||||
api := NewAPI(backend)
|
api := NewAPI(backend)
|
||||||
|
|
||||||
var testSuite = []struct {
|
var testSuite = []struct {
|
||||||
|
|
@ -681,7 +681,7 @@ func TestTracingWithOverrides(t *testing.T) {
|
||||||
signer, accounts[0].key)
|
signer, accounts[0].key)
|
||||||
b.AddTx(tx)
|
b.AddTx(tx)
|
||||||
})
|
})
|
||||||
defer backend.chain.Stop()
|
defer backend.teardown()
|
||||||
api := NewAPI(backend)
|
api := NewAPI(backend)
|
||||||
randomAccounts := newAccounts(3)
|
randomAccounts := newAccounts(3)
|
||||||
type res struct {
|
type res struct {
|
||||||
|
|
@ -1105,6 +1105,7 @@ func TestTraceChain(t *testing.T) {
|
||||||
nonce += 1
|
nonce += 1
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
defer backend.teardown()
|
||||||
backend.refHook = func() { ref.Add(1) }
|
backend.refHook = func() { ref.Add(1) }
|
||||||
backend.relHook = func() { rel.Add(1) }
|
backend.relHook = func() { rel.Add(1) }
|
||||||
api := NewAPI(backend)
|
api := NewAPI(backend)
|
||||||
|
|
@ -1212,7 +1213,7 @@ func TestTraceBlockWithBasefee(t *testing.T) {
|
||||||
txHash = tx.Hash()
|
txHash = tx.Hash()
|
||||||
baseFee.Set(b.BaseFee())
|
baseFee.Set(b.BaseFee())
|
||||||
})
|
})
|
||||||
defer backend.chain.Stop()
|
defer backend.teardown()
|
||||||
api := NewAPI(backend)
|
api := NewAPI(backend)
|
||||||
|
|
||||||
var testSuite = []struct {
|
var testSuite = []struct {
|
||||||
|
|
@ -1298,7 +1299,7 @@ func TestStandardTraceBlockToFile(t *testing.T) {
|
||||||
b.AddTx(tx)
|
b.AddTx(tx)
|
||||||
txHashs = append(txHashs, tx.Hash())
|
txHashs = append(txHashs, tx.Hash())
|
||||||
})
|
})
|
||||||
defer backend.chain.Stop()
|
defer backend.teardown()
|
||||||
|
|
||||||
var testSuite = []struct {
|
var testSuite = []struct {
|
||||||
blockNumber rpc.BlockNumber
|
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