Merge commit '8f7eb9ccd' into merge/geth-v1.13.11

This commit is contained in:
philip-morlier 2024-01-24 09:31:20 -08:00
commit 48be241dbf
163 changed files with 2403 additions and 1623 deletions

View File

@ -17,7 +17,7 @@ jobs:
with: with:
go-version: 1.21.4 go-version: 1.21.4
- name: Run tests - name: Run tests
run: go test ./... run: go test -short ./...
env: env:
GOOS: linux GOOS: linux
GOARCH: 386 GOARCH: 386

View File

@ -44,7 +44,7 @@ func (b *SimulatedBackend) Fork(ctx context.Context, parentHash common.Hash) err
// Deprecated: please use simulated.Backend from package // Deprecated: please use simulated.Backend from package
// github.com/ethereum/go-ethereum/ethclient/simulated instead. // github.com/ethereum/go-ethereum/ethclient/simulated instead.
func NewSimulatedBackend(alloc core.GenesisAlloc, gasLimit uint64) *SimulatedBackend { func NewSimulatedBackend(alloc core.GenesisAlloc, gasLimit uint64) *SimulatedBackend {
b := simulated.New(alloc, gasLimit) b := simulated.NewBackend(alloc, simulated.WithBlockGasLimit(gasLimit))
return &SimulatedBackend{ return &SimulatedBackend{
Backend: b, Backend: b,
Client: b.Client(), Client: b.Client(),

View File

@ -56,11 +56,10 @@ var waitDeployedTests = map[string]struct {
func TestWaitDeployed(t *testing.T) { func TestWaitDeployed(t *testing.T) {
t.Parallel() t.Parallel()
for name, test := range waitDeployedTests { for name, test := range waitDeployedTests {
backend := simulated.New( backend := simulated.NewBackend(
core.GenesisAlloc{ core.GenesisAlloc{
crypto.PubkeyToAddress(testKey.PublicKey): {Balance: big.NewInt(10000000000000000)}, crypto.PubkeyToAddress(testKey.PublicKey): {Balance: big.NewInt(10000000000000000)},
}, },
10000000,
) )
defer backend.Close() defer backend.Close()
@ -102,11 +101,10 @@ func TestWaitDeployed(t *testing.T) {
} }
func TestWaitDeployedCornerCases(t *testing.T) { func TestWaitDeployedCornerCases(t *testing.T) {
backend := simulated.New( backend := simulated.NewBackend(
core.GenesisAlloc{ core.GenesisAlloc{
crypto.PubkeyToAddress(testKey.PublicKey): {Balance: big.NewInt(10000000000000000)}, crypto.PubkeyToAddress(testKey.PublicKey): {Balance: big.NewInt(10000000000000000)},
}, },
10000000,
) )
defer backend.Close() defer backend.Close()

View File

@ -279,7 +279,7 @@ func (w *ledgerDriver) ledgerDerive(derivationPath []uint32) (common.Address, er
} }
hexstr := reply[1 : 1+int(reply[0])] hexstr := reply[1 : 1+int(reply[0])]
// Decode the hex sting into an Ethereum address and return // Decode the hex string into an Ethereum address and return
var address common.Address var address common.Address
if _, err = hex.Decode(address[:], hexstr); err != nil { if _, err = hex.Decode(address[:], hexstr); err != nil {
return common.Address{}, err return common.Address{}, err

View File

@ -26,6 +26,16 @@ import (
"github.com/ethereum/go-ethereum/trie" "github.com/ethereum/go-ethereum/trie"
) )
// PayloadVersion denotes the version of PayloadAttributes used to request the
// building of the payload to commence.
type PayloadVersion byte
var (
PayloadV1 PayloadVersion = 0x1
PayloadV2 PayloadVersion = 0x2
PayloadV3 PayloadVersion = 0x3
)
//go:generate go run github.com/fjl/gencodec -type PayloadAttributes -field-override payloadAttributesMarshaling -out gen_blockparams.go //go:generate go run github.com/fjl/gencodec -type PayloadAttributes -field-override payloadAttributesMarshaling -out gen_blockparams.go
// PayloadAttributes describes the environment context in which a block should // PayloadAttributes describes the environment context in which a block should
@ -115,6 +125,21 @@ type TransitionConfigurationV1 struct {
// PayloadID is an identifier of the payload build process // PayloadID is an identifier of the payload build process
type PayloadID [8]byte type PayloadID [8]byte
// Version returns the payload version associated with the identifier.
func (b PayloadID) Version() PayloadVersion {
return PayloadVersion(b[0])
}
// Is returns whether the identifier matches any of provided payload versions.
func (b PayloadID) Is(versions ...PayloadVersion) bool {
for _, v := range versions {
if v == b.Version() {
return true
}
}
return false
}
func (b PayloadID) String() string { func (b PayloadID) String() string {
return hexutil.Encode(b[:]) return hexutil.Encode(b[:])
} }

View File

@ -5,22 +5,22 @@
# https://github.com/ethereum/execution-spec-tests/releases/download/v1.0.6/ # https://github.com/ethereum/execution-spec-tests/releases/download/v1.0.6/
485af7b66cf41eb3a8c1bd46632913b8eb95995df867cf665617bbc9b4beedd1 fixtures_develop.tar.gz 485af7b66cf41eb3a8c1bd46632913b8eb95995df867cf665617bbc9b4beedd1 fixtures_develop.tar.gz
# version:golang 1.21.5 # version:golang 1.21.6
# https://go.dev/dl/ # https://go.dev/dl/
285cbbdf4b6e6e62ed58f370f3f6d8c30825d6e56c5853c66d3c23bcdb09db19 go1.21.5.src.tar.gz 124926a62e45f78daabbaedb9c011d97633186a33c238ffc1e25320c02046248 go1.21.6.src.tar.gz
a2e1d5743e896e5fe1e7d96479c0a769254aed18cf216cf8f4c3a2300a9b3923 go1.21.5.darwin-amd64.tar.gz 31d6ecca09010ab351e51343a5af81d678902061fee871f912bdd5ef4d778850 go1.21.6.darwin-amd64.tar.gz
d0f8ac0c4fb3efc223a833010901d02954e3923cfe2c9a2ff0e4254a777cc9cc go1.21.5.darwin-arm64.tar.gz 0ff541fb37c38e5e5c5bcecc8f4f43c5ffd5e3a6c33a5d3e4003ded66fcfb331 go1.21.6.darwin-arm64.tar.gz
2c05bbe0dc62456b90b7ddd354a54f373b7c377a98f8b22f52ab694b4f6cca58 go1.21.5.freebsd-386.tar.gz a1d1a149b34bf0f53965a237682c6da1140acabb131bf0e597240e4a140b0e5e go1.21.6.freebsd-386.tar.gz
30b6c64e9a77129605bc12f836422bf09eec577a8c899ee46130aeff81567003 go1.21.5.freebsd-amd64.tar.gz de59e1217e4398b1522eed8dddabab2fa1b97aecbdca3af08e34832b4f0e3f81 go1.21.6.freebsd-amd64.tar.gz
8f4dba9cf5c61757bbd7e9ebdb93b6a30a1b03f4a636a1ba0cc2f27b907ab8e1 go1.21.5.linux-386.tar.gz 05d09041b5a1193c14e4b2db3f7fcc649b236c567f5eb93305c537851b72dd95 go1.21.6.linux-386.tar.gz
e2bc0b3e4b64111ec117295c088bde5f00eeed1567999ff77bc859d7df70078e go1.21.5.linux-amd64.tar.gz 3f934f40ac360b9c01f616a9aa1796d227d8b0328bf64cb045c7b8c4ee9caea4 go1.21.6.linux-amd64.tar.gz
841cced7ecda9b2014f139f5bab5ae31785f35399f236b8b3e75dff2a2978d96 go1.21.5.linux-arm64.tar.gz e2e8aa88e1b5170a0d495d7d9c766af2b2b6c6925a8f8956d834ad6b4cacbd9a go1.21.6.linux-arm64.tar.gz
837f4bf4e22fcdf920ffeaa4abf3d02d1314e03725431065f4d44c46a01b42fe go1.21.5.linux-armv6l.tar.gz 6a8eda6cc6a799ff25e74ce0c13fdc1a76c0983a0bb07c789a2a3454bf6ec9b2 go1.21.6.linux-armv6l.tar.gz
907b8c6ec4be9b184952e5d3493be66b1746442394a8bc78556c56834cd7c38b go1.21.5.linux-ppc64le.tar.gz e872b1e9a3f2f08fd4554615a32ca9123a4ba877ab6d19d36abc3424f86bc07f go1.21.6.linux-ppc64le.tar.gz
9c4a81b72ebe44368813cd03684e1080a818bf915d84163abae2ed325a1b2dc0 go1.21.5.linux-s390x.tar.gz 92894d0f732d3379bc414ffdd617eaadad47e1d72610e10d69a1156db03fc052 go1.21.6.linux-s390x.tar.gz
6da2418889dfb37763d0eb149c4a8d728c029e12f0cd54fbca0a31ae547e2d34 go1.21.5.windows-386.zip 65b38857135cf45c80e1d267e0ce4f80fe149326c68835217da4f2da9b7943fe go1.21.6.windows-386.zip
bbe603cde7c9dee658f45164b4d06de1eff6e6e6b800100824e7c00d56a9a92f go1.21.5.windows-amd64.zip 27ac9dd6e66fb3fd0acfa6792ff053c86e7d2c055b022f4b5d53bfddec9e3301 go1.21.6.windows-amd64.zip
9b7acca50e674294e43202df4fbc26d5af4d8bc3170a3342a1514f09a2dab5e9 go1.21.5.windows-arm64.zip b93aff8f3c882c764c66a39b7a1483b0460e051e9992bf3435479129e5051bcd go1.21.6.windows-arm64.zip
# version:golangci 1.55.2 # version:golangci 1.55.2
# https://github.com/golangci/golangci-lint/releases/ # https://github.com/golangci/golangci-lint/releases/

View File

@ -20,7 +20,7 @@
# - NSIS Large Strings build, http://nsis.sourceforge.net/Special_Builds # - NSIS Large Strings build, http://nsis.sourceforge.net/Special_Builds
# - SFP, http://nsis.sourceforge.net/NSIS_Simple_Firewall_Plugin (put dll in NSIS\Plugins\x86-ansi) # - SFP, http://nsis.sourceforge.net/NSIS_Simple_Firewall_Plugin (put dll in NSIS\Plugins\x86-ansi)
# #
# After intalling NSIS extra the NSIS Large Strings build zip and replace the makensis.exe and the # After installing NSIS extra the NSIS Large Strings build zip and replace the makensis.exe and the
# files found in Stub. # files found in Stub.
# #
# based on: http://nsis.sourceforge.net/A_simple_installer_with_start_menu_shortcut_and_uninstaller # based on: http://nsis.sourceforge.net/A_simple_installer_with_start_menu_shortcut_and_uninstaller

View File

@ -76,9 +76,9 @@ func (s *Suite) EthTests() []utesting.Test {
{Name: "TestMaliciousHandshake", Fn: s.TestMaliciousHandshake}, {Name: "TestMaliciousHandshake", Fn: s.TestMaliciousHandshake},
{Name: "TestMaliciousStatus", Fn: s.TestMaliciousStatus}, {Name: "TestMaliciousStatus", Fn: s.TestMaliciousStatus},
// test transactions // test transactions
{Name: "TestLargeTxRequest", Fn: s.TestLargeTxRequest, Slow: true},
{Name: "TestTransaction", Fn: s.TestTransaction}, {Name: "TestTransaction", Fn: s.TestTransaction},
{Name: "TestInvalidTxs", Fn: s.TestInvalidTxs}, {Name: "TestInvalidTxs", Fn: s.TestInvalidTxs},
{Name: "TestLargeTxRequest", Fn: s.TestLargeTxRequest},
{Name: "TestNewPooledTxs", Fn: s.TestNewPooledTxs}, {Name: "TestNewPooledTxs", Fn: s.TestNewPooledTxs},
{Name: "TestBlobViolations", Fn: s.TestBlobViolations}, {Name: "TestBlobViolations", Fn: s.TestBlobViolations},
} }

View File

@ -63,6 +63,9 @@ func TestEthSuite(t *testing.T) {
} }
for _, test := range suite.EthTests() { for _, test := range suite.EthTests() {
t.Run(test.Name, func(t *testing.T) { t.Run(test.Name, func(t *testing.T) {
if test.Slow && testing.Short() {
t.Skipf("%s: skipping in -short mode", test.Name)
}
result := utesting.RunTests([]utesting.Test{{Name: test.Name, Fn: test.Fn}}, os.Stdout) result := utesting.RunTests([]utesting.Test{{Name: test.Name, Fn: test.Fn}}, os.Stdout)
if result[0].Failed { if result[0].Failed {
t.Fatal() t.Fatal()

View File

@ -497,7 +497,7 @@ func FindnodeAmplificationWrongIP(t *utesting.T) {
// If we receive a NEIGHBORS response, the attack worked and the test fails. // If we receive a NEIGHBORS response, the attack worked and the test fails.
reply, _, _ := te.read(te.l2) reply, _, _ := te.read(te.l2)
if reply != nil { if reply != nil {
t.Error("Got NEIGHORS response for FINDNODE from wrong IP") t.Error("Got NEIGHBORS response for FINDNODE from wrong IP")
} }
} }

View File

@ -36,6 +36,7 @@ import (
"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/ethereum/go-ethereum/trie" "github.com/ethereum/go-ethereum/trie"
"github.com/holiman/uint256"
"golang.org/x/crypto/sha3" "golang.org/x/crypto/sha3"
) )
@ -308,15 +309,15 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig,
reward.Sub(reward, new(big.Int).SetUint64(ommer.Delta)) reward.Sub(reward, new(big.Int).SetUint64(ommer.Delta))
reward.Mul(reward, blockReward) reward.Mul(reward, blockReward)
reward.Div(reward, big.NewInt(8)) reward.Div(reward, big.NewInt(8))
statedb.AddBalance(ommer.Address, reward) statedb.AddBalance(ommer.Address, uint256.MustFromBig(reward))
} }
statedb.AddBalance(pre.Env.Coinbase, minerReward) statedb.AddBalance(pre.Env.Coinbase, uint256.MustFromBig(minerReward))
} }
// Apply withdrawals // Apply withdrawals
for _, w := range pre.Env.Withdrawals { for _, w := range pre.Env.Withdrawals {
// Amount is in gwei, turn into wei // Amount is in gwei, turn into wei
amount := new(big.Int).Mul(new(big.Int).SetUint64(w.Amount), big.NewInt(params.GWei)) amount := new(big.Int).Mul(new(big.Int).SetUint64(w.Amount), big.NewInt(params.GWei))
statedb.AddBalance(w.Address, amount) statedb.AddBalance(w.Address, uint256.MustFromBig(amount))
} }
// Commit block // Commit block
root, err := statedb.Commit(vmContext.BlockNumber.Uint64(), chainConfig.IsEIP158(vmContext.BlockNumber)) root, err := statedb.Commit(vmContext.BlockNumber.Uint64(), chainConfig.IsEIP158(vmContext.BlockNumber))
@ -359,7 +360,7 @@ func MakePreState(db ethdb.Database, accounts core.GenesisAlloc) *state.StateDB
for addr, a := range accounts { for addr, a := range accounts {
statedb.SetCode(addr, a.Code) statedb.SetCode(addr, a.Code)
statedb.SetNonce(addr, a.Nonce) statedb.SetNonce(addr, a.Nonce)
statedb.SetBalance(addr, a.Balance) statedb.SetBalance(addr, uint256.MustFromBig(a.Balance))
for k, v := range a.Storage { for k, v := range a.Storage {
statedb.SetState(addr, k, v) statedb.SetState(addr, k, v)
} }

View File

@ -188,7 +188,7 @@ func Transition(ctx *cli.Context) error {
if err != nil { if err != nil {
return err return err
} }
// Dump the excution result // Dump the execution result
collector := make(Alloc) collector := make(Alloc)
s.DumpToCollector(collector, nil) s.DumpToCollector(collector, nil)
return dispatchOutput(ctx, baseDir, result, collector, body) return dispatchOutput(ctx, baseDir, result, collector, body)
@ -280,7 +280,7 @@ func (g Alloc) OnAccount(addr *common.Address, dumpAccount state.DumpAccount) {
if addr == nil { if addr == nil {
return return
} }
balance, _ := new(big.Int).SetString(dumpAccount.Balance, 10) balance, _ := new(big.Int).SetString(dumpAccount.Balance, 0)
var storage map[common.Hash]common.Hash var storage map[common.Hash]common.Hash
if dumpAccount.Storage != nil { if dumpAccount.Storage != nil {
storage = make(map[common.Hash]common.Hash) storage = make(map[common.Hash]common.Hash)

View File

@ -26,7 +26,6 @@ import (
"time" "time"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/internal/debug"
"github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/log"
"github.com/holiman/uint256" "github.com/holiman/uint256"
"github.com/urfave/cli/v2" "github.com/urfave/cli/v2"
@ -51,9 +50,6 @@ func (c customQuotedStringer) String() string {
// logTest is an entry point which spits out some logs. This is used by testing // logTest is an entry point which spits out some logs. This is used by testing
// to verify expected outputs // to verify expected outputs
func logTest(ctx *cli.Context) error { func logTest(ctx *cli.Context) error {
// clear field padding map
debug.ResetLogging()
{ // big.Int { // big.Int
ba, _ := new(big.Int).SetString("111222333444555678999", 10) // "111,222,333,444,555,678,999" ba, _ := new(big.Int).SetString("111222333444555678999", 10) // "111,222,333,444,555,678,999"
bb, _ := new(big.Int).SetString("-111222333444555678999", 10) // "-111,222,333,444,555,678,999" bb, _ := new(big.Int).SetString("-111222333444555678999", 10) // "-111,222,333,444,555,678,999"

View File

@ -14,7 +14,7 @@
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with go-ethereum. If not, see <http://www.gnu.org/licenses/>. // along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.
// geth is the official command-line client for Ethereum. // geth is a command-line client for Ethereum.
package main package main
import ( import (

View File

@ -25,7 +25,9 @@ import (
"flag" "flag"
"fmt" "fmt"
"io" "io"
"math"
"os" "os"
"strconv"
"strings" "strings"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
@ -37,6 +39,7 @@ var (
reverseMode = flag.Bool("reverse", false, "convert ASCII to rlp") reverseMode = flag.Bool("reverse", false, "convert ASCII to rlp")
noASCII = flag.Bool("noascii", false, "don't print ASCII strings readably") noASCII = flag.Bool("noascii", false, "don't print ASCII strings readably")
single = flag.Bool("single", false, "print only the first element, discard the rest") single = flag.Bool("single", false, "print only the first element, discard the rest")
showpos = flag.Bool("pos", false, "display element byte posititions")
) )
func init() { func init() {
@ -52,17 +55,17 @@ If the filename is omitted, data is read from stdin.`)
func main() { func main() {
flag.Parse() flag.Parse()
var r io.Reader var r *inStream
switch { switch {
case *hexMode != "": case *hexMode != "":
data, err := hex.DecodeString(strings.TrimPrefix(*hexMode, "0x")) data, err := hex.DecodeString(strings.TrimPrefix(*hexMode, "0x"))
if err != nil { if err != nil {
die(err) die(err)
} }
r = bytes.NewReader(data) r = newInStream(bytes.NewReader(data), int64(len(data)))
case flag.NArg() == 0: case flag.NArg() == 0:
r = os.Stdin r = newInStream(bufio.NewReader(os.Stdin), 0)
case flag.NArg() == 1: case flag.NArg() == 1:
fd, err := os.Open(flag.Arg(0)) fd, err := os.Open(flag.Arg(0))
@ -70,13 +73,19 @@ func main() {
die(err) die(err)
} }
defer fd.Close() defer fd.Close()
r = fd var size int64
finfo, err := fd.Stat()
if err == nil {
size = finfo.Size()
}
r = newInStream(bufio.NewReader(fd), size)
default: default:
fmt.Fprintln(os.Stderr, "Error: too many arguments") fmt.Fprintln(os.Stderr, "Error: too many arguments")
flag.Usage() flag.Usage()
os.Exit(2) os.Exit(2)
} }
out := os.Stdout out := os.Stdout
if *reverseMode { if *reverseMode {
data, err := textToRlp(r) data, err := textToRlp(r)
@ -93,10 +102,10 @@ func main() {
} }
} }
func rlpToText(r io.Reader, out io.Writer) error { func rlpToText(in *inStream, out io.Writer) error {
s := rlp.NewStream(r, 0) stream := rlp.NewStream(in, 0)
for { for {
if err := dump(s, 0, out); err != nil { if err := dump(in, stream, 0, out); err != nil {
if err != io.EOF { if err != io.EOF {
return err return err
} }
@ -110,7 +119,10 @@ func rlpToText(r io.Reader, out io.Writer) error {
return nil return nil
} }
func dump(s *rlp.Stream, depth int, out io.Writer) error { func dump(in *inStream, s *rlp.Stream, depth int, out io.Writer) error {
if *showpos {
fmt.Fprintf(out, "%s: ", in.posLabel())
}
kind, size, err := s.Kind() kind, size, err := s.Kind()
if err != nil { if err != nil {
return err return err
@ -137,7 +149,7 @@ func dump(s *rlp.Stream, depth int, out io.Writer) error {
if i > 0 { if i > 0 {
fmt.Fprint(out, ",\n") fmt.Fprint(out, ",\n")
} }
if err := dump(s, depth+1, out); err == rlp.EOL { if err := dump(in, s, depth+1, out); err == rlp.EOL {
break break
} else if err != nil { } else if err != nil {
return err return err
@ -208,3 +220,36 @@ func textToRlp(r io.Reader) ([]byte, error) {
data, err := rlp.EncodeToBytes(obj[0]) data, err := rlp.EncodeToBytes(obj[0])
return data, err return data, err
} }
type inStream struct {
br rlp.ByteReader
pos int
columns int
}
func newInStream(br rlp.ByteReader, totalSize int64) *inStream {
col := int(math.Ceil(math.Log10(float64(totalSize))))
return &inStream{br: br, columns: col}
}
func (rc *inStream) Read(b []byte) (n int, err error) {
n, err = rc.br.Read(b)
rc.pos += n
return n, err
}
func (rc *inStream) ReadByte() (byte, error) {
b, err := rc.br.ReadByte()
if err == nil {
rc.pos++
}
return b, err
}
func (rc *inStream) posLabel() string {
l := strconv.FormatInt(int64(rc.pos), 10)
if len(l) < rc.columns {
l = strings.Repeat(" ", rc.columns-len(l)) + l
}
return l
}

View File

@ -34,7 +34,8 @@ func TestRoundtrip(t *testing.T) {
"0xc780c0c1c0825208", "0xc780c0c1c0825208",
} { } {
var out strings.Builder var out strings.Builder
err := rlpToText(bytes.NewReader(common.FromHex(want)), &out) in := newInStream(bytes.NewReader(common.FromHex(want)), 0)
err := rlpToText(in, &out)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }

View File

@ -16,7 +16,11 @@
package common package common
import "math/big" import (
"math/big"
"github.com/holiman/uint256"
)
// Common big integers often used // Common big integers often used
var ( var (
@ -27,4 +31,6 @@ var (
Big32 = big.NewInt(32) Big32 = big.NewInt(32)
Big256 = big.NewInt(256) Big256 = big.NewInt(256)
Big257 = big.NewInt(257) Big257 = big.NewInt(257)
U2560 = uint256.NewInt(0)
) )

View File

@ -30,6 +30,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"
) )
// Proof-of-stake protocol constants. // Proof-of-stake protocol constants.
@ -355,8 +356,8 @@ func (beacon *Beacon) Finalize(chain consensus.ChainHeaderReader, header *types.
// Withdrawals processing. // Withdrawals processing.
for _, w := range withdrawals { for _, w := range withdrawals {
// Convert amount from gwei to wei. // Convert amount from gwei to wei.
amount := new(big.Int).SetUint64(w.Amount) amount := new(uint256.Int).SetUint64(w.Amount)
amount = amount.Mul(amount, big.NewInt(params.GWei)) amount = amount.Mul(amount, uint256.NewInt(params.GWei))
state.AddBalance(w.Address, amount) state.AddBalance(w.Address, amount)
} }
// No block reward which is issued by consensus layer instead. // No block reward which is issued by consensus layer instead.

View File

@ -33,14 +33,15 @@ import (
"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/ethereum/go-ethereum/trie" "github.com/ethereum/go-ethereum/trie"
"github.com/holiman/uint256"
"golang.org/x/crypto/sha3" "golang.org/x/crypto/sha3"
) )
// Ethash proof-of-work protocol constants. // Ethash proof-of-work protocol constants.
var ( var (
FrontierBlockReward = big.NewInt(5e+18) // Block reward in wei for successfully mining a block FrontierBlockReward = uint256.NewInt(5e+18) // Block reward in wei for successfully mining a block
ByzantiumBlockReward = big.NewInt(3e+18) // Block reward in wei for successfully mining a block upward from Byzantium ByzantiumBlockReward = uint256.NewInt(3e+18) // Block reward in wei for successfully mining a block upward from Byzantium
ConstantinopleBlockReward = big.NewInt(2e+18) // Block reward in wei for successfully mining a block upward from Constantinople ConstantinopleBlockReward = uint256.NewInt(2e+18) // Block reward in wei for successfully mining a block upward from Constantinople
maxUncles = 2 // Maximum number of uncles allowed in a single block maxUncles = 2 // Maximum number of uncles allowed in a single block
allowedFutureBlockTimeSeconds = int64(15) // Max seconds from current time allowed for blocks, before they're considered future blocks allowedFutureBlockTimeSeconds = int64(15) // Max seconds from current time allowed for blocks, before they're considered future blocks
@ -562,8 +563,8 @@ func (ethash *Ethash) SealHash(header *types.Header) (hash common.Hash) {
// Some weird constants to avoid constant memory allocs for them. // Some weird constants to avoid constant memory allocs for them.
var ( var (
big8 = big.NewInt(8) u256_8 = uint256.NewInt(8)
big32 = big.NewInt(32) u256_32 = uint256.NewInt(32)
) )
// AccumulateRewards credits the coinbase of the given block with the mining // AccumulateRewards credits the coinbase of the given block with the mining
@ -579,16 +580,18 @@ func accumulateRewards(config *params.ChainConfig, state *state.StateDB, header
blockReward = ConstantinopleBlockReward blockReward = ConstantinopleBlockReward
} }
// Accumulate the rewards for the miner and any included uncles // Accumulate the rewards for the miner and any included uncles
reward := new(big.Int).Set(blockReward) reward := new(uint256.Int).Set(blockReward)
r := new(big.Int) r := new(uint256.Int)
hNum, _ := uint256.FromBig(header.Number)
for _, uncle := range uncles { for _, uncle := range uncles {
r.Add(uncle.Number, big8) uNum, _ := uint256.FromBig(uncle.Number)
r.Sub(r, header.Number) r.AddUint64(uNum, 8)
r.Sub(r, hNum)
r.Mul(r, blockReward) r.Mul(r, blockReward)
r.Div(r, big8) r.Div(r, u256_8)
state.AddBalance(uncle.Coinbase, r) state.AddBalance(uncle.Coinbase, r)
r.Div(blockReward, big32) r.Div(blockReward, u256_32)
reward.Add(reward, r) reward.Add(reward, r)
} }
state.AddBalance(header.Coinbase, reward) state.AddBalance(header.Coinbase, reward)

View File

@ -24,6 +24,7 @@ import (
"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"
"github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/params"
"github.com/holiman/uint256"
) )
var ( var (
@ -81,6 +82,6 @@ func ApplyDAOHardFork(statedb *state.StateDB) {
// Move every DAO account and extra-balance account funds into the refund contract // Move every DAO account and extra-balance account funds into the refund contract
for _, addr := range params.DAODrainList() { for _, addr := range params.DAODrainList() {
statedb.AddBalance(params.DAORefundContract, statedb.GetBalance(addr)) statedb.AddBalance(params.DAORefundContract, statedb.GetBalance(addr))
statedb.SetBalance(addr, new(big.Int)) statedb.SetBalance(addr, new(uint256.Int))
} }
} }

View File

@ -243,7 +243,7 @@ func BenchmarkChainWrite_full_500k(b *testing.B) {
// makeChainForBench writes a given number of headers or empty blocks/receipts // makeChainForBench writes a given number of headers or empty blocks/receipts
// into a database. // into a database.
func makeChainForBench(db ethdb.Database, full bool, count uint64) { func makeChainForBench(db ethdb.Database, genesis *Genesis, full bool, count uint64) {
var hash common.Hash var hash common.Hash
for n := uint64(0); n < count; n++ { for n := uint64(0); n < count; n++ {
header := &types.Header{ header := &types.Header{
@ -255,6 +255,9 @@ func makeChainForBench(db ethdb.Database, full bool, count uint64) {
TxHash: types.EmptyTxsHash, TxHash: types.EmptyTxsHash,
ReceiptHash: types.EmptyReceiptsHash, ReceiptHash: types.EmptyReceiptsHash,
} }
if n == 0 {
header = genesis.ToBlock().Header()
}
hash = header.Hash() hash = header.Hash()
rawdb.WriteHeader(db, header) rawdb.WriteHeader(db, header)
@ -262,7 +265,7 @@ func makeChainForBench(db ethdb.Database, full bool, count uint64) {
rawdb.WriteTd(db, hash, n, big.NewInt(int64(n+1))) rawdb.WriteTd(db, hash, n, big.NewInt(int64(n+1)))
if n == 0 { if n == 0 {
rawdb.WriteChainConfig(db, hash, params.AllEthashProtocolChanges) rawdb.WriteChainConfig(db, hash, genesis.Config)
} }
rawdb.WriteHeadHeaderHash(db, hash) rawdb.WriteHeadHeaderHash(db, hash)
@ -276,13 +279,14 @@ func makeChainForBench(db ethdb.Database, full bool, count uint64) {
} }
func benchWriteChain(b *testing.B, full bool, count uint64) { func benchWriteChain(b *testing.B, full bool, count uint64) {
genesis := &Genesis{Config: params.AllEthashProtocolChanges}
for i := 0; i < b.N; i++ { for i := 0; i < b.N; i++ {
dir := b.TempDir() dir := b.TempDir()
db, err := rawdb.NewLevelDBDatabase(dir, 128, 1024, "", false) db, err := rawdb.NewLevelDBDatabase(dir, 128, 1024, "", false)
if err != nil { if err != nil {
b.Fatalf("error opening database at %v: %v", dir, err) b.Fatalf("error opening database at %v: %v", dir, err)
} }
makeChainForBench(db, full, count) makeChainForBench(db, genesis, full, count)
db.Close() db.Close()
} }
} }
@ -294,7 +298,8 @@ func benchReadChain(b *testing.B, full bool, count uint64) {
if err != nil { if err != nil {
b.Fatalf("error opening database at %v: %v", dir, err) b.Fatalf("error opening database at %v: %v", dir, err)
} }
makeChainForBench(db, full, count) genesis := &Genesis{Config: params.AllEthashProtocolChanges}
makeChainForBench(db, genesis, full, count)
db.Close() db.Close()
cacheConfig := *defaultCacheConfig cacheConfig := *defaultCacheConfig
cacheConfig.TrieDirtyDisabled = true cacheConfig.TrieDirtyDisabled = true
@ -307,7 +312,7 @@ func benchReadChain(b *testing.B, full bool, count uint64) {
if err != nil { if err != nil {
b.Fatalf("error opening database at %v: %v", dir, err) b.Fatalf("error opening database at %v: %v", dir, err)
} }
chain, err := NewBlockChain(db, &cacheConfig, nil, nil, ethash.NewFaker(), vm.Config{}, nil, nil) chain, err := NewBlockChain(db, &cacheConfig, genesis, nil, ethash.NewFaker(), vm.Config{}, nil, nil)
if err != nil { if err != nil {
b.Fatalf("error creating chain: %v", err) b.Fatalf("error creating chain: %v", err)
} }

View File

@ -185,6 +185,13 @@ func DefaultCacheConfigWithScheme(scheme string) *CacheConfig {
return &config return &config
} }
// txLookup is wrapper over transaction lookup along with the corresponding
// transaction object.
type txLookup struct {
lookup *rawdb.LegacyTxLookupEntry
transaction *types.Transaction
}
// BlockChain represents the canonical chain given a database with a genesis // BlockChain represents the canonical chain given a database with a genesis
// block. The Blockchain manages chain imports, reverts, chain reorganisations. // block. The Blockchain manages chain imports, reverts, chain reorganisations.
// //
@ -211,13 +218,7 @@ type BlockChain struct {
flushInterval atomic.Int64 // Time interval (processing time) after which to flush a state flushInterval atomic.Int64 // Time interval (processing time) after which to flush a state
triedb *trie.Database // The database handler for maintaining trie nodes. triedb *trie.Database // The database handler for maintaining trie nodes.
stateCache state.Database // State database to reuse between imports (contains state cache) stateCache state.Database // State database to reuse between imports (contains state cache)
txIndexer *txIndexer // Transaction indexer, might be nil if not enabled
// txLookupLimit is the maximum number of blocks from head whose tx indices
// are reserved:
// * 0: means no limit and regenerate any missing indexes
// * N: means N block limit [HEAD-N+1, HEAD] and delete extra indexes
// * nil: disable tx reindexer/deleter, but still index new blocks
txLookupLimit uint64
hc *HeaderChain hc *HeaderChain
rmLogsFeed event.Feed rmLogsFeed event.Feed
@ -242,12 +243,12 @@ type BlockChain struct {
bodyRLPCache *lru.Cache[common.Hash, rlp.RawValue] bodyRLPCache *lru.Cache[common.Hash, rlp.RawValue]
receiptsCache *lru.Cache[common.Hash, []*types.Receipt] receiptsCache *lru.Cache[common.Hash, []*types.Receipt]
blockCache *lru.Cache[common.Hash, *types.Block] blockCache *lru.Cache[common.Hash, *types.Block]
txLookupCache *lru.Cache[common.Hash, *rawdb.LegacyTxLookupEntry] txLookupCache *lru.Cache[common.Hash, txLookup]
// future blocks are blocks added for later processing // future blocks are blocks added for later processing
futureBlocks *lru.Cache[common.Hash, *types.Block] futureBlocks *lru.Cache[common.Hash, *types.Block]
wg sync.WaitGroup // wg sync.WaitGroup
quit chan struct{} // shutdown signal, closed in Stop. quit chan struct{} // shutdown signal, closed in Stop.
stopping atomic.Bool // false if chain is running, true when stopped stopping atomic.Bool // false if chain is running, true when stopped
procInterrupt atomic.Bool // interrupt signaler for block processing procInterrupt atomic.Bool // interrupt signaler for block processing
@ -297,7 +298,7 @@ func NewBlockChain(db ethdb.Database, cacheConfig *CacheConfig, genesis *Genesis
bodyRLPCache: lru.NewCache[common.Hash, rlp.RawValue](bodyCacheLimit), bodyRLPCache: lru.NewCache[common.Hash, rlp.RawValue](bodyCacheLimit),
receiptsCache: lru.NewCache[common.Hash, []*types.Receipt](receiptsCacheLimit), receiptsCache: lru.NewCache[common.Hash, []*types.Receipt](receiptsCacheLimit),
blockCache: lru.NewCache[common.Hash, *types.Block](blockCacheLimit), blockCache: lru.NewCache[common.Hash, *types.Block](blockCacheLimit),
txLookupCache: lru.NewCache[common.Hash, *rawdb.LegacyTxLookupEntry](txLookupCacheLimit), txLookupCache: lru.NewCache[common.Hash, txLookup](txLookupCacheLimit),
futureBlocks: lru.NewCache[common.Hash, *types.Block](maxFutureBlocks), futureBlocks: lru.NewCache[common.Hash, *types.Block](maxFutureBlocks),
engine: engine, engine: engine,
vmConfig: vmConfig, vmConfig: vmConfig,
@ -463,12 +464,9 @@ func NewBlockChain(db ethdb.Database, cacheConfig *CacheConfig, genesis *Genesis
} }
rawdb.WriteChainConfig(db, genesisHash, chainConfig) rawdb.WriteChainConfig(db, genesisHash, chainConfig)
} }
// Start tx indexer/unindexer if required. // Start tx indexer if it's enabled.
if txLookupLimit != nil { if txLookupLimit != nil {
bc.txLookupLimit = *txLookupLimit bc.txIndexer = newTxIndexer(*txLookupLimit, bc)
bc.wg.Add(1)
go bc.maintainTxIndex()
} }
return bc, nil return bc, nil
} }
@ -958,7 +956,10 @@ func (bc *BlockChain) stopWithoutSaving() {
if !bc.stopping.CompareAndSwap(false, true) { if !bc.stopping.CompareAndSwap(false, true) {
return return
} }
// Signal shutdown tx indexer.
if bc.txIndexer != nil {
bc.txIndexer.close()
}
// Unsubscribe all subscriptions registered from blockchain. // Unsubscribe all subscriptions registered from blockchain.
bc.scope.Close() bc.scope.Close()
@ -1155,14 +1156,13 @@ func (bc *BlockChain) InsertReceiptChain(blockChain types.Blocks, receiptChain [
// Ensure genesis is in ancients. // Ensure genesis is in ancients.
if first.NumberU64() == 1 { if first.NumberU64() == 1 {
if frozen, _ := bc.db.Ancients(); frozen == 0 { if frozen, _ := bc.db.Ancients(); frozen == 0 {
b := bc.genesisBlock
td := bc.genesisBlock.Difficulty() td := bc.genesisBlock.Difficulty()
writeSize, err := rawdb.WriteAncientBlocks(bc.db, []*types.Block{b}, []types.Receipts{nil}, td) writeSize, err := rawdb.WriteAncientBlocks(bc.db, []*types.Block{bc.genesisBlock}, []types.Receipts{nil}, td)
size += writeSize
if err != nil { if err != nil {
log.Error("Error writing genesis to ancients", "err", err) log.Error("Error writing genesis to ancients", "err", err)
return 0, err return 0, err
} }
size += writeSize
log.Info("Wrote genesis to ancients") log.Info("Wrote genesis to ancients")
} }
} }
@ -1176,44 +1176,11 @@ func (bc *BlockChain) InsertReceiptChain(blockChain types.Blocks, receiptChain [
// Write all chain data to ancients. // Write all chain data to ancients.
td := bc.GetTd(first.Hash(), first.NumberU64()) td := bc.GetTd(first.Hash(), first.NumberU64())
writeSize, err := rawdb.WriteAncientBlocks(bc.db, blockChain, receiptChain, td) writeSize, err := rawdb.WriteAncientBlocks(bc.db, blockChain, receiptChain, td)
size += writeSize
if err != nil { if err != nil {
log.Error("Error importing chain data to ancients", "err", err) log.Error("Error importing chain data to ancients", "err", err)
return 0, err return 0, err
} }
size += writeSize
// Write tx indices if any condition is satisfied:
// * If user requires to reserve all tx indices(txlookuplimit=0)
// * If all ancient tx indices are required to be reserved(txlookuplimit is even higher than ancientlimit)
// * If block number is large enough to be regarded as a recent block
// It means blocks below the ancientLimit-txlookupLimit won't be indexed.
//
// But if the `TxIndexTail` is not nil, e.g. Geth is initialized with
// an external ancient database, during the setup, blockchain will start
// a background routine to re-indexed all indices in [ancients - txlookupLimit, ancients)
// range. In this case, all tx indices of newly imported blocks should be
// generated.
batch := bc.db.NewBatch()
for i, block := range blockChain {
if bc.txLookupLimit == 0 || ancientLimit <= bc.txLookupLimit || block.NumberU64() >= ancientLimit-bc.txLookupLimit {
rawdb.WriteTxLookupEntriesByBlock(batch, block)
} else if rawdb.ReadTxIndexTail(bc.db) != nil {
rawdb.WriteTxLookupEntriesByBlock(batch, block)
}
stats.processed++
if batch.ValueSize() > ethdb.IdealBatchSize || i == len(blockChain)-1 {
size += int64(batch.ValueSize())
if err = batch.Write(); err != nil {
snapBlock := bc.CurrentSnapBlock().Number.Uint64()
if _, err := bc.db.TruncateHead(snapBlock + 1); err != nil {
log.Error("Can't truncate ancient store after failed insert", "err", err)
}
return 0, err
}
batch.Reset()
}
}
// Sync the ancient store explicitly to ensure all data has been flushed to disk. // Sync the ancient store explicitly to ensure all data has been flushed to disk.
if err := bc.db.Sync(); err != nil { if err := bc.db.Sync(); err != nil {
@ -1231,8 +1198,10 @@ func (bc *BlockChain) InsertReceiptChain(blockChain types.Blocks, receiptChain [
} }
// Delete block data from the main database. // Delete block data from the main database.
batch.Reset() var (
canonHashes := make(map[common.Hash]struct{}) batch = bc.db.NewBatch()
canonHashes = make(map[common.Hash]struct{})
)
for _, block := range blockChain { for _, block := range blockChain {
canonHashes[block.Hash()] = struct{}{} canonHashes[block.Hash()] = struct{}{}
if block.NumberU64() == 0 { if block.NumberU64() == 0 {
@ -1250,13 +1219,16 @@ func (bc *BlockChain) InsertReceiptChain(blockChain types.Blocks, receiptChain [
if err := batch.Write(); err != nil { if err := batch.Write(); err != nil {
return 0, err return 0, err
} }
stats.processed += int32(len(blockChain))
return 0, nil return 0, nil
} }
// writeLive writes blockchain and corresponding receipt chain into active store. // writeLive writes blockchain and corresponding receipt chain into active store.
writeLive := func(blockChain types.Blocks, receiptChain []types.Receipts) (int, error) { writeLive := func(blockChain types.Blocks, receiptChain []types.Receipts) (int, error) {
skipPresenceCheck := false var (
batch := bc.db.NewBatch() skipPresenceCheck = false
batch = bc.db.NewBatch()
)
for i, block := range blockChain { for i, block := range blockChain {
// Short circuit insertion if shutting down or processing failed // Short circuit insertion if shutting down or processing failed
if bc.insertStopped() { if bc.insertStopped() {
@ -1281,11 +1253,10 @@ func (bc *BlockChain) InsertReceiptChain(blockChain types.Blocks, receiptChain [
// Write all the data out into the database // Write all the data out into the database
rawdb.WriteBody(batch, block.Hash(), block.NumberU64(), block.Body()) rawdb.WriteBody(batch, block.Hash(), block.NumberU64(), block.Body())
rawdb.WriteReceipts(batch, block.Hash(), block.NumberU64(), receiptChain[i]) rawdb.WriteReceipts(batch, block.Hash(), block.NumberU64(), receiptChain[i])
rawdb.WriteTxLookupEntriesByBlock(batch, block) // Always write tx indices for live blocks, we assume they are needed
// Write everything belongs to the blocks into the database. So that // Write everything belongs to the blocks into the database. So that
// we can ensure all components of body is completed(body, receipts, // we can ensure all components of body is completed(body, receipts)
// tx indexes) // except transaction indexes(will be created once sync is finished).
if batch.ValueSize() >= ethdb.IdealBatchSize { if batch.ValueSize() >= ethdb.IdealBatchSize {
if err := batch.Write(); err != nil { if err := batch.Write(); err != nil {
return 0, err return 0, err
@ -1317,19 +1288,6 @@ func (bc *BlockChain) InsertReceiptChain(blockChain types.Blocks, receiptChain [
return n, err return n, err
} }
} }
// Write the tx index tail (block number from where we index) before write any live blocks
if len(liveBlocks) > 0 && liveBlocks[0].NumberU64() == ancientLimit+1 {
// The tx index tail can only be one of the following two options:
// * 0: all ancient blocks have been indexed
// * ancient-limit: the indices of blocks before ancient-limit are ignored
if tail := rawdb.ReadTxIndexTail(bc.db); tail == nil {
if bc.txLookupLimit == 0 || ancientLimit <= bc.txLookupLimit {
rawdb.WriteTxIndexTail(bc.db, 0)
} else {
rawdb.WriteTxIndexTail(bc.db, ancientLimit-bc.txLookupLimit)
}
}
}
if len(liveBlocks) > 0 { if len(liveBlocks) > 0 {
if n, err := writeLive(liveBlocks, liveReceipts); err != nil { if n, err := writeLive(liveBlocks, liveReceipts); err != nil {
if err == errInsertionInterrupted { if err == errInsertionInterrupted {
@ -1338,13 +1296,14 @@ func (bc *BlockChain) InsertReceiptChain(blockChain types.Blocks, receiptChain [
return n, err return n, err
} }
} }
var (
head := blockChain[len(blockChain)-1] head = blockChain[len(blockChain)-1]
context := []interface{}{ context = []interface{}{
"count", stats.processed, "elapsed", common.PrettyDuration(time.Since(start)), "count", stats.processed, "elapsed", common.PrettyDuration(time.Since(start)),
"number", head.Number(), "hash", head.Hash(), "age", common.PrettyAge(time.Unix(int64(head.Time()), 0)), "number", head.Number(), "hash", head.Hash(), "age", common.PrettyAge(time.Unix(int64(head.Time()), 0)),
"size", common.StorageSize(size), "size", common.StorageSize(size),
} }
)
if stats.ignored > 0 { if stats.ignored > 0 {
context = append(context, []interface{}{"ignored", stats.ignored}...) context = append(context, []interface{}{"ignored", stats.ignored}...)
} }
@ -1360,7 +1319,6 @@ func (bc *BlockChain) writeBlockWithoutState(block *types.Block, td *big.Int) (e
if bc.insertStopped() { if bc.insertStopped() {
return errInsertionInterrupted return errInsertionInterrupted
} }
batch := bc.db.NewBatch() batch := bc.db.NewBatch()
rawdb.WriteTd(batch, block.Hash(), block.NumberU64(), td) rawdb.WriteTd(batch, block.Hash(), block.NumberU64(), td)
rawdb.WriteBlock(batch, block) rawdb.WriteBlock(batch, block)
@ -2456,102 +2414,6 @@ func (bc *BlockChain) skipBlock(err error, it *insertIterator) bool {
return false return false
} }
// indexBlocks reindexes or unindexes transactions depending on user configuration
func (bc *BlockChain) indexBlocks(tail *uint64, head uint64, done chan struct{}) {
defer func() { close(done) }()
// If head is 0, it means the chain is just initialized and no blocks are inserted,
// so don't need to indexing anything.
if head == 0 {
return
}
// The tail flag is not existent, it means the node is just initialized
// and all blocks(may from ancient store) are not indexed yet.
if tail == nil {
from := uint64(0)
if bc.txLookupLimit != 0 && head >= bc.txLookupLimit {
from = head - bc.txLookupLimit + 1
}
rawdb.IndexTransactions(bc.db, from, head+1, bc.quit)
return
}
// The tail flag is existent, but the whole chain is required to be indexed.
if bc.txLookupLimit == 0 || head < bc.txLookupLimit {
if *tail > 0 {
// It can happen when chain is rewound to a historical point which
// is even lower than the indexes tail, recap the indexing target
// to new head to avoid reading non-existent block bodies.
end := *tail
if end > head+1 {
end = head + 1
}
rawdb.IndexTransactions(bc.db, 0, end, bc.quit)
}
return
}
// Update the transaction index to the new chain state
if head-bc.txLookupLimit+1 < *tail {
// Reindex a part of missing indices and rewind index tail to HEAD-limit
rawdb.IndexTransactions(bc.db, head-bc.txLookupLimit+1, *tail, bc.quit)
} else {
// Unindex a part of stale indices and forward index tail to HEAD-limit
rawdb.UnindexTransactions(bc.db, *tail, head-bc.txLookupLimit+1, bc.quit)
}
}
// maintainTxIndex is responsible for the construction and deletion of the
// transaction index.
//
// User can use flag `txlookuplimit` to specify a "recentness" block, below
// which ancient tx indices get deleted. If `txlookuplimit` is 0, it means
// all tx indices will be reserved.
//
// The user can adjust the txlookuplimit value for each launch after sync,
// Geth will automatically construct the missing indices or delete the extra
// indices.
func (bc *BlockChain) maintainTxIndex() {
defer bc.wg.Done()
// Listening to chain events and manipulate the transaction indexes.
var (
done chan struct{} // Non-nil if background unindexing or reindexing routine is active.
headCh = make(chan ChainHeadEvent, 1) // Buffered to avoid locking up the event feed
)
sub := bc.SubscribeChainHeadEvent(headCh)
if sub == nil {
return
}
defer sub.Unsubscribe()
log.Info("Initialized transaction indexer", "limit", bc.TxLookupLimit())
// Launch the initial processing if chain is not empty. This step is
// useful in these scenarios that chain has no progress and indexer
// is never triggered.
if head := rawdb.ReadHeadBlock(bc.db); head != nil {
done = make(chan struct{})
go bc.indexBlocks(rawdb.ReadTxIndexTail(bc.db), head.NumberU64(), done)
}
for {
select {
case head := <-headCh:
if done == nil {
done = make(chan struct{})
go bc.indexBlocks(rawdb.ReadTxIndexTail(bc.db), head.Block.NumberU64(), done)
}
case <-done:
done = nil
case <-bc.quit:
if done != nil {
log.Info("Waiting background transaction indexer to exit")
<-done
}
return
}
}
}
// reportBlock logs a bad block error. // reportBlock logs a bad block error.
func (bc *BlockChain) reportBlock(block *types.Block, receipts types.Receipts, err error) { func (bc *BlockChain) reportBlock(block *types.Block, receipts types.Receipts, err error) {
rawdb.WriteBadBlock(bc.db, block) rawdb.WriteBadBlock(bc.db, block)

View File

@ -17,6 +17,7 @@
package core package core
import ( import (
"errors"
"math/big" "math/big"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
@ -254,20 +255,46 @@ func (bc *BlockChain) GetAncestor(hash common.Hash, number, ancestor uint64, max
return bc.hc.GetAncestor(hash, number, ancestor, maxNonCanonical) return bc.hc.GetAncestor(hash, number, ancestor, maxNonCanonical)
} }
// GetTransactionLookup retrieves the lookup associate with the given transaction // GetTransactionLookup retrieves the lookup along with the transaction
// hash from the cache or database. // itself associate with the given transaction hash.
func (bc *BlockChain) GetTransactionLookup(hash common.Hash) *rawdb.LegacyTxLookupEntry { //
// An error will be returned if the transaction is not found, and background
// indexing for transactions is still in progress. The transaction might be
// reachable shortly once it's indexed.
//
// A null will be returned in the transaction is not found and background
// transaction indexing is already finished. The transaction is not existent
// from the node's perspective.
func (bc *BlockChain) GetTransactionLookup(hash common.Hash) (*rawdb.LegacyTxLookupEntry, *types.Transaction, error) {
// Short circuit if the txlookup already in the cache, retrieve otherwise // Short circuit if the txlookup already in the cache, retrieve otherwise
if lookup, exist := bc.txLookupCache.Get(hash); exist { if item, exist := bc.txLookupCache.Get(hash); exist {
return lookup return item.lookup, item.transaction, nil
} }
tx, blockHash, blockNumber, txIndex := rawdb.ReadTransaction(bc.db, hash) tx, blockHash, blockNumber, txIndex := rawdb.ReadTransaction(bc.db, hash)
if tx == nil { if tx == nil {
return nil progress, err := bc.TxIndexProgress()
if err != nil {
return nil, nil, nil
} }
lookup := &rawdb.LegacyTxLookupEntry{BlockHash: blockHash, BlockIndex: blockNumber, Index: txIndex} // The transaction indexing is not finished yet, returning an
bc.txLookupCache.Add(hash, lookup) // error to explicitly indicate it.
return lookup if !progress.Done() {
return nil, nil, errors.New("transaction indexing still in progress")
}
// The transaction is already indexed, the transaction is either
// not existent or not in the range of index, returning null.
return nil, nil, nil
}
lookup := &rawdb.LegacyTxLookupEntry{
BlockHash: blockHash,
BlockIndex: blockNumber,
Index: txIndex,
}
bc.txLookupCache.Add(hash, txLookup{
lookup: lookup,
transaction: tx,
})
return lookup, tx, nil
} }
// GetTd retrieves a block's total difficulty in the canonical chain from the // GetTd retrieves a block's total difficulty in the canonical chain from the
@ -370,16 +397,12 @@ func (bc *BlockChain) GetVMConfig() *vm.Config {
return &bc.vmConfig return &bc.vmConfig
} }
// SetTxLookupLimit is responsible for updating the txlookup limit to the // TxIndexProgress returns the transaction indexing progress.
// original one stored in db if the new mismatches with the old one. func (bc *BlockChain) TxIndexProgress() (TxIndexProgress, error) {
func (bc *BlockChain) SetTxLookupLimit(limit uint64) { if bc.txIndexer == nil {
bc.txLookupLimit = limit return TxIndexProgress{}, errors.New("tx indexer is not enabled")
} }
return bc.txIndexer.txIndexProgress()
// TxLookupLimit retrieves the txlookup limit used by blockchain to prune
// stale transaction indices.
func (bc *BlockChain) TxLookupLimit() uint64 {
return bc.txLookupLimit
} }
// TrieDB retrieves the low level trie database used for data storage. // TrieDB retrieves the low level trie database used for data storage.

View File

@ -40,6 +40,7 @@ import (
"github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/trie" "github.com/ethereum/go-ethereum/trie"
"github.com/holiman/uint256"
) )
// So we can deterministically seed different blockchains // So we can deterministically seed different blockchains
@ -2722,191 +2723,6 @@ func testReorgToShorterRemovesCanonMappingHeaderChain(t *testing.T, scheme strin
} }
} }
func TestTransactionIndices(t *testing.T) {
// Configure and generate a sample block chain
var (
key, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
address = crypto.PubkeyToAddress(key.PublicKey)
funds = big.NewInt(100000000000000000)
gspec = &Genesis{
Config: params.TestChainConfig,
Alloc: GenesisAlloc{address: {Balance: funds}},
BaseFee: big.NewInt(params.InitialBaseFee),
}
signer = types.LatestSigner(gspec.Config)
)
_, blocks, receipts := GenerateChainWithGenesis(gspec, ethash.NewFaker(), 128, func(i int, block *BlockGen) {
tx, err := types.SignTx(types.NewTransaction(block.TxNonce(address), common.Address{0x00}, big.NewInt(1000), params.TxGas, block.header.BaseFee, nil), signer, key)
if err != nil {
panic(err)
}
block.AddTx(tx)
})
check := func(tail *uint64, chain *BlockChain) {
stored := rawdb.ReadTxIndexTail(chain.db)
if tail == nil && stored != nil {
t.Fatalf("Oldest indexded block mismatch, want nil, have %d", *stored)
}
if tail != nil && *stored != *tail {
t.Fatalf("Oldest indexded block mismatch, want %d, have %d", *tail, *stored)
}
if tail != nil {
for i := *tail; i <= chain.CurrentBlock().Number.Uint64(); i++ {
block := rawdb.ReadBlock(chain.db, rawdb.ReadCanonicalHash(chain.db, i), i)
if block.Transactions().Len() == 0 {
continue
}
for _, tx := range block.Transactions() {
if index := rawdb.ReadTxLookupEntry(chain.db, tx.Hash()); index == nil {
t.Fatalf("Miss transaction indice, number %d hash %s", i, tx.Hash().Hex())
}
}
}
for i := uint64(0); i < *tail; i++ {
block := rawdb.ReadBlock(chain.db, rawdb.ReadCanonicalHash(chain.db, i), i)
if block.Transactions().Len() == 0 {
continue
}
for _, tx := range block.Transactions() {
if index := rawdb.ReadTxLookupEntry(chain.db, tx.Hash()); index != nil {
t.Fatalf("Transaction indice should be deleted, number %d hash %s", i, tx.Hash().Hex())
}
}
}
}
}
// Init block chain with external ancients, check all needed indices has been indexed.
limit := []uint64{0, 32, 64, 128}
for _, l := range limit {
frdir := t.TempDir()
ancientDb, _ := rawdb.NewDatabaseWithFreezer(rawdb.NewMemoryDatabase(), frdir, "", false)
rawdb.WriteAncientBlocks(ancientDb, append([]*types.Block{gspec.ToBlock()}, blocks...), append([]types.Receipts{{}}, receipts...), big.NewInt(0))
l := l
chain, err := NewBlockChain(ancientDb, nil, gspec, nil, ethash.NewFaker(), vm.Config{}, nil, &l)
if err != nil {
t.Fatalf("failed to create tester chain: %v", err)
}
chain.indexBlocks(rawdb.ReadTxIndexTail(ancientDb), 128, make(chan struct{}))
var tail uint64
if l != 0 {
tail = uint64(128) - l + 1
}
check(&tail, chain)
chain.Stop()
ancientDb.Close()
os.RemoveAll(frdir)
}
// Reconstruct a block chain which only reserves HEAD-64 tx indices
ancientDb, _ := rawdb.NewDatabaseWithFreezer(rawdb.NewMemoryDatabase(), t.TempDir(), "", false)
defer ancientDb.Close()
rawdb.WriteAncientBlocks(ancientDb, append([]*types.Block{gspec.ToBlock()}, blocks...), append([]types.Receipts{{}}, receipts...), big.NewInt(0))
limit = []uint64{0, 64 /* drop stale */, 32 /* shorten history */, 64 /* extend history */, 0 /* restore all */}
for _, l := range limit {
l := l
chain, err := NewBlockChain(ancientDb, nil, gspec, nil, ethash.NewFaker(), vm.Config{}, nil, &l)
if err != nil {
t.Fatalf("failed to create tester chain: %v", err)
}
var tail uint64
if l != 0 {
tail = uint64(128) - l + 1
}
chain.indexBlocks(rawdb.ReadTxIndexTail(ancientDb), 128, make(chan struct{}))
check(&tail, chain)
chain.Stop()
}
}
func TestSkipStaleTxIndicesInSnapSync(t *testing.T) {
testSkipStaleTxIndicesInSnapSync(t, rawdb.HashScheme)
testSkipStaleTxIndicesInSnapSync(t, rawdb.PathScheme)
}
func testSkipStaleTxIndicesInSnapSync(t *testing.T, scheme string) {
// Configure and generate a sample block chain
var (
key, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
address = crypto.PubkeyToAddress(key.PublicKey)
funds = big.NewInt(100000000000000000)
gspec = &Genesis{Config: params.TestChainConfig, Alloc: GenesisAlloc{address: {Balance: funds}}}
signer = types.LatestSigner(gspec.Config)
)
_, blocks, receipts := GenerateChainWithGenesis(gspec, ethash.NewFaker(), 128, func(i int, block *BlockGen) {
tx, err := types.SignTx(types.NewTransaction(block.TxNonce(address), common.Address{0x00}, big.NewInt(1000), params.TxGas, block.header.BaseFee, nil), signer, key)
if err != nil {
panic(err)
}
block.AddTx(tx)
})
check := func(tail *uint64, chain *BlockChain) {
stored := rawdb.ReadTxIndexTail(chain.db)
if tail == nil && stored != nil {
t.Fatalf("Oldest indexded block mismatch, want nil, have %d", *stored)
}
if tail != nil && *stored != *tail {
t.Fatalf("Oldest indexded block mismatch, want %d, have %d", *tail, *stored)
}
if tail != nil {
for i := *tail; i <= chain.CurrentBlock().Number.Uint64(); i++ {
block := rawdb.ReadBlock(chain.db, rawdb.ReadCanonicalHash(chain.db, i), i)
if block.Transactions().Len() == 0 {
continue
}
for _, tx := range block.Transactions() {
if index := rawdb.ReadTxLookupEntry(chain.db, tx.Hash()); index == nil {
t.Fatalf("Miss transaction indice, number %d hash %s", i, tx.Hash().Hex())
}
}
}
for i := uint64(0); i < *tail; i++ {
block := rawdb.ReadBlock(chain.db, rawdb.ReadCanonicalHash(chain.db, i), i)
if block.Transactions().Len() == 0 {
continue
}
for _, tx := range block.Transactions() {
if index := rawdb.ReadTxLookupEntry(chain.db, tx.Hash()); index != nil {
t.Fatalf("Transaction indice should be deleted, number %d hash %s", i, tx.Hash().Hex())
}
}
}
}
}
ancientDb, err := rawdb.NewDatabaseWithFreezer(rawdb.NewMemoryDatabase(), t.TempDir(), "", false)
if err != nil {
t.Fatalf("failed to create temp freezer db: %v", err)
}
defer ancientDb.Close()
// Import all blocks into ancient db, only HEAD-32 indices are kept.
l := uint64(32)
chain, err := NewBlockChain(ancientDb, DefaultCacheConfigWithScheme(scheme), gspec, nil, ethash.NewFaker(), vm.Config{}, nil, &l)
if err != nil {
t.Fatalf("failed to create tester chain: %v", err)
}
defer chain.Stop()
headers := make([]*types.Header, len(blocks))
for i, block := range blocks {
headers[i] = block.Header()
}
if n, err := chain.InsertHeaderChain(headers); err != nil {
t.Fatalf("failed to insert header %d: %v", n, err)
}
// The indices before ancient-N(32) should be ignored. After that all blocks should be indexed.
if n, err := chain.InsertReceiptChain(blocks, receipts, 64); err != nil {
t.Fatalf("block %d: failed to insert into chain: %v", n, err)
}
tail := uint64(32)
check(&tail, chain)
}
// Benchmarks large blocks with value transfers to non-existing accounts // Benchmarks large blocks with value transfers to non-existing accounts
func benchmarkLargeNumberOfValueToNonexisting(b *testing.B, numTxs, numBlocks int, recipientFn func(uint64) common.Address, dataFn func(uint64) []byte) { func benchmarkLargeNumberOfValueToNonexisting(b *testing.B, numTxs, numBlocks int, recipientFn func(uint64) common.Address, dataFn func(uint64) []byte) {
var ( var (
@ -3652,7 +3468,7 @@ func testInitThenFailCreateContract(t *testing.T, scheme string) {
defer chain.Stop() defer chain.Stop()
statedb, _ := chain.State() statedb, _ := chain.State()
if got, exp := statedb.GetBalance(aa), big.NewInt(100000); got.Cmp(exp) != 0 { if got, exp := statedb.GetBalance(aa), uint256.NewInt(100000); got.Cmp(exp) != 0 {
t.Fatalf("Genesis err, got %v exp %v", got, exp) t.Fatalf("Genesis err, got %v exp %v", got, exp)
} }
// First block tries to create, but fails // First block tries to create, but fails
@ -3662,7 +3478,7 @@ func testInitThenFailCreateContract(t *testing.T, scheme string) {
t.Fatalf("block %d: failed to insert into chain: %v", block.NumberU64(), err) t.Fatalf("block %d: failed to insert into chain: %v", block.NumberU64(), err)
} }
statedb, _ = chain.State() statedb, _ = chain.State()
if got, exp := statedb.GetBalance(aa), big.NewInt(100000); got.Cmp(exp) != 0 { if got, exp := statedb.GetBalance(aa), uint256.NewInt(100000); got.Cmp(exp) != 0 {
t.Fatalf("block %d: got %v exp %v", block.NumberU64(), got, exp) t.Fatalf("block %d: got %v exp %v", block.NumberU64(), got, exp)
} }
} }
@ -3848,17 +3664,17 @@ func testEIP1559Transition(t *testing.T, scheme string) {
state, _ := chain.State() state, _ := chain.State()
// 3: Ensure that miner received only the tx's tip. // 3: Ensure that miner received only the tx's tip.
actual := state.GetBalance(block.Coinbase()) actual := state.GetBalance(block.Coinbase()).ToBig()
expected := new(big.Int).Add( expected := new(big.Int).Add(
new(big.Int).SetUint64(block.GasUsed()*block.Transactions()[0].GasTipCap().Uint64()), new(big.Int).SetUint64(block.GasUsed()*block.Transactions()[0].GasTipCap().Uint64()),
ethash.ConstantinopleBlockReward, ethash.ConstantinopleBlockReward.ToBig(),
) )
if actual.Cmp(expected) != 0 { if actual.Cmp(expected) != 0 {
t.Fatalf("miner balance incorrect: expected %d, got %d", expected, actual) t.Fatalf("miner balance incorrect: expected %d, got %d", expected, actual)
} }
// 4: Ensure the tx sender paid for the gasUsed * (tip + block baseFee). // 4: Ensure the tx sender paid for the gasUsed * (tip + block baseFee).
actual = new(big.Int).Sub(funds, state.GetBalance(addr1)) actual = new(big.Int).Sub(funds, state.GetBalance(addr1).ToBig())
expected = new(big.Int).SetUint64(block.GasUsed() * (block.Transactions()[0].GasTipCap().Uint64() + block.BaseFee().Uint64())) expected = new(big.Int).SetUint64(block.GasUsed() * (block.Transactions()[0].GasTipCap().Uint64() + block.BaseFee().Uint64()))
if actual.Cmp(expected) != 0 { if actual.Cmp(expected) != 0 {
t.Fatalf("sender balance incorrect: expected %d, got %d", expected, actual) t.Fatalf("sender balance incorrect: expected %d, got %d", expected, actual)
@ -3888,17 +3704,17 @@ func testEIP1559Transition(t *testing.T, scheme string) {
effectiveTip := block.Transactions()[0].GasTipCap().Uint64() - block.BaseFee().Uint64() effectiveTip := block.Transactions()[0].GasTipCap().Uint64() - block.BaseFee().Uint64()
// 6+5: Ensure that miner received only the tx's effective tip. // 6+5: Ensure that miner received only the tx's effective tip.
actual = state.GetBalance(block.Coinbase()) actual = state.GetBalance(block.Coinbase()).ToBig()
expected = new(big.Int).Add( expected = new(big.Int).Add(
new(big.Int).SetUint64(block.GasUsed()*effectiveTip), new(big.Int).SetUint64(block.GasUsed()*effectiveTip),
ethash.ConstantinopleBlockReward, ethash.ConstantinopleBlockReward.ToBig(),
) )
if actual.Cmp(expected) != 0 { if actual.Cmp(expected) != 0 {
t.Fatalf("miner balance incorrect: expected %d, got %d", expected, actual) t.Fatalf("miner balance incorrect: expected %d, got %d", expected, actual)
} }
// 4: Ensure the tx sender paid for the gasUsed * (effectiveTip + block baseFee). // 4: Ensure the tx sender paid for the gasUsed * (effectiveTip + block baseFee).
actual = new(big.Int).Sub(funds, state.GetBalance(addr2)) actual = new(big.Int).Sub(funds, state.GetBalance(addr2).ToBig())
expected = new(big.Int).SetUint64(block.GasUsed() * (effectiveTip + block.BaseFee().Uint64())) expected = new(big.Int).SetUint64(block.GasUsed() * (effectiveTip + block.BaseFee().Uint64()))
if actual.Cmp(expected) != 0 { if actual.Cmp(expected) != 0 {
t.Fatalf("sender balance incorrect: expected %d, got %d", expected, actual) t.Fatalf("sender balance incorrect: expected %d, got %d", expected, actual)
@ -4103,212 +3919,6 @@ func testCanonicalHashMarker(t *testing.T, scheme string) {
} }
} }
// TestTxIndexer tests the tx indexes are updated correctly.
func TestTxIndexer(t *testing.T) {
var (
testBankKey, _ = crypto.GenerateKey()
testBankAddress = crypto.PubkeyToAddress(testBankKey.PublicKey)
testBankFunds = big.NewInt(1000000000000000000)
gspec = &Genesis{
Config: params.TestChainConfig,
Alloc: GenesisAlloc{testBankAddress: {Balance: testBankFunds}},
BaseFee: big.NewInt(params.InitialBaseFee),
}
engine = ethash.NewFaker()
nonce = uint64(0)
)
_, blocks, receipts := GenerateChainWithGenesis(gspec, engine, 128, func(i int, gen *BlockGen) {
tx, _ := types.SignTx(types.NewTransaction(nonce, common.HexToAddress("0xdeadbeef"), big.NewInt(1000), params.TxGas, big.NewInt(10*params.InitialBaseFee), nil), types.HomesteadSigner{}, testBankKey)
gen.AddTx(tx)
nonce += 1
})
// verifyIndexes checks if the transaction indexes are present or not
// of the specified block.
verifyIndexes := func(db ethdb.Database, number uint64, exist bool) {
if number == 0 {
return
}
block := blocks[number-1]
for _, tx := range block.Transactions() {
lookup := rawdb.ReadTxLookupEntry(db, tx.Hash())
if exist && lookup == nil {
t.Fatalf("missing %d %x", number, tx.Hash().Hex())
}
if !exist && lookup != nil {
t.Fatalf("unexpected %d %x", number, tx.Hash().Hex())
}
}
}
// verifyRange runs verifyIndexes for a range of blocks, from and to are included.
verifyRange := func(db ethdb.Database, from, to uint64, exist bool) {
for number := from; number <= to; number += 1 {
verifyIndexes(db, number, exist)
}
}
verify := func(db ethdb.Database, expTail uint64) {
tail := rawdb.ReadTxIndexTail(db)
if tail == nil {
t.Fatal("Failed to write tx index tail")
}
if *tail != expTail {
t.Fatalf("Unexpected tx index tail, want %v, got %d", expTail, *tail)
}
if *tail != 0 {
verifyRange(db, 0, *tail-1, false)
}
verifyRange(db, *tail, 128, true)
}
var cases = []struct {
limitA uint64
tailA uint64
limitB uint64
tailB uint64
limitC uint64
tailC uint64
}{
{
// LimitA: 0
// TailA: 0
//
// all blocks are indexed
limitA: 0,
tailA: 0,
// LimitB: 1
// TailB: 128
//
// block-128 is indexed
limitB: 1,
tailB: 128,
// LimitB: 64
// TailB: 65
//
// block [65, 128] are indexed
limitC: 64,
tailC: 65,
},
{
// LimitA: 64
// TailA: 65
//
// block [65, 128] are indexed
limitA: 64,
tailA: 65,
// LimitB: 1
// TailB: 128
//
// block-128 is indexed
limitB: 1,
tailB: 128,
// LimitB: 64
// TailB: 65
//
// block [65, 128] are indexed
limitC: 64,
tailC: 65,
},
{
// LimitA: 127
// TailA: 2
//
// block [2, 128] are indexed
limitA: 127,
tailA: 2,
// LimitB: 1
// TailB: 128
//
// block-128 is indexed
limitB: 1,
tailB: 128,
// LimitB: 64
// TailB: 65
//
// block [65, 128] are indexed
limitC: 64,
tailC: 65,
},
{
// LimitA: 128
// TailA: 1
//
// block [2, 128] are indexed
limitA: 128,
tailA: 1,
// LimitB: 1
// TailB: 128
//
// block-128 is indexed
limitB: 1,
tailB: 128,
// LimitB: 64
// TailB: 65
//
// block [65, 128] are indexed
limitC: 64,
tailC: 65,
},
{
// LimitA: 129
// TailA: 0
//
// block [0, 128] are indexed
limitA: 129,
tailA: 0,
// LimitB: 1
// TailB: 128
//
// block-128 is indexed
limitB: 1,
tailB: 128,
// LimitB: 64
// TailB: 65
//
// block [65, 128] are indexed
limitC: 64,
tailC: 65,
},
}
for _, c := range cases {
frdir := t.TempDir()
db, _ := rawdb.NewDatabaseWithFreezer(rawdb.NewMemoryDatabase(), frdir, "", false)
rawdb.WriteAncientBlocks(db, append([]*types.Block{gspec.ToBlock()}, blocks...), append([]types.Receipts{{}}, receipts...), big.NewInt(0))
// Index the initial blocks from ancient store
chain, _ := NewBlockChain(db, nil, gspec, nil, engine, vm.Config{}, nil, &c.limitA)
chain.indexBlocks(nil, 128, make(chan struct{}))
verify(db, c.tailA)
chain.SetTxLookupLimit(c.limitB)
chain.indexBlocks(rawdb.ReadTxIndexTail(db), 128, make(chan struct{}))
verify(db, c.tailB)
chain.SetTxLookupLimit(c.limitC)
chain.indexBlocks(rawdb.ReadTxIndexTail(db), 128, make(chan struct{}))
verify(db, c.tailC)
// Recover all indexes
chain.SetTxLookupLimit(0)
chain.indexBlocks(rawdb.ReadTxIndexTail(db), 128, make(chan struct{}))
verify(db, 0)
chain.Stop()
db.Close()
os.RemoveAll(frdir)
}
}
func TestCreateThenDeletePreByzantium(t *testing.T) { func TestCreateThenDeletePreByzantium(t *testing.T) {
// We use Ropsten chain config instead of Testchain config, this is // We use Ropsten chain config instead of Testchain config, this is
// deliberate: we want to use pre-byz rules where we have intermediate state roots // deliberate: we want to use pre-byz rules where we have intermediate state roots
@ -4703,14 +4313,14 @@ func TestEIP3651(t *testing.T) {
state, _ := chain.State() state, _ := chain.State()
// 3: Ensure that miner received only the tx's tip. // 3: Ensure that miner received only the tx's tip.
actual := state.GetBalance(block.Coinbase()) actual := state.GetBalance(block.Coinbase()).ToBig()
expected := new(big.Int).SetUint64(block.GasUsed() * block.Transactions()[0].GasTipCap().Uint64()) expected := new(big.Int).SetUint64(block.GasUsed() * block.Transactions()[0].GasTipCap().Uint64())
if actual.Cmp(expected) != 0 { if actual.Cmp(expected) != 0 {
t.Fatalf("miner balance incorrect: expected %d, got %d", expected, actual) t.Fatalf("miner balance incorrect: expected %d, got %d", expected, actual)
} }
// 4: Ensure the tx sender paid for the gasUsed * (tip + block baseFee). // 4: Ensure the tx sender paid for the gasUsed * (tip + block baseFee).
actual = new(big.Int).Sub(funds, state.GetBalance(addr1)) actual = new(big.Int).Sub(funds, state.GetBalance(addr1).ToBig())
expected = new(big.Int).SetUint64(block.GasUsed() * (block.Transactions()[0].GasTipCap().Uint64() + block.BaseFee().Uint64())) expected = new(big.Int).SetUint64(block.GasUsed() * (block.Transactions()[0].GasTipCap().Uint64() + block.BaseFee().Uint64()))
if actual.Cmp(expected) != 0 { if actual.Cmp(expected) != 0 {
t.Fatalf("sender balance incorrect: expected %d, got %d", expected, actual) t.Fatalf("sender balance incorrect: expected %d, got %d", expected, actual)

View File

@ -32,6 +32,7 @@ import (
"github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/trie" "github.com/ethereum/go-ethereum/trie"
"github.com/holiman/uint256"
) )
// BlockGen creates blocks for testing. // BlockGen creates blocks for testing.
@ -157,7 +158,7 @@ func (b *BlockGen) AddTxWithVMConfig(tx *types.Transaction, config vm.Config) {
} }
// GetBalance returns the balance of the given address at the generated block. // GetBalance returns the balance of the given address at the generated block.
func (b *BlockGen) GetBalance(addr common.Address) *big.Int { func (b *BlockGen) GetBalance(addr common.Address) *uint256.Int {
return b.statedb.GetBalance(addr) return b.statedb.GetBalance(addr)
} }

View File

@ -104,4 +104,10 @@ var (
// ErrBlobFeeCapTooLow is returned if the transaction fee cap is less than the // ErrBlobFeeCapTooLow is returned if the transaction fee cap is less than the
// blob gas fee of the block. // blob gas fee of the block.
ErrBlobFeeCapTooLow = errors.New("max fee per blob gas less than block blob gas fee") ErrBlobFeeCapTooLow = errors.New("max fee per blob gas less than block blob gas fee")
// ErrMissingBlobHashes is returned if a blob transaction has no blob hashes.
ErrMissingBlobHashes = errors.New("blob transaction missing blob hashes")
// ErrBlobTxCreate is returned if a blob transaction has no explicit to field.
ErrBlobTxCreate = errors.New("blob transaction of type create")
) )

View File

@ -24,6 +24,7 @@ import (
"github.com/ethereum/go-ethereum/consensus/misc/eip4844" "github.com/ethereum/go-ethereum/consensus/misc/eip4844"
"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/holiman/uint256"
) )
// ChainContext supports retrieving headers and consensus parameters from the // ChainContext supports retrieving headers and consensus parameters from the
@ -129,12 +130,12 @@ func GetHashFn(ref *types.Header, chain ChainContext) func(n uint64) common.Hash
// CanTransfer checks whether there are enough funds in the address' account to make a transfer. // CanTransfer checks whether there are enough funds in the address' account to make a transfer.
// This does not take the necessary gas in to account to make the transfer valid. // This does not take the necessary gas in to account to make the transfer valid.
func CanTransfer(db vm.StateDB, addr common.Address, amount *big.Int) bool { func CanTransfer(db vm.StateDB, addr common.Address, amount *uint256.Int) bool {
return db.GetBalance(addr).Cmp(amount) >= 0 return db.GetBalance(addr).Cmp(amount) >= 0
} }
// Transfer subtracts amount from sender and adds amount to recipient using the given Db // Transfer subtracts amount from sender and adds amount to recipient using the given Db
func Transfer(db vm.StateDB, sender, recipient common.Address, amount *big.Int) { func Transfer(db vm.StateDB, sender, recipient common.Address, amount *uint256.Int) {
db.SubBalance(sender, amount) db.SubBalance(sender, amount)
db.AddBalance(recipient, amount) db.AddBalance(recipient, amount)
} }

View File

@ -106,7 +106,10 @@ func TestCreation(t *testing.T) {
{1735370, 0, ID{Hash: checksumToBytes(0xfe3366e7), Next: 1735371}}, // Last London block {1735370, 0, ID{Hash: checksumToBytes(0xfe3366e7), Next: 1735371}}, // Last London block
{1735371, 0, ID{Hash: checksumToBytes(0xb96cbd13), Next: 1677557088}}, // First MergeNetsplit block {1735371, 0, ID{Hash: checksumToBytes(0xb96cbd13), Next: 1677557088}}, // First MergeNetsplit block
{1735372, 1677557087, ID{Hash: checksumToBytes(0xb96cbd13), Next: 1677557088}}, // Last MergeNetsplit block {1735372, 1677557087, ID{Hash: checksumToBytes(0xb96cbd13), Next: 1677557088}}, // Last MergeNetsplit block
{1735372, 1677557088, ID{Hash: checksumToBytes(0xf7f9bc08), Next: 0}}, // First Shanghai block {1735372, 1677557088, ID{Hash: checksumToBytes(0xf7f9bc08), Next: 1706655072}}, // First Shanghai block
{1735372, 1706655071, ID{Hash: checksumToBytes(0xf7f9bc08), Next: 1706655072}}, // Last Shanghai block
{1735372, 1706655072, ID{Hash: checksumToBytes(0x88cf81d9), Next: 0}}, // First Cancun block
{1735372, 2706655072, ID{Hash: checksumToBytes(0x88cf81d9), Next: 0}}, // Future Cancun block
}, },
}, },
// Holesky test cases // Holesky test cases
@ -116,7 +119,10 @@ func TestCreation(t *testing.T) {
[]testcase{ []testcase{
{0, 0, ID{Hash: checksumToBytes(0xc61a6098), Next: 1696000704}}, // Unsynced, last Frontier, Homestead, Tangerine, Spurious, Byzantium, Constantinople, Petersburg, Istanbul, Berlin, London, Paris block {0, 0, ID{Hash: checksumToBytes(0xc61a6098), Next: 1696000704}}, // Unsynced, last Frontier, Homestead, Tangerine, Spurious, Byzantium, Constantinople, Petersburg, Istanbul, Berlin, London, Paris block
{123, 0, ID{Hash: checksumToBytes(0xc61a6098), Next: 1696000704}}, // First MergeNetsplit block {123, 0, ID{Hash: checksumToBytes(0xc61a6098), Next: 1696000704}}, // First MergeNetsplit block
{123, 1696000704, ID{Hash: checksumToBytes(0xfd4f016b), Next: 0}}, // Last MergeNetsplit block {123, 1696000704, ID{Hash: checksumToBytes(0xfd4f016b), Next: 1707305664}}, // First Shanghai block
{123, 1707305663, ID{Hash: checksumToBytes(0xfd4f016b), Next: 1707305664}}, // Last Shanghai block
{123, 1707305664, ID{Hash: checksumToBytes(0x9b192ad0), Next: 0}}, // First Cancun block
{123, 2707305664, ID{Hash: checksumToBytes(0x9b192ad0), Next: 0}}, // Future Cancun block
}, },
}, },
} }

View File

@ -38,6 +38,7 @@ import (
"github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/rlp"
"github.com/ethereum/go-ethereum/trie" "github.com/ethereum/go-ethereum/trie"
"github.com/ethereum/go-ethereum/trie/triedb/pathdb" "github.com/ethereum/go-ethereum/trie/triedb/pathdb"
"github.com/holiman/uint256"
) )
//go:generate go run github.com/fjl/gencodec -type Genesis -field-override genesisSpecMarshaling -out gen_genesis.go //go:generate go run github.com/fjl/gencodec -type Genesis -field-override genesisSpecMarshaling -out gen_genesis.go
@ -142,7 +143,7 @@ func (ga *GenesisAlloc) hash(isVerkle bool) (common.Hash, error) {
} }
for addr, account := range *ga { for addr, account := range *ga {
if account.Balance != nil { if account.Balance != nil {
statedb.AddBalance(addr, account.Balance) statedb.AddBalance(addr, uint256.MustFromBig(account.Balance))
} }
statedb.SetCode(addr, account.Code) statedb.SetCode(addr, account.Code)
statedb.SetNonce(addr, account.Nonce) statedb.SetNonce(addr, account.Nonce)
@ -163,7 +164,7 @@ func (ga *GenesisAlloc) flush(db ethdb.Database, triedb *trie.Database, blockhas
} }
for addr, account := range *ga { for addr, account := range *ga {
if account.Balance != nil { if account.Balance != nil {
statedb.AddBalance(addr, account.Balance) statedb.AddBalance(addr, uint256.MustFromBig(account.Balance))
} }
statedb.SetCode(addr, account.Code) statedb.SetCode(addr, account.Code)
statedb.SetNonce(addr, account.Nonce) statedb.SetNonce(addr, account.Nonce)

View File

@ -278,23 +278,6 @@ func WriteTxIndexTail(db ethdb.KeyValueWriter, number uint64) {
} }
} }
// ReadFastTxLookupLimit retrieves the tx lookup limit used in fast sync.
func ReadFastTxLookupLimit(db ethdb.KeyValueReader) *uint64 {
data, _ := db.Get(fastTxLookupLimitKey)
if len(data) != 8 {
return nil
}
number := binary.BigEndian.Uint64(data)
return &number
}
// WriteFastTxLookupLimit stores the txlookup limit used in fast sync into database.
func WriteFastTxLookupLimit(db ethdb.KeyValueWriter, number uint64) {
if err := db.Put(fastTxLookupLimitKey, encodeBlockNumber(number)); err != nil {
log.Crit("Failed to store transaction lookup limit for fast sync", "err", err)
}
}
// ReadHeaderRange returns the rlp-encoded headers, starting at 'number', and going // ReadHeaderRange returns the rlp-encoded headers, starting at 'number', and going
// backwards towards genesis. This method assumes that the caller already has // backwards towards genesis. This method assumes that the caller already has
// placed a cap on count, to prevent DoS issues. // placed a cap on count, to prevent DoS issues.

View File

@ -178,7 +178,7 @@ func iterateTransactions(db ethdb.Database, from uint64, to uint64, reverse bool
// //
// There is a passed channel, the whole procedure will be interrupted if any // There is a passed channel, the whole procedure will be interrupted if any
// signal received. // signal received.
func indexTransactions(db ethdb.Database, from uint64, to uint64, interrupt chan struct{}, hook func(uint64) bool) { func indexTransactions(db ethdb.Database, from uint64, to uint64, interrupt chan struct{}, hook func(uint64) bool, report bool) {
// short circuit for invalid range // short circuit for invalid range
if from >= to { if from >= to {
return return
@ -188,13 +188,13 @@ func indexTransactions(db ethdb.Database, from uint64, to uint64, interrupt chan
batch = db.NewBatch() batch = db.NewBatch()
start = time.Now() start = time.Now()
logged = start.Add(-7 * time.Second) logged = start.Add(-7 * time.Second)
// Since we iterate in reverse, we expect the first number to come // Since we iterate in reverse, we expect the first number to come
// in to be [to-1]. Therefore, setting lastNum to means that the // in to be [to-1]. Therefore, setting lastNum to means that the
// prqueue gap-evaluation will work correctly // queue gap-evaluation will work correctly
lastNum = to lastNum = to
queue = prque.New[int64, *blockTxHashes](nil) queue = prque.New[int64, *blockTxHashes](nil)
// for stats reporting blocks, txs = 0, 0 // for stats reporting
blocks, txs = 0, 0
) )
for chanDelivery := range hashesCh { for chanDelivery := range hashesCh {
// Push the delivery into the queue and process contiguous ranges. // Push the delivery into the queue and process contiguous ranges.
@ -240,11 +240,15 @@ func indexTransactions(db ethdb.Database, from uint64, to uint64, interrupt chan
log.Crit("Failed writing batch to db", "error", err) log.Crit("Failed writing batch to db", "error", err)
return return
} }
logger := log.Debug
if report {
logger = log.Info
}
select { select {
case <-interrupt: case <-interrupt:
log.Debug("Transaction indexing interrupted", "blocks", blocks, "txs", txs, "tail", lastNum, "elapsed", common.PrettyDuration(time.Since(start))) logger("Transaction indexing interrupted", "blocks", blocks, "txs", txs, "tail", lastNum, "elapsed", common.PrettyDuration(time.Since(start)))
default: default:
log.Debug("Indexed transactions", "blocks", blocks, "txs", txs, "tail", lastNum, "elapsed", common.PrettyDuration(time.Since(start))) logger("Indexed transactions", "blocks", blocks, "txs", txs, "tail", lastNum, "elapsed", common.PrettyDuration(time.Since(start)))
} }
} }
@ -257,20 +261,20 @@ func indexTransactions(db ethdb.Database, from uint64, to uint64, interrupt chan
// //
// There is a passed channel, the whole procedure will be interrupted if any // There is a passed channel, the whole procedure will be interrupted if any
// signal received. // signal received.
func IndexTransactions(db ethdb.Database, from uint64, to uint64, interrupt chan struct{}) { func IndexTransactions(db ethdb.Database, from uint64, to uint64, interrupt chan struct{}, report bool) {
indexTransactions(db, from, to, interrupt, nil) indexTransactions(db, from, to, interrupt, nil, report)
} }
// indexTransactionsForTesting is the internal debug version with an additional hook. // indexTransactionsForTesting is the internal debug version with an additional hook.
func indexTransactionsForTesting(db ethdb.Database, from uint64, to uint64, interrupt chan struct{}, hook func(uint64) bool) { func indexTransactionsForTesting(db ethdb.Database, from uint64, to uint64, interrupt chan struct{}, hook func(uint64) bool) {
indexTransactions(db, from, to, interrupt, hook) indexTransactions(db, from, to, interrupt, hook, false)
} }
// unindexTransactions removes txlookup indices of the specified block range. // unindexTransactions removes txlookup indices of the specified block range.
// //
// There is a passed channel, the whole procedure will be interrupted if any // There is a passed channel, the whole procedure will be interrupted if any
// signal received. // signal received.
func unindexTransactions(db ethdb.Database, from uint64, to uint64, interrupt chan struct{}, hook func(uint64) bool) { func unindexTransactions(db ethdb.Database, from uint64, to uint64, interrupt chan struct{}, hook func(uint64) bool, report bool) {
// short circuit for invalid range // short circuit for invalid range
if from >= to { if from >= to {
return return
@ -280,12 +284,12 @@ func unindexTransactions(db ethdb.Database, from uint64, to uint64, interrupt ch
batch = db.NewBatch() batch = db.NewBatch()
start = time.Now() start = time.Now()
logged = start.Add(-7 * time.Second) logged = start.Add(-7 * time.Second)
// we expect the first number to come in to be [from]. Therefore, setting // we expect the first number to come in to be [from]. Therefore, setting
// nextNum to from means that the prqueue gap-evaluation will work correctly // nextNum to from means that the queue gap-evaluation will work correctly
nextNum = from nextNum = from
queue = prque.New[int64, *blockTxHashes](nil) queue = prque.New[int64, *blockTxHashes](nil)
// for stats reporting blocks, txs = 0, 0 // for stats reporting
blocks, txs = 0, 0
) )
// Otherwise spin up the concurrent iterator and unindexer // Otherwise spin up the concurrent iterator and unindexer
for delivery := range hashesCh { for delivery := range hashesCh {
@ -332,11 +336,15 @@ func unindexTransactions(db ethdb.Database, from uint64, to uint64, interrupt ch
log.Crit("Failed writing batch to db", "error", err) log.Crit("Failed writing batch to db", "error", err)
return return
} }
logger := log.Debug
if report {
logger = log.Info
}
select { select {
case <-interrupt: case <-interrupt:
log.Debug("Transaction unindexing interrupted", "blocks", blocks, "txs", txs, "tail", to, "elapsed", common.PrettyDuration(time.Since(start))) logger("Transaction unindexing interrupted", "blocks", blocks, "txs", txs, "tail", to, "elapsed", common.PrettyDuration(time.Since(start)))
default: default:
log.Debug("Unindexed transactions", "blocks", blocks, "txs", txs, "tail", to, "elapsed", common.PrettyDuration(time.Since(start))) logger("Unindexed transactions", "blocks", blocks, "txs", txs, "tail", to, "elapsed", common.PrettyDuration(time.Since(start)))
} }
} }
@ -345,11 +353,11 @@ func unindexTransactions(db ethdb.Database, from uint64, to uint64, interrupt ch
// //
// There is a passed channel, the whole procedure will be interrupted if any // There is a passed channel, the whole procedure will be interrupted if any
// signal received. // signal received.
func UnindexTransactions(db ethdb.Database, from uint64, to uint64, interrupt chan struct{}) { func UnindexTransactions(db ethdb.Database, from uint64, to uint64, interrupt chan struct{}, report bool) {
unindexTransactions(db, from, to, interrupt, nil) unindexTransactions(db, from, to, interrupt, nil, report)
} }
// unindexTransactionsForTesting is the internal debug version with an additional hook. // unindexTransactionsForTesting is the internal debug version with an additional hook.
func unindexTransactionsForTesting(db ethdb.Database, from uint64, to uint64, interrupt chan struct{}, hook func(uint64) bool) { func unindexTransactionsForTesting(db ethdb.Database, from uint64, to uint64, interrupt chan struct{}, hook func(uint64) bool) {
unindexTransactions(db, from, to, interrupt, hook) unindexTransactions(db, from, to, interrupt, hook, false)
} }

View File

@ -162,18 +162,18 @@ func TestIndexTransactions(t *testing.T) {
t.Fatalf("Transaction tail mismatch") t.Fatalf("Transaction tail mismatch")
} }
} }
IndexTransactions(chainDb, 5, 11, nil) IndexTransactions(chainDb, 5, 11, nil, false)
verify(5, 11, true, 5) verify(5, 11, true, 5)
verify(0, 5, false, 5) verify(0, 5, false, 5)
IndexTransactions(chainDb, 0, 5, nil) IndexTransactions(chainDb, 0, 5, nil, false)
verify(0, 11, true, 0) verify(0, 11, true, 0)
UnindexTransactions(chainDb, 0, 5, nil) UnindexTransactions(chainDb, 0, 5, nil, false)
verify(5, 11, true, 5) verify(5, 11, true, 5)
verify(0, 5, false, 5) verify(0, 5, false, 5)
UnindexTransactions(chainDb, 5, 11, nil) UnindexTransactions(chainDb, 5, 11, nil, false)
verify(0, 11, false, 11) verify(0, 11, false, 11)
// Testing corner cases // Testing corner cases
@ -190,7 +190,7 @@ func TestIndexTransactions(t *testing.T) {
}) })
verify(9, 11, true, 9) verify(9, 11, true, 9)
verify(0, 9, false, 9) verify(0, 9, false, 9)
IndexTransactions(chainDb, 0, 9, nil) IndexTransactions(chainDb, 0, 9, nil, false)
signal = make(chan struct{}) signal = make(chan struct{})
var once2 sync.Once var once2 sync.Once

View File

@ -657,7 +657,6 @@ func ReadChainMetadata(db ethdb.KeyValueStore) [][]string {
{"snapshotRecoveryNumber", pp(ReadSnapshotRecoveryNumber(db))}, {"snapshotRecoveryNumber", pp(ReadSnapshotRecoveryNumber(db))},
{"snapshotRoot", fmt.Sprintf("%v", ReadSnapshotRoot(db))}, {"snapshotRoot", fmt.Sprintf("%v", ReadSnapshotRoot(db))},
{"txIndexTail", pp(ReadTxIndexTail(db))}, {"txIndexTail", pp(ReadTxIndexTail(db))},
{"fastTxLookupLimit", pp(ReadFastTxLookupLimit(db))},
} }
if b := ReadSkeletonSyncStatus(db); b != nil { if b := ReadSkeletonSyncStatus(db); b != nil {
data = append(data, []string{"SkeletonSyncStatus", string(b)}) data = append(data, []string{"SkeletonSyncStatus", string(b)})

View File

@ -80,6 +80,8 @@ var (
txIndexTailKey = []byte("TransactionIndexTail") txIndexTailKey = []byte("TransactionIndexTail")
// fastTxLookupLimitKey tracks the transaction lookup limit during fast sync. // fastTxLookupLimitKey tracks the transaction lookup limit during fast sync.
// This flag is deprecated, it's kept to avoid reporting errors when inspect
// database.
fastTxLookupLimitKey = []byte("FastTransactionLookupLimit") fastTxLookupLimitKey = []byte("FastTransactionLookupLimit")
// badBlockKey tracks the list of bad blocks seen by local // badBlockKey tracks the list of bad blocks seen by local

View File

@ -17,9 +17,8 @@
package state package state
import ( import (
"math/big"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/holiman/uint256"
) )
// journalEntry is a modification entry in the state change journal that can be // journalEntry is a modification entry in the state change journal that can be
@ -103,13 +102,13 @@ type (
selfDestructChange struct { selfDestructChange struct {
account *common.Address account *common.Address
prev bool // whether account had already self-destructed prev bool // whether account had already self-destructed
prevbalance *big.Int prevbalance *uint256.Int
} }
// Changes to individual accounts. // Changes to individual accounts.
balanceChange struct { balanceChange struct {
account *common.Address account *common.Address
prev *big.Int prev *uint256.Int
} }
nonceChange struct { nonceChange struct {
account *common.Address account *common.Address

View File

@ -27,17 +27,10 @@ import (
bloomfilter "github.com/holiman/bloomfilter/v2" bloomfilter "github.com/holiman/bloomfilter/v2"
) )
// stateBloomHasher is a wrapper around a byte blob to satisfy the interface API // stateBloomHash is used to convert a trie hash or contract code hash into a 64 bit mini hash.
// requirements of the bloom library used. It's used to convert a trie hash or func stateBloomHash(f []byte) uint64 {
// contract code hash into a 64 bit mini hash. return binary.BigEndian.Uint64(f)
type stateBloomHasher []byte }
func (f stateBloomHasher) Write(p []byte) (n int, err error) { panic("not implemented") }
func (f stateBloomHasher) Sum(b []byte) []byte { panic("not implemented") }
func (f stateBloomHasher) Reset() { panic("not implemented") }
func (f stateBloomHasher) BlockSize() int { panic("not implemented") }
func (f stateBloomHasher) Size() int { return 8 }
func (f stateBloomHasher) Sum64() uint64 { return binary.BigEndian.Uint64(f) }
// stateBloom is a bloom filter used during the state conversion(snapshot->state). // stateBloom is a bloom filter used during the state conversion(snapshot->state).
// The keys of all generated entries will be recorded here so that in the pruning // The keys of all generated entries will be recorded here so that in the pruning
@ -113,10 +106,10 @@ func (bloom *stateBloom) Put(key []byte, value []byte) error {
if !isCode { if !isCode {
return errors.New("invalid entry") return errors.New("invalid entry")
} }
bloom.bloom.Add(stateBloomHasher(codeKey)) bloom.bloom.AddHash(stateBloomHash(codeKey))
return nil return nil
} }
bloom.bloom.Add(stateBloomHasher(key)) bloom.bloom.AddHash(stateBloomHash(key))
return nil return nil
} }
@ -128,5 +121,5 @@ func (bloom *stateBloom) Delete(key []byte) error { panic("not supported") }
// - If it says yes, the key may be contained // - If it says yes, the key may be contained
// - If it says no, the key is definitely not contained. // - If it says no, the key is definitely not contained.
func (bloom *stateBloom) Contain(key []byte) bool { func (bloom *stateBloom) Contain(key []byte) bool {
return bloom.bloom.Contains(stateBloomHasher(key)) return bloom.bloom.ContainsHash(stateBloomHash(key))
} }

View File

@ -124,47 +124,20 @@ type diffLayer struct {
lock sync.RWMutex lock sync.RWMutex
} }
// destructBloomHasher is a wrapper around a common.Hash to satisfy the interface // destructBloomHash is used to convert a destruct event into a 64 bit mini hash.
// API requirements of the bloom library used. It's used to convert a destruct func destructBloomHash(h common.Hash) uint64 {
// event into a 64 bit mini hash.
type destructBloomHasher common.Hash
func (h destructBloomHasher) Write(p []byte) (n int, err error) { panic("not implemented") }
func (h destructBloomHasher) Sum(b []byte) []byte { panic("not implemented") }
func (h destructBloomHasher) Reset() { panic("not implemented") }
func (h destructBloomHasher) BlockSize() int { panic("not implemented") }
func (h destructBloomHasher) Size() int { return 8 }
func (h destructBloomHasher) Sum64() uint64 {
return binary.BigEndian.Uint64(h[bloomDestructHasherOffset : bloomDestructHasherOffset+8]) return binary.BigEndian.Uint64(h[bloomDestructHasherOffset : bloomDestructHasherOffset+8])
} }
// accountBloomHasher is a wrapper around a common.Hash to satisfy the interface // accountBloomHash is used to convert an account hash into a 64 bit mini hash.
// API requirements of the bloom library used. It's used to convert an account func accountBloomHash(h common.Hash) uint64 {
// hash into a 64 bit mini hash.
type accountBloomHasher common.Hash
func (h accountBloomHasher) Write(p []byte) (n int, err error) { panic("not implemented") }
func (h accountBloomHasher) Sum(b []byte) []byte { panic("not implemented") }
func (h accountBloomHasher) Reset() { panic("not implemented") }
func (h accountBloomHasher) BlockSize() int { panic("not implemented") }
func (h accountBloomHasher) Size() int { return 8 }
func (h accountBloomHasher) Sum64() uint64 {
return binary.BigEndian.Uint64(h[bloomAccountHasherOffset : bloomAccountHasherOffset+8]) return binary.BigEndian.Uint64(h[bloomAccountHasherOffset : bloomAccountHasherOffset+8])
} }
// storageBloomHasher is a wrapper around a [2]common.Hash to satisfy the interface // storageBloomHash is used to convert an account hash and a storage hash into a 64 bit mini hash.
// API requirements of the bloom library used. It's used to convert an account func storageBloomHash(h0, h1 common.Hash) uint64 {
// hash into a 64 bit mini hash. return binary.BigEndian.Uint64(h0[bloomStorageHasherOffset:bloomStorageHasherOffset+8]) ^
type storageBloomHasher [2]common.Hash binary.BigEndian.Uint64(h1[bloomStorageHasherOffset:bloomStorageHasherOffset+8])
func (h storageBloomHasher) Write(p []byte) (n int, err error) { panic("not implemented") }
func (h storageBloomHasher) Sum(b []byte) []byte { panic("not implemented") }
func (h storageBloomHasher) Reset() { panic("not implemented") }
func (h storageBloomHasher) BlockSize() int { panic("not implemented") }
func (h storageBloomHasher) Size() int { return 8 }
func (h storageBloomHasher) Sum64() uint64 {
return binary.BigEndian.Uint64(h[0][bloomStorageHasherOffset:bloomStorageHasherOffset+8]) ^
binary.BigEndian.Uint64(h[1][bloomStorageHasherOffset:bloomStorageHasherOffset+8])
} }
// newDiffLayer creates a new diff on top of an existing snapshot, whether that's a low // newDiffLayer creates a new diff on top of an existing snapshot, whether that's a low
@ -233,14 +206,14 @@ func (dl *diffLayer) rebloom(origin *diskLayer) {
} }
// Iterate over all the accounts and storage slots and index them // Iterate over all the accounts and storage slots and index them
for hash := range dl.destructSet { for hash := range dl.destructSet {
dl.diffed.Add(destructBloomHasher(hash)) dl.diffed.AddHash(destructBloomHash(hash))
} }
for hash := range dl.accountData { for hash := range dl.accountData {
dl.diffed.Add(accountBloomHasher(hash)) dl.diffed.AddHash(accountBloomHash(hash))
} }
for accountHash, slots := range dl.storageData { for accountHash, slots := range dl.storageData {
for storageHash := range slots { for storageHash := range slots {
dl.diffed.Add(storageBloomHasher{accountHash, storageHash}) dl.diffed.AddHash(storageBloomHash(accountHash, storageHash))
} }
} }
// Calculate the current false positive rate and update the error rate meter. // Calculate the current false positive rate and update the error rate meter.
@ -301,9 +274,9 @@ func (dl *diffLayer) AccountRLP(hash common.Hash) ([]byte, error) {
} }
// Check the bloom filter first whether there's even a point in reaching into // Check the bloom filter first whether there's even a point in reaching into
// all the maps in all the layers below // all the maps in all the layers below
hit := dl.diffed.Contains(accountBloomHasher(hash)) hit := dl.diffed.ContainsHash(accountBloomHash(hash))
if !hit { if !hit {
hit = dl.diffed.Contains(destructBloomHasher(hash)) hit = dl.diffed.ContainsHash(destructBloomHash(hash))
} }
var origin *diskLayer var origin *diskLayer
if !hit { if !hit {
@ -372,9 +345,9 @@ func (dl *diffLayer) Storage(accountHash, storageHash common.Hash) ([]byte, erro
dl.lock.RUnlock() dl.lock.RUnlock()
return nil, ErrSnapshotStale return nil, ErrSnapshotStale
} }
hit := dl.diffed.Contains(storageBloomHasher{accountHash, storageHash}) hit := dl.diffed.ContainsHash(storageBloomHash(accountHash, storageHash))
if !hit { if !hit {
hit = dl.diffed.Contains(destructBloomHasher(accountHash)) hit = dl.diffed.ContainsHash(destructBloomHash(accountHash))
} }
var origin *diskLayer var origin *diskLayer
if !hit { if !hit {

View File

@ -18,7 +18,6 @@ package snapshot
import ( import (
"fmt" "fmt"
"math/big"
"os" "os"
"testing" "testing"
"time" "time"
@ -33,6 +32,7 @@ import (
"github.com/ethereum/go-ethereum/trie/triedb/hashdb" "github.com/ethereum/go-ethereum/trie/triedb/hashdb"
"github.com/ethereum/go-ethereum/trie/triedb/pathdb" "github.com/ethereum/go-ethereum/trie/triedb/pathdb"
"github.com/ethereum/go-ethereum/trie/trienode" "github.com/ethereum/go-ethereum/trie/trienode"
"github.com/holiman/uint256"
"golang.org/x/crypto/sha3" "golang.org/x/crypto/sha3"
) )
@ -58,9 +58,9 @@ func testGeneration(t *testing.T, scheme string) {
var helper = newHelper(scheme) var helper = newHelper(scheme)
stRoot := helper.makeStorageTrie(common.Hash{}, []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}, false) stRoot := helper.makeStorageTrie(common.Hash{}, []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}, false)
helper.addTrieAccount("acc-1", &types.StateAccount{Balance: big.NewInt(1), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()}) helper.addTrieAccount("acc-1", &types.StateAccount{Balance: uint256.NewInt(1), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()})
helper.addTrieAccount("acc-2", &types.StateAccount{Balance: big.NewInt(2), Root: types.EmptyRootHash, CodeHash: types.EmptyCodeHash.Bytes()}) helper.addTrieAccount("acc-2", &types.StateAccount{Balance: uint256.NewInt(2), Root: types.EmptyRootHash, CodeHash: types.EmptyCodeHash.Bytes()})
helper.addTrieAccount("acc-3", &types.StateAccount{Balance: big.NewInt(3), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()}) helper.addTrieAccount("acc-3", &types.StateAccount{Balance: uint256.NewInt(3), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()})
helper.makeStorageTrie(hashData([]byte("acc-1")), []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}, true) helper.makeStorageTrie(hashData([]byte("acc-1")), []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}, true)
helper.makeStorageTrie(hashData([]byte("acc-3")), []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}, true) helper.makeStorageTrie(hashData([]byte("acc-3")), []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}, true)
@ -97,16 +97,16 @@ func testGenerateExistentState(t *testing.T, scheme string) {
var helper = newHelper(scheme) var helper = newHelper(scheme)
stRoot := helper.makeStorageTrie(hashData([]byte("acc-1")), []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}, true) stRoot := helper.makeStorageTrie(hashData([]byte("acc-1")), []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}, true)
helper.addTrieAccount("acc-1", &types.StateAccount{Balance: big.NewInt(1), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()}) helper.addTrieAccount("acc-1", &types.StateAccount{Balance: uint256.NewInt(1), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()})
helper.addSnapAccount("acc-1", &types.StateAccount{Balance: big.NewInt(1), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()}) helper.addSnapAccount("acc-1", &types.StateAccount{Balance: uint256.NewInt(1), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()})
helper.addSnapStorage("acc-1", []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}) helper.addSnapStorage("acc-1", []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"})
helper.addTrieAccount("acc-2", &types.StateAccount{Balance: big.NewInt(2), Root: types.EmptyRootHash, CodeHash: types.EmptyCodeHash.Bytes()}) helper.addTrieAccount("acc-2", &types.StateAccount{Balance: uint256.NewInt(2), Root: types.EmptyRootHash, CodeHash: types.EmptyCodeHash.Bytes()})
helper.addSnapAccount("acc-2", &types.StateAccount{Balance: big.NewInt(2), Root: types.EmptyRootHash, CodeHash: types.EmptyCodeHash.Bytes()}) helper.addSnapAccount("acc-2", &types.StateAccount{Balance: uint256.NewInt(2), Root: types.EmptyRootHash, CodeHash: types.EmptyCodeHash.Bytes()})
stRoot = helper.makeStorageTrie(hashData([]byte("acc-3")), []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}, true) stRoot = helper.makeStorageTrie(hashData([]byte("acc-3")), []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}, true)
helper.addTrieAccount("acc-3", &types.StateAccount{Balance: big.NewInt(3), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()}) helper.addTrieAccount("acc-3", &types.StateAccount{Balance: uint256.NewInt(3), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()})
helper.addSnapAccount("acc-3", &types.StateAccount{Balance: big.NewInt(3), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()}) helper.addSnapAccount("acc-3", &types.StateAccount{Balance: uint256.NewInt(3), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()})
helper.addSnapStorage("acc-3", []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}) helper.addSnapStorage("acc-3", []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"})
root, snap := helper.CommitAndGenerate() root, snap := helper.CommitAndGenerate()
@ -259,28 +259,28 @@ func testGenerateExistentStateWithWrongStorage(t *testing.T, scheme string) {
helper := newHelper(scheme) helper := newHelper(scheme)
// Account one, empty root but non-empty database // Account one, empty root but non-empty database
helper.addAccount("acc-1", &types.StateAccount{Balance: big.NewInt(1), Root: types.EmptyRootHash, CodeHash: types.EmptyCodeHash.Bytes()}) helper.addAccount("acc-1", &types.StateAccount{Balance: uint256.NewInt(1), Root: types.EmptyRootHash, CodeHash: types.EmptyCodeHash.Bytes()})
helper.addSnapStorage("acc-1", []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}) helper.addSnapStorage("acc-1", []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"})
// Account two, non empty root but empty database // Account two, non empty root but empty database
stRoot := helper.makeStorageTrie(hashData([]byte("acc-2")), []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}, true) stRoot := helper.makeStorageTrie(hashData([]byte("acc-2")), []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}, true)
helper.addAccount("acc-2", &types.StateAccount{Balance: big.NewInt(1), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()}) helper.addAccount("acc-2", &types.StateAccount{Balance: uint256.NewInt(1), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()})
// Miss slots // Miss slots
{ {
// Account three, non empty root but misses slots in the beginning // Account three, non empty root but misses slots in the beginning
helper.makeStorageTrie(hashData([]byte("acc-3")), []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}, true) helper.makeStorageTrie(hashData([]byte("acc-3")), []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}, true)
helper.addAccount("acc-3", &types.StateAccount{Balance: big.NewInt(1), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()}) helper.addAccount("acc-3", &types.StateAccount{Balance: uint256.NewInt(1), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()})
helper.addSnapStorage("acc-3", []string{"key-2", "key-3"}, []string{"val-2", "val-3"}) helper.addSnapStorage("acc-3", []string{"key-2", "key-3"}, []string{"val-2", "val-3"})
// Account four, non empty root but misses slots in the middle // Account four, non empty root but misses slots in the middle
helper.makeStorageTrie(hashData([]byte("acc-4")), []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}, true) helper.makeStorageTrie(hashData([]byte("acc-4")), []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}, true)
helper.addAccount("acc-4", &types.StateAccount{Balance: big.NewInt(1), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()}) helper.addAccount("acc-4", &types.StateAccount{Balance: uint256.NewInt(1), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()})
helper.addSnapStorage("acc-4", []string{"key-1", "key-3"}, []string{"val-1", "val-3"}) helper.addSnapStorage("acc-4", []string{"key-1", "key-3"}, []string{"val-1", "val-3"})
// Account five, non empty root but misses slots in the end // Account five, non empty root but misses slots in the end
helper.makeStorageTrie(hashData([]byte("acc-5")), []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}, true) helper.makeStorageTrie(hashData([]byte("acc-5")), []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}, true)
helper.addAccount("acc-5", &types.StateAccount{Balance: big.NewInt(1), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()}) helper.addAccount("acc-5", &types.StateAccount{Balance: uint256.NewInt(1), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()})
helper.addSnapStorage("acc-5", []string{"key-1", "key-2"}, []string{"val-1", "val-2"}) helper.addSnapStorage("acc-5", []string{"key-1", "key-2"}, []string{"val-1", "val-2"})
} }
@ -288,22 +288,22 @@ func testGenerateExistentStateWithWrongStorage(t *testing.T, scheme string) {
{ {
// Account six, non empty root but wrong slots in the beginning // Account six, non empty root but wrong slots in the beginning
helper.makeStorageTrie(hashData([]byte("acc-6")), []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}, true) helper.makeStorageTrie(hashData([]byte("acc-6")), []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}, true)
helper.addAccount("acc-6", &types.StateAccount{Balance: big.NewInt(1), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()}) helper.addAccount("acc-6", &types.StateAccount{Balance: uint256.NewInt(1), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()})
helper.addSnapStorage("acc-6", []string{"key-1", "key-2", "key-3"}, []string{"badval-1", "val-2", "val-3"}) helper.addSnapStorage("acc-6", []string{"key-1", "key-2", "key-3"}, []string{"badval-1", "val-2", "val-3"})
// Account seven, non empty root but wrong slots in the middle // Account seven, non empty root but wrong slots in the middle
helper.makeStorageTrie(hashData([]byte("acc-7")), []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}, true) helper.makeStorageTrie(hashData([]byte("acc-7")), []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}, true)
helper.addAccount("acc-7", &types.StateAccount{Balance: big.NewInt(1), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()}) helper.addAccount("acc-7", &types.StateAccount{Balance: uint256.NewInt(1), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()})
helper.addSnapStorage("acc-7", []string{"key-1", "key-2", "key-3"}, []string{"val-1", "badval-2", "val-3"}) helper.addSnapStorage("acc-7", []string{"key-1", "key-2", "key-3"}, []string{"val-1", "badval-2", "val-3"})
// Account eight, non empty root but wrong slots in the end // Account eight, non empty root but wrong slots in the end
helper.makeStorageTrie(hashData([]byte("acc-8")), []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}, true) helper.makeStorageTrie(hashData([]byte("acc-8")), []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}, true)
helper.addAccount("acc-8", &types.StateAccount{Balance: big.NewInt(1), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()}) helper.addAccount("acc-8", &types.StateAccount{Balance: uint256.NewInt(1), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()})
helper.addSnapStorage("acc-8", []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "badval-3"}) helper.addSnapStorage("acc-8", []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "badval-3"})
// Account 9, non empty root but rotated slots // Account 9, non empty root but rotated slots
helper.makeStorageTrie(hashData([]byte("acc-9")), []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}, true) helper.makeStorageTrie(hashData([]byte("acc-9")), []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}, true)
helper.addAccount("acc-9", &types.StateAccount{Balance: big.NewInt(1), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()}) helper.addAccount("acc-9", &types.StateAccount{Balance: uint256.NewInt(1), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()})
helper.addSnapStorage("acc-9", []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-3", "val-2"}) helper.addSnapStorage("acc-9", []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-3", "val-2"})
} }
@ -311,17 +311,17 @@ func testGenerateExistentStateWithWrongStorage(t *testing.T, scheme string) {
{ {
// Account 10, non empty root but extra slots in the beginning // Account 10, non empty root but extra slots in the beginning
helper.makeStorageTrie(hashData([]byte("acc-10")), []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}, true) helper.makeStorageTrie(hashData([]byte("acc-10")), []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}, true)
helper.addAccount("acc-10", &types.StateAccount{Balance: big.NewInt(1), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()}) helper.addAccount("acc-10", &types.StateAccount{Balance: uint256.NewInt(1), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()})
helper.addSnapStorage("acc-10", []string{"key-0", "key-1", "key-2", "key-3"}, []string{"val-0", "val-1", "val-2", "val-3"}) helper.addSnapStorage("acc-10", []string{"key-0", "key-1", "key-2", "key-3"}, []string{"val-0", "val-1", "val-2", "val-3"})
// Account 11, non empty root but extra slots in the middle // Account 11, non empty root but extra slots in the middle
helper.makeStorageTrie(hashData([]byte("acc-11")), []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}, true) helper.makeStorageTrie(hashData([]byte("acc-11")), []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}, true)
helper.addAccount("acc-11", &types.StateAccount{Balance: big.NewInt(1), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()}) helper.addAccount("acc-11", &types.StateAccount{Balance: uint256.NewInt(1), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()})
helper.addSnapStorage("acc-11", []string{"key-1", "key-2", "key-2-1", "key-3"}, []string{"val-1", "val-2", "val-2-1", "val-3"}) helper.addSnapStorage("acc-11", []string{"key-1", "key-2", "key-2-1", "key-3"}, []string{"val-1", "val-2", "val-2-1", "val-3"})
// Account 12, non empty root but extra slots in the end // Account 12, non empty root but extra slots in the end
helper.makeStorageTrie(hashData([]byte("acc-12")), []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}, true) helper.makeStorageTrie(hashData([]byte("acc-12")), []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}, true)
helper.addAccount("acc-12", &types.StateAccount{Balance: big.NewInt(1), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()}) helper.addAccount("acc-12", &types.StateAccount{Balance: uint256.NewInt(1), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()})
helper.addSnapStorage("acc-12", []string{"key-1", "key-2", "key-3", "key-4"}, []string{"val-1", "val-2", "val-3", "val-4"}) helper.addSnapStorage("acc-12", []string{"key-1", "key-2", "key-3", "key-4"}, []string{"val-1", "val-2", "val-3", "val-4"})
} }
@ -366,25 +366,25 @@ func testGenerateExistentStateWithWrongAccounts(t *testing.T, scheme string) {
// Missing accounts, only in the trie // Missing accounts, only in the trie
{ {
helper.addTrieAccount("acc-1", &types.StateAccount{Balance: big.NewInt(1), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()}) // Beginning helper.addTrieAccount("acc-1", &types.StateAccount{Balance: uint256.NewInt(1), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()}) // Beginning
helper.addTrieAccount("acc-4", &types.StateAccount{Balance: big.NewInt(1), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()}) // Middle helper.addTrieAccount("acc-4", &types.StateAccount{Balance: uint256.NewInt(1), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()}) // Middle
helper.addTrieAccount("acc-6", &types.StateAccount{Balance: big.NewInt(1), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()}) // End helper.addTrieAccount("acc-6", &types.StateAccount{Balance: uint256.NewInt(1), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()}) // End
} }
// Wrong accounts // Wrong accounts
{ {
helper.addTrieAccount("acc-2", &types.StateAccount{Balance: big.NewInt(1), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()}) helper.addTrieAccount("acc-2", &types.StateAccount{Balance: uint256.NewInt(1), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()})
helper.addSnapAccount("acc-2", &types.StateAccount{Balance: big.NewInt(1), Root: stRoot, CodeHash: common.Hex2Bytes("0x1234")}) helper.addSnapAccount("acc-2", &types.StateAccount{Balance: uint256.NewInt(1), Root: stRoot, CodeHash: common.Hex2Bytes("0x1234")})
helper.addTrieAccount("acc-3", &types.StateAccount{Balance: big.NewInt(1), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()}) helper.addTrieAccount("acc-3", &types.StateAccount{Balance: uint256.NewInt(1), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()})
helper.addSnapAccount("acc-3", &types.StateAccount{Balance: big.NewInt(1), Root: types.EmptyRootHash, CodeHash: types.EmptyCodeHash.Bytes()}) helper.addSnapAccount("acc-3", &types.StateAccount{Balance: uint256.NewInt(1), Root: types.EmptyRootHash, CodeHash: types.EmptyCodeHash.Bytes()})
} }
// Extra accounts, only in the snap // Extra accounts, only in the snap
{ {
helper.addSnapAccount("acc-0", &types.StateAccount{Balance: big.NewInt(1), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()}) // before the beginning helper.addSnapAccount("acc-0", &types.StateAccount{Balance: uint256.NewInt(1), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()}) // before the beginning
helper.addSnapAccount("acc-5", &types.StateAccount{Balance: big.NewInt(1), Root: types.EmptyRootHash, CodeHash: common.Hex2Bytes("0x1234")}) // Middle helper.addSnapAccount("acc-5", &types.StateAccount{Balance: uint256.NewInt(1), Root: types.EmptyRootHash, CodeHash: common.Hex2Bytes("0x1234")}) // Middle
helper.addSnapAccount("acc-7", &types.StateAccount{Balance: big.NewInt(1), Root: types.EmptyRootHash, CodeHash: types.EmptyCodeHash.Bytes()}) // after the end helper.addSnapAccount("acc-7", &types.StateAccount{Balance: uint256.NewInt(1), Root: types.EmptyRootHash, CodeHash: types.EmptyCodeHash.Bytes()}) // after the end
} }
root, snap := helper.CommitAndGenerate() root, snap := helper.CommitAndGenerate()
@ -418,9 +418,9 @@ func testGenerateCorruptAccountTrie(t *testing.T, scheme string) {
// without any storage slots to keep the test smaller. // without any storage slots to keep the test smaller.
helper := newHelper(scheme) helper := newHelper(scheme)
helper.addTrieAccount("acc-1", &types.StateAccount{Balance: big.NewInt(1), Root: types.EmptyRootHash, CodeHash: types.EmptyCodeHash.Bytes()}) // 0xc7a30f39aff471c95d8a837497ad0e49b65be475cc0953540f80cfcdbdcd9074 helper.addTrieAccount("acc-1", &types.StateAccount{Balance: uint256.NewInt(1), Root: types.EmptyRootHash, CodeHash: types.EmptyCodeHash.Bytes()}) // 0xc7a30f39aff471c95d8a837497ad0e49b65be475cc0953540f80cfcdbdcd9074
helper.addTrieAccount("acc-2", &types.StateAccount{Balance: big.NewInt(2), Root: types.EmptyRootHash, CodeHash: types.EmptyCodeHash.Bytes()}) // 0x65145f923027566669a1ae5ccac66f945b55ff6eaeb17d2ea8e048b7d381f2d7 helper.addTrieAccount("acc-2", &types.StateAccount{Balance: uint256.NewInt(2), Root: types.EmptyRootHash, CodeHash: types.EmptyCodeHash.Bytes()}) // 0x65145f923027566669a1ae5ccac66f945b55ff6eaeb17d2ea8e048b7d381f2d7
helper.addTrieAccount("acc-3", &types.StateAccount{Balance: big.NewInt(3), Root: types.EmptyRootHash, CodeHash: types.EmptyCodeHash.Bytes()}) // 0x19ead688e907b0fab07176120dceec244a72aff2f0aa51e8b827584e378772f4 helper.addTrieAccount("acc-3", &types.StateAccount{Balance: uint256.NewInt(3), Root: types.EmptyRootHash, CodeHash: types.EmptyCodeHash.Bytes()}) // 0x19ead688e907b0fab07176120dceec244a72aff2f0aa51e8b827584e378772f4
root := helper.Commit() // Root: 0xa04693ea110a31037fb5ee814308a6f1d76bdab0b11676bdf4541d2de55ba978 root := helper.Commit() // Root: 0xa04693ea110a31037fb5ee814308a6f1d76bdab0b11676bdf4541d2de55ba978
@ -463,10 +463,10 @@ func testGenerateMissingStorageTrie(t *testing.T, scheme string) {
helper = newHelper(scheme) helper = newHelper(scheme)
) )
stRoot := helper.makeStorageTrie(hashData([]byte("acc-1")), []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}, true) // 0xddefcd9376dd029653ef384bd2f0a126bb755fe84fdcc9e7cf421ba454f2bc67 stRoot := helper.makeStorageTrie(hashData([]byte("acc-1")), []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}, true) // 0xddefcd9376dd029653ef384bd2f0a126bb755fe84fdcc9e7cf421ba454f2bc67
helper.addTrieAccount("acc-1", &types.StateAccount{Balance: big.NewInt(1), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()}) // 0x9250573b9c18c664139f3b6a7a8081b7d8f8916a8fcc5d94feec6c29f5fd4e9e helper.addTrieAccount("acc-1", &types.StateAccount{Balance: uint256.NewInt(1), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()}) // 0x9250573b9c18c664139f3b6a7a8081b7d8f8916a8fcc5d94feec6c29f5fd4e9e
helper.addTrieAccount("acc-2", &types.StateAccount{Balance: big.NewInt(2), Root: types.EmptyRootHash, CodeHash: types.EmptyCodeHash.Bytes()}) // 0x65145f923027566669a1ae5ccac66f945b55ff6eaeb17d2ea8e048b7d381f2d7 helper.addTrieAccount("acc-2", &types.StateAccount{Balance: uint256.NewInt(2), Root: types.EmptyRootHash, CodeHash: types.EmptyCodeHash.Bytes()}) // 0x65145f923027566669a1ae5ccac66f945b55ff6eaeb17d2ea8e048b7d381f2d7
stRoot = helper.makeStorageTrie(hashData([]byte("acc-3")), []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}, true) stRoot = helper.makeStorageTrie(hashData([]byte("acc-3")), []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}, true)
helper.addTrieAccount("acc-3", &types.StateAccount{Balance: big.NewInt(3), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()}) // 0x50815097425d000edfc8b3a4a13e175fc2bdcfee8bdfbf2d1ff61041d3c235b2 helper.addTrieAccount("acc-3", &types.StateAccount{Balance: uint256.NewInt(3), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()}) // 0x50815097425d000edfc8b3a4a13e175fc2bdcfee8bdfbf2d1ff61041d3c235b2
root := helper.Commit() root := helper.Commit()
@ -503,10 +503,10 @@ func testGenerateCorruptStorageTrie(t *testing.T, scheme string) {
helper := newHelper(scheme) helper := newHelper(scheme)
stRoot := helper.makeStorageTrie(hashData([]byte("acc-1")), []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}, true) // 0xddefcd9376dd029653ef384bd2f0a126bb755fe84fdcc9e7cf421ba454f2bc67 stRoot := helper.makeStorageTrie(hashData([]byte("acc-1")), []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}, true) // 0xddefcd9376dd029653ef384bd2f0a126bb755fe84fdcc9e7cf421ba454f2bc67
helper.addTrieAccount("acc-1", &types.StateAccount{Balance: big.NewInt(1), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()}) // 0x9250573b9c18c664139f3b6a7a8081b7d8f8916a8fcc5d94feec6c29f5fd4e9e helper.addTrieAccount("acc-1", &types.StateAccount{Balance: uint256.NewInt(1), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()}) // 0x9250573b9c18c664139f3b6a7a8081b7d8f8916a8fcc5d94feec6c29f5fd4e9e
helper.addTrieAccount("acc-2", &types.StateAccount{Balance: big.NewInt(2), Root: types.EmptyRootHash, CodeHash: types.EmptyCodeHash.Bytes()}) // 0x65145f923027566669a1ae5ccac66f945b55ff6eaeb17d2ea8e048b7d381f2d7 helper.addTrieAccount("acc-2", &types.StateAccount{Balance: uint256.NewInt(2), Root: types.EmptyRootHash, CodeHash: types.EmptyCodeHash.Bytes()}) // 0x65145f923027566669a1ae5ccac66f945b55ff6eaeb17d2ea8e048b7d381f2d7
stRoot = helper.makeStorageTrie(hashData([]byte("acc-3")), []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}, true) stRoot = helper.makeStorageTrie(hashData([]byte("acc-3")), []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}, true)
helper.addTrieAccount("acc-3", &types.StateAccount{Balance: big.NewInt(3), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()}) // 0x50815097425d000edfc8b3a4a13e175fc2bdcfee8bdfbf2d1ff61041d3c235b2 helper.addTrieAccount("acc-3", &types.StateAccount{Balance: uint256.NewInt(3), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()}) // 0x50815097425d000edfc8b3a4a13e175fc2bdcfee8bdfbf2d1ff61041d3c235b2
root := helper.Commit() root := helper.Commit()
@ -546,7 +546,7 @@ func testGenerateWithExtraAccounts(t *testing.T, scheme string) {
[]string{"val-1", "val-2", "val-3", "val-4", "val-5"}, []string{"val-1", "val-2", "val-3", "val-4", "val-5"},
true, true,
) )
acc := &types.StateAccount{Balance: big.NewInt(1), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()} acc := &types.StateAccount{Balance: uint256.NewInt(1), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()}
val, _ := rlp.EncodeToBytes(acc) val, _ := rlp.EncodeToBytes(acc)
helper.accTrie.MustUpdate([]byte("acc-1"), val) // 0x9250573b9c18c664139f3b6a7a8081b7d8f8916a8fcc5d94feec6c29f5fd4e9e helper.accTrie.MustUpdate([]byte("acc-1"), val) // 0x9250573b9c18c664139f3b6a7a8081b7d8f8916a8fcc5d94feec6c29f5fd4e9e
@ -566,7 +566,7 @@ func testGenerateWithExtraAccounts(t *testing.T, scheme string) {
[]string{"val-1", "val-2", "val-3", "val-4", "val-5"}, []string{"val-1", "val-2", "val-3", "val-4", "val-5"},
true, true,
) )
acc := &types.StateAccount{Balance: big.NewInt(1), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()} acc := &types.StateAccount{Balance: uint256.NewInt(1), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()}
val, _ := rlp.EncodeToBytes(acc) val, _ := rlp.EncodeToBytes(acc)
key := hashData([]byte("acc-2")) key := hashData([]byte("acc-2"))
rawdb.WriteAccountSnapshot(helper.diskdb, key, val) rawdb.WriteAccountSnapshot(helper.diskdb, key, val)
@ -622,7 +622,7 @@ func testGenerateWithManyExtraAccounts(t *testing.T, scheme string) {
[]string{"val-1", "val-2", "val-3"}, []string{"val-1", "val-2", "val-3"},
true, true,
) )
acc := &types.StateAccount{Balance: big.NewInt(1), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()} acc := &types.StateAccount{Balance: uint256.NewInt(1), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()}
val, _ := rlp.EncodeToBytes(acc) val, _ := rlp.EncodeToBytes(acc)
helper.accTrie.MustUpdate([]byte("acc-1"), val) // 0x9250573b9c18c664139f3b6a7a8081b7d8f8916a8fcc5d94feec6c29f5fd4e9e helper.accTrie.MustUpdate([]byte("acc-1"), val) // 0x9250573b9c18c664139f3b6a7a8081b7d8f8916a8fcc5d94feec6c29f5fd4e9e
@ -636,7 +636,7 @@ func testGenerateWithManyExtraAccounts(t *testing.T, scheme string) {
{ {
// 100 accounts exist only in snapshot // 100 accounts exist only in snapshot
for i := 0; i < 1000; i++ { for i := 0; i < 1000; i++ {
acc := &types.StateAccount{Balance: big.NewInt(int64(i)), Root: types.EmptyRootHash, CodeHash: types.EmptyCodeHash.Bytes()} acc := &types.StateAccount{Balance: uint256.NewInt(uint64(i)), Root: types.EmptyRootHash, CodeHash: types.EmptyCodeHash.Bytes()}
val, _ := rlp.EncodeToBytes(acc) val, _ := rlp.EncodeToBytes(acc)
key := hashData([]byte(fmt.Sprintf("acc-%d", i))) key := hashData([]byte(fmt.Sprintf("acc-%d", i)))
rawdb.WriteAccountSnapshot(helper.diskdb, key, val) rawdb.WriteAccountSnapshot(helper.diskdb, key, val)
@ -678,7 +678,7 @@ func testGenerateWithExtraBeforeAndAfter(t *testing.T, scheme string) {
} }
helper := newHelper(scheme) helper := newHelper(scheme)
{ {
acc := &types.StateAccount{Balance: big.NewInt(1), Root: types.EmptyRootHash, CodeHash: types.EmptyCodeHash.Bytes()} acc := &types.StateAccount{Balance: uint256.NewInt(1), Root: types.EmptyRootHash, CodeHash: types.EmptyCodeHash.Bytes()}
val, _ := rlp.EncodeToBytes(acc) val, _ := rlp.EncodeToBytes(acc)
helper.accTrie.MustUpdate(common.HexToHash("0x03").Bytes(), val) helper.accTrie.MustUpdate(common.HexToHash("0x03").Bytes(), val)
helper.accTrie.MustUpdate(common.HexToHash("0x07").Bytes(), val) helper.accTrie.MustUpdate(common.HexToHash("0x07").Bytes(), val)
@ -720,7 +720,7 @@ func testGenerateWithMalformedSnapdata(t *testing.T, scheme string) {
} }
helper := newHelper(scheme) helper := newHelper(scheme)
{ {
acc := &types.StateAccount{Balance: big.NewInt(1), Root: types.EmptyRootHash, CodeHash: types.EmptyCodeHash.Bytes()} acc := &types.StateAccount{Balance: uint256.NewInt(1), Root: types.EmptyRootHash, CodeHash: types.EmptyCodeHash.Bytes()}
val, _ := rlp.EncodeToBytes(acc) val, _ := rlp.EncodeToBytes(acc)
helper.accTrie.MustUpdate(common.HexToHash("0x03").Bytes(), val) helper.accTrie.MustUpdate(common.HexToHash("0x03").Bytes(), val)
@ -764,7 +764,7 @@ func testGenerateFromEmptySnap(t *testing.T, scheme string) {
for i := 0; i < 400; i++ { for i := 0; i < 400; i++ {
stRoot := helper.makeStorageTrie(hashData([]byte(fmt.Sprintf("acc-%d", i))), []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}, true) stRoot := helper.makeStorageTrie(hashData([]byte(fmt.Sprintf("acc-%d", i))), []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}, true)
helper.addTrieAccount(fmt.Sprintf("acc-%d", i), helper.addTrieAccount(fmt.Sprintf("acc-%d", i),
&types.StateAccount{Balance: big.NewInt(1), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()}) &types.StateAccount{Balance: uint256.NewInt(1), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()})
} }
root, snap := helper.CommitAndGenerate() root, snap := helper.CommitAndGenerate()
t.Logf("Root: %#x\n", root) // Root: 0x6f7af6d2e1a1bf2b84a3beb3f8b64388465fbc1e274ca5d5d3fc787ca78f59e4 t.Logf("Root: %#x\n", root) // Root: 0x6f7af6d2e1a1bf2b84a3beb3f8b64388465fbc1e274ca5d5d3fc787ca78f59e4
@ -806,7 +806,7 @@ func testGenerateWithIncompleteStorage(t *testing.T, scheme string) {
for i := 0; i < 8; i++ { for i := 0; i < 8; i++ {
accKey := fmt.Sprintf("acc-%d", i) accKey := fmt.Sprintf("acc-%d", i)
stRoot := helper.makeStorageTrie(hashData([]byte(accKey)), stKeys, stVals, true) stRoot := helper.makeStorageTrie(hashData([]byte(accKey)), stKeys, stVals, true)
helper.addAccount(accKey, &types.StateAccount{Balance: big.NewInt(int64(i)), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()}) helper.addAccount(accKey, &types.StateAccount{Balance: uint256.NewInt(uint64(i)), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()})
var moddedKeys []string var moddedKeys []string
var moddedVals []string var moddedVals []string
for ii := 0; ii < 8; ii++ { for ii := 0; ii < 8; ii++ {
@ -903,11 +903,11 @@ func testGenerateCompleteSnapshotWithDanglingStorage(t *testing.T, scheme string
var helper = newHelper(scheme) var helper = newHelper(scheme)
stRoot := helper.makeStorageTrie(hashData([]byte("acc-1")), []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}, true) stRoot := helper.makeStorageTrie(hashData([]byte("acc-1")), []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}, true)
helper.addAccount("acc-1", &types.StateAccount{Balance: big.NewInt(1), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()}) helper.addAccount("acc-1", &types.StateAccount{Balance: uint256.NewInt(1), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()})
helper.addAccount("acc-2", &types.StateAccount{Balance: big.NewInt(1), Root: types.EmptyRootHash, CodeHash: types.EmptyCodeHash.Bytes()}) helper.addAccount("acc-2", &types.StateAccount{Balance: uint256.NewInt(1), Root: types.EmptyRootHash, CodeHash: types.EmptyCodeHash.Bytes()})
helper.makeStorageTrie(hashData([]byte("acc-3")), []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}, true) helper.makeStorageTrie(hashData([]byte("acc-3")), []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}, true)
helper.addAccount("acc-3", &types.StateAccount{Balance: big.NewInt(1), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()}) helper.addAccount("acc-3", &types.StateAccount{Balance: uint256.NewInt(1), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()})
helper.addSnapStorage("acc-1", []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}) helper.addSnapStorage("acc-1", []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"})
helper.addSnapStorage("acc-3", []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}) helper.addSnapStorage("acc-3", []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"})
@ -943,11 +943,11 @@ func testGenerateBrokenSnapshotWithDanglingStorage(t *testing.T, scheme string)
var helper = newHelper(scheme) var helper = newHelper(scheme)
stRoot := helper.makeStorageTrie(hashData([]byte("acc-1")), []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}, true) stRoot := helper.makeStorageTrie(hashData([]byte("acc-1")), []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}, true)
helper.addTrieAccount("acc-1", &types.StateAccount{Balance: big.NewInt(1), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()}) helper.addTrieAccount("acc-1", &types.StateAccount{Balance: uint256.NewInt(1), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()})
helper.addTrieAccount("acc-2", &types.StateAccount{Balance: big.NewInt(2), Root: types.EmptyRootHash, CodeHash: types.EmptyCodeHash.Bytes()}) helper.addTrieAccount("acc-2", &types.StateAccount{Balance: uint256.NewInt(2), Root: types.EmptyRootHash, CodeHash: types.EmptyCodeHash.Bytes()})
helper.makeStorageTrie(hashData([]byte("acc-3")), []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}, true) helper.makeStorageTrie(hashData([]byte("acc-3")), []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}, true)
helper.addTrieAccount("acc-3", &types.StateAccount{Balance: big.NewInt(3), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()}) helper.addTrieAccount("acc-3", &types.StateAccount{Balance: uint256.NewInt(3), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()})
populateDangling(helper.diskdb) populateDangling(helper.diskdb)

View File

@ -20,7 +20,6 @@ import (
crand "crypto/rand" crand "crypto/rand"
"encoding/binary" "encoding/binary"
"fmt" "fmt"
"math/big"
"math/rand" "math/rand"
"testing" "testing"
"time" "time"
@ -30,6 +29,7 @@ import (
"github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/core/rawdb"
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/rlp"
"github.com/holiman/uint256"
) )
// randomHash generates a random blob of data and returns it as a hash. // randomHash generates a random blob of data and returns it as a hash.
@ -44,7 +44,7 @@ func randomHash() common.Hash {
// randomAccount generates a random account and returns it RLP encoded. // randomAccount generates a random account and returns it RLP encoded.
func randomAccount() []byte { func randomAccount() []byte {
a := &types.StateAccount{ a := &types.StateAccount{
Balance: big.NewInt(rand.Int63()), Balance: uint256.NewInt(rand.Uint64()),
Nonce: rand.Uint64(), Nonce: rand.Uint64(),
Root: randomHash(), Root: randomHash(),
CodeHash: types.EmptyCodeHash[:], CodeHash: types.EmptyCodeHash[:],

View File

@ -20,7 +20,6 @@ import (
"bytes" "bytes"
"fmt" "fmt"
"io" "io"
"math/big"
"time" "time"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
@ -29,6 +28,7 @@ import (
"github.com/ethereum/go-ethereum/metrics" "github.com/ethereum/go-ethereum/metrics"
"github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/rlp"
"github.com/ethereum/go-ethereum/trie/trienode" "github.com/ethereum/go-ethereum/trie/trienode"
"github.com/holiman/uint256"
) )
type Code []byte type Code []byte
@ -405,7 +405,7 @@ func (s *stateObject) commit() (*trienode.NodeSet, error) {
// AddBalance adds amount to s's balance. // AddBalance adds amount to s's balance.
// It is used to add funds to the destination account of a transfer. // It is used to add funds to the destination account of a transfer.
func (s *stateObject) AddBalance(amount *big.Int) { func (s *stateObject) AddBalance(amount *uint256.Int) {
// EIP161: We must check emptiness for the objects such that the account // EIP161: We must check emptiness for the objects such that the account
// clearing (0,0,0 objects) can take effect. // clearing (0,0,0 objects) can take effect.
if amount.Sign() == 0 { if amount.Sign() == 0 {
@ -414,27 +414,27 @@ func (s *stateObject) AddBalance(amount *big.Int) {
} }
return return
} }
s.SetBalance(new(big.Int).Add(s.Balance(), amount)) s.SetBalance(new(uint256.Int).Add(s.Balance(), amount))
} }
// SubBalance removes amount from s's balance. // SubBalance removes amount from s's balance.
// It is used to remove funds from the origin account of a transfer. // It is used to remove funds from the origin account of a transfer.
func (s *stateObject) SubBalance(amount *big.Int) { func (s *stateObject) SubBalance(amount *uint256.Int) {
if amount.Sign() == 0 { if amount.Sign() == 0 {
return return
} }
s.SetBalance(new(big.Int).Sub(s.Balance(), amount)) s.SetBalance(new(uint256.Int).Sub(s.Balance(), amount))
} }
func (s *stateObject) SetBalance(amount *big.Int) { func (s *stateObject) SetBalance(amount *uint256.Int) {
s.db.journal.append(balanceChange{ s.db.journal.append(balanceChange{
account: &s.address, account: &s.address,
prev: new(big.Int).Set(s.data.Balance), prev: new(uint256.Int).Set(s.data.Balance),
}) })
s.setBalance(amount) s.setBalance(amount)
} }
func (s *stateObject) setBalance(amount *big.Int) { func (s *stateObject) setBalance(amount *uint256.Int) {
s.data.Balance = amount s.data.Balance = amount
} }
@ -533,7 +533,7 @@ func (s *stateObject) CodeHash() []byte {
return s.data.CodeHash return s.data.CodeHash
} }
func (s *stateObject) Balance() *big.Int { func (s *stateObject) Balance() *uint256.Int {
return s.data.Balance return s.data.Balance
} }

View File

@ -19,7 +19,6 @@ package state
import ( import (
"bytes" "bytes"
"encoding/json" "encoding/json"
"math/big"
"testing" "testing"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
@ -28,6 +27,7 @@ import (
"github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/trie" "github.com/ethereum/go-ethereum/trie"
"github.com/holiman/uint256"
) )
type stateEnv struct { type stateEnv struct {
@ -48,12 +48,12 @@ func TestDump(t *testing.T) {
s := &stateEnv{db: db, state: sdb} s := &stateEnv{db: db, state: sdb}
// generate a few entries // generate a few entries
obj1 := s.state.GetOrNewStateObject(common.BytesToAddress([]byte{0x01})) obj1 := s.state.getOrNewStateObject(common.BytesToAddress([]byte{0x01}))
obj1.AddBalance(big.NewInt(22)) obj1.AddBalance(uint256.NewInt(22))
obj2 := s.state.GetOrNewStateObject(common.BytesToAddress([]byte{0x01, 0x02})) obj2 := s.state.getOrNewStateObject(common.BytesToAddress([]byte{0x01, 0x02}))
obj2.SetCode(crypto.Keccak256Hash([]byte{3, 3, 3, 3, 3, 3, 3}), []byte{3, 3, 3, 3, 3, 3, 3}) obj2.SetCode(crypto.Keccak256Hash([]byte{3, 3, 3, 3, 3, 3, 3}), []byte{3, 3, 3, 3, 3, 3, 3})
obj3 := s.state.GetOrNewStateObject(common.BytesToAddress([]byte{0x02})) obj3 := s.state.getOrNewStateObject(common.BytesToAddress([]byte{0x02}))
obj3.SetBalance(big.NewInt(44)) obj3.SetBalance(uint256.NewInt(44))
// write some of them to the trie // write some of them to the trie
s.state.updateStateObject(obj1) s.state.updateStateObject(obj1)
@ -105,14 +105,14 @@ func TestIterativeDump(t *testing.T) {
s := &stateEnv{db: db, state: sdb} s := &stateEnv{db: db, state: sdb}
// generate a few entries // generate a few entries
obj1 := s.state.GetOrNewStateObject(common.BytesToAddress([]byte{0x01})) obj1 := s.state.getOrNewStateObject(common.BytesToAddress([]byte{0x01}))
obj1.AddBalance(big.NewInt(22)) obj1.AddBalance(uint256.NewInt(22))
obj2 := s.state.GetOrNewStateObject(common.BytesToAddress([]byte{0x01, 0x02})) obj2 := s.state.getOrNewStateObject(common.BytesToAddress([]byte{0x01, 0x02}))
obj2.SetCode(crypto.Keccak256Hash([]byte{3, 3, 3, 3, 3, 3, 3}), []byte{3, 3, 3, 3, 3, 3, 3}) obj2.SetCode(crypto.Keccak256Hash([]byte{3, 3, 3, 3, 3, 3, 3}), []byte{3, 3, 3, 3, 3, 3, 3})
obj3 := s.state.GetOrNewStateObject(common.BytesToAddress([]byte{0x02})) obj3 := s.state.getOrNewStateObject(common.BytesToAddress([]byte{0x02}))
obj3.SetBalance(big.NewInt(44)) obj3.SetBalance(uint256.NewInt(44))
obj4 := s.state.GetOrNewStateObject(common.BytesToAddress([]byte{0x00})) obj4 := s.state.getOrNewStateObject(common.BytesToAddress([]byte{0x00}))
obj4.AddBalance(big.NewInt(1337)) obj4.AddBalance(uint256.NewInt(1337))
// write some of them to the trie // write some of them to the trie
s.state.updateStateObject(obj1) s.state.updateStateObject(obj1)
@ -208,7 +208,7 @@ func TestSnapshot2(t *testing.T) {
// db, trie are already non-empty values // db, trie are already non-empty values
so0 := state.getStateObject(stateobjaddr0) so0 := state.getStateObject(stateobjaddr0)
so0.SetBalance(big.NewInt(42)) so0.SetBalance(uint256.NewInt(42))
so0.SetNonce(43) so0.SetNonce(43)
so0.SetCode(crypto.Keccak256Hash([]byte{'c', 'a', 'f', 'e'}), []byte{'c', 'a', 'f', 'e'}) so0.SetCode(crypto.Keccak256Hash([]byte{'c', 'a', 'f', 'e'}), []byte{'c', 'a', 'f', 'e'})
so0.selfDestructed = false so0.selfDestructed = false
@ -220,7 +220,7 @@ func TestSnapshot2(t *testing.T) {
// and one with deleted == true // and one with deleted == true
so1 := state.getStateObject(stateobjaddr1) so1 := state.getStateObject(stateobjaddr1)
so1.SetBalance(big.NewInt(52)) so1.SetBalance(uint256.NewInt(52))
so1.SetNonce(53) so1.SetNonce(53)
so1.SetCode(crypto.Keccak256Hash([]byte{'c', 'a', 'f', 'e', '2'}), []byte{'c', 'a', 'f', 'e', '2'}) so1.SetCode(crypto.Keccak256Hash([]byte{'c', 'a', 'f', 'e', '2'}), []byte{'c', 'a', 'f', 'e', '2'})
so1.selfDestructed = true so1.selfDestructed = true

View File

@ -19,7 +19,6 @@ package state
import ( import (
"fmt" "fmt"
"math/big"
"sort" "sort"
"time" "time"
@ -34,6 +33,7 @@ import (
"github.com/ethereum/go-ethereum/trie" "github.com/ethereum/go-ethereum/trie"
"github.com/ethereum/go-ethereum/trie/trienode" "github.com/ethereum/go-ethereum/trie/trienode"
"github.com/ethereum/go-ethereum/trie/triestate" "github.com/ethereum/go-ethereum/trie/triestate"
"github.com/holiman/uint256"
) )
const ( const (
@ -289,12 +289,12 @@ func (s *StateDB) Empty(addr common.Address) bool {
} }
// GetBalance retrieves the balance from the given address or 0 if object not found // GetBalance retrieves the balance from the given address or 0 if object not found
func (s *StateDB) GetBalance(addr common.Address) *big.Int { func (s *StateDB) GetBalance(addr common.Address) *uint256.Int {
stateObject := s.getStateObject(addr) stateObject := s.getStateObject(addr)
if stateObject != nil { if stateObject != nil {
return stateObject.Balance() return stateObject.Balance()
} }
return common.Big0 return common.U2560
} }
// GetNonce retrieves the nonce from the given address or 0 if object not found // GetNonce retrieves the nonce from the given address or 0 if object not found
@ -382,44 +382,44 @@ func (s *StateDB) HasSelfDestructed(addr common.Address) bool {
*/ */
// AddBalance adds amount to the account associated with addr. // AddBalance adds amount to the account associated with addr.
func (s *StateDB) AddBalance(addr common.Address, amount *big.Int) { func (s *StateDB) AddBalance(addr common.Address, amount *uint256.Int) {
stateObject := s.GetOrNewStateObject(addr) stateObject := s.getOrNewStateObject(addr)
if stateObject != nil { if stateObject != nil {
stateObject.AddBalance(amount) stateObject.AddBalance(amount)
} }
} }
// SubBalance subtracts amount from the account associated with addr. // SubBalance subtracts amount from the account associated with addr.
func (s *StateDB) SubBalance(addr common.Address, amount *big.Int) { func (s *StateDB) SubBalance(addr common.Address, amount *uint256.Int) {
stateObject := s.GetOrNewStateObject(addr) stateObject := s.getOrNewStateObject(addr)
if stateObject != nil { if stateObject != nil {
stateObject.SubBalance(amount) stateObject.SubBalance(amount)
} }
} }
func (s *StateDB) SetBalance(addr common.Address, amount *big.Int) { func (s *StateDB) SetBalance(addr common.Address, amount *uint256.Int) {
stateObject := s.GetOrNewStateObject(addr) stateObject := s.getOrNewStateObject(addr)
if stateObject != nil { if stateObject != nil {
stateObject.SetBalance(amount) stateObject.SetBalance(amount)
} }
} }
func (s *StateDB) SetNonce(addr common.Address, nonce uint64) { func (s *StateDB) SetNonce(addr common.Address, nonce uint64) {
stateObject := s.GetOrNewStateObject(addr) stateObject := s.getOrNewStateObject(addr)
if stateObject != nil { if stateObject != nil {
stateObject.SetNonce(nonce) stateObject.SetNonce(nonce)
} }
} }
func (s *StateDB) SetCode(addr common.Address, code []byte) { func (s *StateDB) SetCode(addr common.Address, code []byte) {
stateObject := s.GetOrNewStateObject(addr) stateObject := s.getOrNewStateObject(addr)
if stateObject != nil { if stateObject != nil {
stateObject.SetCode(crypto.Keccak256Hash(code), code) stateObject.SetCode(crypto.Keccak256Hash(code), code)
} }
} }
func (s *StateDB) SetState(addr common.Address, key, value common.Hash) { func (s *StateDB) SetState(addr common.Address, key, value common.Hash) {
stateObject := s.GetOrNewStateObject(addr) stateObject := s.getOrNewStateObject(addr)
if stateObject != nil { if stateObject != nil {
stateObject.SetState(key, value) stateObject.SetState(key, value)
} }
@ -440,7 +440,7 @@ func (s *StateDB) SetStorage(addr common.Address, storage map[common.Hash]common
if _, ok := s.stateObjectsDestruct[addr]; !ok { if _, ok := s.stateObjectsDestruct[addr]; !ok {
s.stateObjectsDestruct[addr] = nil s.stateObjectsDestruct[addr] = nil
} }
stateObject := s.GetOrNewStateObject(addr) stateObject := s.getOrNewStateObject(addr)
for k, v := range storage { for k, v := range storage {
stateObject.SetState(k, v) stateObject.SetState(k, v)
} }
@ -459,10 +459,10 @@ func (s *StateDB) SelfDestruct(addr common.Address) {
s.journal.append(selfDestructChange{ s.journal.append(selfDestructChange{
account: &addr, account: &addr,
prev: stateObject.selfDestructed, prev: stateObject.selfDestructed,
prevbalance: new(big.Int).Set(stateObject.Balance()), prevbalance: new(uint256.Int).Set(stateObject.Balance()),
}) })
stateObject.markSelfdestructed() stateObject.markSelfdestructed()
stateObject.data.Balance = new(big.Int) stateObject.data.Balance = new(uint256.Int)
} }
func (s *StateDB) Selfdestruct6780(addr common.Address) { func (s *StateDB) Selfdestruct6780(addr common.Address) {
@ -623,8 +623,8 @@ func (s *StateDB) setStateObject(object *stateObject) {
s.stateObjects[object.Address()] = object s.stateObjects[object.Address()] = object
} }
// GetOrNewStateObject retrieves a state object or create a new state object if nil. // getOrNewStateObject retrieves a state object or create a new state object if nil.
func (s *StateDB) GetOrNewStateObject(addr common.Address) *stateObject { func (s *StateDB) getOrNewStateObject(addr common.Address) *stateObject {
stateObject := s.getStateObject(addr) stateObject := s.getStateObject(addr)
if stateObject == nil { if stateObject == nil {
stateObject, _ = s.createObject(addr) stateObject, _ = s.createObject(addr)

View File

@ -22,7 +22,6 @@ import (
"errors" "errors"
"fmt" "fmt"
"math" "math"
"math/big"
"math/rand" "math/rand"
"reflect" "reflect"
"strings" "strings"
@ -38,6 +37,7 @@ import (
"github.com/ethereum/go-ethereum/trie" "github.com/ethereum/go-ethereum/trie"
"github.com/ethereum/go-ethereum/trie/triedb/pathdb" "github.com/ethereum/go-ethereum/trie/triedb/pathdb"
"github.com/ethereum/go-ethereum/trie/triestate" "github.com/ethereum/go-ethereum/trie/triestate"
"github.com/holiman/uint256"
) )
// A stateTest checks that the state changes are correctly captured. Instances // A stateTest checks that the state changes are correctly captured. Instances
@ -60,7 +60,7 @@ func newStateTestAction(addr common.Address, r *rand.Rand, index int) testAction
{ {
name: "SetBalance", name: "SetBalance",
fn: func(a testAction, s *StateDB) { fn: func(a testAction, s *StateDB) {
s.SetBalance(addr, big.NewInt(a.args[0])) s.SetBalance(addr, uint256.NewInt(uint64(a.args[0])))
}, },
args: make([]int64, 1), args: make([]int64, 1),
}, },

View File

@ -22,7 +22,6 @@ import (
"errors" "errors"
"fmt" "fmt"
"math" "math"
"math/big"
"math/rand" "math/rand"
"reflect" "reflect"
"strings" "strings"
@ -56,7 +55,7 @@ func TestUpdateLeaks(t *testing.T) {
// Update it with some accounts // Update it with some accounts
for i := byte(0); i < 255; i++ { for i := byte(0); i < 255; i++ {
addr := common.BytesToAddress([]byte{i}) addr := common.BytesToAddress([]byte{i})
state.AddBalance(addr, big.NewInt(int64(11*i))) state.AddBalance(addr, uint256.NewInt(uint64(11*i)))
state.SetNonce(addr, uint64(42*i)) state.SetNonce(addr, uint64(42*i))
if i%2 == 0 { if i%2 == 0 {
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}))
@ -91,7 +90,7 @@ func TestIntermediateLeaks(t *testing.T) {
finalState, _ := New(types.EmptyRootHash, NewDatabaseWithNodeDB(finalDb, finalNdb), nil) finalState, _ := New(types.EmptyRootHash, NewDatabaseWithNodeDB(finalDb, finalNdb), nil)
modify := func(state *StateDB, addr common.Address, i, tweak byte) { modify := func(state *StateDB, addr common.Address, i, tweak byte) {
state.SetBalance(addr, big.NewInt(int64(11*i)+int64(tweak))) state.SetBalance(addr, uint256.NewInt(uint64(11*i)+uint64(tweak)))
state.SetNonce(addr, uint64(42*i+tweak)) state.SetNonce(addr, uint64(42*i+tweak))
if i%2 == 0 { if i%2 == 0 {
state.SetState(addr, common.Hash{i, i, i, 0}, common.Hash{}) state.SetState(addr, common.Hash{i, i, i, 0}, common.Hash{})
@ -166,8 +165,8 @@ func TestCopy(t *testing.T) {
orig, _ := New(types.EmptyRootHash, NewDatabase(rawdb.NewMemoryDatabase()), nil) orig, _ := New(types.EmptyRootHash, NewDatabase(rawdb.NewMemoryDatabase()), nil)
for i := byte(0); i < 255; i++ { for i := byte(0); i < 255; i++ {
obj := orig.GetOrNewStateObject(common.BytesToAddress([]byte{i})) obj := orig.getOrNewStateObject(common.BytesToAddress([]byte{i}))
obj.AddBalance(big.NewInt(int64(i))) obj.AddBalance(uint256.NewInt(uint64(i)))
orig.updateStateObject(obj) orig.updateStateObject(obj)
} }
orig.Finalise(false) orig.Finalise(false)
@ -180,13 +179,13 @@ func TestCopy(t *testing.T) {
// modify all in memory // modify all in memory
for i := byte(0); i < 255; i++ { for i := byte(0); i < 255; i++ {
origObj := orig.GetOrNewStateObject(common.BytesToAddress([]byte{i})) origObj := orig.getOrNewStateObject(common.BytesToAddress([]byte{i}))
copyObj := copy.GetOrNewStateObject(common.BytesToAddress([]byte{i})) copyObj := copy.getOrNewStateObject(common.BytesToAddress([]byte{i}))
ccopyObj := ccopy.GetOrNewStateObject(common.BytesToAddress([]byte{i})) ccopyObj := ccopy.getOrNewStateObject(common.BytesToAddress([]byte{i}))
origObj.AddBalance(big.NewInt(2 * int64(i))) origObj.AddBalance(uint256.NewInt(2 * uint64(i)))
copyObj.AddBalance(big.NewInt(3 * int64(i))) copyObj.AddBalance(uint256.NewInt(3 * uint64(i)))
ccopyObj.AddBalance(big.NewInt(4 * int64(i))) ccopyObj.AddBalance(uint256.NewInt(4 * uint64(i)))
orig.updateStateObject(origObj) orig.updateStateObject(origObj)
copy.updateStateObject(copyObj) copy.updateStateObject(copyObj)
@ -208,17 +207,17 @@ func TestCopy(t *testing.T) {
// Verify that the three states have been updated independently // Verify that the three states have been updated independently
for i := byte(0); i < 255; i++ { for i := byte(0); i < 255; i++ {
origObj := orig.GetOrNewStateObject(common.BytesToAddress([]byte{i})) origObj := orig.getOrNewStateObject(common.BytesToAddress([]byte{i}))
copyObj := copy.GetOrNewStateObject(common.BytesToAddress([]byte{i})) copyObj := copy.getOrNewStateObject(common.BytesToAddress([]byte{i}))
ccopyObj := ccopy.GetOrNewStateObject(common.BytesToAddress([]byte{i})) ccopyObj := ccopy.getOrNewStateObject(common.BytesToAddress([]byte{i}))
if want := big.NewInt(3 * int64(i)); origObj.Balance().Cmp(want) != 0 { if want := uint256.NewInt(3 * uint64(i)); origObj.Balance().Cmp(want) != 0 {
t.Errorf("orig obj %d: balance mismatch: have %v, want %v", i, origObj.Balance(), want) t.Errorf("orig obj %d: balance mismatch: have %v, want %v", i, origObj.Balance(), want)
} }
if want := big.NewInt(4 * int64(i)); copyObj.Balance().Cmp(want) != 0 { if want := uint256.NewInt(4 * uint64(i)); copyObj.Balance().Cmp(want) != 0 {
t.Errorf("copy obj %d: balance mismatch: have %v, want %v", i, copyObj.Balance(), want) t.Errorf("copy obj %d: balance mismatch: have %v, want %v", i, copyObj.Balance(), want)
} }
if want := big.NewInt(5 * int64(i)); ccopyObj.Balance().Cmp(want) != 0 { if want := uint256.NewInt(5 * uint64(i)); ccopyObj.Balance().Cmp(want) != 0 {
t.Errorf("copy obj %d: balance mismatch: have %v, want %v", i, ccopyObj.Balance(), want) t.Errorf("copy obj %d: balance mismatch: have %v, want %v", i, ccopyObj.Balance(), want)
} }
} }
@ -266,14 +265,14 @@ func newTestAction(addr common.Address, r *rand.Rand) testAction {
{ {
name: "SetBalance", name: "SetBalance",
fn: func(a testAction, s *StateDB) { fn: func(a testAction, s *StateDB) {
s.SetBalance(addr, big.NewInt(a.args[0])) s.SetBalance(addr, uint256.NewInt(uint64(a.args[0])))
}, },
args: make([]int64, 1), args: make([]int64, 1),
}, },
{ {
name: "AddBalance", name: "AddBalance",
fn: func(a testAction, s *StateDB) { fn: func(a testAction, s *StateDB) {
s.AddBalance(addr, big.NewInt(a.args[0])) s.AddBalance(addr, uint256.NewInt(uint64(a.args[0])))
}, },
args: make([]int64, 1), args: make([]int64, 1),
}, },
@ -531,12 +530,12 @@ func (test *snapshotTest) checkEqual(state, checkstate *StateDB) error {
func TestTouchDelete(t *testing.T) { func TestTouchDelete(t *testing.T) {
s := newStateEnv() s := newStateEnv()
s.state.GetOrNewStateObject(common.Address{}) s.state.getOrNewStateObject(common.Address{})
root, _ := s.state.Commit(0, false) root, _ := s.state.Commit(0, false)
s.state, _ = New(root, s.state.db, s.state.snaps) s.state, _ = New(root, s.state.db, s.state.snaps)
snapshot := s.state.Snapshot() snapshot := s.state.Snapshot()
s.state.AddBalance(common.Address{}, new(big.Int)) s.state.AddBalance(common.Address{}, new(uint256.Int))
if len(s.state.journal.dirties) != 1 { if len(s.state.journal.dirties) != 1 {
t.Fatal("expected one dirty state object") t.Fatal("expected one dirty state object")
@ -552,7 +551,7 @@ func TestTouchDelete(t *testing.T) {
func TestCopyOfCopy(t *testing.T) { func TestCopyOfCopy(t *testing.T) {
state, _ := New(types.EmptyRootHash, NewDatabase(rawdb.NewMemoryDatabase()), nil) state, _ := New(types.EmptyRootHash, NewDatabase(rawdb.NewMemoryDatabase()), nil)
addr := common.HexToAddress("aaaa") addr := common.HexToAddress("aaaa")
state.SetBalance(addr, big.NewInt(42)) state.SetBalance(addr, uint256.NewInt(42))
if got := state.Copy().GetBalance(addr).Uint64(); got != 42 { if got := state.Copy().GetBalance(addr).Uint64(); got != 42 {
t.Fatalf("1st copy fail, expected 42, got %v", got) t.Fatalf("1st copy fail, expected 42, got %v", got)
@ -575,11 +574,11 @@ func TestCopyCommitCopy(t *testing.T) {
skey := common.HexToHash("aaa") skey := common.HexToHash("aaa")
sval := common.HexToHash("bbb") sval := common.HexToHash("bbb")
state.SetBalance(addr, big.NewInt(42)) // Change the account trie state.SetBalance(addr, uint256.NewInt(42)) // Change the account trie
state.SetCode(addr, []byte("hello")) // Change an external metadata state.SetCode(addr, []byte("hello")) // 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(big.NewInt(42)) != 0 { if balance := state.GetBalance(addr); balance.Cmp(uint256.NewInt(42)) != 0 {
t.Fatalf("initial balance mismatch: have %v, want %v", balance, 42) t.Fatalf("initial balance mismatch: have %v, want %v", balance, 42)
} }
if code := state.GetCode(addr); !bytes.Equal(code, []byte("hello")) { if code := state.GetCode(addr); !bytes.Equal(code, []byte("hello")) {
@ -593,7 +592,7 @@ func TestCopyCommitCopy(t *testing.T) {
} }
// Copy the non-committed state database and check pre/post commit balance // Copy the non-committed state database and check pre/post commit balance
copyOne := state.Copy() copyOne := state.Copy()
if balance := copyOne.GetBalance(addr); balance.Cmp(big.NewInt(42)) != 0 { if balance := copyOne.GetBalance(addr); balance.Cmp(uint256.NewInt(42)) != 0 {
t.Fatalf("first copy pre-commit balance mismatch: have %v, want %v", balance, 42) t.Fatalf("first copy pre-commit balance mismatch: have %v, want %v", balance, 42)
} }
if code := copyOne.GetCode(addr); !bytes.Equal(code, []byte("hello")) { if code := copyOne.GetCode(addr); !bytes.Equal(code, []byte("hello")) {
@ -607,7 +606,7 @@ func TestCopyCommitCopy(t *testing.T) {
} }
// Copy the copy and check the balance once more // Copy the copy and check the balance once more
copyTwo := copyOne.Copy() copyTwo := copyOne.Copy()
if balance := copyTwo.GetBalance(addr); balance.Cmp(big.NewInt(42)) != 0 { if balance := copyTwo.GetBalance(addr); balance.Cmp(uint256.NewInt(42)) != 0 {
t.Fatalf("second copy balance mismatch: have %v, want %v", balance, 42) t.Fatalf("second copy balance mismatch: have %v, want %v", balance, 42)
} }
if code := copyTwo.GetCode(addr); !bytes.Equal(code, []byte("hello")) { if code := copyTwo.GetCode(addr); !bytes.Equal(code, []byte("hello")) {
@ -622,7 +621,7 @@ func TestCopyCommitCopy(t *testing.T) {
// Commit state, ensure states can be loaded from disk // Commit state, ensure states can be loaded from disk
root, _ := state.Commit(0, false) root, _ := state.Commit(0, false)
state, _ = New(root, tdb, nil) state, _ = New(root, tdb, nil)
if balance := state.GetBalance(addr); balance.Cmp(big.NewInt(42)) != 0 { if balance := state.GetBalance(addr); balance.Cmp(uint256.NewInt(42)) != 0 {
t.Fatalf("state post-commit balance mismatch: have %v, want %v", balance, 42) t.Fatalf("state post-commit balance mismatch: have %v, want %v", balance, 42)
} }
if code := state.GetCode(addr); !bytes.Equal(code, []byte("hello")) { if code := state.GetCode(addr); !bytes.Equal(code, []byte("hello")) {
@ -648,11 +647,11 @@ func TestCopyCopyCommitCopy(t *testing.T) {
skey := common.HexToHash("aaa") skey := common.HexToHash("aaa")
sval := common.HexToHash("bbb") sval := common.HexToHash("bbb")
state.SetBalance(addr, big.NewInt(42)) // Change the account trie state.SetBalance(addr, uint256.NewInt(42)) // Change the account trie
state.SetCode(addr, []byte("hello")) // Change an external metadata state.SetCode(addr, []byte("hello")) // 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(big.NewInt(42)) != 0 { if balance := state.GetBalance(addr); balance.Cmp(uint256.NewInt(42)) != 0 {
t.Fatalf("initial balance mismatch: have %v, want %v", balance, 42) t.Fatalf("initial balance mismatch: have %v, want %v", balance, 42)
} }
if code := state.GetCode(addr); !bytes.Equal(code, []byte("hello")) { if code := state.GetCode(addr); !bytes.Equal(code, []byte("hello")) {
@ -666,7 +665,7 @@ func TestCopyCopyCommitCopy(t *testing.T) {
} }
// Copy the non-committed state database and check pre/post commit balance // Copy the non-committed state database and check pre/post commit balance
copyOne := state.Copy() copyOne := state.Copy()
if balance := copyOne.GetBalance(addr); balance.Cmp(big.NewInt(42)) != 0 { if balance := copyOne.GetBalance(addr); balance.Cmp(uint256.NewInt(42)) != 0 {
t.Fatalf("first copy balance mismatch: have %v, want %v", balance, 42) t.Fatalf("first copy balance mismatch: have %v, want %v", balance, 42)
} }
if code := copyOne.GetCode(addr); !bytes.Equal(code, []byte("hello")) { if code := copyOne.GetCode(addr); !bytes.Equal(code, []byte("hello")) {
@ -680,7 +679,7 @@ func TestCopyCopyCommitCopy(t *testing.T) {
} }
// Copy the copy and check the balance once more // Copy the copy and check the balance once more
copyTwo := copyOne.Copy() copyTwo := copyOne.Copy()
if balance := copyTwo.GetBalance(addr); balance.Cmp(big.NewInt(42)) != 0 { if balance := copyTwo.GetBalance(addr); balance.Cmp(uint256.NewInt(42)) != 0 {
t.Fatalf("second copy pre-commit balance mismatch: have %v, want %v", balance, 42) t.Fatalf("second copy pre-commit balance mismatch: have %v, want %v", balance, 42)
} }
if code := copyTwo.GetCode(addr); !bytes.Equal(code, []byte("hello")) { if code := copyTwo.GetCode(addr); !bytes.Equal(code, []byte("hello")) {
@ -694,7 +693,7 @@ func TestCopyCopyCommitCopy(t *testing.T) {
} }
// Copy the copy-copy and check the balance once more // Copy the copy-copy and check the balance once more
copyThree := copyTwo.Copy() copyThree := copyTwo.Copy()
if balance := copyThree.GetBalance(addr); balance.Cmp(big.NewInt(42)) != 0 { if balance := copyThree.GetBalance(addr); balance.Cmp(uint256.NewInt(42)) != 0 {
t.Fatalf("third copy balance mismatch: have %v, want %v", balance, 42) t.Fatalf("third copy balance mismatch: have %v, want %v", balance, 42)
} }
if code := copyThree.GetCode(addr); !bytes.Equal(code, []byte("hello")) { if code := copyThree.GetCode(addr); !bytes.Equal(code, []byte("hello")) {
@ -717,11 +716,11 @@ func TestCommitCopy(t *testing.T) {
skey := common.HexToHash("aaa") skey := common.HexToHash("aaa")
sval := common.HexToHash("bbb") sval := common.HexToHash("bbb")
state.SetBalance(addr, big.NewInt(42)) // Change the account trie state.SetBalance(addr, uint256.NewInt(42)) // Change the account trie
state.SetCode(addr, []byte("hello")) // Change an external metadata state.SetCode(addr, []byte("hello")) // 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(big.NewInt(42)) != 0 { if balance := state.GetBalance(addr); balance.Cmp(uint256.NewInt(42)) != 0 {
t.Fatalf("initial balance mismatch: have %v, want %v", balance, 42) t.Fatalf("initial balance mismatch: have %v, want %v", balance, 42)
} }
if code := state.GetCode(addr); !bytes.Equal(code, []byte("hello")) { if code := state.GetCode(addr); !bytes.Equal(code, []byte("hello")) {
@ -736,7 +735,7 @@ func TestCommitCopy(t *testing.T) {
// Copy the committed state database, the copied one is not functional. // Copy the committed state database, the copied one is not functional.
state.Commit(0, true) state.Commit(0, true)
copied := state.Copy() copied := state.Copy()
if balance := copied.GetBalance(addr); balance.Cmp(big.NewInt(0)) != 0 { if balance := copied.GetBalance(addr); balance.Cmp(uint256.NewInt(0)) != 0 {
t.Fatalf("unexpected balance: have %v", balance) t.Fatalf("unexpected balance: have %v", balance)
} }
if code := copied.GetCode(addr); code != nil { if code := copied.GetCode(addr); code != nil {
@ -766,7 +765,7 @@ func TestDeleteCreateRevert(t *testing.T) {
state, _ := New(types.EmptyRootHash, NewDatabase(rawdb.NewMemoryDatabase()), nil) state, _ := New(types.EmptyRootHash, NewDatabase(rawdb.NewMemoryDatabase()), nil)
addr := common.BytesToAddress([]byte("so")) addr := common.BytesToAddress([]byte("so"))
state.SetBalance(addr, big.NewInt(1)) state.SetBalance(addr, uint256.NewInt(1))
root, _ := state.Commit(0, false) root, _ := state.Commit(0, false)
state, _ = New(root, state.db, state.snaps) state, _ = New(root, state.db, state.snaps)
@ -776,7 +775,7 @@ func TestDeleteCreateRevert(t *testing.T) {
state.Finalise(true) state.Finalise(true)
id := state.Snapshot() id := state.Snapshot()
state.SetBalance(addr, big.NewInt(2)) state.SetBalance(addr, uint256.NewInt(2))
state.RevertToSnapshot(id) state.RevertToSnapshot(id)
// Commit the entire state and make sure we don't crash and have the correct state // Commit the entire state and make sure we don't crash and have the correct state
@ -818,10 +817,10 @@ func testMissingTrieNodes(t *testing.T, scheme string) {
state, _ := New(types.EmptyRootHash, db, nil) state, _ := New(types.EmptyRootHash, db, nil)
addr := common.BytesToAddress([]byte("so")) addr := common.BytesToAddress([]byte("so"))
{ {
state.SetBalance(addr, big.NewInt(1)) state.SetBalance(addr, uint256.NewInt(1))
state.SetCode(addr, []byte{1, 2, 3}) state.SetCode(addr, []byte{1, 2, 3})
a2 := common.BytesToAddress([]byte("another")) a2 := common.BytesToAddress([]byte("another"))
state.SetBalance(a2, big.NewInt(100)) state.SetBalance(a2, uint256.NewInt(100))
state.SetCode(a2, []byte{1, 2, 4}) state.SetCode(a2, []byte{1, 2, 4})
root, _ = state.Commit(0, false) root, _ = state.Commit(0, false)
t.Logf("root: %x", root) t.Logf("root: %x", root)
@ -846,7 +845,7 @@ func testMissingTrieNodes(t *testing.T, scheme string) {
t.Errorf("expected %d, got %d", exp, got) t.Errorf("expected %d, got %d", exp, got)
} }
// Modify the state // Modify the state
state.SetBalance(addr, big.NewInt(2)) state.SetBalance(addr, uint256.NewInt(2))
root, err := state.Commit(0, false) root, err := state.Commit(0, false)
if err == nil { if err == nil {
t.Fatalf("expected error, got root :%x", root) t.Fatalf("expected error, got root :%x", root)
@ -1114,13 +1113,13 @@ func TestResetObject(t *testing.T) {
slotB = common.HexToHash("0x2") slotB = common.HexToHash("0x2")
) )
// Initialize account with balance and storage in first transaction. // Initialize account with balance and storage in first transaction.
state.SetBalance(addr, big.NewInt(1)) state.SetBalance(addr, uint256.NewInt(1))
state.SetState(addr, slotA, common.BytesToHash([]byte{0x1})) state.SetState(addr, slotA, common.BytesToHash([]byte{0x1}))
state.IntermediateRoot(true) state.IntermediateRoot(true)
// Reset account and mutate balance and storages // Reset account and mutate balance and storages
state.CreateAccount(addr) state.CreateAccount(addr)
state.SetBalance(addr, big.NewInt(2)) state.SetBalance(addr, uint256.NewInt(2))
state.SetState(addr, slotB, common.BytesToHash([]byte{0x2})) state.SetState(addr, slotB, common.BytesToHash([]byte{0x2}))
root, _ := state.Commit(0, true) root, _ := state.Commit(0, true)
@ -1146,7 +1145,7 @@ func TestDeleteStorage(t *testing.T) {
addr = common.HexToAddress("0x1") addr = common.HexToAddress("0x1")
) )
// Initialize account and populate storage // Initialize account and populate storage
state.SetBalance(addr, big.NewInt(1)) state.SetBalance(addr, uint256.NewInt(1))
state.CreateAccount(addr) state.CreateAccount(addr)
for i := 0; i < 1000; i++ { for i := 0; i < 1000; i++ {
slot := common.Hash(uint256.NewInt(uint64(i)).Bytes32()) slot := common.Hash(uint256.NewInt(uint64(i)).Bytes32())
@ -1158,7 +1157,7 @@ func TestDeleteStorage(t *testing.T) {
fastState, _ := New(root, db, snaps) fastState, _ := New(root, db, snaps)
slowState, _ := New(root, db, nil) slowState, _ := New(root, db, nil)
obj := fastState.GetOrNewStateObject(addr) obj := fastState.getOrNewStateObject(addr)
storageRoot := obj.data.Root storageRoot := obj.data.Root
_, _, fastNodes, err := fastState.deleteStorage(addr, crypto.Keccak256Hash(addr[:]), storageRoot) _, _, fastNodes, err := fastState.deleteStorage(addr, crypto.Keccak256Hash(addr[:]), storageRoot)

View File

@ -18,7 +18,6 @@ package state
import ( import (
"bytes" "bytes"
"math/big"
"testing" "testing"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
@ -30,12 +29,13 @@ import (
"github.com/ethereum/go-ethereum/trie" "github.com/ethereum/go-ethereum/trie"
"github.com/ethereum/go-ethereum/trie/triedb/hashdb" "github.com/ethereum/go-ethereum/trie/triedb/hashdb"
"github.com/ethereum/go-ethereum/trie/triedb/pathdb" "github.com/ethereum/go-ethereum/trie/triedb/pathdb"
"github.com/holiman/uint256"
) )
// testAccount is the data associated with an account used by the state tests. // testAccount is the data associated with an account used by the state tests.
type testAccount struct { type testAccount struct {
address common.Address address common.Address
balance *big.Int balance *uint256.Int
nonce uint64 nonce uint64
code []byte code []byte
} }
@ -57,11 +57,11 @@ func makeTestState(scheme string) (ethdb.Database, Database, *trie.Database, com
// Fill it with some arbitrary data // Fill it with some arbitrary data
var accounts []*testAccount var accounts []*testAccount
for i := byte(0); i < 96; i++ { for i := byte(0); i < 96; i++ {
obj := state.GetOrNewStateObject(common.BytesToAddress([]byte{i})) obj := state.getOrNewStateObject(common.BytesToAddress([]byte{i}))
acc := &testAccount{address: common.BytesToAddress([]byte{i})} acc := &testAccount{address: common.BytesToAddress([]byte{i})}
obj.AddBalance(big.NewInt(int64(11 * i))) obj.AddBalance(uint256.NewInt(uint64(11 * i)))
acc.balance = big.NewInt(int64(11 * i)) acc.balance = uint256.NewInt(uint64(11 * i))
obj.SetNonce(uint64(42 * i)) obj.SetNonce(uint64(42 * i))
acc.nonce = uint64(42 * i) acc.nonce = uint64(42 * i)

View File

@ -24,6 +24,7 @@ import (
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/core/rawdb"
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
"github.com/holiman/uint256"
) )
func filledStateDB() *StateDB { func filledStateDB() *StateDB {
@ -34,7 +35,7 @@ func filledStateDB() *StateDB {
skey := common.HexToHash("aaa") skey := common.HexToHash("aaa")
sval := common.HexToHash("bbb") sval := common.HexToHash("bbb")
state.SetBalance(addr, big.NewInt(42)) // Change the account trie state.SetBalance(addr, uint256.NewInt(42)) // Change the account trie
state.SetCode(addr, []byte("hello")) // Change an external metadata state.SetCode(addr, []byte("hello")) // 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++ {

View File

@ -217,6 +217,6 @@ func ProcessBeaconBlockRoot(beaconRoot common.Hash, vmenv *vm.EVM, statedb *stat
} }
vmenv.Reset(NewEVMTxContext(msg), statedb) vmenv.Reset(NewEVMTxContext(msg), statedb)
statedb.AddAddressToAccessList(params.BeaconRootsStorageAddress) statedb.AddAddressToAccessList(params.BeaconRootsStorageAddress)
_, _, _ = vmenv.Call(vm.AccountRef(msg.From), *msg.To, msg.Data, 30_000_000, common.Big0) _, _, _ = vmenv.Call(vm.AccountRef(msg.From), *msg.To, msg.Data, 30_000_000, common.U2560)
statedb.Finalise(true) statedb.Finalise(true)
} }

View File

@ -232,7 +232,7 @@ func TestStateProcessorErrors(t *testing.T) {
txs: []*types.Transaction{ txs: []*types.Transaction{
mkDynamicTx(0, common.Address{}, params.TxGas, bigNumber, bigNumber), mkDynamicTx(0, common.Address{}, params.TxGas, bigNumber, bigNumber),
}, },
want: "could not apply tx 0 [0xd82a0c2519acfeac9a948258c47e784acd20651d9d80f9a1c67b4137651c3a24]: insufficient funds for gas * price + value: address 0x71562b71999873DB5b286dF957af199Ec94617F7 have 1000000000000000000 want 2431633873983640103894990685182446064918669677978451844828609264166175722438635000", want: "could not apply tx 0 [0xd82a0c2519acfeac9a948258c47e784acd20651d9d80f9a1c67b4137651c3a24]: insufficient funds for gas * price + value: address 0x71562b71999873DB5b286dF957af199Ec94617F7 required balance exceeds 256 bits",
}, },
{ // ErrMaxInitCodeSizeExceeded { // ErrMaxInitCodeSizeExceeded
txs: []*types.Transaction{ txs: []*types.Transaction{

View File

@ -17,7 +17,6 @@
package core package core
import ( import (
"errors"
"fmt" "fmt"
"math" "math"
"math/big" "math/big"
@ -26,7 +25,9 @@ import (
cmath "github.com/ethereum/go-ethereum/common/math" cmath "github.com/ethereum/go-ethereum/common/math"
"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/kzg4844"
"github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/params"
"github.com/holiman/uint256"
) )
// ExecutionResult includes all output after executing given evm // ExecutionResult includes all output after executing given evm
@ -252,7 +253,11 @@ func (st *StateTransition) buyGas() error {
mgval.Add(mgval, blobFee) mgval.Add(mgval, blobFee)
} }
} }
if have, want := st.state.GetBalance(st.msg.From), balanceCheck; have.Cmp(want) < 0 { balanceCheckU256, overflow := uint256.FromBig(balanceCheck)
if overflow {
return fmt.Errorf("%w: address %v required balance exceeds 256 bits", ErrInsufficientFunds, st.msg.From.Hex())
}
if have, want := st.state.GetBalance(st.msg.From), balanceCheckU256; have.Cmp(want) < 0 {
return fmt.Errorf("%w: address %v have %v want %v", ErrInsufficientFunds, st.msg.From.Hex(), have, want) return fmt.Errorf("%w: address %v have %v want %v", ErrInsufficientFunds, st.msg.From.Hex(), have, want)
} }
if err := st.gp.SubGas(st.msg.GasLimit); err != nil { if err := st.gp.SubGas(st.msg.GasLimit); err != nil {
@ -261,7 +266,8 @@ func (st *StateTransition) buyGas() error {
st.gasRemaining += st.msg.GasLimit st.gasRemaining += st.msg.GasLimit
st.initialGas = st.msg.GasLimit st.initialGas = st.msg.GasLimit
st.state.SubBalance(st.msg.From, mgval) mgvalU256, _ := uint256.FromBig(mgval)
st.state.SubBalance(st.msg.From, mgvalU256)
return nil return nil
} }
@ -317,13 +323,18 @@ func (st *StateTransition) preCheck() error {
} }
// Check the blob version validity // Check the blob version validity
if msg.BlobHashes != nil { if msg.BlobHashes != nil {
// The to field of a blob tx type is mandatory, and a `BlobTx` transaction internally
// has it as a non-nillable value, so any msg derived from blob transaction has it non-nil.
// However, messages created through RPC (eth_call) don't have this restriction.
if msg.To == nil {
return ErrBlobTxCreate
}
if len(msg.BlobHashes) == 0 { if len(msg.BlobHashes) == 0 {
return errors.New("blob transaction missing blob hashes") return ErrMissingBlobHashes
} }
for i, hash := range msg.BlobHashes { for i, hash := range msg.BlobHashes {
if hash[0] != params.BlobTxHashVersion { if !kzg4844.IsValidVersionedHash(hash[:]) {
return fmt.Errorf("blob %d hash version mismatch (have %d, supported %d)", return fmt.Errorf("blob %d has invalid hash version", i)
i, hash[0], params.BlobTxHashVersion)
} }
} }
} }
@ -404,7 +415,11 @@ func (st *StateTransition) TransitionDb() (*ExecutionResult, error) {
st.gasRemaining -= gas st.gasRemaining -= gas
// Check clause 6 // Check clause 6
if msg.Value.Sign() > 0 && !st.evm.Context.CanTransfer(st.state, msg.From, msg.Value) { value, overflow := uint256.FromBig(msg.Value)
if overflow {
return nil, fmt.Errorf("%w: address %v", ErrInsufficientFundsForTransfer, msg.From.Hex())
}
if !value.IsZero() && !st.evm.Context.CanTransfer(st.state, msg.From, value) {
return nil, fmt.Errorf("%w: address %v", ErrInsufficientFundsForTransfer, msg.From.Hex()) return nil, fmt.Errorf("%w: address %v", ErrInsufficientFundsForTransfer, msg.From.Hex())
} }
@ -423,11 +438,11 @@ func (st *StateTransition) TransitionDb() (*ExecutionResult, error) {
vmerr error // vm errors do not effect consensus and are therefore not assigned to err vmerr error // vm errors do not effect consensus and are therefore not assigned to err
) )
if contractCreation { if contractCreation {
ret, _, st.gasRemaining, vmerr = st.evm.Create(sender, msg.Data, st.gasRemaining, msg.Value) ret, _, st.gasRemaining, vmerr = st.evm.Create(sender, msg.Data, st.gasRemaining, value)
} else { } else {
// Increment the nonce for the next transaction // Increment the nonce for the next transaction
st.state.SetNonce(msg.From, st.state.GetNonce(sender.Address())+1) st.state.SetNonce(msg.From, st.state.GetNonce(sender.Address())+1)
ret, st.gasRemaining, vmerr = st.evm.Call(sender, st.to(), msg.Data, st.gasRemaining, msg.Value) ret, st.gasRemaining, vmerr = st.evm.Call(sender, st.to(), msg.Data, st.gasRemaining, value)
} }
var gasRefund uint64 var gasRefund uint64
@ -446,14 +461,15 @@ func (st *StateTransition) TransitionDb() (*ExecutionResult, error) {
effectiveTip = cmath.BigMin(msg.GasTipCap, new(big.Int).Sub(msg.GasFeeCap, st.evm.Context.BaseFee)) effectiveTip = cmath.BigMin(msg.GasTipCap, new(big.Int).Sub(msg.GasFeeCap, st.evm.Context.BaseFee))
} }
} }
effectiveTipU256, _ := uint256.FromBig(effectiveTip)
if st.evm.Config.NoBaseFee && msg.GasFeeCap.Sign() == 0 && msg.GasTipCap.Sign() == 0 { if st.evm.Config.NoBaseFee && msg.GasFeeCap.Sign() == 0 && msg.GasTipCap.Sign() == 0 {
// Skip fee payment when NoBaseFee is set and the fee fields // Skip fee payment when NoBaseFee is set and the fee fields
// are 0. This avoids a negative effectiveTip being applied to // are 0. This avoids a negative effectiveTip being applied to
// the coinbase when simulating calls. // the coinbase when simulating calls.
} else { } else {
fee := new(big.Int).SetUint64(st.gasUsed()) fee := new(uint256.Int).SetUint64(st.gasUsed())
fee.Mul(fee, effectiveTip) fee.Mul(fee, effectiveTipU256)
st.state.AddBalance(st.evm.Context.Coinbase, fee) st.state.AddBalance(st.evm.Context.Coinbase, fee)
} }
@ -474,7 +490,8 @@ func (st *StateTransition) refundGas(refundQuotient uint64) uint64 {
st.gasRemaining += refund st.gasRemaining += refund
// Return ETH for remaining gas, exchanged at the original rate. // Return ETH for remaining gas, exchanged at the original rate.
remaining := new(big.Int).Mul(new(big.Int).SetUint64(st.gasRemaining), st.msg.GasPrice) remaining := uint256.NewInt(st.gasRemaining)
remaining = remaining.Mul(remaining, uint256.MustFromBig(st.msg.GasPrice))
st.state.AddBalance(st.msg.From, remaining) st.state.AddBalance(st.msg.From, remaining)
// Also return remaining gas to the block gas counter so it is // Also return remaining gas to the block gas counter so it is

220
core/txindexer.go Normal file
View File

@ -0,0 +1,220 @@
// Copyright 2024 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 core
import (
"errors"
"fmt"
"github.com/ethereum/go-ethereum/core/rawdb"
"github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/log"
)
// TxIndexProgress is the struct describing the progress for transaction indexing.
type TxIndexProgress struct {
Indexed uint64 // number of blocks whose transactions are indexed
Remaining uint64 // number of blocks whose transactions are not indexed yet
}
// Done returns an indicator if the transaction indexing is finished.
func (progress TxIndexProgress) Done() bool {
return progress.Remaining == 0
}
// txIndexer is the module responsible for maintaining transaction indexes
// according to the configured indexing range by users.
type txIndexer struct {
// limit is the maximum number of blocks from head whose tx indexes
// are reserved:
// * 0: means the entire chain should be indexed
// * N: means the latest N blocks [HEAD-N+1, HEAD] should be indexed
// and all others shouldn't.
limit uint64
db ethdb.Database
progress chan chan TxIndexProgress
term chan chan struct{}
closed chan struct{}
}
// newTxIndexer initializes the transaction indexer.
func newTxIndexer(limit uint64, chain *BlockChain) *txIndexer {
indexer := &txIndexer{
limit: limit,
db: chain.db,
progress: make(chan chan TxIndexProgress),
term: make(chan chan struct{}),
closed: make(chan struct{}),
}
go indexer.loop(chain)
var msg string
if limit == 0 {
msg = "entire chain"
} else {
msg = fmt.Sprintf("last %d blocks", limit)
}
log.Info("Initialized transaction indexer", "range", msg)
return indexer
}
// run executes the scheduled indexing/unindexing task in a separate thread.
// If the stop channel is closed, the task should be terminated as soon as
// possible, the done channel will be closed once the task is finished.
func (indexer *txIndexer) run(tail *uint64, head uint64, stop chan struct{}, done chan struct{}) {
defer func() { close(done) }()
// Short circuit if chain is empty and nothing to index.
if head == 0 {
return
}
// The tail flag is not existent, it means the node is just initialized
// and all blocks in the chain (part of them may from ancient store) are
// not indexed yet, index the chain according to the configured limit.
if tail == nil {
from := uint64(0)
if indexer.limit != 0 && head >= indexer.limit {
from = head - indexer.limit + 1
}
rawdb.IndexTransactions(indexer.db, from, head+1, stop, true)
return
}
// The tail flag is existent (which means indexes in [tail, head] should be
// present), while the whole chain are requested for indexing.
if indexer.limit == 0 || head < indexer.limit {
if *tail > 0 {
// It can happen when chain is rewound to a historical point which
// is even lower than the indexes tail, recap the indexing target
// to new head to avoid reading non-existent block bodies.
end := *tail
if end > head+1 {
end = head + 1
}
rawdb.IndexTransactions(indexer.db, 0, end, stop, true)
}
return
}
// The tail flag is existent, adjust the index range according to configured
// limit and the latest chain head.
if head-indexer.limit+1 < *tail {
// Reindex a part of missing indices and rewind index tail to HEAD-limit
rawdb.IndexTransactions(indexer.db, head-indexer.limit+1, *tail, stop, true)
} else {
// Unindex a part of stale indices and forward index tail to HEAD-limit
rawdb.UnindexTransactions(indexer.db, *tail, head-indexer.limit+1, stop, false)
}
}
// loop is the scheduler of the indexer, assigning indexing/unindexing tasks depending
// on the received chain event.
func (indexer *txIndexer) loop(chain *BlockChain) {
defer close(indexer.closed)
// Listening to chain events and manipulate the transaction indexes.
var (
stop chan struct{} // Non-nil if background routine is active.
done chan struct{} // Non-nil if background routine is active.
lastHead uint64 // The latest announced chain head (whose tx indexes are assumed created)
headCh = make(chan ChainHeadEvent)
sub = chain.SubscribeChainHeadEvent(headCh)
)
defer sub.Unsubscribe()
// Launch the initial processing if chain is not empty (head != genesis).
// This step is useful in these scenarios that chain has no progress.
if head := rawdb.ReadHeadBlock(indexer.db); head != nil && head.Number().Uint64() != 0 {
stop = make(chan struct{})
done = make(chan struct{})
lastHead = head.Number().Uint64()
go indexer.run(rawdb.ReadTxIndexTail(indexer.db), head.NumberU64(), stop, done)
}
for {
select {
case head := <-headCh:
if done == nil {
stop = make(chan struct{})
done = make(chan struct{})
go indexer.run(rawdb.ReadTxIndexTail(indexer.db), head.Block.NumberU64(), stop, done)
}
lastHead = head.Block.NumberU64()
case <-done:
stop = nil
done = nil
case ch := <-indexer.progress:
ch <- indexer.report(lastHead)
case ch := <-indexer.term:
if stop != nil {
close(stop)
}
if done != nil {
log.Info("Waiting background transaction indexer to exit")
<-done
}
close(ch)
return
}
}
}
// report returns the tx indexing progress.
func (indexer *txIndexer) report(head uint64) TxIndexProgress {
var (
remaining uint64
tail = rawdb.ReadTxIndexTail(indexer.db)
)
total := indexer.limit
if indexer.limit == 0 || total > head {
total = head + 1 // genesis included
}
var indexed uint64
if tail != nil {
indexed = head - *tail + 1
}
// The value of indexed might be larger than total if some blocks need
// to be unindexed, avoiding a negative remaining.
if indexed < total {
remaining = total - indexed
}
return TxIndexProgress{
Indexed: indexed,
Remaining: remaining,
}
}
// txIndexProgress retrieves the tx indexing progress, or an error if the
// background tx indexer is already stopped.
func (indexer *txIndexer) txIndexProgress() (TxIndexProgress, error) {
ch := make(chan TxIndexProgress, 1)
select {
case indexer.progress <- ch:
return <-ch, nil
case <-indexer.closed:
return TxIndexProgress{}, errors.New("indexer is closed")
}
}
// close shutdown the indexer. Safe to be called for multiple times.
func (indexer *txIndexer) close() {
ch := make(chan struct{})
select {
case indexer.term <- ch:
<-ch
case <-indexer.closed:
}
}

243
core/txindexer_test.go Normal file
View File

@ -0,0 +1,243 @@
// Copyright 2024 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 core
import (
"math/big"
"os"
"testing"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/consensus/ethash"
"github.com/ethereum/go-ethereum/core/rawdb"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/params"
)
// TestTxIndexer tests the functionalities for managing transaction indexes.
func TestTxIndexer(t *testing.T) {
var (
testBankKey, _ = crypto.GenerateKey()
testBankAddress = crypto.PubkeyToAddress(testBankKey.PublicKey)
testBankFunds = big.NewInt(1000000000000000000)
gspec = &Genesis{
Config: params.TestChainConfig,
Alloc: GenesisAlloc{testBankAddress: {Balance: testBankFunds}},
BaseFee: big.NewInt(params.InitialBaseFee),
}
engine = ethash.NewFaker()
nonce = uint64(0)
chainHead = uint64(128)
)
_, blocks, receipts := GenerateChainWithGenesis(gspec, engine, int(chainHead), func(i int, gen *BlockGen) {
tx, _ := types.SignTx(types.NewTransaction(nonce, common.HexToAddress("0xdeadbeef"), big.NewInt(1000), params.TxGas, big.NewInt(10*params.InitialBaseFee), nil), types.HomesteadSigner{}, testBankKey)
gen.AddTx(tx)
nonce += 1
})
// verifyIndexes checks if the transaction indexes are present or not
// of the specified block.
verifyIndexes := func(db ethdb.Database, number uint64, exist bool) {
if number == 0 {
return
}
block := blocks[number-1]
for _, tx := range block.Transactions() {
lookup := rawdb.ReadTxLookupEntry(db, tx.Hash())
if exist && lookup == nil {
t.Fatalf("missing %d %x", number, tx.Hash().Hex())
}
if !exist && lookup != nil {
t.Fatalf("unexpected %d %x", number, tx.Hash().Hex())
}
}
}
verify := func(db ethdb.Database, expTail uint64, indexer *txIndexer) {
tail := rawdb.ReadTxIndexTail(db)
if tail == nil {
t.Fatal("Failed to write tx index tail")
}
if *tail != expTail {
t.Fatalf("Unexpected tx index tail, want %v, got %d", expTail, *tail)
}
if *tail != 0 {
for number := uint64(0); number < *tail; number += 1 {
verifyIndexes(db, number, false)
}
}
for number := *tail; number <= chainHead; number += 1 {
verifyIndexes(db, number, true)
}
progress := indexer.report(chainHead)
if !progress.Done() {
t.Fatalf("Expect fully indexed")
}
}
var cases = []struct {
limitA uint64
tailA uint64
limitB uint64
tailB uint64
limitC uint64
tailC uint64
}{
{
// LimitA: 0
// TailA: 0
//
// all blocks are indexed
limitA: 0,
tailA: 0,
// LimitB: 1
// TailB: 128
//
// block-128 is indexed
limitB: 1,
tailB: 128,
// LimitB: 64
// TailB: 65
//
// block [65, 128] are indexed
limitC: 64,
tailC: 65,
},
{
// LimitA: 64
// TailA: 65
//
// block [65, 128] are indexed
limitA: 64,
tailA: 65,
// LimitB: 1
// TailB: 128
//
// block-128 is indexed
limitB: 1,
tailB: 128,
// LimitB: 64
// TailB: 65
//
// block [65, 128] are indexed
limitC: 64,
tailC: 65,
},
{
// LimitA: 127
// TailA: 2
//
// block [2, 128] are indexed
limitA: 127,
tailA: 2,
// LimitB: 1
// TailB: 128
//
// block-128 is indexed
limitB: 1,
tailB: 128,
// LimitB: 64
// TailB: 65
//
// block [65, 128] are indexed
limitC: 64,
tailC: 65,
},
{
// LimitA: 128
// TailA: 1
//
// block [2, 128] are indexed
limitA: 128,
tailA: 1,
// LimitB: 1
// TailB: 128
//
// block-128 is indexed
limitB: 1,
tailB: 128,
// LimitB: 64
// TailB: 65
//
// block [65, 128] are indexed
limitC: 64,
tailC: 65,
},
{
// LimitA: 129
// TailA: 0
//
// block [0, 128] are indexed
limitA: 129,
tailA: 0,
// LimitB: 1
// TailB: 128
//
// block-128 is indexed
limitB: 1,
tailB: 128,
// LimitB: 64
// TailB: 65
//
// block [65, 128] are indexed
limitC: 64,
tailC: 65,
},
}
for _, c := range cases {
frdir := t.TempDir()
db, _ := rawdb.NewDatabaseWithFreezer(rawdb.NewMemoryDatabase(), frdir, "", false)
rawdb.WriteAncientBlocks(db, append([]*types.Block{gspec.ToBlock()}, blocks...), append([]types.Receipts{{}}, receipts...), big.NewInt(0))
// Index the initial blocks from ancient store
indexer := &txIndexer{
limit: c.limitA,
db: db,
progress: make(chan chan TxIndexProgress),
}
indexer.run(nil, 128, make(chan struct{}), make(chan struct{}))
verify(db, c.tailA, indexer)
indexer.limit = c.limitB
indexer.run(rawdb.ReadTxIndexTail(db), 128, make(chan struct{}), make(chan struct{}))
verify(db, c.tailB, indexer)
indexer.limit = c.limitC
indexer.run(rawdb.ReadTxIndexTail(db), 128, make(chan struct{}), make(chan struct{}))
verify(db, c.tailC, indexer)
// Recover all indexes
indexer.limit = 0
indexer.run(rawdb.ReadTxIndexTail(db), 128, make(chan struct{}), make(chan struct{}))
verify(db, 0, indexer)
db.Close()
os.RemoveAll(frdir)
}
}

View File

@ -583,7 +583,7 @@ func (p *BlobPool) recheck(addr common.Address, inclusions map[common.Hash]uint6
txs[0].evictionBlobFeeJumps = txs[0].blobfeeJumps txs[0].evictionBlobFeeJumps = txs[0].blobfeeJumps
for i := 1; i < len(txs); i++ { for i := 1; i < len(txs); i++ {
// If there's no nonce gap, initialize the evicion thresholds as the // If there's no nonce gap, initialize the eviction thresholds as the
// minimum between the cumulative thresholds and the current tx fees // minimum between the cumulative thresholds and the current tx fees
if txs[i].nonce == txs[i-1].nonce+1 { if txs[i].nonce == txs[i-1].nonce+1 {
txs[i].evictionExecTip = txs[i-1].evictionExecTip txs[i].evictionExecTip = txs[i-1].evictionExecTip
@ -632,7 +632,7 @@ func (p *BlobPool) recheck(addr common.Address, inclusions map[common.Hash]uint6
// Ensure that there's no over-draft, this is expected to happen when some // Ensure that there's no over-draft, this is expected to happen when some
// transactions get included without publishing on the network // transactions get included without publishing on the network
var ( var (
balance = uint256.MustFromBig(p.state.GetBalance(addr)) balance = p.state.GetBalance(addr)
spent = p.spent[addr] spent = p.spent[addr]
) )
if spent.Cmp(balance) > 0 { if spent.Cmp(balance) > 0 {
@ -1355,7 +1355,7 @@ func (p *BlobPool) drop() {
p.stored -= uint64(drop.size) p.stored -= uint64(drop.size)
delete(p.lookup, drop.hash) delete(p.lookup, drop.hash)
// Remove the transaction from the pool's evicion heap: // Remove the transaction from the pool's eviction heap:
// - If the entire account was dropped, pop off the address // - If the entire account was dropped, pop off the address
// - Otherwise, if the new tail has better eviction caps, fix the heap // - Otherwise, if the new tail has better eviction caps, fix the heap
if last { if last {

View File

@ -51,21 +51,9 @@ var (
emptyBlob = kzg4844.Blob{} emptyBlob = kzg4844.Blob{}
emptyBlobCommit, _ = kzg4844.BlobToCommitment(emptyBlob) emptyBlobCommit, _ = kzg4844.BlobToCommitment(emptyBlob)
emptyBlobProof, _ = kzg4844.ComputeBlobProof(emptyBlob, emptyBlobCommit) emptyBlobProof, _ = kzg4844.ComputeBlobProof(emptyBlob, emptyBlobCommit)
emptyBlobVHash = blobHash(emptyBlobCommit) emptyBlobVHash = kzg4844.CalcBlobHashV1(sha256.New(), &emptyBlobCommit)
) )
func blobHash(commit kzg4844.Commitment) common.Hash {
hasher := sha256.New()
hasher.Write(commit[:])
hash := hasher.Sum(nil)
var vhash common.Hash
vhash[0] = params.BlobTxHashVersion
copy(vhash[1:], hash[1:])
return vhash
}
// Chain configuration with Cancun enabled. // Chain configuration with Cancun enabled.
// //
// TODO(karalabe): replace with params.MainnetChainConfig after Cancun. // TODO(karalabe): replace with params.MainnetChainConfig after Cancun.
@ -512,17 +500,17 @@ func TestOpenDrops(t *testing.T) {
// Create a blob pool out of the pre-seeded data // Create a blob pool out of the pre-seeded data
statedb, _ := state.New(types.EmptyRootHash, state.NewDatabase(rawdb.NewDatabase(memorydb.New())), nil) statedb, _ := state.New(types.EmptyRootHash, state.NewDatabase(rawdb.NewDatabase(memorydb.New())), nil)
statedb.AddBalance(crypto.PubkeyToAddress(gapper.PublicKey), big.NewInt(1000000)) statedb.AddBalance(crypto.PubkeyToAddress(gapper.PublicKey), uint256.NewInt(1000000))
statedb.AddBalance(crypto.PubkeyToAddress(dangler.PublicKey), big.NewInt(1000000)) statedb.AddBalance(crypto.PubkeyToAddress(dangler.PublicKey), uint256.NewInt(1000000))
statedb.AddBalance(crypto.PubkeyToAddress(filler.PublicKey), big.NewInt(1000000)) statedb.AddBalance(crypto.PubkeyToAddress(filler.PublicKey), uint256.NewInt(1000000))
statedb.SetNonce(crypto.PubkeyToAddress(filler.PublicKey), 3) statedb.SetNonce(crypto.PubkeyToAddress(filler.PublicKey), 3)
statedb.AddBalance(crypto.PubkeyToAddress(overlapper.PublicKey), big.NewInt(1000000)) statedb.AddBalance(crypto.PubkeyToAddress(overlapper.PublicKey), uint256.NewInt(1000000))
statedb.SetNonce(crypto.PubkeyToAddress(overlapper.PublicKey), 2) statedb.SetNonce(crypto.PubkeyToAddress(overlapper.PublicKey), 2)
statedb.AddBalance(crypto.PubkeyToAddress(underpayer.PublicKey), big.NewInt(1000000)) statedb.AddBalance(crypto.PubkeyToAddress(underpayer.PublicKey), uint256.NewInt(1000000))
statedb.AddBalance(crypto.PubkeyToAddress(outpricer.PublicKey), big.NewInt(1000000)) statedb.AddBalance(crypto.PubkeyToAddress(outpricer.PublicKey), uint256.NewInt(1000000))
statedb.AddBalance(crypto.PubkeyToAddress(exceeder.PublicKey), big.NewInt(1000000)) statedb.AddBalance(crypto.PubkeyToAddress(exceeder.PublicKey), uint256.NewInt(1000000))
statedb.AddBalance(crypto.PubkeyToAddress(overdrafter.PublicKey), big.NewInt(1000000)) statedb.AddBalance(crypto.PubkeyToAddress(overdrafter.PublicKey), uint256.NewInt(1000000))
statedb.AddBalance(crypto.PubkeyToAddress(overcapper.PublicKey), big.NewInt(10000000)) statedb.AddBalance(crypto.PubkeyToAddress(overcapper.PublicKey), uint256.NewInt(10000000))
statedb.Commit(0, true) statedb.Commit(0, true)
chain := &testBlockChain{ chain := &testBlockChain{
@ -637,7 +625,7 @@ func TestOpenIndex(t *testing.T) {
// Create a blob pool out of the pre-seeded data // Create a blob pool out of the pre-seeded data
statedb, _ := state.New(types.EmptyRootHash, state.NewDatabase(rawdb.NewDatabase(memorydb.New())), nil) statedb, _ := state.New(types.EmptyRootHash, state.NewDatabase(rawdb.NewDatabase(memorydb.New())), nil)
statedb.AddBalance(addr, big.NewInt(1_000_000_000)) statedb.AddBalance(addr, uint256.NewInt(1_000_000_000))
statedb.Commit(0, true) statedb.Commit(0, true)
chain := &testBlockChain{ chain := &testBlockChain{
@ -737,9 +725,9 @@ func TestOpenHeap(t *testing.T) {
// Create a blob pool out of the pre-seeded data // Create a blob pool out of the pre-seeded data
statedb, _ := state.New(types.EmptyRootHash, state.NewDatabase(rawdb.NewDatabase(memorydb.New())), nil) statedb, _ := state.New(types.EmptyRootHash, state.NewDatabase(rawdb.NewDatabase(memorydb.New())), nil)
statedb.AddBalance(addr1, big.NewInt(1_000_000_000)) statedb.AddBalance(addr1, uint256.NewInt(1_000_000_000))
statedb.AddBalance(addr2, big.NewInt(1_000_000_000)) statedb.AddBalance(addr2, uint256.NewInt(1_000_000_000))
statedb.AddBalance(addr3, big.NewInt(1_000_000_000)) statedb.AddBalance(addr3, uint256.NewInt(1_000_000_000))
statedb.Commit(0, true) statedb.Commit(0, true)
chain := &testBlockChain{ chain := &testBlockChain{
@ -817,9 +805,9 @@ func TestOpenCap(t *testing.T) {
for _, datacap := range []uint64{2 * (txAvgSize + blobSize), 100 * (txAvgSize + blobSize)} { for _, datacap := range []uint64{2 * (txAvgSize + blobSize), 100 * (txAvgSize + blobSize)} {
// Create a blob pool out of the pre-seeded data, but cap it to 2 blob transaction // Create a blob pool out of the pre-seeded data, but cap it to 2 blob transaction
statedb, _ := state.New(types.EmptyRootHash, state.NewDatabase(rawdb.NewDatabase(memorydb.New())), nil) statedb, _ := state.New(types.EmptyRootHash, state.NewDatabase(rawdb.NewDatabase(memorydb.New())), nil)
statedb.AddBalance(addr1, big.NewInt(1_000_000_000)) statedb.AddBalance(addr1, uint256.NewInt(1_000_000_000))
statedb.AddBalance(addr2, big.NewInt(1_000_000_000)) statedb.AddBalance(addr2, uint256.NewInt(1_000_000_000))
statedb.AddBalance(addr3, big.NewInt(1_000_000_000)) statedb.AddBalance(addr3, uint256.NewInt(1_000_000_000))
statedb.Commit(0, true) statedb.Commit(0, true)
chain := &testBlockChain{ chain := &testBlockChain{
@ -1210,7 +1198,7 @@ func TestAdd(t *testing.T) {
addrs[acc] = crypto.PubkeyToAddress(keys[acc].PublicKey) addrs[acc] = crypto.PubkeyToAddress(keys[acc].PublicKey)
// Seed the state database with this acocunt // Seed the state database with this acocunt
statedb.AddBalance(addrs[acc], new(big.Int).SetUint64(seed.balance)) statedb.AddBalance(addrs[acc], new(uint256.Int).SetUint64(seed.balance))
statedb.SetNonce(addrs[acc], seed.nonce) statedb.SetNonce(addrs[acc], seed.nonce)
// Sign the seed transactions and store them in the data store // Sign the seed transactions and store them in the data store

View File

@ -1441,7 +1441,7 @@ func (pool *LegacyPool) promoteExecutables(accounts []common.Address) []*types.T
} }
log.Trace("Removed old queued transactions", "count", len(forwards)) log.Trace("Removed old queued transactions", "count", len(forwards))
// Drop all transactions that are too costly (low balance or out of gas) // Drop all transactions that are too costly (low balance or out of gas)
drops, _ := list.Filter(pool.currentState.GetBalance(addr), gasLimit) drops, _ := list.Filter(pool.currentState.GetBalance(addr).ToBig(), gasLimit)
for _, tx := range drops { for _, tx := range drops {
hash := tx.Hash() hash := tx.Hash()
pool.all.Remove(hash) pool.all.Remove(hash)
@ -1642,7 +1642,7 @@ func (pool *LegacyPool) demoteUnexecutables() {
log.Trace("Removed old pending transaction", "hash", hash) log.Trace("Removed old pending transaction", "hash", hash)
} }
// Drop all transactions that are too costly (low balance or out of gas), and queue any invalids back for later // Drop all transactions that are too costly (low balance or out of gas), and queue any invalids back for later
drops, invalids := list.Filter(pool.currentState.GetBalance(addr), gasLimit) drops, invalids := list.Filter(pool.currentState.GetBalance(addr).ToBig(), gasLimit)
for _, tx := range drops { for _, tx := range drops {
hash := tx.Hash() hash := tx.Hash()
log.Trace("Removed unpayable pending transaction", "hash", hash) log.Trace("Removed unpayable pending transaction", "hash", hash)

View File

@ -26,6 +26,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/event" "github.com/ethereum/go-ethereum/event"
"github.com/holiman/uint256"
) )
func pricedValuedTransaction(nonce uint64, value int64, gaslimit uint64, gasprice *big.Int, key *ecdsa.PrivateKey) *types.Transaction { func pricedValuedTransaction(nonce uint64, value int64, gaslimit uint64, gasprice *big.Int, key *ecdsa.PrivateKey) *types.Transaction {
@ -49,7 +50,7 @@ func fillPool(t testing.TB, pool *LegacyPool) {
nonExecutableTxs := types.Transactions{} nonExecutableTxs := types.Transactions{}
for i := 0; i < 384; i++ { for i := 0; i < 384; i++ {
key, _ := crypto.GenerateKey() key, _ := crypto.GenerateKey()
pool.currentState.AddBalance(crypto.PubkeyToAddress(key.PublicKey), big.NewInt(10000000000)) pool.currentState.AddBalance(crypto.PubkeyToAddress(key.PublicKey), uint256.NewInt(10000000000))
// Add executable ones // Add executable ones
for j := 0; j < int(pool.config.AccountSlots); j++ { for j := 0; j < int(pool.config.AccountSlots); j++ {
executableTxs = append(executableTxs, pricedTransaction(uint64(j), 100000, big.NewInt(300), key)) executableTxs = append(executableTxs, pricedTransaction(uint64(j), 100000, big.NewInt(300), key))
@ -91,7 +92,7 @@ func TestTransactionFutureAttack(t *testing.T) {
// Now, future transaction attack starts, let's add a bunch of expensive non-executables, and see if the pending-count drops // Now, future transaction attack starts, let's add a bunch of expensive non-executables, and see if the pending-count drops
{ {
key, _ := crypto.GenerateKey() key, _ := crypto.GenerateKey()
pool.currentState.AddBalance(crypto.PubkeyToAddress(key.PublicKey), big.NewInt(100000000000)) pool.currentState.AddBalance(crypto.PubkeyToAddress(key.PublicKey), uint256.NewInt(100000000000))
futureTxs := types.Transactions{} futureTxs := types.Transactions{}
for j := 0; j < int(pool.config.GlobalSlots+pool.config.GlobalQueue); j++ { for j := 0; j < int(pool.config.GlobalSlots+pool.config.GlobalQueue); j++ {
futureTxs = append(futureTxs, pricedTransaction(1000+uint64(j), 100000, big.NewInt(500), key)) futureTxs = append(futureTxs, pricedTransaction(1000+uint64(j), 100000, big.NewInt(500), key))
@ -128,7 +129,7 @@ func TestTransactionFuture1559(t *testing.T) {
// Now, future transaction attack starts, let's add a bunch of expensive non-executables, and see if the pending-count drops // Now, future transaction attack starts, let's add a bunch of expensive non-executables, and see if the pending-count drops
{ {
key, _ := crypto.GenerateKey() key, _ := crypto.GenerateKey()
pool.currentState.AddBalance(crypto.PubkeyToAddress(key.PublicKey), big.NewInt(100000000000)) pool.currentState.AddBalance(crypto.PubkeyToAddress(key.PublicKey), uint256.NewInt(100000000000))
futureTxs := types.Transactions{} futureTxs := types.Transactions{}
for j := 0; j < int(pool.config.GlobalSlots+pool.config.GlobalQueue); j++ { for j := 0; j < int(pool.config.GlobalSlots+pool.config.GlobalQueue); j++ {
futureTxs = append(futureTxs, dynamicFeeTx(1000+uint64(j), 100000, big.NewInt(200), big.NewInt(101), key)) futureTxs = append(futureTxs, dynamicFeeTx(1000+uint64(j), 100000, big.NewInt(200), big.NewInt(101), key))
@ -161,7 +162,7 @@ func TestTransactionZAttack(t *testing.T) {
var ivpendingNum int var ivpendingNum int
pendingtxs, _ := pool.Content() pendingtxs, _ := pool.Content()
for account, txs := range pendingtxs { for account, txs := range pendingtxs {
cur_balance := new(big.Int).Set(pool.currentState.GetBalance(account)) cur_balance := new(big.Int).Set(pool.currentState.GetBalance(account).ToBig())
for _, tx := range txs { for _, tx := range txs {
if cur_balance.Cmp(tx.Value()) <= 0 { if cur_balance.Cmp(tx.Value()) <= 0 {
ivpendingNum++ ivpendingNum++
@ -182,7 +183,7 @@ func TestTransactionZAttack(t *testing.T) {
for j := 0; j < int(pool.config.GlobalQueue); j++ { for j := 0; j < int(pool.config.GlobalQueue); j++ {
futureTxs := types.Transactions{} futureTxs := types.Transactions{}
key, _ := crypto.GenerateKey() key, _ := crypto.GenerateKey()
pool.currentState.AddBalance(crypto.PubkeyToAddress(key.PublicKey), big.NewInt(100000000000)) pool.currentState.AddBalance(crypto.PubkeyToAddress(key.PublicKey), uint256.NewInt(100000000000))
futureTxs = append(futureTxs, pricedTransaction(1000+uint64(j), 21000, big.NewInt(500), key)) futureTxs = append(futureTxs, pricedTransaction(1000+uint64(j), 21000, big.NewInt(500), key))
pool.addRemotesSync(futureTxs) pool.addRemotesSync(futureTxs)
} }
@ -190,7 +191,7 @@ func TestTransactionZAttack(t *testing.T) {
overDraftTxs := types.Transactions{} overDraftTxs := types.Transactions{}
{ {
key, _ := crypto.GenerateKey() key, _ := crypto.GenerateKey()
pool.currentState.AddBalance(crypto.PubkeyToAddress(key.PublicKey), big.NewInt(100000000000)) pool.currentState.AddBalance(crypto.PubkeyToAddress(key.PublicKey), uint256.NewInt(100000000000))
for j := 0; j < int(pool.config.GlobalSlots); j++ { for j := 0; j < int(pool.config.GlobalSlots); j++ {
overDraftTxs = append(overDraftTxs, pricedValuedTransaction(uint64(j), 600000000000, 21000, big.NewInt(500), key)) overDraftTxs = append(overDraftTxs, pricedValuedTransaction(uint64(j), 600000000000, 21000, big.NewInt(500), key))
} }
@ -227,7 +228,7 @@ func BenchmarkFutureAttack(b *testing.B) {
fillPool(b, pool) fillPool(b, pool)
key, _ := crypto.GenerateKey() key, _ := crypto.GenerateKey()
pool.currentState.AddBalance(crypto.PubkeyToAddress(key.PublicKey), big.NewInt(100000000000)) pool.currentState.AddBalance(crypto.PubkeyToAddress(key.PublicKey), uint256.NewInt(100000000000))
futureTxs := types.Transactions{} futureTxs := types.Transactions{}
for n := 0; n < b.N; n++ { for n := 0; n < b.N; n++ {

View File

@ -39,6 +39,7 @@ import (
"github.com/ethereum/go-ethereum/event" "github.com/ethereum/go-ethereum/event"
"github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/trie" "github.com/ethereum/go-ethereum/trie"
"github.com/holiman/uint256"
) )
var ( var (
@ -255,7 +256,7 @@ func (c *testChain) State() (*state.StateDB, error) {
c.statedb, _ = state.New(types.EmptyRootHash, state.NewDatabase(rawdb.NewMemoryDatabase()), nil) c.statedb, _ = state.New(types.EmptyRootHash, state.NewDatabase(rawdb.NewMemoryDatabase()), nil)
// simulate that the new head block included tx0 and tx1 // simulate that the new head block included tx0 and tx1
c.statedb.SetNonce(c.address, 2) c.statedb.SetNonce(c.address, 2)
c.statedb.SetBalance(c.address, new(big.Int).SetUint64(params.Ether)) c.statedb.SetBalance(c.address, new(uint256.Int).SetUint64(params.Ether))
*c.trigger = false *c.trigger = false
} }
return stdb, nil return stdb, nil
@ -275,7 +276,7 @@ func TestStateChangeDuringReset(t *testing.T) {
) )
// setup pool with 2 transaction in it // setup pool with 2 transaction in it
statedb.SetBalance(address, new(big.Int).SetUint64(params.Ether)) statedb.SetBalance(address, new(uint256.Int).SetUint64(params.Ether))
blockchain := &testChain{newTestBlockChain(params.TestChainConfig, 1000000000, statedb, new(event.Feed)), address, &trigger} blockchain := &testChain{newTestBlockChain(params.TestChainConfig, 1000000000, statedb, new(event.Feed)), address, &trigger}
tx0 := transaction(0, 100000, key) tx0 := transaction(0, 100000, key)
@ -309,7 +310,7 @@ func TestStateChangeDuringReset(t *testing.T) {
func testAddBalance(pool *LegacyPool, addr common.Address, amount *big.Int) { func testAddBalance(pool *LegacyPool, addr common.Address, amount *big.Int) {
pool.mu.Lock() pool.mu.Lock()
pool.currentState.AddBalance(addr, amount) pool.currentState.AddBalance(addr, uint256.MustFromBig(amount))
pool.mu.Unlock() pool.mu.Unlock()
} }
@ -470,7 +471,7 @@ func TestChainFork(t *testing.T) {
addr := crypto.PubkeyToAddress(key.PublicKey) addr := crypto.PubkeyToAddress(key.PublicKey)
resetState := func() { resetState := func() {
statedb, _ := state.New(types.EmptyRootHash, state.NewDatabase(rawdb.NewMemoryDatabase()), nil) statedb, _ := state.New(types.EmptyRootHash, state.NewDatabase(rawdb.NewMemoryDatabase()), nil)
statedb.AddBalance(addr, big.NewInt(100000000000000)) statedb.AddBalance(addr, uint256.NewInt(100000000000000))
pool.chain = newTestBlockChain(pool.chainconfig, 1000000, statedb, new(event.Feed)) pool.chain = newTestBlockChain(pool.chainconfig, 1000000, statedb, new(event.Feed))
<-pool.requestReset(nil, nil) <-pool.requestReset(nil, nil)
@ -499,7 +500,7 @@ func TestDoubleNonce(t *testing.T) {
addr := crypto.PubkeyToAddress(key.PublicKey) addr := crypto.PubkeyToAddress(key.PublicKey)
resetState := func() { resetState := func() {
statedb, _ := state.New(types.EmptyRootHash, state.NewDatabase(rawdb.NewMemoryDatabase()), nil) statedb, _ := state.New(types.EmptyRootHash, state.NewDatabase(rawdb.NewMemoryDatabase()), nil)
statedb.AddBalance(addr, big.NewInt(100000000000000)) statedb.AddBalance(addr, uint256.NewInt(100000000000000))
pool.chain = newTestBlockChain(pool.chainconfig, 1000000, statedb, new(event.Feed)) pool.chain = newTestBlockChain(pool.chainconfig, 1000000, statedb, new(event.Feed))
<-pool.requestReset(nil, nil) <-pool.requestReset(nil, nil)
@ -2662,7 +2663,7 @@ func BenchmarkMultiAccountBatchInsert(b *testing.B) {
for i := 0; i < b.N; i++ { for i := 0; i < b.N; i++ {
key, _ := crypto.GenerateKey() key, _ := crypto.GenerateKey()
account := crypto.PubkeyToAddress(key.PublicKey) account := crypto.PubkeyToAddress(key.PublicKey)
pool.currentState.AddBalance(account, big.NewInt(1000000)) pool.currentState.AddBalance(account, uint256.NewInt(1000000))
tx := transaction(uint64(0), 100000, key) tx := transaction(uint64(0), 100000, key)
batches[i] = tx batches[i] = tx
} }

View File

@ -72,6 +72,9 @@ type TxPool struct {
subs event.SubscriptionScope // Subscription scope to unsubscribe all on shutdown subs event.SubscriptionScope // Subscription scope to unsubscribe all on shutdown
quit chan chan error // Quit channel to tear down the head updater quit chan chan error // Quit channel to tear down the head updater
term chan struct{} // Termination channel to detect a closed pool
sync chan chan error // Testing / simulator channel to block until internal reset is done
} }
// New creates a new transaction pool to gather, sort and filter inbound // New creates a new transaction pool to gather, sort and filter inbound
@ -86,6 +89,8 @@ func New(gasTip *big.Int, chain BlockChain, subpools []SubPool) (*TxPool, error)
subpools: subpools, subpools: subpools,
reservations: make(map[common.Address]SubPool), reservations: make(map[common.Address]SubPool),
quit: make(chan chan error), quit: make(chan chan error),
term: make(chan struct{}),
sync: make(chan chan error),
} }
for i, subpool := range subpools { for i, subpool := range subpools {
if err := subpool.Init(gasTip, head, pool.reserver(i, subpool)); err != nil { if err := subpool.Init(gasTip, head, pool.reserver(i, subpool)); err != nil {
@ -174,6 +179,9 @@ func (p *TxPool) Close() error {
// outside blockchain events as well as for various reporting and transaction // outside blockchain events as well as for various reporting and transaction
// eviction events. // eviction events.
func (p *TxPool) loop(head *types.Header, chain BlockChain) { func (p *TxPool) loop(head *types.Header, chain BlockChain) {
// Close the termination marker when the pool stops
defer close(p.term)
// Subscribe to chain head events to trigger subpool resets // Subscribe to chain head events to trigger subpool resets
var ( var (
newHeadCh = make(chan core.ChainHeadEvent) newHeadCh = make(chan core.ChainHeadEvent)
@ -190,13 +198,23 @@ func (p *TxPool) loop(head *types.Header, chain BlockChain) {
var ( var (
resetBusy = make(chan struct{}, 1) // Allow 1 reset to run concurrently resetBusy = make(chan struct{}, 1) // Allow 1 reset to run concurrently
resetDone = make(chan *types.Header) resetDone = make(chan *types.Header)
resetForced bool // Whether a forced reset was requested, only used in simulator mode
resetWaiter chan error // Channel waiting on a forced reset, only used in simulator mode
) )
// Notify the live reset waiter to not block if the txpool is closed.
defer func() {
if resetWaiter != nil {
resetWaiter <- errors.New("pool already terminated")
resetWaiter = nil
}
}()
var errc chan error var errc chan error
for errc == nil { for errc == nil {
// Something interesting might have happened, run a reset if there is // Something interesting might have happened, run a reset if there is
// one needed but none is running. The resetter will run on its own // one needed but none is running. The resetter will run on its own
// goroutine to allow chain head events to be consumed contiguously. // goroutine to allow chain head events to be consumed contiguously.
if newHead != oldHead { if newHead != oldHead || resetForced {
// Try to inject a busy marker and start a reset if successful // Try to inject a busy marker and start a reset if successful
select { select {
case resetBusy <- struct{}{}: case resetBusy <- struct{}{}:
@ -208,8 +226,17 @@ func (p *TxPool) loop(head *types.Header, chain BlockChain) {
resetDone <- newHead resetDone <- newHead
}(oldHead, newHead) }(oldHead, newHead)
// If the reset operation was explicitly requested, consider it
// being fulfilled and drop the request marker. If it was not,
// this is a noop.
resetForced = false
default: default:
// Reset already running, wait until it finishes // Reset already running, wait until it finishes.
//
// Note, this will not drop any forced reset request. If a forced
// reset was requested, but we were busy, then when the currently
// running reset finishes, a new one will be spun up.
} }
} }
// Wait for the next chain head event or a previous reset finish // Wait for the next chain head event or a previous reset finish
@ -223,8 +250,26 @@ func (p *TxPool) loop(head *types.Header, chain BlockChain) {
oldHead = head oldHead = head
<-resetBusy <-resetBusy
// If someone is waiting for a reset to finish, notify them, unless
// the forced op is still pending. In that case, wait another round
// of resets.
if resetWaiter != nil && !resetForced {
resetWaiter <- nil
resetWaiter = nil
}
case errc = <-p.quit: case errc = <-p.quit:
// Termination requested, break out on the next loop round // Termination requested, break out on the next loop round
case syncc := <-p.sync:
// Transaction pool is running inside a simulator, and we are about
// to create a new block. Request a forced sync operation to ensure
// that any running reset operation finishes to make block imports
// deterministic. On top of that, run a new reset operation to make
// transaction insertions deterministic instead of being stuck in a
// queue waiting for a reset.
resetForced = true
resetWaiter = syncc
} }
} }
// Notify the closer of termination (no error possible for now) // Notify the closer of termination (no error possible for now)
@ -415,3 +460,20 @@ func (p *TxPool) Status(hash common.Hash) TxStatus {
} }
return TxStatusUnknown return TxStatusUnknown
} }
// Sync is a helper method for unit tests or simulator runs where the chain events
// are arriving in quick succession, without any time in between them to run the
// internal background reset operations. This method will run an explicit reset
// operation to ensure the pool stabilises, thus avoiding flakey behavior.
//
// Note, do not use this in production / live code. In live code, the pool is
// meant to reset on a separate thread to avoid DoS vectors.
func (p *TxPool) Sync() error {
sync := make(chan error)
select {
case p.sync <- sync:
return <-sync
case <-p.term:
return errors.New("pool already terminated")
}
}

View File

@ -143,17 +143,10 @@ func validateBlobSidecar(hashes []common.Hash, sidecar *types.BlobTxSidecar) err
// Blob quantities match up, validate that the provers match with the // Blob quantities match up, validate that the provers match with the
// transaction hash before getting to the cryptography // transaction hash before getting to the cryptography
hasher := sha256.New() hasher := sha256.New()
for i, want := range hashes { for i, vhash := range hashes {
hasher.Write(sidecar.Commitments[i][:]) computed := kzg4844.CalcBlobHashV1(hasher, &sidecar.Commitments[i])
hash := hasher.Sum(nil) if vhash != computed {
hasher.Reset() return fmt.Errorf("blob %d: computed hash %#x mismatches transaction one %#x", i, computed, vhash)
var vhash common.Hash
vhash[0] = params.BlobTxHashVersion
copy(vhash[1:], hash[1:])
if vhash != want {
return fmt.Errorf("blob %d: computed hash %#x mismatches transaction one %#x", i, vhash, want)
} }
} }
// Blob commitments match with the hashes in the transaction, verify the // Blob commitments match with the hashes in the transaction, verify the
@ -216,7 +209,7 @@ func ValidateTransactionWithState(tx *types.Transaction, signer types.Signer, op
} }
// Ensure the transactor has enough funds to cover the transaction costs // Ensure the transactor has enough funds to cover the transaction costs
var ( var (
balance = opts.State.GetBalance(from) balance = opts.State.GetBalance(from).ToBig()
cost = tx.Cost() cost = tx.Cost()
) )
if balance.Cmp(cost) < 0 { if balance.Cmp(cost) < 0 {

View File

@ -12,10 +12,7 @@ func (obj *StateAccount) EncodeRLP(_w io.Writer) error {
if obj.Balance == nil { if obj.Balance == nil {
w.Write(rlp.EmptyString) w.Write(rlp.EmptyString)
} else { } else {
if obj.Balance.Sign() == -1 { w.WriteUint256(obj.Balance)
return rlp.ErrNegativeBigInt
}
w.WriteBigInt(obj.Balance)
} }
w.WriteBytes(obj.Root[:]) w.WriteBytes(obj.Root[:])
w.WriteBytes(obj.CodeHash) w.WriteBytes(obj.CodeHash)

View File

@ -18,10 +18,10 @@ package types
import ( import (
"bytes" "bytes"
"math/big"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/rlp"
"github.com/holiman/uint256"
) )
//go:generate go run ../../rlp/rlpgen -type StateAccount -out gen_account_rlp.go //go:generate go run ../../rlp/rlpgen -type StateAccount -out gen_account_rlp.go
@ -30,7 +30,7 @@ import (
// These objects are stored in the main account trie. // These objects are stored in the main account trie.
type StateAccount struct { type StateAccount struct {
Nonce uint64 Nonce uint64
Balance *big.Int Balance *uint256.Int
Root common.Hash // merkle root of the storage trie Root common.Hash // merkle root of the storage trie
CodeHash []byte CodeHash []byte
} }
@ -38,7 +38,7 @@ type StateAccount struct {
// NewEmptyStateAccount constructs an empty state account. // NewEmptyStateAccount constructs an empty state account.
func NewEmptyStateAccount() *StateAccount { func NewEmptyStateAccount() *StateAccount {
return &StateAccount{ return &StateAccount{
Balance: new(big.Int), Balance: new(uint256.Int),
Root: EmptyRootHash, Root: EmptyRootHash,
CodeHash: EmptyCodeHash.Bytes(), CodeHash: EmptyCodeHash.Bytes(),
} }
@ -46,9 +46,9 @@ func NewEmptyStateAccount() *StateAccount {
// Copy returns a deep-copied state account object. // Copy returns a deep-copied state account object.
func (acct *StateAccount) Copy() *StateAccount { func (acct *StateAccount) Copy() *StateAccount {
var balance *big.Int var balance *uint256.Int
if acct.Balance != nil { if acct.Balance != nil {
balance = new(big.Int).Set(acct.Balance) balance = new(uint256.Int).Set(acct.Balance)
} }
return &StateAccount{ return &StateAccount{
Nonce: acct.Nonce, Nonce: acct.Nonce,
@ -63,7 +63,7 @@ func (acct *StateAccount) Copy() *StateAccount {
// or slim format which replaces the empty root and code hash as nil byte slice. // or slim format which replaces the empty root and code hash as nil byte slice.
type SlimAccount struct { type SlimAccount struct {
Nonce uint64 Nonce uint64
Balance *big.Int Balance *uint256.Int
Root []byte // Nil if root equals to types.EmptyRootHash Root []byte // Nil if root equals to types.EmptyRootHash
CodeHash []byte // Nil if hash equals to types.EmptyCodeHash CodeHash []byte // Nil if hash equals to types.EmptyCodeHash
} }

View File

@ -61,9 +61,10 @@ type BlobTxSidecar struct {
// BlobHashes computes the blob hashes of the given blobs. // BlobHashes computes the blob hashes of the given blobs.
func (sc *BlobTxSidecar) BlobHashes() []common.Hash { func (sc *BlobTxSidecar) BlobHashes() []common.Hash {
hasher := sha256.New()
h := make([]common.Hash, len(sc.Commitments)) h := make([]common.Hash, len(sc.Commitments))
for i := range sc.Blobs { for i := range sc.Blobs {
h[i] = blobHash(&sc.Commitments[i]) h[i] = kzg4844.CalcBlobHashV1(hasher, &sc.Commitments[i])
} }
return h return h
} }
@ -235,12 +236,3 @@ func (tx *BlobTx) decode(input []byte) error {
} }
return nil return nil
} }
func blobHash(commit *kzg4844.Commitment) common.Hash {
hasher := sha256.New()
hasher.Write(commit[:])
var vhash common.Hash
hasher.Sum(vhash[:0])
vhash[0] = params.BlobTxHashVersion
return vhash
}

View File

@ -17,8 +17,6 @@
package vm package vm
import ( import (
"math/big"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/holiman/uint256" "github.com/holiman/uint256"
) )
@ -59,11 +57,11 @@ type Contract struct {
Input []byte Input []byte
Gas uint64 Gas uint64
value *big.Int value *uint256.Int
} }
// NewContract returns a new contract environment for the execution of EVM. // NewContract returns a new contract environment for the execution of EVM.
func NewContract(caller ContractRef, object ContractRef, value *big.Int, gas uint64) *Contract { func NewContract(caller ContractRef, object ContractRef, value *uint256.Int, gas uint64) *Contract {
c := &Contract{CallerAddress: caller.Address(), caller: caller, self: object} c := &Contract{CallerAddress: caller.Address(), caller: caller, self: object}
if parent, ok := caller.(*Contract); ok { if parent, ok := caller.(*Contract); ok {
@ -173,7 +171,7 @@ func (c *Contract) Address() common.Address {
} }
// Value returns the contract's value (sent to it from it's caller) // Value returns the contract's value (sent to it from it's caller)
func (c *Contract) Value() *big.Int { func (c *Contract) Value() *uint256.Int {
return c.value return c.value
} }

View File

@ -267,7 +267,6 @@ type bigModExp struct {
} }
var ( var (
big0 = big.NewInt(0)
big1 = big.NewInt(1) big1 = big.NewInt(1)
big3 = big.NewInt(3) big3 = big.NewInt(3)
big4 = big.NewInt(4) big4 = big.NewInt(4)

View File

@ -85,7 +85,7 @@ func enable1884(jt *JumpTable) {
} }
func opSelfBalance(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { func opSelfBalance(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
balance, _ := uint256.FromBig(interpreter.evm.StateDB.GetBalance(scope.Contract.Address())) balance := interpreter.evm.StateDB.GetBalance(scope.Contract.Address())
scope.Stack.push(balance) scope.Stack.push(balance)
return nil, nil return nil, nil
} }

View File

@ -29,9 +29,9 @@ import (
type ( type (
// CanTransferFunc is the signature of a transfer guard function // CanTransferFunc is the signature of a transfer guard function
CanTransferFunc func(StateDB, common.Address, *big.Int) bool CanTransferFunc func(StateDB, common.Address, *uint256.Int) bool
// TransferFunc is the signature of a transfer function // TransferFunc is the signature of a transfer function
TransferFunc func(StateDB, common.Address, common.Address, *big.Int) TransferFunc func(StateDB, common.Address, common.Address, *uint256.Int)
// GetHashFunc returns the n'th block hash in the blockchain // GetHashFunc returns the n'th block hash in the blockchain
// and is used by the BLOCKHASH EVM op code. // and is used by the BLOCKHASH EVM op code.
GetHashFunc func(uint64) common.Hash GetHashFunc func(uint64) common.Hash
@ -176,7 +176,7 @@ func (evm *EVM) Interpreter() *EVMInterpreter {
// parameters. It also handles any necessary value transfer required and takes // parameters. It also handles any necessary value transfer required and takes
// the necessary steps to create accounts and reverses the state in case of an // the necessary steps to create accounts and reverses the state in case of an
// execution error or failed value transfer. // execution error or failed value transfer.
func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas uint64, value *big.Int) (ret []byte, leftOverGas uint64, err error) { func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas uint64, value *uint256.Int) (ret []byte, leftOverGas uint64, err error) {
// Fail if we're trying to execute above the call depth limit // Fail if we're trying to execute above the call depth limit
if evm.depth > int(params.CallCreateDepth) { if evm.depth > int(params.CallCreateDepth) {
return nil, gas, ErrDepth return nil, gas, ErrDepth
@ -194,10 +194,10 @@ func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas
// Calling a non existing account, don't do anything, but ping the tracer // Calling a non existing account, don't do anything, but ping the tracer
if debug { if debug {
if evm.depth == 0 { if evm.depth == 0 {
evm.Config.Tracer.CaptureStart(evm, caller.Address(), addr, false, input, gas, value) evm.Config.Tracer.CaptureStart(evm, caller.Address(), addr, false, input, gas, value.ToBig())
evm.Config.Tracer.CaptureEnd(ret, 0, nil) evm.Config.Tracer.CaptureEnd(ret, 0, nil)
} else { } else {
evm.Config.Tracer.CaptureEnter(CALL, caller.Address(), addr, input, gas, value) evm.Config.Tracer.CaptureEnter(CALL, caller.Address(), addr, input, gas, value.ToBig())
evm.Config.Tracer.CaptureExit(ret, 0, nil) evm.Config.Tracer.CaptureExit(ret, 0, nil)
} }
} }
@ -210,13 +210,13 @@ func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas
// Capture the tracer start/end events in debug mode // Capture the tracer start/end events in debug mode
if debug { if debug {
if evm.depth == 0 { if evm.depth == 0 {
evm.Config.Tracer.CaptureStart(evm, caller.Address(), addr, false, input, gas, value) evm.Config.Tracer.CaptureStart(evm, caller.Address(), addr, false, input, gas, value.ToBig())
defer func(startGas uint64) { // Lazy evaluation of the parameters defer func(startGas uint64) { // Lazy evaluation of the parameters
evm.Config.Tracer.CaptureEnd(ret, startGas-gas, err) evm.Config.Tracer.CaptureEnd(ret, startGas-gas, err)
}(gas) }(gas)
} else { } else {
// Handle tracer events for entering and exiting a call frame // Handle tracer events for entering and exiting a call frame
evm.Config.Tracer.CaptureEnter(CALL, caller.Address(), addr, input, gas, value) evm.Config.Tracer.CaptureEnter(CALL, caller.Address(), addr, input, gas, value.ToBig())
defer func(startGas uint64) { defer func(startGas uint64) {
evm.Config.Tracer.CaptureExit(ret, startGas-gas, err) evm.Config.Tracer.CaptureExit(ret, startGas-gas, err)
}(gas) }(gas)
@ -263,7 +263,7 @@ func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas
// //
// CallCode differs from Call in the sense that it executes the given address' // CallCode differs from Call in the sense that it executes the given address'
// code with the caller as context. // code with the caller as context.
func (evm *EVM) CallCode(caller ContractRef, addr common.Address, input []byte, gas uint64, value *big.Int) (ret []byte, leftOverGas uint64, err error) { func (evm *EVM) CallCode(caller ContractRef, addr common.Address, input []byte, gas uint64, value *uint256.Int) (ret []byte, leftOverGas uint64, err error) {
// Fail if we're trying to execute above the call depth limit // Fail if we're trying to execute above the call depth limit
if evm.depth > int(params.CallCreateDepth) { if evm.depth > int(params.CallCreateDepth) {
return nil, gas, ErrDepth return nil, gas, ErrDepth
@ -279,7 +279,7 @@ func (evm *EVM) CallCode(caller ContractRef, addr common.Address, input []byte,
// Invoke tracer hooks that signal entering/exiting a call frame // Invoke tracer hooks that signal entering/exiting a call frame
if evm.Config.Tracer != nil { if evm.Config.Tracer != nil {
evm.Config.Tracer.CaptureEnter(CALLCODE, caller.Address(), addr, input, gas, value) evm.Config.Tracer.CaptureEnter(CALLCODE, caller.Address(), addr, input, gas, value.ToBig())
defer func(startGas uint64) { defer func(startGas uint64) {
evm.Config.Tracer.CaptureExit(ret, startGas-gas, err) evm.Config.Tracer.CaptureExit(ret, startGas-gas, err)
}(gas) }(gas)
@ -324,7 +324,7 @@ func (evm *EVM) DelegateCall(caller ContractRef, addr common.Address, input []by
// that caller is something other than a Contract. // that caller is something other than a Contract.
parent := caller.(*Contract) parent := caller.(*Contract)
// DELEGATECALL inherits value from parent call // DELEGATECALL inherits value from parent call
evm.Config.Tracer.CaptureEnter(DELEGATECALL, caller.Address(), addr, input, gas, parent.value) evm.Config.Tracer.CaptureEnter(DELEGATECALL, caller.Address(), addr, input, gas, parent.value.ToBig())
defer func(startGas uint64) { defer func(startGas uint64) {
evm.Config.Tracer.CaptureExit(ret, startGas-gas, err) evm.Config.Tracer.CaptureExit(ret, startGas-gas, err)
}(gas) }(gas)
@ -370,7 +370,7 @@ func (evm *EVM) StaticCall(caller ContractRef, addr common.Address, input []byte
// This doesn't matter on Mainnet, where all empties are gone at the time of Byzantium, // This doesn't matter on Mainnet, where all empties are gone at the time of Byzantium,
// but is the correct thing to do and matters on other networks, in tests, and potential // but is the correct thing to do and matters on other networks, in tests, and potential
// future scenarios // future scenarios
evm.StateDB.AddBalance(addr, big0) evm.StateDB.AddBalance(addr, new(uint256.Int))
// Invoke tracer hooks that signal entering/exiting a call frame // Invoke tracer hooks that signal entering/exiting a call frame
if evm.Config.Tracer != nil { if evm.Config.Tracer != nil {
@ -389,7 +389,7 @@ func (evm *EVM) StaticCall(caller ContractRef, addr common.Address, input []byte
addrCopy := addr addrCopy := addr
// Initialise a new contract and set the code that is to be used by the EVM. // Initialise a new contract and set the code that is to be used by the EVM.
// The contract is a scoped environment for this execution context only. // The contract is a scoped environment for this execution context only.
contract := NewContract(caller, AccountRef(addrCopy), new(big.Int), gas) contract := NewContract(caller, AccountRef(addrCopy), new(uint256.Int), gas)
contract.SetCallCode(&addrCopy, evm.StateDB.GetCodeHash(addrCopy), evm.StateDB.GetCode(addrCopy)) contract.SetCallCode(&addrCopy, evm.StateDB.GetCodeHash(addrCopy), evm.StateDB.GetCode(addrCopy))
// When an error was returned by the EVM or when setting the creation code // When an error was returned by the EVM or when setting the creation code
// above we revert to the snapshot and consume any gas remaining. Additionally // above we revert to the snapshot and consume any gas remaining. Additionally
@ -419,7 +419,7 @@ func (c *codeAndHash) Hash() common.Hash {
} }
// create creates a new contract using code as deployment code. // create creates a new contract using code as deployment code.
func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64, value *big.Int, address common.Address, typ OpCode) ([]byte, common.Address, uint64, error) { func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64, value *uint256.Int, address common.Address, typ OpCode) ([]byte, common.Address, uint64, error) {
// Depth check execution. Fail if we're trying to execute above the // Depth check execution. Fail if we're trying to execute above the
// limit. // limit.
if evm.depth > int(params.CallCreateDepth) { if evm.depth > int(params.CallCreateDepth) {
@ -458,9 +458,9 @@ func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64,
if evm.Config.Tracer != nil { if evm.Config.Tracer != nil {
if evm.depth == 0 { if evm.depth == 0 {
evm.Config.Tracer.CaptureStart(evm, caller.Address(), address, true, codeAndHash.code, gas, value) evm.Config.Tracer.CaptureStart(evm, caller.Address(), address, true, codeAndHash.code, gas, value.ToBig())
} else { } else {
evm.Config.Tracer.CaptureEnter(typ, caller.Address(), address, codeAndHash.code, gas, value) evm.Config.Tracer.CaptureEnter(typ, caller.Address(), address, codeAndHash.code, gas, value.ToBig())
} }
} }
@ -510,7 +510,7 @@ func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64,
} }
// Create creates a new contract using code as deployment code. // Create creates a new contract using code as deployment code.
func (evm *EVM) Create(caller ContractRef, code []byte, gas uint64, value *big.Int) (ret []byte, contractAddr common.Address, leftOverGas uint64, err error) { func (evm *EVM) Create(caller ContractRef, code []byte, gas uint64, value *uint256.Int) (ret []byte, contractAddr common.Address, leftOverGas uint64, err error) {
contractAddr = crypto.CreateAddress(caller.Address(), evm.StateDB.GetNonce(caller.Address())) contractAddr = crypto.CreateAddress(caller.Address(), evm.StateDB.GetNonce(caller.Address()))
return evm.create(caller, &codeAndHash{code: code}, gas, value, contractAddr, CREATE) return evm.create(caller, &codeAndHash{code: code}, gas, value, contractAddr, CREATE)
} }
@ -519,7 +519,7 @@ func (evm *EVM) Create(caller ContractRef, code []byte, gas uint64, value *big.I
// //
// The different between Create2 with Create is Create2 uses keccak256(0xff ++ msg.sender ++ salt ++ keccak256(init_code))[12:] // The different between Create2 with Create is Create2 uses keccak256(0xff ++ msg.sender ++ salt ++ keccak256(init_code))[12:]
// instead of the usual sender-and-nonce-hash as the address where the contract is initialized at. // instead of the usual sender-and-nonce-hash as the address where the contract is initialized at.
func (evm *EVM) Create2(caller ContractRef, code []byte, gas uint64, endowment *big.Int, salt *uint256.Int) (ret []byte, contractAddr common.Address, leftOverGas uint64, err error) { func (evm *EVM) Create2(caller ContractRef, code []byte, gas uint64, endowment *uint256.Int, salt *uint256.Int) (ret []byte, contractAddr common.Address, leftOverGas uint64, err error) {
codeAndHash := &codeAndHash{code: code} codeAndHash := &codeAndHash{code: code}
contractAddr = crypto.CreateAddress2(caller.Address(), salt.Bytes32(), codeAndHash.Hash().Bytes()) contractAddr = crypto.CreateAddress2(caller.Address(), salt.Bytes32(), codeAndHash.Hash().Bytes())
return evm.create(caller, codeAndHash, gas, endowment, contractAddr, CREATE2) return evm.create(caller, codeAndHash, gas, endowment, contractAddr, CREATE2)

View File

@ -29,6 +29,7 @@ import (
"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"
"github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/params"
"github.com/holiman/uint256"
) )
func TestMemoryGasCost(t *testing.T) { func TestMemoryGasCost(t *testing.T) {
@ -91,12 +92,12 @@ func TestEIP2200(t *testing.T) {
statedb.Finalise(true) // Push the state into the "original" slot statedb.Finalise(true) // Push the state into the "original" slot
vmctx := BlockContext{ vmctx := BlockContext{
CanTransfer: func(StateDB, common.Address, *big.Int) bool { return true }, CanTransfer: func(StateDB, common.Address, *uint256.Int) bool { return true },
Transfer: func(StateDB, common.Address, common.Address, *big.Int) {}, Transfer: func(StateDB, common.Address, common.Address, *uint256.Int) {},
} }
vmenv := NewEVM(vmctx, TxContext{}, statedb, params.AllEthashProtocolChanges, Config{ExtraEips: []int{2200}}) vmenv := NewEVM(vmctx, TxContext{}, statedb, params.AllEthashProtocolChanges, Config{ExtraEips: []int{2200}})
_, gas, err := vmenv.Call(AccountRef(common.Address{}), address, nil, tt.gaspool, new(big.Int)) _, gas, err := vmenv.Call(AccountRef(common.Address{}), address, nil, tt.gaspool, new(uint256.Int))
if err != tt.failure { if err != tt.failure {
t.Errorf("test %d: failure mismatch: have %v, want %v", i, err, tt.failure) t.Errorf("test %d: failure mismatch: have %v, want %v", i, err, tt.failure)
} }
@ -141,8 +142,8 @@ func TestCreateGas(t *testing.T) {
statedb.SetCode(address, hexutil.MustDecode(tt.code)) statedb.SetCode(address, hexutil.MustDecode(tt.code))
statedb.Finalise(true) statedb.Finalise(true)
vmctx := BlockContext{ vmctx := BlockContext{
CanTransfer: func(StateDB, common.Address, *big.Int) bool { return true }, CanTransfer: func(StateDB, common.Address, *uint256.Int) bool { return true },
Transfer: func(StateDB, common.Address, common.Address, *big.Int) {}, Transfer: func(StateDB, common.Address, common.Address, *uint256.Int) {},
BlockNumber: big.NewInt(0), BlockNumber: big.NewInt(0),
} }
config := Config{} config := Config{}
@ -152,7 +153,7 @@ func TestCreateGas(t *testing.T) {
vmenv := NewEVM(vmctx, TxContext{}, statedb, params.AllEthashProtocolChanges, config) vmenv := NewEVM(vmctx, TxContext{}, statedb, params.AllEthashProtocolChanges, config)
var startGas = uint64(testGas) var startGas = uint64(testGas)
ret, gas, err := vmenv.Call(AccountRef(common.Address{}), address, nil, startGas, new(big.Int)) ret, gas, err := vmenv.Call(AccountRef(common.Address{}), address, nil, startGas, new(uint256.Int))
if err != nil { if err != nil {
return false return false
} }

View File

@ -260,7 +260,7 @@ func opAddress(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]
func opBalance(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { func opBalance(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
slot := scope.Stack.peek() slot := scope.Stack.peek()
address := common.Address(slot.Bytes20()) address := common.Address(slot.Bytes20())
slot.SetFromBig(interpreter.evm.StateDB.GetBalance(address)) slot.Set(interpreter.evm.StateDB.GetBalance(address))
return nil, nil return nil, nil
} }
@ -275,8 +275,7 @@ func opCaller(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]b
} }
func opCallValue(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { func opCallValue(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
v, _ := uint256.FromBig(scope.Contract.value) scope.Stack.push(scope.Contract.value)
scope.Stack.push(v)
return nil, nil return nil, nil
} }
@ -592,13 +591,8 @@ func opCreate(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]b
stackvalue := size stackvalue := size
scope.Contract.UseGas(gas) scope.Contract.UseGas(gas)
//TODO: use uint256.Int instead of converting with toBig()
var bigVal = big0
if !value.IsZero() {
bigVal = value.ToBig()
}
res, addr, returnGas, suberr := interpreter.evm.Create(scope.Contract, input, gas, bigVal) res, addr, returnGas, suberr := interpreter.evm.Create(scope.Contract, input, gas, &value)
// Push item on the stack based on the returned error. If the ruleset is // Push item on the stack based on the returned error. If the ruleset is
// homestead we must check for CodeStoreOutOfGasError (homestead only // homestead we must check for CodeStoreOutOfGasError (homestead only
// rule) and treat as an error, if the ruleset is frontier we must // rule) and treat as an error, if the ruleset is frontier we must
@ -637,13 +631,8 @@ func opCreate2(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]
scope.Contract.UseGas(gas) scope.Contract.UseGas(gas)
// reuse size int for stackvalue // reuse size int for stackvalue
stackvalue := size stackvalue := size
//TODO: use uint256.Int instead of converting with toBig()
bigEndowment := big0
if !endowment.IsZero() {
bigEndowment = endowment.ToBig()
}
res, addr, returnGas, suberr := interpreter.evm.Create2(scope.Contract, input, gas, res, addr, returnGas, suberr := interpreter.evm.Create2(scope.Contract, input, gas,
bigEndowment, &salt) &endowment, &salt)
// Push item on the stack based on the returned error. // Push item on the stack based on the returned error.
if suberr != nil { if suberr != nil {
stackvalue.Clear() stackvalue.Clear()
@ -676,16 +665,10 @@ func opCall(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byt
if interpreter.readOnly && !value.IsZero() { if interpreter.readOnly && !value.IsZero() {
return nil, ErrWriteProtection return nil, ErrWriteProtection
} }
var bigVal = big0
//TODO: use uint256.Int instead of converting with toBig()
// By using big0 here, we save an alloc for the most common case (non-ether-transferring contract calls),
// but it would make more sense to extend the usage of uint256.Int
if !value.IsZero() { if !value.IsZero() {
gas += params.CallStipend gas += params.CallStipend
bigVal = value.ToBig()
} }
ret, returnGas, err := interpreter.evm.Call(scope.Contract, toAddr, args, gas, &value)
ret, returnGas, err := interpreter.evm.Call(scope.Contract, toAddr, args, gas, bigVal)
if err != nil { if err != nil {
temp.Clear() temp.Clear()
@ -714,14 +697,11 @@ func opCallCode(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([
// Get arguments from the memory. // Get arguments from the memory.
args := scope.Memory.GetPtr(int64(inOffset.Uint64()), int64(inSize.Uint64())) args := scope.Memory.GetPtr(int64(inOffset.Uint64()), int64(inSize.Uint64()))
//TODO: use uint256.Int instead of converting with toBig()
var bigVal = big0
if !value.IsZero() { if !value.IsZero() {
gas += params.CallStipend gas += params.CallStipend
bigVal = value.ToBig()
} }
ret, returnGas, err := interpreter.evm.CallCode(scope.Contract, toAddr, args, gas, bigVal) ret, returnGas, err := interpreter.evm.CallCode(scope.Contract, toAddr, args, gas, &value)
if err != nil { if err != nil {
temp.Clear() temp.Clear()
} else { } else {
@ -825,7 +805,7 @@ func opSelfdestruct(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext
interpreter.evm.StateDB.AddBalance(beneficiary.Bytes20(), balance) interpreter.evm.StateDB.AddBalance(beneficiary.Bytes20(), balance)
interpreter.evm.StateDB.SelfDestruct(scope.Contract.Address()) interpreter.evm.StateDB.SelfDestruct(scope.Contract.Address())
if tracer := interpreter.evm.Config.Tracer; tracer != nil { if tracer := interpreter.evm.Config.Tracer; tracer != nil {
tracer.CaptureEnter(SELFDESTRUCT, scope.Contract.Address(), beneficiary.Bytes20(), []byte{}, 0, balance) tracer.CaptureEnter(SELFDESTRUCT, scope.Contract.Address(), beneficiary.Bytes20(), []byte{}, 0, balance.ToBig())
tracer.CaptureExit([]byte{}, 0, nil) tracer.CaptureExit([]byte{}, 0, nil)
} }
return nil, errStopToken return nil, errStopToken
@ -841,7 +821,7 @@ func opSelfdestruct6780(pc *uint64, interpreter *EVMInterpreter, scope *ScopeCon
interpreter.evm.StateDB.AddBalance(beneficiary.Bytes20(), balance) interpreter.evm.StateDB.AddBalance(beneficiary.Bytes20(), balance)
interpreter.evm.StateDB.Selfdestruct6780(scope.Contract.Address()) interpreter.evm.StateDB.Selfdestruct6780(scope.Contract.Address())
if tracer := interpreter.evm.Config.Tracer; tracer != nil { if tracer := interpreter.evm.Config.Tracer; tracer != nil {
tracer.CaptureEnter(SELFDESTRUCT, scope.Contract.Address(), beneficiary.Bytes20(), []byte{}, 0, balance) tracer.CaptureEnter(SELFDESTRUCT, scope.Contract.Address(), beneficiary.Bytes20(), []byte{}, 0, balance.ToBig())
tracer.CaptureExit([]byte{}, 0, nil) tracer.CaptureExit([]byte{}, 0, nil)
} }
return nil, errStopToken return nil, errStopToken

View File

@ -590,7 +590,7 @@ func TestOpTstore(t *testing.T) {
caller = common.Address{} caller = common.Address{}
to = common.Address{1} to = common.Address{1}
contractRef = contractRef{caller} contractRef = contractRef{caller}
contract = NewContract(contractRef, AccountRef(to), new(big.Int), 0) contract = NewContract(contractRef, AccountRef(to), new(uint256.Int), 0)
scopeContext = ScopeContext{mem, stack, contract} scopeContext = ScopeContext{mem, stack, contract}
value = common.Hex2Bytes("abcdef00000000000000abba000000000deaf000000c0de00100000000133700") value = common.Hex2Bytes("abcdef00000000000000abba000000000deaf000000c0de00100000000133700")
) )

View File

@ -22,15 +22,16 @@ import (
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"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"
) )
// StateDB is an EVM database for full state querying. // StateDB is an EVM database for full state querying.
type StateDB interface { type StateDB interface {
CreateAccount(common.Address) CreateAccount(common.Address)
SubBalance(common.Address, *big.Int) SubBalance(common.Address, *uint256.Int)
AddBalance(common.Address, *big.Int) AddBalance(common.Address, *uint256.Int)
GetBalance(common.Address) *big.Int GetBalance(common.Address) *uint256.Int
GetNonce(common.Address) uint64 GetNonce(common.Address) uint64
SetNonce(common.Address, uint64) SetNonce(common.Address, uint64)

View File

@ -17,7 +17,6 @@
package vm package vm
import ( import (
"math/big"
"testing" "testing"
"time" "time"
@ -27,6 +26,7 @@ import (
"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"
"github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/params"
"github.com/holiman/uint256"
) )
var loopInterruptTests = []string{ var loopInterruptTests = []string{
@ -39,7 +39,7 @@ var loopInterruptTests = []string{
func TestLoopInterrupt(t *testing.T) { func TestLoopInterrupt(t *testing.T) {
address := common.BytesToAddress([]byte("contract")) address := common.BytesToAddress([]byte("contract"))
vmctx := BlockContext{ vmctx := BlockContext{
Transfer: func(StateDB, common.Address, common.Address, *big.Int) {}, Transfer: func(StateDB, common.Address, common.Address, *uint256.Int) {},
} }
for i, tt := range loopInterruptTests { for i, tt := range loopInterruptTests {
@ -54,7 +54,7 @@ func TestLoopInterrupt(t *testing.T) {
timeout := make(chan bool) timeout := make(chan bool)
go func(evm *EVM) { go func(evm *EVM) {
_, _, err := evm.Call(AccountRef(common.Address{}), address, nil, math.MaxUint64, new(big.Int)) _, _, err := evm.Call(AccountRef(common.Address{}), address, nil, math.MaxUint64, new(uint256.Int))
errChannel <- err errChannel <- err
}(evm) }(evm)

View File

@ -122,7 +122,7 @@ func newLondonInstructionSet() JumpTable {
// constantinople, istanbul, petersburg and berlin instructions. // constantinople, istanbul, petersburg and berlin instructions.
func newBerlinInstructionSet() JumpTable { func newBerlinInstructionSet() JumpTable {
instructionSet := newIstanbulInstructionSet() instructionSet := newIstanbulInstructionSet()
enable2929(&instructionSet) // Access lists for trie accesses https://eips.ethereum.org/EIPS/eip-2929 enable2929(&instructionSet) // Gas cost increases for state access opcodes https://eips.ethereum.org/EIPS/eip-2929
return validate(instructionSet) return validate(instructionSet)
} }

View File

@ -27,6 +27,7 @@ import (
"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"
"github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/params"
"github.com/holiman/uint256"
) )
// Config is a basic type specifying certain configuration flags for running // Config is a basic type specifying certain configuration flags for running
@ -135,7 +136,7 @@ func Execute(code, input []byte, cfg *Config) ([]byte, *state.StateDB, error) {
common.BytesToAddress([]byte("contract")), common.BytesToAddress([]byte("contract")),
input, input,
cfg.GasLimit, cfg.GasLimit,
cfg.Value, uint256.MustFromBig(cfg.Value),
) )
return ret, cfg.State, err return ret, cfg.State, err
} }
@ -164,7 +165,7 @@ func Create(input []byte, cfg *Config) ([]byte, common.Address, uint64, error) {
sender, sender,
input, input,
cfg.GasLimit, cfg.GasLimit,
cfg.Value, uint256.MustFromBig(cfg.Value),
) )
return code, address, leftOverGas, err return code, address, leftOverGas, err
} }
@ -179,7 +180,7 @@ func Call(address common.Address, input []byte, cfg *Config) ([]byte, uint64, er
var ( var (
vmenv = NewEnv(cfg) vmenv = NewEnv(cfg)
sender = cfg.State.GetOrNewStateObject(cfg.Origin) sender = vm.AccountRef(cfg.Origin)
statedb = cfg.State statedb = cfg.State
rules = cfg.ChainConfig.Rules(vmenv.Context.BlockNumber, vmenv.Context.Random != nil, vmenv.Context.Time) rules = cfg.ChainConfig.Rules(vmenv.Context.BlockNumber, vmenv.Context.Random != nil, vmenv.Context.Time)
) )
@ -194,7 +195,7 @@ func Call(address common.Address, input []byte, cfg *Config) ([]byte, uint64, er
address, address,
input, input,
cfg.GasLimit, cfg.GasLimit,
cfg.Value, uint256.MustFromBig(cfg.Value),
) )
return ret, leftOverGas, err return ret, leftOverGas, err
} }

View File

@ -38,6 +38,7 @@ import (
// force-load js tracers to trigger registration // force-load js tracers to trigger registration
_ "github.com/ethereum/go-ethereum/eth/tracers/js" _ "github.com/ethereum/go-ethereum/eth/tracers/js"
"github.com/holiman/uint256"
) )
func TestDefaults(t *testing.T) { func TestDefaults(t *testing.T) {
@ -362,12 +363,12 @@ func benchmarkNonModifyingCode(gas uint64, code []byte, name string, tracerCode
//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)
vmenv.Call(sender, destination, nil, gas, cfg.Value) vmenv.Call(sender, destination, nil, gas, uint256.MustFromBig(cfg.Value))
b.Run(name, func(b *testing.B) { b.Run(name, func(b *testing.B) {
b.ReportAllocs() b.ReportAllocs()
for i := 0; i < b.N; i++ { for i := 0; i < b.N; i++ {
vmenv.Call(sender, destination, nil, gas, cfg.Value) vmenv.Call(sender, destination, nil, gas, uint256.MustFromBig(cfg.Value))
} }
}) })
} }

View File

@ -20,6 +20,7 @@ package kzg4844
import ( import (
"embed" "embed"
"errors" "errors"
"hash"
"sync/atomic" "sync/atomic"
) )
@ -108,3 +109,21 @@ func VerifyBlobProof(blob Blob, commitment Commitment, proof Proof) error {
} }
return gokzgVerifyBlobProof(blob, commitment, proof) return gokzgVerifyBlobProof(blob, commitment, proof)
} }
// CalcBlobHashV1 calculates the 'versioned blob hash' of a commitment.
// The given hasher must be a sha256 hash instance, otherwise the result will be invalid!
func CalcBlobHashV1(hasher hash.Hash, commit *Commitment) (vh [32]byte) {
if hasher.Size() != 32 {
panic("wrong hash size")
}
hasher.Reset()
hasher.Write(commit[:])
hasher.Sum(vh[:0])
vh[0] = 0x01 // version
return vh
}
// IsValidVersionedHash checks that h is a structurally-valid versioned blob hash.
func IsValidVersionedHash(h []byte) bool {
return len(h) == 32 && h[0] == 0x01
}

View File

@ -260,7 +260,7 @@ func (b *EthAPIBackend) GetEVM(ctx context.Context, msg *core.Message, state *st
} else { } else {
context = core.NewEVMBlockContext(header, b.eth.BlockChain(), nil) context = core.NewEVMBlockContext(header, b.eth.BlockChain(), nil)
} }
return vm.NewEVM(context, txContext, state, b.eth.blockchain.Config(), *vmConfig) return vm.NewEVM(context, txContext, state, b.ChainConfig(), *vmConfig)
} }
func (b *EthAPIBackend) SubscribeRemovedLogsEvent(ch chan<- core.RemovedLogsEvent) event.Subscription { func (b *EthAPIBackend) SubscribeRemovedLogsEvent(ch chan<- core.RemovedLogsEvent) event.Subscription {
@ -308,9 +308,25 @@ func (b *EthAPIBackend) GetPoolTransaction(hash common.Hash) *types.Transaction
return b.eth.txPool.Get(hash) return b.eth.txPool.Get(hash)
} }
func (b *EthAPIBackend) GetTransaction(ctx context.Context, txHash common.Hash) (*types.Transaction, common.Hash, uint64, uint64, error) { // GetTransaction retrieves the lookup along with the transaction itself associate
tx, blockHash, blockNumber, index := rawdb.ReadTransaction(b.eth.ChainDb(), txHash) // with the given transaction hash.
return tx, blockHash, blockNumber, index, nil //
// An error will be returned if the transaction is not found, and background
// indexing for transactions is still in progress. The error is used to indicate the
// scenario explicitly that the transaction might be reachable shortly.
//
// A null will be returned in the transaction is not found and background transaction
// indexing is already finished. The transaction is not existent from the perspective
// of node.
func (b *EthAPIBackend) GetTransaction(ctx context.Context, txHash common.Hash) (bool, *types.Transaction, common.Hash, uint64, uint64, error) {
lookup, tx, err := b.eth.blockchain.GetTransactionLookup(txHash)
if err != nil {
return false, nil, common.Hash{}, 0, 0, err
}
if lookup == nil || tx == nil {
return false, nil, common.Hash{}, 0, 0, nil
}
return true, tx, lookup.BlockHash, lookup.BlockIndex, lookup.Index, nil
} }
func (b *EthAPIBackend) GetPoolNonce(ctx context.Context, addr common.Address) (uint64, error) { func (b *EthAPIBackend) GetPoolNonce(ctx context.Context, addr common.Address) (uint64, error) {
@ -338,7 +354,12 @@ func (b *EthAPIBackend) SubscribeNewTxsEvent(ch chan<- core.NewTxsEvent) event.S
} }
func (b *EthAPIBackend) SyncProgress() ethereum.SyncProgress { func (b *EthAPIBackend) SyncProgress() ethereum.SyncProgress {
return b.eth.Downloader().Progress() prog := b.eth.Downloader().Progress()
if txProg, err := b.eth.blockchain.TxIndexProgress(); err == nil {
prog.TxIndexFinishedBlocks = txProg.Indexed
prog.TxIndexRemainingBlocks = txProg.Remaining
}
return prog
} }
func (b *EthAPIBackend) SuggestGasTipCap(ctx context.Context) (*big.Int, error) { func (b *EthAPIBackend) SuggestGasTipCap(ctx context.Context) (*big.Int, error) {

View File

@ -19,7 +19,6 @@ package eth
import ( import (
"bytes" "bytes"
"fmt" "fmt"
"math/big"
"reflect" "reflect"
"strings" "strings"
"testing" "testing"
@ -31,6 +30,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/trie" "github.com/ethereum/go-ethereum/trie"
"github.com/holiman/uint256"
"golang.org/x/exp/slices" "golang.org/x/exp/slices"
) )
@ -73,7 +73,7 @@ func TestAccountRange(t *testing.T) {
hash := common.HexToHash(fmt.Sprintf("%x", i)) hash := common.HexToHash(fmt.Sprintf("%x", i))
addr := common.BytesToAddress(crypto.Keccak256Hash(hash.Bytes()).Bytes()) addr := common.BytesToAddress(crypto.Keccak256Hash(hash.Bytes()).Bytes())
addrs[i] = addr addrs[i] = addr
sdb.SetBalance(addrs[i], big.NewInt(1)) sdb.SetBalance(addrs[i], uint256.NewInt(1))
if _, ok := m[addr]; ok { if _, ok := m[addr]; ok {
t.Fatalf("bad") t.Fatalf("bad")
} else { } else {

View File

@ -322,7 +322,7 @@ func (s *Ethereum) APIs() []rpc.API {
Service: NewMinerAPI(s), Service: NewMinerAPI(s),
}, { }, {
Namespace: "eth", Namespace: "eth",
Service: downloader.NewDownloaderAPI(s.handler.downloader, s.eventMux), Service: downloader.NewDownloaderAPI(s.handler.downloader, s.blockchain, s.eventMux),
}, { }, {
Namespace: "admin", Namespace: "admin",
Service: NewAdminAPI(s), Service: NewAdminAPI(s),

View File

@ -20,7 +20,6 @@ package catalyst
import ( import (
"errors" "errors"
"fmt" "fmt"
"math/big"
"sync" "sync"
"time" "time"
@ -34,6 +33,7 @@ import (
"github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/miner" "github.com/ethereum/go-ethereum/miner"
"github.com/ethereum/go-ethereum/node" "github.com/ethereum/go-ethereum/node"
"github.com/ethereum/go-ethereum/params/forks"
"github.com/ethereum/go-ethereum/rpc" "github.com/ethereum/go-ethereum/rpc"
) )
@ -180,54 +180,50 @@ func (api *ConsensusAPI) ForkchoiceUpdatedV1(update engine.ForkchoiceStateV1, pa
return engine.STATUS_INVALID, engine.InvalidParams.With(errors.New("forkChoiceUpdateV1 called post-shanghai")) return engine.STATUS_INVALID, engine.InvalidParams.With(errors.New("forkChoiceUpdateV1 called post-shanghai"))
} }
} }
return api.forkchoiceUpdated(update, payloadAttributes) return api.forkchoiceUpdated(update, payloadAttributes, engine.PayloadV1, false)
} }
// ForkchoiceUpdatedV2 is equivalent to V1 with the addition of withdrawals in the payload attributes. // ForkchoiceUpdatedV2 is equivalent to V1 with the addition of withdrawals in the payload attributes.
func (api *ConsensusAPI) ForkchoiceUpdatedV2(update engine.ForkchoiceStateV1, payloadAttributes *engine.PayloadAttributes) (engine.ForkChoiceResponse, error) { func (api *ConsensusAPI) ForkchoiceUpdatedV2(update engine.ForkchoiceStateV1, params *engine.PayloadAttributes) (engine.ForkChoiceResponse, error) {
if payloadAttributes != nil { if params != nil {
if err := api.verifyPayloadAttributes(payloadAttributes); err != nil { if params.Withdrawals == nil {
return engine.STATUS_INVALID, engine.InvalidParams.With(err) return engine.STATUS_INVALID, engine.InvalidParams.With(errors.New("missing withdrawals"))
}
if params.BeaconRoot != nil {
return engine.STATUS_INVALID, engine.InvalidParams.With(errors.New("unexpected beacon root"))
}
if api.eth.BlockChain().Config().LatestFork(params.Timestamp) != forks.Shanghai {
return engine.STATUS_INVALID, engine.UnsupportedFork.With(errors.New("forkchoiceUpdatedV2 must only be called for shanghai payloads"))
} }
} }
return api.forkchoiceUpdated(update, payloadAttributes) return api.forkchoiceUpdated(update, params, engine.PayloadV2, false)
} }
// ForkchoiceUpdatedV3 is equivalent to V2 with the addition of parent beacon block root in the payload attributes. // ForkchoiceUpdatedV3 is equivalent to V2 with the addition of parent beacon block root in the payload attributes.
func (api *ConsensusAPI) ForkchoiceUpdatedV3(update engine.ForkchoiceStateV1, payloadAttributes *engine.PayloadAttributes) (engine.ForkChoiceResponse, error) { func (api *ConsensusAPI) ForkchoiceUpdatedV3(update engine.ForkchoiceStateV1, params *engine.PayloadAttributes) (engine.ForkChoiceResponse, error) {
if payloadAttributes != nil { if params != nil {
if err := api.verifyPayloadAttributes(payloadAttributes); err != nil { // TODO(matt): according to https://github.com/ethereum/execution-apis/pull/498,
return engine.STATUS_INVALID, engine.InvalidParams.With(err) // payload attributes that are invalid should return error
// engine.InvalidPayloadAttributes. Once hive updates this, we should update
// on our end.
if params.Withdrawals == nil {
return engine.STATUS_INVALID, engine.InvalidParams.With(errors.New("missing withdrawals"))
}
if params.BeaconRoot == nil {
return engine.STATUS_INVALID, engine.InvalidParams.With(errors.New("missing beacon root"))
}
if api.eth.BlockChain().Config().LatestFork(params.Timestamp) != forks.Cancun {
return engine.STATUS_INVALID, engine.UnsupportedFork.With(errors.New("forkchoiceUpdatedV3 must only be called for cancun payloads"))
} }
} }
return api.forkchoiceUpdated(update, payloadAttributes) // TODO(matt): the spec requires that fcu is applied when called on a valid
// hash, even if params are wrong. To do this we need to split up
// forkchoiceUpdate into a function that only updates the head and then a
// function that kicks off block construction.
return api.forkchoiceUpdated(update, params, engine.PayloadV3, false)
} }
func (api *ConsensusAPI) verifyPayloadAttributes(attr *engine.PayloadAttributes) error { func (api *ConsensusAPI) forkchoiceUpdated(update engine.ForkchoiceStateV1, payloadAttributes *engine.PayloadAttributes, payloadVersion engine.PayloadVersion, simulatorMode bool) (engine.ForkChoiceResponse, error) {
c := api.eth.BlockChain().Config()
// Verify withdrawals attribute for Shanghai.
if err := checkAttribute(c.IsShanghai, attr.Withdrawals != nil, c.LondonBlock, attr.Timestamp); err != nil {
return fmt.Errorf("invalid withdrawals: %w", err)
}
// Verify beacon root attribute for Cancun.
if err := checkAttribute(c.IsCancun, attr.BeaconRoot != nil, c.LondonBlock, attr.Timestamp); err != nil {
return fmt.Errorf("invalid parent beacon block root: %w", err)
}
return nil
}
func checkAttribute(active func(*big.Int, uint64) bool, exists bool, block *big.Int, time uint64) error {
if active(block, time) && !exists {
return errors.New("fork active, missing expected attribute")
}
if !active(block, time) && exists {
return errors.New("fork inactive, unexpected attribute set")
}
return nil
}
func (api *ConsensusAPI) forkchoiceUpdated(update engine.ForkchoiceStateV1, payloadAttributes *engine.PayloadAttributes) (engine.ForkChoiceResponse, error) {
api.forkchoiceLock.Lock() api.forkchoiceLock.Lock()
defer api.forkchoiceLock.Unlock() defer api.forkchoiceLock.Unlock()
@ -334,7 +330,7 @@ func (api *ConsensusAPI) forkchoiceUpdated(update engine.ForkchoiceStateV1, payl
if merger := api.eth.Merger(); !merger.PoSFinalized() { if merger := api.eth.Merger(); !merger.PoSFinalized() {
merger.FinalizePoS() merger.FinalizePoS()
} }
// If the finalized block is not in our canonical tree, somethings wrong // If the finalized block is not in our canonical tree, something is wrong
finalBlock := api.eth.BlockChain().GetBlockByHash(update.FinalizedBlockHash) finalBlock := api.eth.BlockChain().GetBlockByHash(update.FinalizedBlockHash)
if finalBlock == nil { if finalBlock == nil {
log.Warn("Final block not available in database", "hash", update.FinalizedBlockHash) log.Warn("Final block not available in database", "hash", update.FinalizedBlockHash)
@ -346,7 +342,7 @@ func (api *ConsensusAPI) forkchoiceUpdated(update engine.ForkchoiceStateV1, payl
// Set the finalized block // Set the finalized block
api.eth.BlockChain().SetFinalized(finalBlock.Header()) api.eth.BlockChain().SetFinalized(finalBlock.Header())
} }
// Check if the safe block hash is in our canonical tree, if not somethings wrong // Check if the safe block hash is in our canonical tree, if not something is wrong
if update.SafeBlockHash != (common.Hash{}) { if update.SafeBlockHash != (common.Hash{}) {
safeBlock := api.eth.BlockChain().GetBlockByHash(update.SafeBlockHash) safeBlock := api.eth.BlockChain().GetBlockByHash(update.SafeBlockHash)
if safeBlock == nil { if safeBlock == nil {
@ -371,6 +367,7 @@ func (api *ConsensusAPI) forkchoiceUpdated(update engine.ForkchoiceStateV1, payl
Random: payloadAttributes.Random, Random: payloadAttributes.Random,
Withdrawals: payloadAttributes.Withdrawals, Withdrawals: payloadAttributes.Withdrawals,
BeaconRoot: payloadAttributes.BeaconRoot, BeaconRoot: payloadAttributes.BeaconRoot,
Version: payloadVersion,
} }
id := args.Id() id := args.Id()
// If we already are busy generating this work, then we do not need // If we already are busy generating this work, then we do not need
@ -378,6 +375,19 @@ func (api *ConsensusAPI) forkchoiceUpdated(update engine.ForkchoiceStateV1, payl
if api.localBlocks.has(id) { if api.localBlocks.has(id) {
return valid(&id), nil return valid(&id), nil
} }
// If the beacon chain is ran by a simulator, then transaction insertion,
// block insertion and block production will happen without any timing
// delay between them. This will cause flaky simulator executions due to
// the transaction pool running its internal reset operation on a back-
// ground thread. To avoid the racey behavior - in simulator mode - the
// pool will be explicitly blocked on its reset before continuing to the
// block production below.
if simulatorMode {
if err := api.eth.TxPool().Sync(); err != nil {
log.Error("Failed to sync transaction pool", "err", err)
return valid(nil), engine.InvalidPayloadAttributes.With(err)
}
}
payload, err := api.eth.Miner().BuildPayload(args) payload, err := api.eth.Miner().BuildPayload(args)
if err != nil { if err != nil {
log.Error("Failed to build payload", "err", err) log.Error("Failed to build payload", "err", err)
@ -421,6 +431,9 @@ func (api *ConsensusAPI) ExchangeTransitionConfigurationV1(config engine.Transit
// GetPayloadV1 returns a cached payload by id. // GetPayloadV1 returns a cached payload by id.
func (api *ConsensusAPI) GetPayloadV1(payloadID engine.PayloadID) (*engine.ExecutableData, error) { func (api *ConsensusAPI) GetPayloadV1(payloadID engine.PayloadID) (*engine.ExecutableData, error) {
if !payloadID.Is(engine.PayloadV1) {
return nil, engine.UnsupportedFork
}
data, err := api.getPayload(payloadID, false) data, err := api.getPayload(payloadID, false)
if err != nil { if err != nil {
return nil, err return nil, err
@ -430,11 +443,17 @@ func (api *ConsensusAPI) GetPayloadV1(payloadID engine.PayloadID) (*engine.Execu
// GetPayloadV2 returns a cached payload by id. // GetPayloadV2 returns a cached payload by id.
func (api *ConsensusAPI) GetPayloadV2(payloadID engine.PayloadID) (*engine.ExecutionPayloadEnvelope, error) { func (api *ConsensusAPI) GetPayloadV2(payloadID engine.PayloadID) (*engine.ExecutionPayloadEnvelope, error) {
if !payloadID.Is(engine.PayloadV1, engine.PayloadV2) {
return nil, engine.UnsupportedFork
}
return api.getPayload(payloadID, false) return api.getPayload(payloadID, false)
} }
// GetPayloadV3 returns a cached payload by id. // GetPayloadV3 returns a cached payload by id.
func (api *ConsensusAPI) GetPayloadV3(payloadID engine.PayloadID) (*engine.ExecutionPayloadEnvelope, error) { func (api *ConsensusAPI) GetPayloadV3(payloadID engine.PayloadID) (*engine.ExecutionPayloadEnvelope, error) {
if !payloadID.Is(engine.PayloadV3) {
return nil, engine.UnsupportedFork
}
return api.getPayload(payloadID, false) return api.getPayload(payloadID, false)
} }
@ -457,27 +476,39 @@ func (api *ConsensusAPI) NewPayloadV1(params engine.ExecutableData) (engine.Payl
// NewPayloadV2 creates an Eth1 block, inserts it in the chain, and returns the status of the chain. // NewPayloadV2 creates an Eth1 block, inserts it in the chain, and returns the status of the chain.
func (api *ConsensusAPI) NewPayloadV2(params engine.ExecutableData) (engine.PayloadStatusV1, error) { func (api *ConsensusAPI) NewPayloadV2(params engine.ExecutableData) (engine.PayloadStatusV1, error) {
if api.eth.BlockChain().Config().IsShanghai(new(big.Int).SetUint64(params.Number), params.Timestamp) { if api.eth.BlockChain().Config().IsCancun(api.eth.BlockChain().Config().LondonBlock, params.Timestamp) {
return engine.PayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("can't use new payload v2 post-shanghai"))
}
if api.eth.BlockChain().Config().LatestFork(params.Timestamp) == forks.Shanghai {
if params.Withdrawals == nil { if params.Withdrawals == nil {
return engine.PayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("nil withdrawals post-shanghai")) return engine.PayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("nil withdrawals post-shanghai"))
} }
} else if params.Withdrawals != nil { } else {
if params.Withdrawals != nil {
return engine.PayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("non-nil withdrawals pre-shanghai")) return engine.PayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("non-nil withdrawals pre-shanghai"))
} }
if api.eth.BlockChain().Config().IsCancun(new(big.Int).SetUint64(params.Number), params.Timestamp) { }
return engine.PayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("newPayloadV2 called post-cancun")) if params.ExcessBlobGas != nil {
return engine.PayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("non-nil excessBlobGas pre-cancun"))
}
if params.BlobGasUsed != nil {
return engine.PayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("non-nil params.BlobGasUsed pre-cancun"))
} }
return api.newPayload(params, nil, nil) return api.newPayload(params, nil, nil)
} }
// NewPayloadV3 creates an Eth1 block, inserts it in the chain, and returns the status of the chain. // NewPayloadV3 creates an Eth1 block, inserts it in the chain, and returns the status of the chain.
func (api *ConsensusAPI) NewPayloadV3(params engine.ExecutableData, versionedHashes []common.Hash, beaconRoot *common.Hash) (engine.PayloadStatusV1, error) { func (api *ConsensusAPI) NewPayloadV3(params engine.ExecutableData, versionedHashes []common.Hash, beaconRoot *common.Hash) (engine.PayloadStatusV1, error) {
if params.Withdrawals == nil {
return engine.PayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("nil withdrawals post-shanghai"))
}
if params.ExcessBlobGas == nil { if params.ExcessBlobGas == nil {
return engine.PayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("nil excessBlobGas post-cancun")) return engine.PayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("nil excessBlobGas post-cancun"))
} }
if params.BlobGasUsed == nil { if params.BlobGasUsed == nil {
return engine.PayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("nil params.BlobGasUsed post-cancun")) return engine.PayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("nil params.BlobGasUsed post-cancun"))
} }
if versionedHashes == nil { if versionedHashes == nil {
return engine.PayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("nil versionedHashes post-cancun")) return engine.PayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("nil versionedHashes post-cancun"))
} }
@ -485,10 +516,9 @@ func (api *ConsensusAPI) NewPayloadV3(params engine.ExecutableData, versionedHas
return engine.PayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("nil parentBeaconBlockRoot post-cancun")) return engine.PayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("nil parentBeaconBlockRoot post-cancun"))
} }
if !api.eth.BlockChain().Config().IsCancun(new(big.Int).SetUint64(params.Number), params.Timestamp) { if api.eth.BlockChain().Config().LatestFork(params.Timestamp) != forks.Cancun {
return engine.PayloadStatusV1{Status: engine.INVALID}, engine.UnsupportedFork.With(errors.New("newPayloadV3 called pre-cancun")) return engine.PayloadStatusV1{Status: engine.INVALID}, engine.UnsupportedFork.With(errors.New("newPayloadV3 must only be called for cancun payloads"))
} }
return api.newPayload(params, versionedHashes, beaconRoot) return api.newPayload(params, versionedHashes, beaconRoot)
} }

View File

@ -210,6 +210,7 @@ func TestEth2PrepareAndGetPayload(t *testing.T) {
FeeRecipient: blockParams.SuggestedFeeRecipient, FeeRecipient: blockParams.SuggestedFeeRecipient,
Random: blockParams.Random, Random: blockParams.Random,
BeaconRoot: blockParams.BeaconRoot, BeaconRoot: blockParams.BeaconRoot,
Version: engine.PayloadV1,
}).Id() }).Id()
execData, err := api.GetPayloadV1(payloadID) execData, err := api.GetPayloadV1(payloadID)
if err != nil { if err != nil {
@ -1076,6 +1077,7 @@ func TestWithdrawals(t *testing.T) {
Random: blockParams.Random, Random: blockParams.Random,
Withdrawals: blockParams.Withdrawals, Withdrawals: blockParams.Withdrawals,
BeaconRoot: blockParams.BeaconRoot, BeaconRoot: blockParams.BeaconRoot,
Version: engine.PayloadV2,
}).Id() }).Id()
execData, err := api.GetPayloadV2(payloadID) execData, err := api.GetPayloadV2(payloadID)
if err != nil { if err != nil {
@ -1124,6 +1126,7 @@ func TestWithdrawals(t *testing.T) {
Random: blockParams.Random, Random: blockParams.Random,
Withdrawals: blockParams.Withdrawals, Withdrawals: blockParams.Withdrawals,
BeaconRoot: blockParams.BeaconRoot, BeaconRoot: blockParams.BeaconRoot,
Version: engine.PayloadV2,
}).Id() }).Id()
execData, err = api.GetPayloadV2(payloadID) execData, err = api.GetPayloadV2(payloadID)
if err != nil { if err != nil {
@ -1237,7 +1240,18 @@ func TestNilWithdrawals(t *testing.T) {
} }
for _, test := range tests { for _, test := range tests {
_, err := api.ForkchoiceUpdatedV2(fcState, &test.blockParams) var (
err error
payloadVersion engine.PayloadVersion
shanghai = genesis.Config.IsShanghai(genesis.Config.LondonBlock, test.blockParams.Timestamp)
)
if !shanghai {
payloadVersion = engine.PayloadV1
_, err = api.ForkchoiceUpdatedV1(fcState, &test.blockParams)
} else {
payloadVersion = engine.PayloadV2
_, err = api.ForkchoiceUpdatedV2(fcState, &test.blockParams)
}
if test.wantErr { if test.wantErr {
if err == nil { if err == nil {
t.Fatal("wanted error on fcuv2 with invalid withdrawals") t.Fatal("wanted error on fcuv2 with invalid withdrawals")
@ -1254,14 +1268,20 @@ func TestNilWithdrawals(t *testing.T) {
Timestamp: test.blockParams.Timestamp, Timestamp: test.blockParams.Timestamp,
FeeRecipient: test.blockParams.SuggestedFeeRecipient, FeeRecipient: test.blockParams.SuggestedFeeRecipient,
Random: test.blockParams.Random, Random: test.blockParams.Random,
BeaconRoot: test.blockParams.BeaconRoot, Version: payloadVersion,
}).Id() }).Id()
execData, err := api.GetPayloadV2(payloadID) execData, err := api.GetPayloadV2(payloadID)
if err != nil { if err != nil {
t.Fatalf("error getting payload, err=%v", err) t.Fatalf("error getting payload, err=%v", err)
} }
if status, err := api.NewPayloadV2(*execData.ExecutionPayload); err != nil { var status engine.PayloadStatusV1
t.Fatalf("error validating payload: %v", err) if !shanghai {
status, err = api.NewPayloadV1(*execData.ExecutionPayload)
} else {
status, err = api.NewPayloadV2(*execData.ExecutionPayload)
}
if err != nil {
t.Fatalf("error validating payload: %v", err.(*engine.EngineAPIError).ErrorData())
} else if status.Status != engine.VALID { } else if status.Status != engine.VALID {
t.Fatalf("invalid payload") t.Fatalf("invalid payload")
} }
@ -1587,7 +1607,7 @@ func TestParentBeaconBlockRoot(t *testing.T) {
fcState := engine.ForkchoiceStateV1{ fcState := engine.ForkchoiceStateV1{
HeadBlockHash: parent.Hash(), HeadBlockHash: parent.Hash(),
} }
resp, err := api.ForkchoiceUpdatedV2(fcState, &blockParams) resp, err := api.ForkchoiceUpdatedV3(fcState, &blockParams)
if err != nil { if err != nil {
t.Fatalf("error preparing payload, err=%v", err.(*engine.EngineAPIError).ErrorData()) t.Fatalf("error preparing payload, err=%v", err.(*engine.EngineAPIError).ErrorData())
} }
@ -1603,6 +1623,7 @@ func TestParentBeaconBlockRoot(t *testing.T) {
Random: blockParams.Random, Random: blockParams.Random,
Withdrawals: blockParams.Withdrawals, Withdrawals: blockParams.Withdrawals,
BeaconRoot: blockParams.BeaconRoot, BeaconRoot: blockParams.BeaconRoot,
Version: engine.PayloadV3,
}).Id() }).Id()
execData, err := api.GetPayloadV3(payloadID) execData, err := api.GetPayloadV3(payloadID)
if err != nil { if err != nil {

View File

@ -155,12 +155,12 @@ func (c *SimulatedBeacon) sealBlock(withdrawals []*types.Withdrawal, timestamp u
var random [32]byte var random [32]byte
rand.Read(random[:]) rand.Read(random[:])
fcResponse, err := c.engineAPI.ForkchoiceUpdatedV2(c.curForkchoiceState, &engine.PayloadAttributes{ fcResponse, err := c.engineAPI.forkchoiceUpdated(c.curForkchoiceState, &engine.PayloadAttributes{
Timestamp: timestamp, Timestamp: timestamp,
SuggestedFeeRecipient: feeRecipient, SuggestedFeeRecipient: feeRecipient,
Withdrawals: withdrawals, Withdrawals: withdrawals,
Random: random, Random: random,
}) }, engine.PayloadV2, true)
if err != nil { if err != nil {
return err return err
} }

View File

@ -19,16 +19,20 @@ package downloader
import ( import (
"context" "context"
"sync" "sync"
"time"
"github.com/ethereum/go-ethereum" "github.com/ethereum/go-ethereum"
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/event" "github.com/ethereum/go-ethereum/event"
"github.com/ethereum/go-ethereum/rpc" "github.com/ethereum/go-ethereum/rpc"
) )
// DownloaderAPI provides an API which gives information about the current synchronisation status. // DownloaderAPI provides an API which gives information about the current
// It offers only methods that operates on data that can be available to anyone without security risks. // synchronisation status. It offers only methods that operates on data that
// can be available to anyone without security risks.
type DownloaderAPI struct { type DownloaderAPI struct {
d *Downloader d *Downloader
chain *core.BlockChain
mux *event.TypeMux mux *event.TypeMux
installSyncSubscription chan chan interface{} installSyncSubscription chan chan interface{}
uninstallSyncSubscription chan *uninstallSyncSubscriptionRequest uninstallSyncSubscription chan *uninstallSyncSubscriptionRequest
@ -38,31 +42,57 @@ type DownloaderAPI struct {
// listens for events from the downloader through the global event mux. In case it receives one of // listens for events from the downloader through the global event mux. In case it receives one of
// these events it broadcasts it to all syncing subscriptions that are installed through the // these events it broadcasts it to all syncing subscriptions that are installed through the
// installSyncSubscription channel. // installSyncSubscription channel.
func NewDownloaderAPI(d *Downloader, m *event.TypeMux) *DownloaderAPI { func NewDownloaderAPI(d *Downloader, chain *core.BlockChain, m *event.TypeMux) *DownloaderAPI {
api := &DownloaderAPI{ api := &DownloaderAPI{
d: d, d: d,
chain: chain,
mux: m, mux: m,
installSyncSubscription: make(chan chan interface{}), installSyncSubscription: make(chan chan interface{}),
uninstallSyncSubscription: make(chan *uninstallSyncSubscriptionRequest), uninstallSyncSubscription: make(chan *uninstallSyncSubscriptionRequest),
} }
go api.eventLoop() go api.eventLoop()
return api return api
} }
// eventLoop runs a loop until the event mux closes. It will install and uninstall new // eventLoop runs a loop until the event mux closes. It will install and uninstall
// sync subscriptions and broadcasts sync status updates to the installed sync subscriptions. // new sync subscriptions and broadcasts sync status updates to the installed sync
// subscriptions.
//
// The sync status pushed to subscriptions can be a stream like:
// >>> {Syncing: true, Progress: {...}}
// >>> {false}
//
// If the node is already synced up, then only a single event subscribers will
// receive is {false}.
func (api *DownloaderAPI) eventLoop() { func (api *DownloaderAPI) eventLoop() {
var ( var (
sub = api.mux.Subscribe(StartEvent{}, DoneEvent{}, FailedEvent{}) sub = api.mux.Subscribe(StartEvent{})
syncSubscriptions = make(map[chan interface{}]struct{}) syncSubscriptions = make(map[chan interface{}]struct{})
checkInterval = time.Second * 60
checkTimer = time.NewTimer(checkInterval)
// status flags
started bool
done bool
getProgress = func() ethereum.SyncProgress {
prog := api.d.Progress()
if txProg, err := api.chain.TxIndexProgress(); err == nil {
prog.TxIndexFinishedBlocks = txProg.Indexed
prog.TxIndexRemainingBlocks = txProg.Remaining
}
return prog
}
) )
defer checkTimer.Stop()
for { for {
select { select {
case i := <-api.installSyncSubscription: case i := <-api.installSyncSubscription:
syncSubscriptions[i] = struct{}{} syncSubscriptions[i] = struct{}{}
if done {
i <- false
}
case u := <-api.uninstallSyncSubscription: case u := <-api.uninstallSyncSubscription:
delete(syncSubscriptions, u.c) delete(syncSubscriptions, u.c)
close(u.uninstalled) close(u.uninstalled)
@ -70,21 +100,31 @@ func (api *DownloaderAPI) eventLoop() {
if event == nil { if event == nil {
return return
} }
var notification interface{}
switch event.Data.(type) { switch event.Data.(type) {
case StartEvent: case StartEvent:
notification = &SyncingResult{ started = true
}
case <-checkTimer.C:
if !started {
checkTimer.Reset(checkInterval)
continue
}
prog := getProgress()
if !prog.Done() {
notification := &SyncingResult{
Syncing: true, Syncing: true,
Status: api.d.Progress(), Status: prog,
} }
case DoneEvent, FailedEvent:
notification = false
}
// broadcast
for c := range syncSubscriptions { for c := range syncSubscriptions {
c <- notification c <- notification
} }
checkTimer.Reset(checkInterval)
continue
}
for c := range syncSubscriptions {
c <- false
}
done = true
} }
} }
} }

View File

@ -29,6 +29,7 @@ import (
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/prque" "github.com/ethereum/go-ethereum/common/prque"
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto/kzg4844"
"github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/metrics" "github.com/ethereum/go-ethereum/metrics"
"github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/params"
@ -810,7 +811,7 @@ func (q *queue) DeliverBodies(id string, txLists [][]*types.Transaction, txListH
return errInvalidBody return errInvalidBody
} }
for _, hash := range tx.BlobHashes() { for _, hash := range tx.BlobHashes() {
if hash[0] != params.BlobTxHashVersion { if !kzg4844.IsValidVersionedHash(hash[:]) {
return errInvalidBody return errInvalidBody
} }
} }

View File

@ -99,6 +99,7 @@ func BenchmarkFilters(b *testing.B) {
filter := sys.NewRangeFilter(0, -1, []common.Address{addr1, addr2, addr3, addr4}, nil) filter := sys.NewRangeFilter(0, -1, []common.Address{addr1, addr2, addr3, addr4}, nil)
for i := 0; i < b.N; i++ { for i := 0; i < b.N; i++ {
filter.begin = 0
logs, _ := filter.Logs(context.Background()) logs, _ := filter.Logs(context.Background())
if len(logs) != 4 { if len(logs) != 4 {
b.Fatal("expected 4 logs, got", len(logs)) b.Fatal("expected 4 logs, got", len(logs))

View File

@ -71,9 +71,9 @@ func Estimate(ctx context.Context, call *core.Message, opts *Options, gasCap uin
} }
// Recap the highest gas limit with account's available balance. // Recap the highest gas limit with account's available balance.
if feeCap.BitLen() != 0 { if feeCap.BitLen() != 0 {
balance := opts.State.GetBalance(call.From) balance := opts.State.GetBalance(call.From).ToBig()
available := new(big.Int).Set(balance) available := balance
if call.Value != nil { if call.Value != nil {
if call.Value.Cmp(available) >= 0 { if call.Value.Cmp(available) >= 0 {
return 0, nil, core.ErrInsufficientFundsForTransfer return 0, nil, core.ErrInsufficientFundsForTransfer

View File

@ -57,6 +57,7 @@ type peerSet struct {
lock sync.RWMutex lock sync.RWMutex
closed bool closed bool
quitCh chan struct{} // Quit channel to signal termination
} }
// newPeerSet creates a new peer set to track the active participants. // newPeerSet creates a new peer set to track the active participants.
@ -65,6 +66,7 @@ func newPeerSet() *peerSet {
peers: make(map[string]*ethPeer), peers: make(map[string]*ethPeer),
snapWait: make(map[string]chan *snap.Peer), snapWait: make(map[string]chan *snap.Peer),
snapPend: make(map[string]*snap.Peer), snapPend: make(map[string]*snap.Peer),
quitCh: make(chan struct{}),
} }
} }
@ -129,7 +131,15 @@ func (ps *peerSet) waitSnapExtension(peer *eth.Peer) (*snap.Peer, error) {
ps.snapWait[id] = wait ps.snapWait[id] = wait
ps.lock.Unlock() ps.lock.Unlock()
return <-wait, nil select {
case p := <-wait:
return p, nil
case <-ps.quitCh:
ps.lock.Lock()
delete(ps.snapWait, id)
ps.lock.Unlock()
return nil, errPeerSetClosed
}
} }
// registerPeer injects a new `eth` peer into the working set, or returns an error // registerPeer injects a new `eth` peer into the working set, or returns an error
@ -256,5 +266,8 @@ func (ps *peerSet) close() {
for _, p := range ps.peers { for _, p := range ps.peers {
p.Disconnect(p2p.DiscQuitting) p.Disconnect(p2p.DiscQuitting)
} }
if !ps.closed {
close(ps.quitCh)
}
ps.closed = true ps.closed = true
} }

View File

@ -38,6 +38,7 @@ import (
"github.com/ethereum/go-ethereum/trie/testutil" "github.com/ethereum/go-ethereum/trie/testutil"
"github.com/ethereum/go-ethereum/trie/triedb/pathdb" "github.com/ethereum/go-ethereum/trie/triedb/pathdb"
"github.com/ethereum/go-ethereum/trie/trienode" "github.com/ethereum/go-ethereum/trie/trienode"
"github.com/holiman/uint256"
"golang.org/x/crypto/sha3" "golang.org/x/crypto/sha3"
"golang.org/x/exp/slices" "golang.org/x/exp/slices"
) )
@ -1510,7 +1511,7 @@ func makeAccountTrieNoStorage(n int, scheme string) (string, *trie.Trie, []*kv)
for i := uint64(1); i <= uint64(n); i++ { for i := uint64(1); i <= uint64(n); i++ {
value, _ := rlp.EncodeToBytes(&types.StateAccount{ value, _ := rlp.EncodeToBytes(&types.StateAccount{
Nonce: i, Nonce: i,
Balance: big.NewInt(int64(i)), Balance: uint256.NewInt(i),
Root: types.EmptyRootHash, Root: types.EmptyRootHash,
CodeHash: getCodeHash(i), CodeHash: getCodeHash(i),
}) })
@ -1561,7 +1562,7 @@ func makeBoundaryAccountTrie(scheme string, n int) (string, *trie.Trie, []*kv) {
for i := 0; i < len(boundaries); i++ { for i := 0; i < len(boundaries); i++ {
value, _ := rlp.EncodeToBytes(&types.StateAccount{ value, _ := rlp.EncodeToBytes(&types.StateAccount{
Nonce: uint64(0), Nonce: uint64(0),
Balance: big.NewInt(int64(i)), Balance: uint256.NewInt(uint64(i)),
Root: types.EmptyRootHash, Root: types.EmptyRootHash,
CodeHash: getCodeHash(uint64(i)), CodeHash: getCodeHash(uint64(i)),
}) })
@ -1573,7 +1574,7 @@ func makeBoundaryAccountTrie(scheme string, n int) (string, *trie.Trie, []*kv) {
for i := uint64(1); i <= uint64(n); i++ { for i := uint64(1); i <= uint64(n); i++ {
value, _ := rlp.EncodeToBytes(&types.StateAccount{ value, _ := rlp.EncodeToBytes(&types.StateAccount{
Nonce: i, Nonce: i,
Balance: big.NewInt(int64(i)), Balance: uint256.NewInt(i),
Root: types.EmptyRootHash, Root: types.EmptyRootHash,
CodeHash: getCodeHash(i), CodeHash: getCodeHash(i),
}) })
@ -1617,7 +1618,7 @@ func makeAccountTrieWithStorageWithUniqueStorage(scheme string, accounts, slots
value, _ := rlp.EncodeToBytes(&types.StateAccount{ value, _ := rlp.EncodeToBytes(&types.StateAccount{
Nonce: i, Nonce: i,
Balance: big.NewInt(int64(i)), Balance: uint256.NewInt(i),
Root: stRoot, Root: stRoot,
CodeHash: codehash, CodeHash: codehash,
}) })
@ -1683,7 +1684,7 @@ func makeAccountTrieWithStorage(scheme string, accounts, slots int, code, bounda
value, _ := rlp.EncodeToBytes(&types.StateAccount{ value, _ := rlp.EncodeToBytes(&types.StateAccount{
Nonce: i, Nonce: i,
Balance: big.NewInt(int64(i)), Balance: uint256.NewInt(i),
Root: stRoot, Root: stRoot,
CodeHash: codehash, CodeHash: codehash,
}) })

View File

@ -228,24 +228,6 @@ func (cs *chainSyncer) startSync(op *chainSyncOp) {
// doSync synchronizes the local blockchain with a remote peer. // doSync synchronizes the local blockchain with a remote peer.
func (h *handler) doSync(op *chainSyncOp) error { func (h *handler) doSync(op *chainSyncOp) error {
if op.mode == downloader.SnapSync {
// Before launch the snap sync, we have to ensure user uses the same
// txlookup limit.
// The main concern here is: during the snap sync Geth won't index the
// block(generate tx indices) before the HEAD-limit. But if user changes
// the limit in the next snap sync(e.g. user kill Geth manually and
// restart) then it will be hard for Geth to figure out the oldest block
// has been indexed. So here for the user-experience wise, it's non-optimal
// that user can't change limit during the snap sync. If changed, Geth
// will just blindly use the original one.
limit := h.chain.TxLookupLimit()
if stored := rawdb.ReadFastTxLookupLimit(h.database); stored == nil {
rawdb.WriteFastTxLookupLimit(h.database, limit)
} else if *stored != limit {
h.chain.SetTxLookupLimit(*stored)
log.Warn("Update txLookup limit", "provided", limit, "updated", *stored)
}
}
// Run the sync cycle, and disable snap sync if we're past the pivot block // Run the sync cycle, and disable snap sync if we're past the pivot block
err := h.downloader.LegacySync(op.peer.ID(), op.head, op.td, h.chain.Config().TerminalTotalDifficulty, op.mode) err := h.downloader.LegacySync(op.peer.ID(), op.head, op.td, h.chain.Config().TerminalTotalDifficulty, op.mode)
if err != nil { if err != nil {

View File

@ -80,7 +80,7 @@ type Backend interface {
HeaderByNumber(ctx context.Context, number rpc.BlockNumber) (*types.Header, error) HeaderByNumber(ctx context.Context, number rpc.BlockNumber) (*types.Header, error)
BlockByHash(ctx context.Context, hash common.Hash) (*types.Block, error) BlockByHash(ctx context.Context, hash common.Hash) (*types.Block, error)
BlockByNumber(ctx context.Context, number rpc.BlockNumber) (*types.Block, error) BlockByNumber(ctx context.Context, number rpc.BlockNumber) (*types.Block, error)
GetTransaction(ctx context.Context, txHash common.Hash) (*types.Transaction, common.Hash, uint64, uint64, error) GetTransaction(ctx context.Context, txHash common.Hash) (bool, *types.Transaction, common.Hash, uint64, uint64, error)
RPCGasCap() uint64 RPCGasCap() uint64
ChainConfig() *params.ChainConfig ChainConfig() *params.ChainConfig
Engine() consensus.Engine Engine() consensus.Engine
@ -826,12 +826,12 @@ func containsTx(block *types.Block, hash common.Hash) bool {
// TraceTransaction returns the structured logs created during the execution of EVM // TraceTransaction returns the structured logs created during the execution of EVM
// and returns them as a JSON object. // and returns them as a JSON object.
func (api *API) TraceTransaction(ctx context.Context, hash common.Hash, config *TraceConfig) (interface{}, error) { func (api *API) TraceTransaction(ctx context.Context, hash common.Hash, config *TraceConfig) (interface{}, error) {
tx, blockHash, blockNumber, index, err := api.backend.GetTransaction(ctx, hash) found, _, blockHash, blockNumber, index, err := api.backend.GetTransaction(ctx, hash)
if err != nil { if err != nil {
return nil, err return nil, ethapi.NewTxIndexingError()
} }
// Only mined txes are supported // Only mined txes are supported
if tx == nil { if !found {
return nil, errTxNotFound return nil, errTxNotFound
} }
// It shouldn't happen in practice. // It shouldn't happen in practice.

View File

@ -113,9 +113,9 @@ func (b *testBackend) BlockByNumber(ctx context.Context, number rpc.BlockNumber)
return b.chain.GetBlockByNumber(uint64(number)), nil return b.chain.GetBlockByNumber(uint64(number)), nil
} }
func (b *testBackend) GetTransaction(ctx context.Context, txHash common.Hash) (*types.Transaction, common.Hash, uint64, uint64, error) { func (b *testBackend) GetTransaction(ctx context.Context, txHash common.Hash) (bool, *types.Transaction, common.Hash, uint64, uint64, error) {
tx, hash, blockNumber, index := rawdb.ReadTransaction(b.chaindb, txHash) tx, hash, blockNumber, index := rawdb.ReadTransaction(b.chaindb, txHash)
return tx, hash, blockNumber, index, nil return tx != nil, tx, hash, blockNumber, index, nil
} }
func (b *testBackend) RPCGasCap() uint64 { func (b *testBackend) RPCGasCap() uint64 {

View File

@ -123,11 +123,6 @@ func testCallTracer(tracerName string, dirPath string, t *testing.T) {
// Configure a blockchain with the given prestate // Configure a blockchain with the given prestate
var ( var (
signer = types.MakeSigner(test.Genesis.Config, new(big.Int).SetUint64(uint64(test.Context.Number)), uint64(test.Context.Time)) signer = types.MakeSigner(test.Genesis.Config, new(big.Int).SetUint64(uint64(test.Context.Number)), uint64(test.Context.Time))
origin, _ = signer.Sender(tx)
txContext = vm.TxContext{
Origin: origin,
GasPrice: tx.GasPrice(),
}
context = vm.BlockContext{ context = vm.BlockContext{
CanTransfer: core.CanTransfer, CanTransfer: core.CanTransfer,
Transfer: core.Transfer, Transfer: core.Transfer,
@ -146,11 +141,11 @@ func testCallTracer(tracerName string, dirPath string, t *testing.T) {
if err != nil { if err != nil {
t.Fatalf("failed to create call tracer: %v", err) t.Fatalf("failed to create call tracer: %v", err)
} }
evm := vm.NewEVM(context, txContext, statedb, test.Genesis.Config, vm.Config{Tracer: tracer}) msg, err := core.TransactionToMessage(tx, signer, context.BaseFee)
msg, err := core.TransactionToMessage(tx, signer, nil)
if err != nil { if err != nil {
t.Fatalf("failed to prepare transaction for tracing: %v", err) t.Fatalf("failed to prepare transaction for tracing: %v", err)
} }
evm := vm.NewEVM(context, core.NewEVMTxContext(msg), statedb, test.Genesis.Config, vm.Config{Tracer: tracer})
vmRet, err := core.ApplyMessage(evm, msg, new(core.GasPool).AddGas(tx.Gas())) vmRet, err := core.ApplyMessage(evm, msg, new(core.GasPool).AddGas(tx.Gas()))
if err != nil { if err != nil {
t.Fatalf("failed to execute transaction: %v", err) t.Fatalf("failed to execute transaction: %v", err)
@ -222,10 +217,6 @@ func benchTracer(tracerName string, test *callTracerTest, b *testing.B) {
b.Fatalf("failed to parse testcase input: %v", err) b.Fatalf("failed to parse testcase input: %v", err)
} }
signer := types.MakeSigner(test.Genesis.Config, new(big.Int).SetUint64(uint64(test.Context.Number)), uint64(test.Context.Time)) signer := types.MakeSigner(test.Genesis.Config, new(big.Int).SetUint64(uint64(test.Context.Number)), uint64(test.Context.Time))
msg, err := core.TransactionToMessage(tx, signer, nil)
if err != nil {
b.Fatalf("failed to prepare transaction for tracing: %v", err)
}
origin, _ := signer.Sender(tx) origin, _ := signer.Sender(tx)
txContext := vm.TxContext{ txContext := vm.TxContext{
Origin: origin, Origin: origin,
@ -240,6 +231,10 @@ func benchTracer(tracerName string, test *callTracerTest, b *testing.B) {
Difficulty: (*big.Int)(test.Context.Difficulty), Difficulty: (*big.Int)(test.Context.Difficulty),
GasLimit: uint64(test.Context.GasLimit), GasLimit: uint64(test.Context.GasLimit),
} }
msg, err := core.TransactionToMessage(tx, signer, context.BaseFee)
if err != nil {
b.Fatalf("failed to prepare transaction for tracing: %v", err)
}
triedb, _, statedb := tests.MakePreState(rawdb.NewMemoryDatabase(), test.Genesis.Alloc, false, rawdb.HashScheme) triedb, _, statedb := tests.MakePreState(rawdb.NewMemoryDatabase(), test.Genesis.Alloc, false, rawdb.HashScheme)
defer triedb.Close() defer triedb.Close()

View File

@ -86,11 +86,6 @@ func flatCallTracerTestRunner(tracerName string, filename string, dirPath string
return fmt.Errorf("failed to parse testcase input: %v", err) return fmt.Errorf("failed to parse testcase input: %v", err)
} }
signer := types.MakeSigner(test.Genesis.Config, new(big.Int).SetUint64(uint64(test.Context.Number)), uint64(test.Context.Time)) signer := types.MakeSigner(test.Genesis.Config, new(big.Int).SetUint64(uint64(test.Context.Number)), uint64(test.Context.Time))
origin, _ := signer.Sender(tx)
txContext := vm.TxContext{
Origin: origin,
GasPrice: tx.GasPrice(),
}
context := vm.BlockContext{ context := vm.BlockContext{
CanTransfer: core.CanTransfer, CanTransfer: core.CanTransfer,
Transfer: core.Transfer, Transfer: core.Transfer,
@ -108,12 +103,11 @@ func flatCallTracerTestRunner(tracerName string, filename string, dirPath string
if err != nil { if err != nil {
return fmt.Errorf("failed to create call tracer: %v", err) return fmt.Errorf("failed to create call tracer: %v", err)
} }
evm := vm.NewEVM(context, txContext, statedb, test.Genesis.Config, vm.Config{Tracer: tracer}) msg, err := core.TransactionToMessage(tx, signer, context.BaseFee)
msg, err := core.TransactionToMessage(tx, signer, nil)
if err != nil { if err != nil {
return fmt.Errorf("failed to prepare transaction for tracing: %v", err) return fmt.Errorf("failed to prepare transaction for tracing: %v", err)
} }
evm := vm.NewEVM(context, core.NewEVMTxContext(msg), statedb, test.Genesis.Config, vm.Config{Tracer: tracer})
st := core.NewStateTransition(evm, msg, new(core.GasPool).AddGas(tx.Gas())) st := core.NewStateTransition(evm, msg, new(core.GasPool).AddGas(tx.Gas()))
if _, err = st.TransitionDb(); err != nil { if _, err = st.TransitionDb(); err != nil {

View File

@ -93,11 +93,6 @@ func testPrestateDiffTracer(tracerName string, dirPath string, t *testing.T) {
// Configure a blockchain with the given prestate // Configure a blockchain with the given prestate
var ( var (
signer = types.MakeSigner(test.Genesis.Config, new(big.Int).SetUint64(uint64(test.Context.Number)), uint64(test.Context.Time)) signer = types.MakeSigner(test.Genesis.Config, new(big.Int).SetUint64(uint64(test.Context.Number)), uint64(test.Context.Time))
origin, _ = signer.Sender(tx)
txContext = vm.TxContext{
Origin: origin,
GasPrice: tx.GasPrice(),
}
context = vm.BlockContext{ context = vm.BlockContext{
CanTransfer: core.CanTransfer, CanTransfer: core.CanTransfer,
Transfer: core.Transfer, Transfer: core.Transfer,
@ -116,11 +111,11 @@ func testPrestateDiffTracer(tracerName string, dirPath string, t *testing.T) {
if err != nil { if err != nil {
t.Fatalf("failed to create call tracer: %v", err) t.Fatalf("failed to create call tracer: %v", err)
} }
evm := vm.NewEVM(context, txContext, statedb, test.Genesis.Config, vm.Config{Tracer: tracer}) msg, err := core.TransactionToMessage(tx, signer, context.BaseFee)
msg, err := core.TransactionToMessage(tx, signer, nil)
if err != nil { if err != nil {
t.Fatalf("failed to prepare transaction for tracing: %v", err) t.Fatalf("failed to prepare transaction for tracing: %v", err)
} }
evm := vm.NewEVM(context, core.NewEVMTxContext(msg), statedb, test.Genesis.Config, vm.Config{Tracer: tracer})
st := core.NewStateTransition(evm, msg, new(core.GasPool).AddGas(tx.Gas())) st := core.NewStateTransition(evm, msg, new(core.GasPool).AddGas(tx.Gas()))
if _, err = st.TransitionDb(); err != nil { if _, err = st.TransitionDb(); err != nil {
t.Fatalf("failed to execute transaction: %v", err) t.Fatalf("failed to execute transaction: %v", err)

View File

@ -83,7 +83,7 @@
}, },
"post": { "post": {
"0x808b4da0be6c9512e948521452227efc619bea52": { "0x808b4da0be6c9512e948521452227efc619bea52": {
"balance": "0x2cd72a36dd031f089", "balance": "0x2cd987071ba2346b6",
"nonce": 1223933 "nonce": 1223933
}, },
"0x8f03f1a3f10c05e7cccf75c1fd10168e06659be7": { "0x8f03f1a3f10c05e7cccf75c1fd10168e06659be7": {

View File

@ -29,6 +29,7 @@ import (
"github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/eth/tracers" "github.com/ethereum/go-ethereum/eth/tracers"
"github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/params"
"github.com/holiman/uint256"
) )
type account struct{} type account struct{}
@ -37,9 +38,9 @@ func (account) SubBalance(amount *big.Int) {}
func (account) AddBalance(amount *big.Int) {} func (account) AddBalance(amount *big.Int) {}
func (account) SetAddress(common.Address) {} func (account) SetAddress(common.Address) {}
func (account) Value() *big.Int { return nil } func (account) Value() *big.Int { return nil }
func (account) SetBalance(*big.Int) {} func (account) SetBalance(*uint256.Int) {}
func (account) SetNonce(uint64) {} func (account) SetNonce(uint64) {}
func (account) Balance() *big.Int { return nil } func (account) Balance() *uint256.Int { return nil }
func (account) Address() common.Address { return common.Address{} } func (account) Address() common.Address { return common.Address{} }
func (account) SetCode(common.Hash, []byte) {} func (account) SetCode(common.Hash, []byte) {}
func (account) ForEachStorage(cb func(key, value common.Hash) bool) {} func (account) ForEachStorage(cb func(key, value common.Hash) bool) {}
@ -49,7 +50,7 @@ type dummyStatedb struct {
} }
func (*dummyStatedb) GetRefund() uint64 { return 1337 } func (*dummyStatedb) GetRefund() uint64 { return 1337 }
func (*dummyStatedb) GetBalance(addr common.Address) *big.Int { return new(big.Int) } func (*dummyStatedb) GetBalance(addr common.Address) *uint256.Int { return new(uint256.Int) }
type vmContext struct { type vmContext struct {
blockCtx vm.BlockContext blockCtx vm.BlockContext
@ -65,7 +66,7 @@ func runTrace(tracer tracers.Tracer, vmctx *vmContext, chaincfg *params.ChainCon
env = vm.NewEVM(vmctx.blockCtx, vmctx.txCtx, &dummyStatedb{}, chaincfg, vm.Config{Tracer: tracer}) env = vm.NewEVM(vmctx.blockCtx, vmctx.txCtx, &dummyStatedb{}, chaincfg, vm.Config{Tracer: tracer})
gasLimit uint64 = 31000 gasLimit uint64 = 31000
startGas uint64 = 10000 startGas uint64 = 10000
value = big.NewInt(0) value = uint256.NewInt(0)
contract = vm.NewContract(account{}, account{}, value, startGas) contract = vm.NewContract(account{}, account{}, value, startGas)
) )
contract.Code = []byte{byte(vm.PUSH1), 0x1, byte(vm.PUSH1), 0x1, 0x0} contract.Code = []byte{byte(vm.PUSH1), 0x1, byte(vm.PUSH1), 0x1, 0x0}
@ -74,7 +75,7 @@ func runTrace(tracer tracers.Tracer, vmctx *vmContext, chaincfg *params.ChainCon
} }
tracer.CaptureTxStart(gasLimit) tracer.CaptureTxStart(gasLimit)
tracer.CaptureStart(env, contract.Caller(), contract.Address(), false, []byte{}, startGas, value) tracer.CaptureStart(env, contract.Caller(), contract.Address(), false, []byte{}, startGas, value.ToBig())
ret, err := env.Interpreter().Run(contract, []byte{}, false) ret, err := env.Interpreter().Run(contract, []byte{}, false)
tracer.CaptureEnd(ret, startGas-contract.Gas, err) tracer.CaptureEnd(ret, startGas-contract.Gas, err)
// Rest gas assumes no refund // Rest gas assumes no refund
@ -182,7 +183,7 @@ func TestHaltBetweenSteps(t *testing.T) {
} }
env := vm.NewEVM(vm.BlockContext{BlockNumber: big.NewInt(1)}, vm.TxContext{GasPrice: big.NewInt(1)}, &dummyStatedb{}, params.TestChainConfig, vm.Config{Tracer: tracer}) env := vm.NewEVM(vm.BlockContext{BlockNumber: big.NewInt(1)}, vm.TxContext{GasPrice: big.NewInt(1)}, &dummyStatedb{}, params.TestChainConfig, vm.Config{Tracer: tracer})
scope := &vm.ScopeContext{ scope := &vm.ScopeContext{
Contract: vm.NewContract(&account{}, &account{}, big.NewInt(0), 0), Contract: vm.NewContract(&account{}, &account{}, uint256.NewInt(0), 0),
} }
tracer.CaptureStart(env, common.Address{}, common.Address{}, false, []byte{}, 0, big.NewInt(0)) tracer.CaptureStart(env, common.Address{}, common.Address{}, false, []byte{}, 0, big.NewInt(0))
tracer.CaptureState(0, 0, 0, 0, scope, nil, 0, nil) tracer.CaptureState(0, 0, 0, 0, scope, nil, 0, nil)
@ -273,7 +274,7 @@ func TestEnterExit(t *testing.T) {
t.Fatal(err) t.Fatal(err)
} }
scope := &vm.ScopeContext{ scope := &vm.ScopeContext{
Contract: vm.NewContract(&account{}, &account{}, big.NewInt(0), 0), Contract: vm.NewContract(&account{}, &account{}, uint256.NewInt(0), 0),
} }
tracer.CaptureEnter(vm.CALL, scope.Contract.Caller(), scope.Contract.Address(), []byte{}, 1000, new(big.Int)) tracer.CaptureEnter(vm.CALL, scope.Contract.Caller(), scope.Contract.Address(), []byte{}, 1000, new(big.Int))
tracer.CaptureExit([]byte{}, 400, nil) tracer.CaptureExit([]byte{}, 400, nil)

View File

@ -26,6 +26,7 @@ import (
"github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/params"
"github.com/holiman/uint256"
) )
type dummyContractRef struct { type dummyContractRef struct {
@ -56,7 +57,7 @@ func TestStoreCapture(t *testing.T) {
var ( var (
logger = NewStructLogger(nil) logger = NewStructLogger(nil)
env = vm.NewEVM(vm.BlockContext{}, vm.TxContext{}, &dummyStatedb{}, params.TestChainConfig, vm.Config{Tracer: logger}) env = vm.NewEVM(vm.BlockContext{}, vm.TxContext{}, &dummyStatedb{}, params.TestChainConfig, vm.Config{Tracer: logger})
contract = vm.NewContract(&dummyContractRef{}, &dummyContractRef{}, new(big.Int), 100000) contract = vm.NewContract(&dummyContractRef{}, &dummyContractRef{}, new(uint256.Int), 100000)
) )
contract.Code = []byte{byte(vm.PUSH1), 0x1, byte(vm.PUSH1), 0x0, byte(vm.SSTORE)} contract.Code = []byte{byte(vm.PUSH1), 0x1, byte(vm.PUSH1), 0x0, byte(vm.SSTORE)}
var index common.Hash var index common.Hash

View File

@ -195,7 +195,7 @@ func (t *prestateTracer) CaptureTxEnd(restGas uint64) {
} }
modified := false modified := false
postAccount := &account{Storage: make(map[common.Hash]common.Hash)} postAccount := &account{Storage: make(map[common.Hash]common.Hash)}
newBalance := t.env.StateDB.GetBalance(addr) newBalance := t.env.StateDB.GetBalance(addr).ToBig()
newNonce := t.env.StateDB.GetNonce(addr) newNonce := t.env.StateDB.GetNonce(addr)
newCode := t.env.StateDB.GetCode(addr) newCode := t.env.StateDB.GetCode(addr)
@ -279,7 +279,7 @@ func (t *prestateTracer) lookupAccount(addr common.Address) {
} }
t.pre[addr] = &account{ t.pre[addr] = &account{
Balance: t.env.StateDB.GetBalance(addr), Balance: t.env.StateDB.GetBalance(addr).ToBig(),
Nonce: t.env.StateDB.GetNonce(addr), Nonce: t.env.StateDB.GetNonce(addr),
Code: t.env.StateDB.GetCode(addr), Code: t.env.StateDB.GetCode(addr),
Storage: make(map[common.Hash]common.Hash), Storage: make(map[common.Hash]common.Hash),

View File

@ -90,7 +90,7 @@ func BenchmarkTransactionTrace(b *testing.B) {
//EnableReturnData: false, //EnableReturnData: false,
}) })
evm := vm.NewEVM(context, txContext, statedb, params.AllEthashProtocolChanges, vm.Config{Tracer: tracer}) evm := vm.NewEVM(context, txContext, statedb, params.AllEthashProtocolChanges, vm.Config{Tracer: tracer})
msg, err := core.TransactionToMessage(tx, signer, nil) msg, err := core.TransactionToMessage(tx, signer, context.BaseFee)
if err != nil { if err != nil {
b.Fatalf("failed to prepare transaction for tracing: %v", err) b.Fatalf("failed to prepare transaction for tracing: %v", err)
} }

View File

@ -662,6 +662,9 @@ func toCallArg(msg ethereum.CallMsg) interface{} {
if msg.GasTipCap != nil { if msg.GasTipCap != nil {
arg["maxPriorityFeePerGas"] = (*hexutil.Big)(msg.GasTipCap) arg["maxPriorityFeePerGas"] = (*hexutil.Big)(msg.GasTipCap)
} }
if msg.AccessList != nil {
arg["accessList"] = msg.AccessList
}
return arg return arg
} }
@ -686,6 +689,8 @@ type rpcProgress struct {
HealedBytecodeBytes hexutil.Uint64 HealedBytecodeBytes hexutil.Uint64
HealingTrienodes hexutil.Uint64 HealingTrienodes hexutil.Uint64
HealingBytecode hexutil.Uint64 HealingBytecode hexutil.Uint64
TxIndexFinishedBlocks hexutil.Uint64
TxIndexRemainingBlocks hexutil.Uint64
} }
func (p *rpcProgress) toSyncProgress() *ethereum.SyncProgress { func (p *rpcProgress) toSyncProgress() *ethereum.SyncProgress {
@ -710,5 +715,7 @@ func (p *rpcProgress) toSyncProgress() *ethereum.SyncProgress {
HealedBytecodeBytes: uint64(p.HealedBytecodeBytes), HealedBytecodeBytes: uint64(p.HealedBytecodeBytes),
HealingTrienodes: uint64(p.HealingTrienodes), HealingTrienodes: uint64(p.HealingTrienodes),
HealingBytecode: uint64(p.HealingBytecode), HealingBytecode: uint64(p.HealingBytecode),
TxIndexFinishedBlocks: uint64(p.TxIndexFinishedBlocks),
TxIndexRemainingBlocks: uint64(p.TxIndexRemainingBlocks),
} }
} }

View File

@ -231,6 +231,13 @@ func newTestBackend(t *testing.T) (*node.Node, []*types.Block) {
if _, err := ethservice.BlockChain().InsertChain(blocks[1:]); err != nil { if _, err := ethservice.BlockChain().InsertChain(blocks[1:]); err != nil {
t.Fatalf("can't import test blocks: %v", err) t.Fatalf("can't import test blocks: %v", err)
} }
// Ensure the tx indexing is fully generated
for ; ; time.Sleep(time.Millisecond * 100) {
progress, err := ethservice.BlockChain().TxIndexProgress()
if err == nil && progress.Done() {
break
}
}
return n, blocks return n, blocks
} }
@ -264,7 +271,7 @@ func TestEthClient(t *testing.T) {
func(t *testing.T) { testBalanceAt(t, client) }, func(t *testing.T) { testBalanceAt(t, client) },
}, },
"TxInBlockInterrupted": { "TxInBlockInterrupted": {
func(t *testing.T) { testTransactionInBlockInterrupted(t, client) }, func(t *testing.T) { testTransactionInBlock(t, client) },
}, },
"ChainID": { "ChainID": {
func(t *testing.T) { testChainID(t, client) }, func(t *testing.T) { testChainID(t, client) },
@ -329,7 +336,7 @@ func testHeader(t *testing.T, chain []*types.Block, client *rpc.Client) {
got.Number = big.NewInt(0) // hack to make DeepEqual work got.Number = big.NewInt(0) // hack to make DeepEqual work
} }
if !reflect.DeepEqual(got, tt.want) { if !reflect.DeepEqual(got, tt.want) {
t.Fatalf("HeaderByNumber(%v)\n = %v\nwant %v", tt.block, got, tt.want) t.Fatalf("HeaderByNumber(%v) got = %v, want %v", tt.block, got, tt.want)
} }
}) })
} }
@ -381,7 +388,7 @@ func testBalanceAt(t *testing.T, client *rpc.Client) {
} }
} }
func testTransactionInBlockInterrupted(t *testing.T, client *rpc.Client) { func testTransactionInBlock(t *testing.T, client *rpc.Client) {
ec := NewClient(client) ec := NewClient(client)
// Get current block by number. // Get current block by number.
@ -390,22 +397,27 @@ func testTransactionInBlockInterrupted(t *testing.T, client *rpc.Client) {
t.Fatalf("unexpected error: %v", err) t.Fatalf("unexpected error: %v", err)
} }
// Test tx in block interrupted.
ctx, cancel := context.WithCancel(context.Background())
cancel()
<-ctx.Done() // Ensure the close of the Done channel
tx, err := ec.TransactionInBlock(ctx, block.Hash(), 0)
if tx != nil {
t.Fatal("transaction should be nil")
}
if err == nil || err == ethereum.NotFound {
t.Fatal("error should not be nil/notfound")
}
// Test tx in block not found. // Test tx in block not found.
if _, err := ec.TransactionInBlock(context.Background(), block.Hash(), 20); err != ethereum.NotFound { if _, err := ec.TransactionInBlock(context.Background(), block.Hash(), 20); err != ethereum.NotFound {
t.Fatal("error should be ethereum.NotFound") t.Fatal("error should be ethereum.NotFound")
} }
// Test tx in block found.
tx, err := ec.TransactionInBlock(context.Background(), block.Hash(), 0)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if tx.Hash() != testTx1.Hash() {
t.Fatalf("unexpected transaction: %v", tx)
}
tx, err = ec.TransactionInBlock(context.Background(), block.Hash(), 1)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if tx.Hash() != testTx2.Hash() {
t.Fatalf("unexpected transaction: %v", tx)
}
} }
func testChainID(t *testing.T, client *rpc.Client) { func testChainID(t *testing.T, client *rpc.Client) {

View File

@ -236,6 +236,15 @@ func toCallArg(msg ethereum.CallMsg) interface{} {
if msg.GasPrice != nil { if msg.GasPrice != nil {
arg["gasPrice"] = (*hexutil.Big)(msg.GasPrice) arg["gasPrice"] = (*hexutil.Big)(msg.GasPrice)
} }
if msg.GasFeeCap != nil {
arg["maxFeePerGas"] = (*hexutil.Big)(msg.GasFeeCap)
}
if msg.GasTipCap != nil {
arg["maxPriorityFeePerGas"] = (*hexutil.Big)(msg.GasTipCap)
}
if msg.AccessList != nil {
arg["accessList"] = msg.AccessList
}
return arg return arg
} }

Some files were not shown because too many files have changed in this diff Show More