Compare commits
147 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
26675454bf | ||
|
1d99573192 | ||
|
f38abc55f1 | ||
|
dfeb2f7e80 | ||
|
bb1f7ebf20 | ||
|
d02c605367 | ||
|
c368f728c1 | ||
|
5566e5d152 | ||
|
57feabea66 | ||
|
16ecdd5839 | ||
|
85b9bdd641 | ||
|
6902485767 | ||
|
fb4007bb22 | ||
|
0a68558e7e | ||
|
fd604becbb | ||
|
5f98020a21 | ||
|
a580f7d6c5 | ||
|
12f0ff40b1 | ||
|
971df49fe2 | ||
|
f4ad493870 | ||
|
2a451f9eb3 | ||
|
278ec7176a | ||
|
deff5056fb | ||
|
c27bd3481e | ||
|
0fbc94eabc | ||
|
9097d0a325 | ||
|
5d0ab07343 | ||
|
9d6480c3cd | ||
|
a879c42bd3 | ||
|
39fe7eca6b | ||
|
66948316f7 | ||
|
57d9e0ac75 | ||
|
e4b687cf46 | ||
|
6d175460df | ||
|
520f25688a | ||
|
3b38a83274 | ||
|
97bd6cd216 | ||
|
d60cfd2604 | ||
|
9e59474e46 | ||
|
8a24b56331 | ||
|
0658712f65 | ||
|
d3e3a460ec | ||
|
28ba686cbf | ||
|
f311488d2c | ||
|
c38fab912b | ||
|
4cd6a1458e | ||
|
82c5085399 | ||
|
95bbd46eab | ||
|
85afdeef37 | ||
|
860184d542 | ||
|
3526f69047 | ||
|
295bc35ecf | ||
|
8f11d279d2 | ||
|
b157bae2c9 | ||
|
64a5e125c5 | ||
|
fb8ea5993f | ||
|
5c13012b56 | ||
|
523866c2cc | ||
|
56e9001a1a | ||
|
0730acc5a0 | ||
|
2faf796d2a | ||
|
3aea432b35 | ||
|
b20bc5c0ca | ||
|
5c89ec9b98 | ||
|
bbfa6488ac | ||
|
a1f16bc74c | ||
|
576681f29b | ||
|
370680a7a9 | ||
|
97aacd9b35 | ||
|
f05419f0fb | ||
|
a5e3aa693c | ||
|
89fde59a80 | ||
|
f0b1bddac4 | ||
|
33ca98ece9 | ||
|
1fac96c1f9 | ||
|
b9e6e43722 | ||
|
c49e065fea | ||
|
846badc480 | ||
|
8fe47b0a0d | ||
|
58b0420a8a | ||
|
afd4227df8 | ||
|
9624f92ede | ||
|
dea71556cc | ||
|
ff4ff30a68 | ||
|
00b922fc5d | ||
|
7522642393 | ||
|
b9d4412715 | ||
|
5441a8fa47 | ||
|
e13d14e6a3 | ||
|
d21a069619 | ||
|
13bc9c0c6e | ||
|
5afc82de6e | ||
|
bd566977e8 | ||
|
99169016d2 | ||
|
78c34fdc3c | ||
|
d081c935d7 | ||
|
bb0191f22b | ||
|
c619562313 | ||
|
6b6d3190cf | ||
|
3b05318525 | ||
|
a182c76815 | ||
|
6ed812db13 | ||
|
3212fb6838 | ||
|
f5f906dd0d | ||
|
bbbeb7d8ba | ||
|
c131e812ae | ||
|
686b2884ee | ||
|
e7c8693635 | ||
|
ec88bd0cd0 | ||
|
acdf9238fb | ||
|
d3f018fde8 | ||
|
4fcc93d922 | ||
|
61f4b5aa89 | ||
|
35dbf7a8a3 | ||
|
1b5582acf7 | ||
|
dde6f1e92d | ||
|
2d4eff21ca | ||
|
bca8c03e57 | ||
|
c07918e7d8 | ||
|
0e6961366a | ||
|
948a600ed5 | ||
|
9e23610b0f | ||
|
29905d86ae | ||
|
10eb654f27 | ||
|
4dde0665c8 | ||
|
a750bf8686 | ||
|
bef78efb49 | ||
|
ddf10250c7 | ||
|
fcd7bdc2b7 | ||
|
1e44c3585f | ||
|
5228b2a353 | ||
|
e0123026b6 | ||
|
653a30f4ca | ||
|
0f2347d070 | ||
|
da000c8314 | ||
|
f915a4bf20 | ||
|
732a6a3666 | ||
|
7b6c8363da | ||
|
4695117f2e | ||
|
e9f99d1c91 | ||
|
ef946a6c87 | ||
|
58aeab77d2 | ||
|
97ce6dfa6d | ||
|
bbb2b30506 | ||
|
15fe3050a1 | ||
|
c63c2d855e | ||
|
87a11a87c2 |
25
.travis.yml
25
.travis.yml
@@ -24,10 +24,12 @@ jobs:
|
||||
script:
|
||||
- go run build/ci.go lint
|
||||
|
||||
# This builder does the Docker Hub build and upload for amd64
|
||||
# These builders create the Docker sub-images for multi-arch push and each
|
||||
# will attempt to push the multi-arch image if they are the last builder
|
||||
- stage: build
|
||||
if: type = push
|
||||
os: linux
|
||||
arch: amd64
|
||||
dist: bionic
|
||||
go: 1.16.x
|
||||
env:
|
||||
@@ -36,8 +38,27 @@ jobs:
|
||||
- docker
|
||||
git:
|
||||
submodules: false # avoid cloning ethereum/tests
|
||||
before_install:
|
||||
- export DOCKER_CLI_EXPERIMENTAL=enabled
|
||||
script:
|
||||
- go run build/ci.go docker -upload karalabe/geth-docker-test
|
||||
- go run build/ci.go docker -image -manifest amd64,arm64 -upload ethereum/client-go
|
||||
|
||||
- stage: build
|
||||
if: type = push
|
||||
os: linux
|
||||
arch: arm64
|
||||
dist: bionic
|
||||
go: 1.16.x
|
||||
env:
|
||||
- docker
|
||||
services:
|
||||
- docker
|
||||
git:
|
||||
submodules: false # avoid cloning ethereum/tests
|
||||
before_install:
|
||||
- export DOCKER_CLI_EXPERIMENTAL=enabled
|
||||
script:
|
||||
- go run build/ci.go docker -image -manifest amd64,arm64 -upload ethereum/client-go
|
||||
|
||||
# This builder does the Ubuntu PPA upload
|
||||
- stage: build
|
||||
|
16
Dockerfile
16
Dockerfile
@@ -1,10 +1,15 @@
|
||||
# Support setting various labels on the final image
|
||||
ARG COMMIT=""
|
||||
ARG VERSION=""
|
||||
ARG BUILDNUM=""
|
||||
|
||||
# Build Geth in a stock Go builder container
|
||||
FROM golang:1.16-alpine as builder
|
||||
|
||||
RUN apk add --no-cache make gcc musl-dev linux-headers git
|
||||
RUN apk add --no-cache gcc musl-dev linux-headers git
|
||||
|
||||
ADD . /go-ethereum
|
||||
RUN cd /go-ethereum && make geth
|
||||
RUN cd /go-ethereum && go run build/ci.go install ./cmd/geth
|
||||
|
||||
# Pull Geth into a second stage deploy alpine container
|
||||
FROM alpine:latest
|
||||
@@ -14,3 +19,10 @@ COPY --from=builder /go-ethereum/build/bin/geth /usr/local/bin/
|
||||
|
||||
EXPOSE 8545 8546 30303 30303/udp
|
||||
ENTRYPOINT ["geth"]
|
||||
|
||||
# Add some metadata labels to help programatic image consumption
|
||||
ARG COMMIT=""
|
||||
ARG VERSION=""
|
||||
ARG BUILDNUM=""
|
||||
|
||||
LABEL commit="$COMMIT" version="$VERSION" buildnum="$BUILDNUM"
|
||||
|
@@ -1,10 +1,15 @@
|
||||
# Support setting various labels on the final image
|
||||
ARG COMMIT=""
|
||||
ARG VERSION=""
|
||||
ARG BUILDNUM=""
|
||||
|
||||
# Build Geth in a stock Go builder container
|
||||
FROM golang:1.16-alpine as builder
|
||||
|
||||
RUN apk add --no-cache make gcc musl-dev linux-headers git
|
||||
RUN apk add --no-cache gcc musl-dev linux-headers git
|
||||
|
||||
ADD . /go-ethereum
|
||||
RUN cd /go-ethereum && make all
|
||||
RUN cd /go-ethereum && go run build/ci.go install
|
||||
|
||||
# Pull all binaries into a second stage deploy alpine container
|
||||
FROM alpine:latest
|
||||
@@ -13,3 +18,10 @@ RUN apk add --no-cache ca-certificates
|
||||
COPY --from=builder /go-ethereum/build/bin/* /usr/local/bin/
|
||||
|
||||
EXPOSE 8545 8546 30303 30303/udp
|
||||
|
||||
# Add some metadata labels to help programatic image consumption
|
||||
ARG COMMIT=""
|
||||
ARG VERSION=""
|
||||
ARG BUILDNUM=""
|
||||
|
||||
LABEL commit="$COMMIT" version="$VERSION" buildnum="$BUILDNUM"
|
||||
|
@@ -64,7 +64,7 @@ $ geth console
|
||||
```
|
||||
|
||||
This command will:
|
||||
* Start `geth` in fast sync mode (default, can be changed with the `--syncmode` flag),
|
||||
* Start `geth` in snap sync mode (default, can be changed with the `--syncmode` flag),
|
||||
causing it to download more data in exchange for avoiding processing the entire history
|
||||
of the Ethereum network, which is very CPU intensive.
|
||||
* Start up `geth`'s built-in interactive [JavaScript console](https://geth.ethereum.org/docs/interface/javascript-console),
|
||||
@@ -332,7 +332,7 @@ from anyone on the internet, and are grateful for even the smallest of fixes!
|
||||
|
||||
If you'd like to contribute to go-ethereum, please fork, fix, commit and send a pull request
|
||||
for the maintainers to review and merge into the main code base. If you wish to submit
|
||||
more complex changes though, please check up with the core devs first on [our gitter channel](https://gitter.im/ethereum/go-ethereum)
|
||||
more complex changes though, please check up with the core devs first on [our Discord Server](https://discord.gg/invite/nthXNEv)
|
||||
to ensure those changes are in line with the general philosophy of the project and/or get
|
||||
some early feedback which can make both your efforts much lighter as well as our review
|
||||
and merge procedures quick and simple.
|
||||
|
@@ -17,6 +17,7 @@
|
||||
package bind
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/ecdsa"
|
||||
"errors"
|
||||
"io"
|
||||
@@ -74,6 +75,7 @@ func NewKeyStoreTransactor(keystore *keystore.KeyStore, account accounts.Account
|
||||
}
|
||||
return tx.WithSignature(signer, signature)
|
||||
},
|
||||
Context: context.Background(),
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -97,6 +99,7 @@ func NewKeyedTransactor(key *ecdsa.PrivateKey) *TransactOpts {
|
||||
}
|
||||
return tx.WithSignature(signer, signature)
|
||||
},
|
||||
Context: context.Background(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -133,6 +136,7 @@ func NewKeyStoreTransactorWithChainID(keystore *keystore.KeyStore, account accou
|
||||
}
|
||||
return tx.WithSignature(signer, signature)
|
||||
},
|
||||
Context: context.Background(),
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -156,6 +160,7 @@ func NewKeyedTransactorWithChainID(key *ecdsa.PrivateKey, chainID *big.Int) (*Tr
|
||||
}
|
||||
return tx.WithSignature(signer, signature)
|
||||
},
|
||||
Context: context.Background(),
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -170,5 +175,6 @@ func NewClefTransactor(clef *external.ExternalSigner, account accounts.Account)
|
||||
}
|
||||
return clef.SignTx(account, transaction, nil) // Clef enforces its own chain id
|
||||
},
|
||||
Context: context.Background(),
|
||||
}
|
||||
}
|
||||
|
@@ -488,8 +488,19 @@ func (b *SimulatedBackend) EstimateGas(ctx context.Context, call ethereum.CallMs
|
||||
} else {
|
||||
hi = b.pendingBlock.GasLimit()
|
||||
}
|
||||
// Normalize the max fee per gas the call is willing to spend.
|
||||
var feeCap *big.Int
|
||||
if call.GasPrice != nil && (call.GasFeeCap != nil || call.GasTipCap != nil) {
|
||||
return 0, errors.New("both gasPrice and (maxFeePerGas or maxPriorityFeePerGas) specified")
|
||||
} else if call.GasPrice != nil {
|
||||
feeCap = call.GasPrice
|
||||
} else if call.GasFeeCap != nil {
|
||||
feeCap = call.GasFeeCap
|
||||
} else {
|
||||
feeCap = common.Big0
|
||||
}
|
||||
// Recap the highest gas allowance with account's balance.
|
||||
if call.GasPrice != nil && call.GasPrice.BitLen() != 0 {
|
||||
if feeCap.BitLen() != 0 {
|
||||
balance := b.pendingState.GetBalance(call.From) // from can't be nil
|
||||
available := new(big.Int).Set(balance)
|
||||
if call.Value != nil {
|
||||
@@ -498,14 +509,14 @@ func (b *SimulatedBackend) EstimateGas(ctx context.Context, call ethereum.CallMs
|
||||
}
|
||||
available.Sub(available, call.Value)
|
||||
}
|
||||
allowance := new(big.Int).Div(available, call.GasPrice)
|
||||
allowance := new(big.Int).Div(available, feeCap)
|
||||
if allowance.IsUint64() && hi > allowance.Uint64() {
|
||||
transfer := call.Value
|
||||
if transfer == nil {
|
||||
transfer = new(big.Int)
|
||||
}
|
||||
log.Warn("Gas estimation capped by limited funds", "original", hi, "balance", balance,
|
||||
"sent", transfer, "gasprice", call.GasPrice, "fundable", allowance)
|
||||
"sent", transfer, "feecap", feeCap, "fundable", allowance)
|
||||
hi = allowance.Uint64()
|
||||
}
|
||||
}
|
||||
@@ -784,7 +795,7 @@ type callMsg struct {
|
||||
|
||||
func (m callMsg) From() common.Address { return m.CallMsg.From }
|
||||
func (m callMsg) Nonce() uint64 { return 0 }
|
||||
func (m callMsg) CheckNonce() bool { return false }
|
||||
func (m callMsg) IsFake() bool { return true }
|
||||
func (m callMsg) To() *common.Address { return m.CallMsg.To }
|
||||
func (m callMsg) GasPrice() *big.Int { return m.CallMsg.GasPrice }
|
||||
func (m callMsg) GasFeeCap() *big.Int { return m.CallMsg.GasFeeCap }
|
||||
|
@@ -580,6 +580,26 @@ func TestEstimateGasWithPrice(t *testing.T) {
|
||||
Value: big.NewInt(100000000000),
|
||||
Data: nil,
|
||||
}, 21000, errors.New("gas required exceeds allowance (10999)")}, // 10999=(2.2ether-1000wei)/(2e14)
|
||||
|
||||
{"EstimateEIP1559WithHighFees", ethereum.CallMsg{
|
||||
From: addr,
|
||||
To: &addr,
|
||||
Gas: 0,
|
||||
GasFeeCap: big.NewInt(1e14), // maxgascost = 2.1ether
|
||||
GasTipCap: big.NewInt(1),
|
||||
Value: big.NewInt(1e17), // the remaining balance for fee is 2.1ether
|
||||
Data: nil,
|
||||
}, params.TxGas, nil},
|
||||
|
||||
{"EstimateEIP1559WithSuperHighFees", ethereum.CallMsg{
|
||||
From: addr,
|
||||
To: &addr,
|
||||
Gas: 0,
|
||||
GasFeeCap: big.NewInt(1e14), // maxgascost = 2.1ether
|
||||
GasTipCap: big.NewInt(1),
|
||||
Value: big.NewInt(1e17 + 1), // the remaining balance for fee is 2.1ether
|
||||
Data: nil,
|
||||
}, params.TxGas, errors.New("gas required exceeds allowance (20999)")}, // 20999=(2.2ether-0.1ether-1wei)/(1e14)
|
||||
}
|
||||
for i, c := range cases {
|
||||
got, err := sim.EstimateGas(context.Background(), c.message)
|
||||
@@ -592,6 +612,9 @@ func TestEstimateGasWithPrice(t *testing.T) {
|
||||
}
|
||||
continue
|
||||
}
|
||||
if c.expectError == nil && err != nil {
|
||||
t.Fatalf("test %d: didn't expect error, got %v", i, err)
|
||||
}
|
||||
if got != c.expect {
|
||||
t.Fatalf("test %d: gas estimation mismatch, want %d, got %d", i, c.expect, got)
|
||||
}
|
||||
|
@@ -21,6 +21,8 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"math/big"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/ethereum/go-ethereum"
|
||||
"github.com/ethereum/go-ethereum/accounts/abi"
|
||||
@@ -76,6 +78,29 @@ type WatchOpts struct {
|
||||
Context context.Context // Network context to support cancellation and timeouts (nil = no timeout)
|
||||
}
|
||||
|
||||
// MetaData collects all metadata for a bound contract.
|
||||
type MetaData struct {
|
||||
mu sync.Mutex
|
||||
Sigs map[string]string
|
||||
Bin string
|
||||
ABI string
|
||||
ab *abi.ABI
|
||||
}
|
||||
|
||||
func (m *MetaData) GetAbi() (*abi.ABI, error) {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
if m.ab != nil {
|
||||
return m.ab, nil
|
||||
}
|
||||
if parsed, err := abi.JSON(strings.NewReader(m.ABI)); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
m.ab = &parsed
|
||||
}
|
||||
return m.ab, nil
|
||||
}
|
||||
|
||||
// BoundContract is the base wrapper object that reflects a contract on the
|
||||
// Ethereum network. It contains a collection of methods that are used by the
|
||||
// higher level contract bindings to operate.
|
||||
@@ -229,13 +254,13 @@ func (c *BoundContract) transact(opts *TransactOpts, contract *common.Address, i
|
||||
if opts.GasPrice != nil && (opts.GasFeeCap != nil || opts.GasTipCap != nil) {
|
||||
return nil, errors.New("both gasPrice and (maxFeePerGas or maxPriorityFeePerGas) specified")
|
||||
}
|
||||
head, err := c.transactor.HeaderByNumber(opts.Context, nil)
|
||||
head, err := c.transactor.HeaderByNumber(ensureContext(opts.Context), nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if head.BaseFee != nil && opts.GasPrice == nil {
|
||||
if opts.GasTipCap == nil {
|
||||
tip, err := c.transactor.SuggestGasTipCap(opts.Context)
|
||||
tip, err := c.transactor.SuggestGasTipCap(ensureContext(opts.Context))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -256,13 +281,10 @@ func (c *BoundContract) transact(opts *TransactOpts, contract *common.Address, i
|
||||
return nil, errors.New("maxFeePerGas or maxPriorityFeePerGas specified but london is not active yet")
|
||||
}
|
||||
if opts.GasPrice == nil {
|
||||
price, err := c.transactor.SuggestGasTipCap(opts.Context)
|
||||
price, err := c.transactor.SuggestGasPrice(ensureContext(opts.Context))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if head.BaseFee != nil {
|
||||
price.Add(price, head.BaseFee)
|
||||
}
|
||||
opts.GasPrice = price
|
||||
}
|
||||
}
|
||||
@@ -443,7 +465,7 @@ func (c *BoundContract) UnpackLogIntoMap(out map[string]interface{}, event strin
|
||||
// user specified it as such.
|
||||
func ensureContext(ctx context.Context) context.Context {
|
||||
if ctx == nil {
|
||||
return context.TODO()
|
||||
return context.Background()
|
||||
}
|
||||
return ctx
|
||||
}
|
||||
|
@@ -90,6 +90,7 @@ package {{.Package}}
|
||||
import (
|
||||
"math/big"
|
||||
"strings"
|
||||
"errors"
|
||||
|
||||
ethereum "github.com/ethereum/go-ethereum"
|
||||
"github.com/ethereum/go-ethereum/accounts/abi"
|
||||
@@ -101,6 +102,7 @@ import (
|
||||
|
||||
// Reference imports to suppress errors if they are not otherwise used.
|
||||
var (
|
||||
_ = errors.New
|
||||
_ = big.NewInt
|
||||
_ = strings.NewReader
|
||||
_ = ethereum.NotFound
|
||||
@@ -120,32 +122,48 @@ var (
|
||||
{{end}}
|
||||
|
||||
{{range $contract := .Contracts}}
|
||||
// {{.Type}}ABI is the input ABI used to generate the binding from.
|
||||
const {{.Type}}ABI = "{{.InputABI}}"
|
||||
|
||||
{{if $contract.FuncSigs}}
|
||||
// {{.Type}}FuncSigs maps the 4-byte function signature to its string representation.
|
||||
var {{.Type}}FuncSigs = map[string]string{
|
||||
// {{.Type}}MetaData contains all meta data concerning the {{.Type}} contract.
|
||||
var {{.Type}}MetaData = &bind.MetaData{
|
||||
ABI: "{{.InputABI}}",
|
||||
{{if $contract.FuncSigs -}}
|
||||
Sigs: map[string]string{
|
||||
{{range $strsig, $binsig := .FuncSigs}}"{{$binsig}}": "{{$strsig}}",
|
||||
{{end}}
|
||||
}
|
||||
},
|
||||
{{end -}}
|
||||
{{if .InputBin -}}
|
||||
Bin: "0x{{.InputBin}}",
|
||||
{{end}}
|
||||
}
|
||||
// {{.Type}}ABI is the input ABI used to generate the binding from.
|
||||
// Deprecated: Use {{.Type}}MetaData.ABI instead.
|
||||
var {{.Type}}ABI = {{.Type}}MetaData.ABI
|
||||
|
||||
{{if $contract.FuncSigs}}
|
||||
// Deprecated: Use {{.Type}}MetaData.Sigs instead.
|
||||
// {{.Type}}FuncSigs maps the 4-byte function signature to its string representation.
|
||||
var {{.Type}}FuncSigs = {{.Type}}MetaData.Sigs
|
||||
{{end}}
|
||||
|
||||
{{if .InputBin}}
|
||||
// {{.Type}}Bin is the compiled bytecode used for deploying new contracts.
|
||||
var {{.Type}}Bin = "0x{{.InputBin}}"
|
||||
// Deprecated: Use {{.Type}}MetaData.Bin instead.
|
||||
var {{.Type}}Bin = {{.Type}}MetaData.Bin
|
||||
|
||||
// Deploy{{.Type}} deploys a new Ethereum contract, binding an instance of {{.Type}} to it.
|
||||
func Deploy{{.Type}}(auth *bind.TransactOpts, backend bind.ContractBackend {{range .Constructor.Inputs}}, {{.Name}} {{bindtype .Type $structs}}{{end}}) (common.Address, *types.Transaction, *{{.Type}}, error) {
|
||||
parsed, err := abi.JSON(strings.NewReader({{.Type}}ABI))
|
||||
parsed, err := {{.Type}}MetaData.GetAbi()
|
||||
if err != nil {
|
||||
return common.Address{}, nil, nil, err
|
||||
}
|
||||
if parsed == nil {
|
||||
return common.Address{}, nil, nil, errors.New("GetABI returned nil")
|
||||
}
|
||||
{{range $pattern, $name := .Libraries}}
|
||||
{{decapitalise $name}}Addr, _, _, _ := Deploy{{capitalise $name}}(auth, backend)
|
||||
{{$contract.Type}}Bin = strings.Replace({{$contract.Type}}Bin, "__${{$pattern}}$__", {{decapitalise $name}}Addr.String()[2:], -1)
|
||||
{{end}}
|
||||
address, tx, contract, err := bind.DeployContract(auth, parsed, common.FromHex({{.Type}}Bin), backend {{range .Constructor.Inputs}}, {{.Name}}{{end}})
|
||||
address, tx, contract, err := bind.DeployContract(auth, *parsed, common.FromHex({{.Type}}Bin), backend {{range .Constructor.Inputs}}, {{.Name}}{{end}})
|
||||
if err != nil {
|
||||
return common.Address{}, nil, nil, err
|
||||
}
|
||||
|
21
accounts/external/backend.go
vendored
21
accounts/external/backend.go
vendored
@@ -29,7 +29,7 @@ import (
|
||||
"github.com/ethereum/go-ethereum/event"
|
||||
"github.com/ethereum/go-ethereum/log"
|
||||
"github.com/ethereum/go-ethereum/rpc"
|
||||
"github.com/ethereum/go-ethereum/signer/core"
|
||||
"github.com/ethereum/go-ethereum/signer/core/apitypes"
|
||||
)
|
||||
|
||||
type ExternalBackend struct {
|
||||
@@ -196,6 +196,10 @@ type signTransactionResult struct {
|
||||
Tx *types.Transaction `json:"tx"`
|
||||
}
|
||||
|
||||
// SignTx sends the transaction to the external signer.
|
||||
// If chainID is nil, or tx.ChainID is zero, the chain ID will be assigned
|
||||
// by the external signer. For non-legacy transactions, the chain ID of the
|
||||
// transaction overrides the chainID parameter.
|
||||
func (api *ExternalSigner) SignTx(account accounts.Account, tx *types.Transaction, chainID *big.Int) (*types.Transaction, error) {
|
||||
data := hexutil.Bytes(tx.Data())
|
||||
var to *common.MixedcaseAddress
|
||||
@@ -203,7 +207,7 @@ func (api *ExternalSigner) SignTx(account accounts.Account, tx *types.Transactio
|
||||
t := common.NewMixedcaseAddress(*tx.To())
|
||||
to = &t
|
||||
}
|
||||
args := &core.SendTxArgs{
|
||||
args := &apitypes.SendTxArgs{
|
||||
Data: &data,
|
||||
Nonce: hexutil.Uint64(tx.Nonce()),
|
||||
Value: hexutil.Big(*tx.Value()),
|
||||
@@ -211,21 +215,24 @@ func (api *ExternalSigner) SignTx(account accounts.Account, tx *types.Transactio
|
||||
To: to,
|
||||
From: common.NewMixedcaseAddress(account.Address),
|
||||
}
|
||||
if tx.GasFeeCap() != nil {
|
||||
switch tx.Type() {
|
||||
case types.LegacyTxType, types.AccessListTxType:
|
||||
args.GasPrice = (*hexutil.Big)(tx.GasPrice())
|
||||
case types.DynamicFeeTxType:
|
||||
args.MaxFeePerGas = (*hexutil.Big)(tx.GasFeeCap())
|
||||
args.MaxPriorityFeePerGas = (*hexutil.Big)(tx.GasTipCap())
|
||||
} else {
|
||||
args.GasPrice = (*hexutil.Big)(tx.GasPrice())
|
||||
default:
|
||||
return nil, fmt.Errorf("unsupported tx type %d", tx.Type())
|
||||
}
|
||||
// We should request the default chain id that we're operating with
|
||||
// (the chain we're executing on)
|
||||
if chainID != nil {
|
||||
if chainID != nil && chainID.Sign() != 0 {
|
||||
args.ChainID = (*hexutil.Big)(chainID)
|
||||
}
|
||||
if tx.Type() != types.LegacyTxType {
|
||||
// However, if the user asked for a particular chain id, then we should
|
||||
// use that instead.
|
||||
if tx.ChainId() != nil {
|
||||
if tx.ChainId().Sign() != 0 {
|
||||
args.ChainID = (*hexutil.Big)(tx.ChainId())
|
||||
}
|
||||
accessList := tx.AccessList()
|
||||
|
159
build/ci.go
159
build/ci.go
@@ -54,6 +54,7 @@ import (
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
@@ -183,7 +184,7 @@ func main() {
|
||||
case "archive":
|
||||
doArchive(os.Args[2:])
|
||||
case "docker":
|
||||
doDockerImage(os.Args[2:])
|
||||
doDocker(os.Args[2:])
|
||||
case "debsrc":
|
||||
doDebianSource(os.Args[2:])
|
||||
case "nsis":
|
||||
@@ -449,15 +450,17 @@ func maybeSkipArchive(env build.Environment) {
|
||||
os.Exit(0)
|
||||
}
|
||||
if env.Branch != "master" && !strings.HasPrefix(env.Tag, "v1.") {
|
||||
log.Printf("skipping archive creation because branch %q, tag %q is not on the whitelist", env.Branch, env.Tag)
|
||||
log.Printf("skipping archive creation because branch %q, tag %q is not on the inclusion list", env.Branch, env.Tag)
|
||||
os.Exit(0)
|
||||
}
|
||||
}
|
||||
|
||||
// Builds the docker images and optionally uploads them to Docker Hub.
|
||||
func doDockerImage(cmdline []string) {
|
||||
func doDocker(cmdline []string) {
|
||||
var (
|
||||
upload = flag.String("upload", "", `Where to upload the docker image (usually "ethereum/client-go")`)
|
||||
image = flag.Bool("image", false, `Whether to build and push an arch specific docker image`)
|
||||
manifest = flag.String("manifest", "", `Push a multi-arch docker image for the specified architectures (usually "amd64,arm64")`)
|
||||
upload = flag.String("upload", "", `Where to upload the docker image (usually "ethereum/client-go")`)
|
||||
)
|
||||
flag.CommandLine.Parse(cmdline)
|
||||
|
||||
@@ -465,6 +468,15 @@ func doDockerImage(cmdline []string) {
|
||||
env := build.Env()
|
||||
maybeSkipArchive(env)
|
||||
|
||||
// Retrieve the upload credentials and authenticate
|
||||
user := getenvBase64("DOCKER_HUB_USERNAME")
|
||||
pass := getenvBase64("DOCKER_HUB_PASSWORD")
|
||||
|
||||
if len(user) > 0 && len(pass) > 0 {
|
||||
auther := exec.Command("docker", "login", "-u", string(user), "--password-stdin")
|
||||
auther.Stdin = bytes.NewReader(pass)
|
||||
build.MustRun(auther)
|
||||
}
|
||||
// Retrieve the version infos to build and push to the following paths:
|
||||
// - ethereum/client-go:latest - Pushes to the master branch, Geth only
|
||||
// - ethereum/client-go:stable - Version tag publish on GitHub, Geth only
|
||||
@@ -480,27 +492,132 @@ func doDockerImage(cmdline []string) {
|
||||
case env.Branch == "master":
|
||||
tags = []string{"latest"}
|
||||
case strings.HasPrefix(env.Tag, "v1."):
|
||||
tags = []string{"stable", fmt.Sprintf("release-1.%d", params.VersionMinor), params.Version}
|
||||
tags = []string{"stable", fmt.Sprintf("release-1.%d", params.VersionMinor), "v" + params.Version}
|
||||
}
|
||||
// Build the docker images via CLI (don't pull in the `moby` dep to call 3 commands)
|
||||
build.MustRunCommand("docker", "build", "--tag", fmt.Sprintf("%s:TAG", *upload), ".")
|
||||
build.MustRunCommand("docker", "build", "--tag", fmt.Sprintf("%s:alltools-TAG", *upload), "-f", "Dockerfile.alltools", ".")
|
||||
// If architecture specific image builds are requested, build and push them
|
||||
if *image {
|
||||
build.MustRunCommand("docker", "build", "--build-arg", "COMMIT="+env.Commit, "--build-arg", "VERSION="+params.VersionWithMeta, "--build-arg", "BUILDNUM="+env.Buildnum, "--tag", fmt.Sprintf("%s:TAG", *upload), ".")
|
||||
build.MustRunCommand("docker", "build", "--build-arg", "COMMIT="+env.Commit, "--build-arg", "VERSION="+params.VersionWithMeta, "--build-arg", "BUILDNUM="+env.Buildnum, "--tag", fmt.Sprintf("%s:alltools-TAG", *upload), "-f", "Dockerfile.alltools", ".")
|
||||
|
||||
// Retrieve the upload credentials and authenticate
|
||||
user := getenvBase64("DOCKER_HUB_USERNAME")
|
||||
pass := getenvBase64("DOCKER_HUB_PASSWORD")
|
||||
// Tag and upload the images to Docker Hub
|
||||
for _, tag := range tags {
|
||||
gethImage := fmt.Sprintf("%s:%s-%s", *upload, tag, runtime.GOARCH)
|
||||
toolImage := fmt.Sprintf("%s:alltools-%s-%s", *upload, tag, runtime.GOARCH)
|
||||
|
||||
if len(user) > 0 && len(pass) > 0 {
|
||||
auther := exec.Command("docker", "login", "-u", string(user), "--password-stdin")
|
||||
auther.Stdin = bytes.NewReader(pass)
|
||||
build.MustRun(auther)
|
||||
// If the image already exists (non version tag), check the build
|
||||
// number to prevent overwriting a newer commit if concurrent builds
|
||||
// are running. This is still a tiny bit racey if two published are
|
||||
// done at the same time, but that's extremely unlikely even on the
|
||||
// master branch.
|
||||
for _, img := range []string{gethImage, toolImage} {
|
||||
if exec.Command("docker", "pull", img).Run() != nil {
|
||||
continue // Generally the only failure is a missing image, which is good
|
||||
}
|
||||
buildnum, err := exec.Command("docker", "inspect", "--format", "{{index .Config.Labels \"buildnum\"}}", img).CombinedOutput()
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to inspect container: %v\nOutput: %s", err, string(buildnum))
|
||||
}
|
||||
buildnum = bytes.TrimSpace(buildnum)
|
||||
|
||||
if len(buildnum) > 0 && len(env.Buildnum) > 0 {
|
||||
oldnum, err := strconv.Atoi(string(buildnum))
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to parse old image build number: %v", err)
|
||||
}
|
||||
newnum, err := strconv.Atoi(env.Buildnum)
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to parse current build number: %v", err)
|
||||
}
|
||||
if oldnum > newnum {
|
||||
log.Fatalf("Current build number %d not newer than existing %d", newnum, oldnum)
|
||||
} else {
|
||||
log.Printf("Updating %s from build %d to %d", img, oldnum, newnum)
|
||||
}
|
||||
}
|
||||
}
|
||||
build.MustRunCommand("docker", "image", "tag", fmt.Sprintf("%s:TAG", *upload), gethImage)
|
||||
build.MustRunCommand("docker", "image", "tag", fmt.Sprintf("%s:alltools-TAG", *upload), toolImage)
|
||||
build.MustRunCommand("docker", "push", gethImage)
|
||||
build.MustRunCommand("docker", "push", toolImage)
|
||||
}
|
||||
}
|
||||
// Tag and upload the images to Docker Hub
|
||||
for _, tag := range tags {
|
||||
build.MustRunCommand("docker", "image", "tag", fmt.Sprintf("%s:TAG", *upload), fmt.Sprintf("%s:%s", *upload, tag))
|
||||
build.MustRunCommand("docker", "image", "tag", fmt.Sprintf("%s:alltools-TAG", *upload), fmt.Sprintf("%s:alltools-%s", *upload, tag))
|
||||
build.MustRunCommand("docker", "push", fmt.Sprintf("%s:%s", *upload, tag))
|
||||
build.MustRunCommand("docker", "push", fmt.Sprintf("%s:alltools-%s", *upload, tag))
|
||||
// If multi-arch image manifest push is requested, assemble it
|
||||
if len(*manifest) != 0 {
|
||||
// Since different architectures are pushed by different builders, wait
|
||||
// until all required images are updated.
|
||||
var mismatch bool
|
||||
for i := 0; i < 2; i++ { // 2 attempts, second is race check
|
||||
mismatch = false // hope there's no mismatch now
|
||||
|
||||
for _, tag := range tags {
|
||||
for _, arch := range strings.Split(*manifest, ",") {
|
||||
gethImage := fmt.Sprintf("%s:%s-%s", *upload, tag, arch)
|
||||
toolImage := fmt.Sprintf("%s:alltools-%s-%s", *upload, tag, arch)
|
||||
|
||||
for _, img := range []string{gethImage, toolImage} {
|
||||
if out, err := exec.Command("docker", "pull", img).CombinedOutput(); err != nil {
|
||||
log.Printf("Required image %s unavailable: %v\nOutput: %s", img, err, out)
|
||||
mismatch = true
|
||||
break
|
||||
}
|
||||
buildnum, err := exec.Command("docker", "inspect", "--format", "{{index .Config.Labels \"buildnum\"}}", img).CombinedOutput()
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to inspect container: %v\nOutput: %s", err, string(buildnum))
|
||||
}
|
||||
buildnum = bytes.TrimSpace(buildnum)
|
||||
|
||||
if string(buildnum) != env.Buildnum {
|
||||
log.Printf("Build number mismatch on %s: want %s, have %s", img, env.Buildnum, buildnum)
|
||||
mismatch = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if mismatch {
|
||||
break
|
||||
}
|
||||
}
|
||||
if mismatch {
|
||||
break
|
||||
}
|
||||
}
|
||||
if mismatch {
|
||||
// Build numbers mismatching, retry in a short time to
|
||||
// avoid concurrent failes in both publisher images. If
|
||||
// however the retry failed too, it means the concurrent
|
||||
// builder is still crunching, let that do the publish.
|
||||
if i == 0 {
|
||||
time.Sleep(30 * time.Second)
|
||||
}
|
||||
continue
|
||||
}
|
||||
break
|
||||
}
|
||||
if mismatch {
|
||||
log.Println("Relinquishing publish to other builder")
|
||||
return
|
||||
}
|
||||
// Assemble and push the Geth manifest image
|
||||
for _, tag := range tags {
|
||||
gethImage := fmt.Sprintf("%s:%s", *upload, tag)
|
||||
|
||||
var gethSubImages []string
|
||||
for _, arch := range strings.Split(*manifest, ",") {
|
||||
gethSubImages = append(gethSubImages, gethImage+"-"+arch)
|
||||
}
|
||||
build.MustRunCommand("docker", append([]string{"manifest", "create", gethImage}, gethSubImages...)...)
|
||||
build.MustRunCommand("docker", "manifest", "push", gethImage)
|
||||
}
|
||||
// Assemble and push the alltools manifest image
|
||||
for _, tag := range tags {
|
||||
toolImage := fmt.Sprintf("%s:alltools-%s", *upload, tag)
|
||||
|
||||
var toolSubImages []string
|
||||
for _, arch := range strings.Split(*manifest, ",") {
|
||||
toolSubImages = append(toolSubImages, toolImage+"-"+arch)
|
||||
}
|
||||
build.MustRunCommand("docker", append([]string{"manifest", "create", toolImage}, toolSubImages...)...)
|
||||
build.MustRunCommand("docker", "manifest", "push", toolImage)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -23,7 +23,7 @@ import (
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/ethereum/go-ethereum/signer/core"
|
||||
"github.com/ethereum/go-ethereum/signer/core/apitypes"
|
||||
"github.com/ethereum/go-ethereum/signer/fourbyte"
|
||||
)
|
||||
|
||||
@@ -41,7 +41,7 @@ func parse(data []byte) {
|
||||
if err != nil {
|
||||
die(err)
|
||||
}
|
||||
messages := core.ValidationMessages{}
|
||||
messages := apitypes.ValidationMessages{}
|
||||
db.ValidateCallData(nil, data, &messages)
|
||||
for _, m := range messages.Messages {
|
||||
fmt.Printf("%v: %v\n", m.Typ, m.Message)
|
||||
|
@@ -50,10 +50,10 @@ import (
|
||||
"github.com/ethereum/go-ethereum/rlp"
|
||||
"github.com/ethereum/go-ethereum/rpc"
|
||||
"github.com/ethereum/go-ethereum/signer/core"
|
||||
"github.com/ethereum/go-ethereum/signer/core/apitypes"
|
||||
"github.com/ethereum/go-ethereum/signer/fourbyte"
|
||||
"github.com/ethereum/go-ethereum/signer/rules"
|
||||
"github.com/ethereum/go-ethereum/signer/storage"
|
||||
|
||||
"github.com/mattn/go-colorable"
|
||||
"github.com/mattn/go-isatty"
|
||||
"gopkg.in/urfave/cli.v1"
|
||||
@@ -657,7 +657,7 @@ func signer(c *cli.Context) error {
|
||||
cors := utils.SplitAndTrim(c.GlobalString(utils.HTTPCORSDomainFlag.Name))
|
||||
|
||||
srv := rpc.NewServer()
|
||||
err := node.RegisterApisFromWhitelist(rpcAPI, []string{"account"}, srv, false)
|
||||
err := node.RegisterApis(rpcAPI, []string{"account"}, srv, false)
|
||||
if err != nil {
|
||||
utils.Fatalf("Could not register API: %w", err)
|
||||
}
|
||||
@@ -923,7 +923,7 @@ func testExternalUI(api *core.SignerAPI) {
|
||||
time.Sleep(delay)
|
||||
data := hexutil.Bytes([]byte{})
|
||||
to := common.NewMixedcaseAddress(a)
|
||||
tx := core.SendTxArgs{
|
||||
tx := apitypes.SendTxArgs{
|
||||
Data: &data,
|
||||
Nonce: 0x1,
|
||||
Value: hexutil.Big(*big.NewInt(6)),
|
||||
@@ -1055,11 +1055,11 @@ func GenDoc(ctx *cli.Context) {
|
||||
data := hexutil.Bytes([]byte{0x01, 0x02, 0x03, 0x04})
|
||||
add("SignTxRequest", desc, &core.SignTxRequest{
|
||||
Meta: meta,
|
||||
Callinfo: []core.ValidationInfo{
|
||||
Callinfo: []apitypes.ValidationInfo{
|
||||
{Typ: "Warning", Message: "Something looks odd, show this message as a warning"},
|
||||
{Typ: "Info", Message: "User should see this as well"},
|
||||
},
|
||||
Transaction: core.SendTxArgs{
|
||||
Transaction: apitypes.SendTxArgs{
|
||||
Data: &data,
|
||||
Nonce: 0x1,
|
||||
Value: hexutil.Big(*big.NewInt(6)),
|
||||
@@ -1075,7 +1075,7 @@ func GenDoc(ctx *cli.Context) {
|
||||
add("SignTxResponse - approve", "Response to request to sign a transaction. This response needs to contain the `transaction`"+
|
||||
", because the UI is free to make modifications to the transaction.",
|
||||
&core.SignTxResponse{Approved: true,
|
||||
Transaction: core.SendTxArgs{
|
||||
Transaction: apitypes.SendTxArgs{
|
||||
Data: &data,
|
||||
Nonce: 0x4,
|
||||
Value: hexutil.Big(*big.NewInt(6)),
|
||||
|
@@ -24,6 +24,7 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/davecgh/go-spew/spew"
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/eth/protocols/eth"
|
||||
@@ -649,58 +650,68 @@ func (s *Suite) hashAnnounce(isEth66 bool) error {
|
||||
return fmt.Errorf("peering failed: %v", err)
|
||||
}
|
||||
// create NewBlockHashes announcement
|
||||
nextBlock := s.fullChain.blocks[s.chain.Len()]
|
||||
newBlockHash := &NewBlockHashes{
|
||||
{Hash: nextBlock.Hash(), Number: nextBlock.Number().Uint64()},
|
||||
type anno struct {
|
||||
Hash common.Hash // Hash of one particular block being announced
|
||||
Number uint64 // Number of one particular block being announced
|
||||
}
|
||||
|
||||
nextBlock := s.fullChain.blocks[s.chain.Len()]
|
||||
announcement := anno{Hash: nextBlock.Hash(), Number: nextBlock.Number().Uint64()}
|
||||
newBlockHash := &NewBlockHashes{announcement}
|
||||
if err := sendConn.Write(newBlockHash); err != nil {
|
||||
return fmt.Errorf("failed to write to connection: %v", err)
|
||||
}
|
||||
// Announcement sent, now wait for a header request
|
||||
var (
|
||||
id uint64
|
||||
msg Message
|
||||
blockHeaderReq GetBlockHeaders
|
||||
)
|
||||
if isEth66 {
|
||||
// expect GetBlockHeaders request, and respond
|
||||
id, msg := sendConn.Read66()
|
||||
id, msg = sendConn.Read66()
|
||||
switch msg := msg.(type) {
|
||||
case GetBlockHeaders:
|
||||
blockHeaderReq := msg
|
||||
if blockHeaderReq.Amount != 1 {
|
||||
return fmt.Errorf("unexpected number of block headers requested: %v", blockHeaderReq.Amount)
|
||||
}
|
||||
if blockHeaderReq.Origin.Hash != nextBlock.Hash() {
|
||||
return fmt.Errorf("unexpected block header requested: %v", pretty.Sdump(blockHeaderReq))
|
||||
}
|
||||
resp := ð.BlockHeadersPacket66{
|
||||
RequestId: id,
|
||||
BlockHeadersPacket: eth.BlockHeadersPacket{
|
||||
nextBlock.Header(),
|
||||
},
|
||||
}
|
||||
if err := sendConn.Write66(resp, BlockHeaders{}.Code()); err != nil {
|
||||
return fmt.Errorf("failed to write to connection: %v", err)
|
||||
}
|
||||
blockHeaderReq = msg
|
||||
default:
|
||||
return fmt.Errorf("unexpected %s", pretty.Sdump(msg))
|
||||
}
|
||||
if blockHeaderReq.Amount != 1 {
|
||||
return fmt.Errorf("unexpected number of block headers requested: %v", blockHeaderReq.Amount)
|
||||
}
|
||||
if blockHeaderReq.Origin.Hash != announcement.Hash {
|
||||
return fmt.Errorf("unexpected block header requested. Announced:\n %v\n Remote request:\n%v",
|
||||
pretty.Sdump(announcement),
|
||||
pretty.Sdump(blockHeaderReq))
|
||||
}
|
||||
if err := sendConn.Write66(ð.BlockHeadersPacket66{
|
||||
RequestId: id,
|
||||
BlockHeadersPacket: eth.BlockHeadersPacket{
|
||||
nextBlock.Header(),
|
||||
},
|
||||
}, BlockHeaders{}.Code()); err != nil {
|
||||
return fmt.Errorf("failed to write to connection: %v", err)
|
||||
}
|
||||
} else {
|
||||
// expect GetBlockHeaders request, and respond
|
||||
switch msg := sendConn.Read().(type) {
|
||||
msg = sendConn.Read()
|
||||
switch msg := msg.(type) {
|
||||
case *GetBlockHeaders:
|
||||
blockHeaderReq := *msg
|
||||
if blockHeaderReq.Amount != 1 {
|
||||
return fmt.Errorf("unexpected number of block headers requested: %v", blockHeaderReq.Amount)
|
||||
}
|
||||
if blockHeaderReq.Origin.Hash != nextBlock.Hash() {
|
||||
return fmt.Errorf("unexpected block header requested: %v", pretty.Sdump(blockHeaderReq))
|
||||
}
|
||||
if err := sendConn.Write(&BlockHeaders{nextBlock.Header()}); err != nil {
|
||||
return fmt.Errorf("failed to write to connection: %v", err)
|
||||
}
|
||||
blockHeaderReq = *msg
|
||||
default:
|
||||
return fmt.Errorf("unexpected %s", pretty.Sdump(msg))
|
||||
}
|
||||
if blockHeaderReq.Amount != 1 {
|
||||
return fmt.Errorf("unexpected number of block headers requested: %v", blockHeaderReq.Amount)
|
||||
}
|
||||
if blockHeaderReq.Origin.Hash != announcement.Hash {
|
||||
return fmt.Errorf("unexpected block header requested. Announced:\n %v\n Remote request:\n%v",
|
||||
pretty.Sdump(announcement),
|
||||
pretty.Sdump(blockHeaderReq))
|
||||
}
|
||||
if err := sendConn.Write(&BlockHeaders{nextBlock.Header()}); err != nil {
|
||||
return fmt.Errorf("failed to write to connection: %v", err)
|
||||
}
|
||||
}
|
||||
// wait for block announcement
|
||||
msg := recvConn.readAndServe(s.chain, timeout)
|
||||
msg = recvConn.readAndServe(s.chain, timeout)
|
||||
switch msg := msg.(type) {
|
||||
case *NewBlockHashes:
|
||||
hashes := *msg
|
||||
|
@@ -21,7 +21,6 @@ import (
|
||||
"crypto/rand"
|
||||
"fmt"
|
||||
"net"
|
||||
"reflect"
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
@@ -80,25 +79,57 @@ func BasicPing(t *utesting.T) {
|
||||
To: te.remoteEndpoint(),
|
||||
Expiration: futureExpiration(),
|
||||
})
|
||||
|
||||
reply, _, _ := te.read(te.l1)
|
||||
if err := te.checkPong(reply, pingHash); err != nil {
|
||||
if err := te.checkPingPong(pingHash); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
// checkPong verifies that reply is a valid PONG matching the given ping hash.
|
||||
// checkPingPong verifies that the remote side sends both a PONG with the
|
||||
// correct hash, and a PING.
|
||||
// The two packets do not have to be in any particular order.
|
||||
func (te *testenv) checkPingPong(pingHash []byte) error {
|
||||
var (
|
||||
pings int
|
||||
pongs int
|
||||
)
|
||||
for i := 0; i < 2; i++ {
|
||||
reply, _, err := te.read(te.l1)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
switch reply.Kind() {
|
||||
case v4wire.PongPacket:
|
||||
if err := te.checkPong(reply, pingHash); err != nil {
|
||||
return err
|
||||
}
|
||||
pongs++
|
||||
case v4wire.PingPacket:
|
||||
pings++
|
||||
default:
|
||||
return fmt.Errorf("expected PING or PONG, got %v %v", reply.Name(), reply)
|
||||
}
|
||||
}
|
||||
if pongs == 1 && pings == 1 {
|
||||
return nil
|
||||
}
|
||||
return fmt.Errorf("expected 1 PING (got %d) and 1 PONG (got %d)", pings, pongs)
|
||||
}
|
||||
|
||||
// checkPong verifies that reply is a valid PONG matching the given ping hash,
|
||||
// and a PING. The two packets do not have to be in any particular order.
|
||||
func (te *testenv) checkPong(reply v4wire.Packet, pingHash []byte) error {
|
||||
if reply == nil || reply.Kind() != v4wire.PongPacket {
|
||||
return fmt.Errorf("expected PONG reply, got %v", reply)
|
||||
if reply == nil {
|
||||
return fmt.Errorf("expected PONG reply, got nil")
|
||||
}
|
||||
if reply.Kind() != v4wire.PongPacket {
|
||||
return fmt.Errorf("expected PONG reply, got %v %v", reply.Name(), reply)
|
||||
}
|
||||
pong := reply.(*v4wire.Pong)
|
||||
if !bytes.Equal(pong.ReplyTok, pingHash) {
|
||||
return fmt.Errorf("PONG reply token mismatch: got %x, want %x", pong.ReplyTok, pingHash)
|
||||
}
|
||||
wantEndpoint := te.localEndpoint(te.l1)
|
||||
if !reflect.DeepEqual(pong.To, wantEndpoint) {
|
||||
return fmt.Errorf("PONG 'to' endpoint mismatch: got %+v, want %+v", pong.To, wantEndpoint)
|
||||
if want := te.localEndpoint(te.l1); !want.IP.Equal(pong.To.IP) || want.UDP != pong.To.UDP {
|
||||
return fmt.Errorf("PONG 'to' endpoint mismatch: got %+v, want %+v", pong.To, want)
|
||||
}
|
||||
if v4wire.Expired(pong.Expiration) {
|
||||
return fmt.Errorf("PONG is expired (%v)", pong.Expiration)
|
||||
@@ -118,9 +149,7 @@ func PingWrongTo(t *utesting.T) {
|
||||
To: wrongEndpoint,
|
||||
Expiration: futureExpiration(),
|
||||
})
|
||||
|
||||
reply, _, _ := te.read(te.l1)
|
||||
if err := te.checkPong(reply, pingHash); err != nil {
|
||||
if err := te.checkPingPong(pingHash); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
@@ -138,8 +167,7 @@ func PingWrongFrom(t *utesting.T) {
|
||||
Expiration: futureExpiration(),
|
||||
})
|
||||
|
||||
reply, _, _ := te.read(te.l1)
|
||||
if err := te.checkPong(reply, pingHash); err != nil {
|
||||
if err := te.checkPingPong(pingHash); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
@@ -160,8 +188,7 @@ func PingExtraData(t *utesting.T) {
|
||||
JunkData2: []byte{9, 8, 7, 6, 5, 4, 3, 2, 1},
|
||||
})
|
||||
|
||||
reply, _, _ := te.read(te.l1)
|
||||
if err := te.checkPong(reply, pingHash); err != nil {
|
||||
if err := te.checkPingPong(pingHash); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
@@ -182,8 +209,7 @@ func PingExtraDataWrongFrom(t *utesting.T) {
|
||||
JunkData2: []byte{9, 8, 7, 6, 5, 4, 3, 2, 1},
|
||||
}
|
||||
pingHash := te.send(te.l1, &req)
|
||||
reply, _, _ := te.read(te.l1)
|
||||
if err := te.checkPong(reply, pingHash); err != nil {
|
||||
if err := te.checkPingPong(pingHash); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
@@ -239,9 +265,9 @@ func BondThenPingWithWrongFrom(t *utesting.T) {
|
||||
To: te.remoteEndpoint(),
|
||||
Expiration: futureExpiration(),
|
||||
})
|
||||
|
||||
reply, _, _ := te.read(te.l1)
|
||||
if err := te.checkPong(reply, pingHash); err != nil {
|
||||
if reply, _, err := te.read(te.l1); err != nil {
|
||||
t.Fatal(err)
|
||||
} else if err := te.checkPong(reply, pingHash); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
@@ -4,15 +4,15 @@ The `evm t8n` tool is a stateless state transition utility. It is a utility
|
||||
which can
|
||||
|
||||
1. Take a prestate, including
|
||||
- Accounts,
|
||||
- Block context information,
|
||||
- Previous blockshashes (*optional)
|
||||
- Accounts,
|
||||
- Block context information,
|
||||
- Previous blockshashes (*optional)
|
||||
2. Apply a set of transactions,
|
||||
3. Apply a mining-reward (*optional),
|
||||
4. And generate a post-state, including
|
||||
- State root, transaction root, receipt root,
|
||||
- Information about rejected transactions,
|
||||
- Optionally: a full or partial post-state dump
|
||||
- State root, transaction root, receipt root,
|
||||
- Information about rejected transactions,
|
||||
- Optionally: a full or partial post-state dump
|
||||
|
||||
## Specification
|
||||
|
||||
@@ -37,6 +37,8 @@ Command line params that has to be supported are
|
||||
--output.result result Determines where to put the result (stateroot, txroot etc) of the post-state.
|
||||
`stdout` - into the stdout output
|
||||
`stderr` - into the stderr output
|
||||
--output.body value If set, the RLP of the transactions (block body) will be written to this file.
|
||||
--input.txs stdin stdin or file name of where to find the transactions to apply. If the file prefix is '.rlp', then the data is interpreted as an RLP list of signed transactions.The '.rlp' format is identical to the output.body format. (default: "txs.json")
|
||||
--state.fork value Name of ruleset to use.
|
||||
--state.chainid value ChainID to use (default: 1)
|
||||
--state.reward value Mining reward. Set to -1 to disable (default: 0)
|
||||
@@ -110,7 +112,10 @@ Two resulting files:
|
||||
}
|
||||
],
|
||||
"rejected": [
|
||||
1
|
||||
{
|
||||
"index": 1,
|
||||
"error": "nonce too low: address 0x8A8eAFb1cf62BfBeb1741769DAE1a9dd47996192, tx: 0 state: 1"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
@@ -156,7 +161,10 @@ Output:
|
||||
}
|
||||
],
|
||||
"rejected": [
|
||||
1
|
||||
{
|
||||
"index": 1,
|
||||
"error": "nonce too low: address 0x8A8eAFb1cf62BfBeb1741769DAE1a9dd47996192, tx: 0 state: 1"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -168,9 +176,9 @@ Mining rewards and ommer rewards might need to be added. This is how those are a
|
||||
|
||||
- `block_reward` is the block mining reward for the miner (`0xaa`), of a block at height `N`.
|
||||
- For each ommer (mined by `0xbb`), with blocknumber `N-delta`
|
||||
- (where `delta` is the difference between the current block and the ommer)
|
||||
- The account `0xbb` (ommer miner) is awarded `(8-delta)/ 8 * block_reward`
|
||||
- The account `0xaa` (block miner) is awarded `block_reward / 32`
|
||||
- (where `delta` is the difference between the current block and the ommer)
|
||||
- The account `0xbb` (ommer miner) is awarded `(8-delta)/ 8 * block_reward`
|
||||
- The account `0xaa` (block miner) is awarded `block_reward / 32`
|
||||
|
||||
To make `state_t8n` apply these, the following inputs are required:
|
||||
|
||||
@@ -220,7 +228,7 @@ Output:
|
||||
### Future EIPS
|
||||
|
||||
It is also possible to experiment with future eips that are not yet defined in a hard fork.
|
||||
Example, putting EIP-1344 into Frontier:
|
||||
Example, putting EIP-1344 into Frontier:
|
||||
```
|
||||
./evm t8n --state.fork=Frontier+1344 --input.pre=./testdata/1/pre.json --input.txs=./testdata/1/txs.json --input.env=/testdata/1/env.json
|
||||
```
|
||||
@@ -229,41 +237,102 @@ Example, putting EIP-1344 into Frontier:
|
||||
|
||||
The `BLOCKHASH` opcode requires blockhashes to be provided by the caller, inside the `env`.
|
||||
If a required blockhash is not provided, the exit code should be `4`:
|
||||
Example where blockhashes are provided:
|
||||
Example where blockhashes are provided:
|
||||
```
|
||||
./evm t8n --input.alloc=./testdata/3/alloc.json --input.txs=./testdata/3/txs.json --input.env=./testdata/3/env.json --trace
|
||||
./evm --verbosity=1 t8n --input.alloc=./testdata/3/alloc.json --input.txs=./testdata/3/txs.json --input.env=./testdata/3/env.json --trace
|
||||
INFO [07-27|11:53:40.960] Trie dumping started root=b7341d..857ea1
|
||||
INFO [07-27|11:53:40.960] Trie dumping complete accounts=3 elapsed="103.298µs"
|
||||
INFO [07-27|11:53:40.960] Wrote file file=alloc.json
|
||||
INFO [07-27|11:53:40.960] Wrote file file=result.json
|
||||
|
||||
```
|
||||
|
||||
```
|
||||
cat trace-0-0x72fadbef39cd251a437eea619cfeda752271a5faaaa2147df012e112159ffb81.jsonl | grep BLOCKHASH -C2
|
||||
```
|
||||
```
|
||||
{"pc":0,"op":96,"gas":"0x5f58ef8","gasCost":"0x3","memory":"0x","memSize":0,"stack":[],"returnStack":[],"returnData":"0x","depth":1,"refund":0,"opName":"PUSH1","error":""}
|
||||
{"pc":2,"op":64,"gas":"0x5f58ef5","gasCost":"0x14","memory":"0x","memSize":0,"stack":["0x1"],"returnStack":[],"returnData":"0x","depth":1,"refund":0,"opName":"BLOCKHASH","error":""}
|
||||
{"pc":3,"op":0,"gas":"0x5f58ee1","gasCost":"0x0","memory":"0x","memSize":0,"stack":["0xdac58aa524e50956d0c0bae7f3f8bb9d35381365d07804dd5b48a5a297c06af4"],"returnStack":[],"returnData":"0x","depth":1,"refund":0,"opName":"STOP","error":""}
|
||||
{"output":"","gasUsed":"0x17","time":142709}
|
||||
{"pc":0,"op":96,"gas":"0x5f58ef8","gasCost":"0x3","memory":"0x","memSize":0,"stack":[],"returnData":"0x","depth":1,"refund":0,"opName":"PUSH1","error":""}
|
||||
{"pc":2,"op":64,"gas":"0x5f58ef5","gasCost":"0x14","memory":"0x","memSize":0,"stack":["0x1"],"returnData":"0x","depth":1,"refund":0,"opName":"BLOCKHASH","error":""}
|
||||
{"pc":3,"op":0,"gas":"0x5f58ee1","gasCost":"0x0","memory":"0x","memSize":0,"stack":["0xdac58aa524e50956d0c0bae7f3f8bb9d35381365d07804dd5b48a5a297c06af4"],"returnData":"0x","depth":1,"refund":0,"opName":"STOP","error":""}
|
||||
{"output":"","gasUsed":"0x17","time":156276}
|
||||
```
|
||||
|
||||
In this example, the caller has not provided the required blockhash:
|
||||
```
|
||||
./evm t8n --input.alloc=./testdata/4/alloc.json --input.txs=./testdata/4/txs.json --input.env=./testdata/4/env.json --trace
|
||||
```
|
||||
```
|
||||
ERROR(4): getHash(3) invoked, blockhash for that block not provided
|
||||
```
|
||||
Error code: 4
|
||||
|
||||
### Chaining
|
||||
|
||||
Another thing that can be done, is to chain invocations:
|
||||
```
|
||||
./evm t8n --input.alloc=./testdata/1/alloc.json --input.txs=./testdata/1/txs.json --input.env=./testdata/1/env.json --output.alloc=stdout | ./evm t8n --input.alloc=stdin --input.env=./testdata/1/env.json --input.txs=./testdata/1/txs.json
|
||||
INFO [01-21|22:41:22.963] rejected tx index=1 hash=0557ba..18d673 from=0x8A8eAFb1cf62BfBeb1741769DAE1a9dd47996192 error="nonce too low: address 0x8A8eAFb1cf62BfBeb1741769DAE1a9dd47996192, tx: 0 state: 1"
|
||||
INFO [01-21|22:41:22.966] rejected tx index=0 hash=0557ba..18d673 from=0x8A8eAFb1cf62BfBeb1741769DAE1a9dd47996192 error="nonce too low: address 0x8A8eAFb1cf62BfBeb1741769DAE1a9dd47996192, tx: 0 state: 1"
|
||||
INFO [01-21|22:41:22.967] rejected tx index=1 hash=0557ba..18d673 from=0x8A8eAFb1cf62BfBeb1741769DAE1a9dd47996192 error="nonce too low: address 0x8A8eAFb1cf62BfBeb1741769DAE1a9dd47996192, tx: 0 state: 1"
|
||||
INFO [07-27|11:53:41.049] rejected tx index=1 hash=0557ba..18d673 from=0x8A8eAFb1cf62BfBeb1741769DAE1a9dd47996192 error="nonce too low: address 0x8A8eAFb1cf62BfBeb1741769DAE1a9dd47996192, tx: 0 state: 1"
|
||||
INFO [07-27|11:53:41.050] Trie dumping started root=84208a..ae4e13
|
||||
INFO [07-27|11:53:41.050] Trie dumping complete accounts=3 elapsed="59.412µs"
|
||||
INFO [07-27|11:53:41.050] Wrote file file=result.json
|
||||
INFO [07-27|11:53:41.051] rejected tx index=0 hash=0557ba..18d673 from=0x8A8eAFb1cf62BfBeb1741769DAE1a9dd47996192 error="nonce too low: address 0x8A8eAFb1cf62BfBeb1741769DAE1a9dd47996192, tx: 0 state: 1"
|
||||
INFO [07-27|11:53:41.051] rejected tx index=1 hash=0557ba..18d673 from=0x8A8eAFb1cf62BfBeb1741769DAE1a9dd47996192 error="nonce too low: address 0x8A8eAFb1cf62BfBeb1741769DAE1a9dd47996192, tx: 0 state: 1"
|
||||
INFO [07-27|11:53:41.052] Trie dumping started root=84208a..ae4e13
|
||||
INFO [07-27|11:53:41.052] Trie dumping complete accounts=3 elapsed="45.734µs"
|
||||
INFO [07-27|11:53:41.052] Wrote file file=alloc.json
|
||||
INFO [07-27|11:53:41.052] Wrote file file=result.json
|
||||
|
||||
```
|
||||
What happened here, is that we first applied two identical transactions, so the second one was rejected.
|
||||
What happened here, is that we first applied two identical transactions, so the second one was rejected.
|
||||
Then, taking the poststate alloc as the input for the next state, we tried again to include
|
||||
the same two transactions: this time, both failed due to too low nonce.
|
||||
|
||||
In order to meaningfully chain invocations, one would need to provide meaningful new `env`, otherwise the
|
||||
actual blocknumber (exposed to the EVM) would not increase.
|
||||
|
||||
### Transactions in RLP form
|
||||
|
||||
It is possible to provide already-signed transactions as input to, using an `input.txs` which ends with the `rlp` suffix.
|
||||
The input format for RLP-form transactions is _identical_ to the _output_ format for block bodies. Therefore, it's fully possible
|
||||
to use the evm to go from `json` input to `rlp` input.
|
||||
|
||||
The following command takes **json** the transactions in `./testdata/13/txs.json` and signs them. After execution, they are output to `signed_txs.rlp`.:
|
||||
```
|
||||
./evm t8n --state.fork=London --input.alloc=./testdata/13/alloc.json --input.txs=./testdata/13/txs.json --input.env=./testdata/13/env.json --output.result=alloc_jsontx.json --output.body=signed_txs.rlp
|
||||
INFO [07-27|11:53:41.124] Trie dumping started root=e4b924..6aef61
|
||||
INFO [07-27|11:53:41.124] Trie dumping complete accounts=3 elapsed="94.284µs"
|
||||
INFO [07-27|11:53:41.125] Wrote file file=alloc.json
|
||||
INFO [07-27|11:53:41.125] Wrote file file=alloc_jsontx.json
|
||||
INFO [07-27|11:53:41.125] Wrote file file=signed_txs.rlp
|
||||
|
||||
```
|
||||
|
||||
The `output.body` is the rlp-list of transactions, encoded in hex and placed in a string a'la `json` encoding rules:
|
||||
```
|
||||
cat signed_txs.rlp
|
||||
"0xf8d2b86702f864010180820fa08284d09411111111111111111111111111111111111111118080c001a0b7dfab36232379bb3d1497a4f91c1966b1f932eae3ade107bf5d723b9cb474e0a06261c359a10f2132f126d250485b90cf20f30340801244a08ef6142ab33d1904b86702f864010280820fa08284d09411111111111111111111111111111111111111118080c080a0d4ec563b6568cd42d998fc4134b36933c6568d01533b5adf08769270243c6c7fa072bf7c21eac6bbeae5143371eef26d5e279637f3bd73482b55979d76d935b1e9"
|
||||
```
|
||||
|
||||
We can use `rlpdump` to check what the contents are:
|
||||
```
|
||||
rlpdump -hex $(cat signed_txs.rlp | jq -r )
|
||||
[
|
||||
02f864010180820fa08284d09411111111111111111111111111111111111111118080c001a0b7dfab36232379bb3d1497a4f91c1966b1f932eae3ade107bf5d723b9cb474e0a06261c359a10f2132f126d250485b90cf20f30340801244a08ef6142ab33d1904,
|
||||
02f864010280820fa08284d09411111111111111111111111111111111111111118080c080a0d4ec563b6568cd42d998fc4134b36933c6568d01533b5adf08769270243c6c7fa072bf7c21eac6bbeae5143371eef26d5e279637f3bd73482b55979d76d935b1e9,
|
||||
]
|
||||
```
|
||||
Now, we can now use those (or any other already signed transactions), as input, like so:
|
||||
```
|
||||
./evm t8n --state.fork=London --input.alloc=./testdata/13/alloc.json --input.txs=./signed_txs.rlp --input.env=./testdata/13/env.json --output.result=alloc_rlptx.json
|
||||
INFO [07-27|11:53:41.253] Trie dumping started root=e4b924..6aef61
|
||||
INFO [07-27|11:53:41.253] Trie dumping complete accounts=3 elapsed="128.445µs"
|
||||
INFO [07-27|11:53:41.253] Wrote file file=alloc.json
|
||||
INFO [07-27|11:53:41.255] Wrote file file=alloc_rlptx.json
|
||||
|
||||
```
|
||||
|
||||
You might have noticed that the results from these two invocations were stored in two separate files.
|
||||
And we can now finally check that they match.
|
||||
```
|
||||
cat alloc_jsontx.json | jq .stateRoot && cat alloc_rlptx.json | jq .stateRoot
|
||||
"0xe4b924a6adb5959fccf769d5b7bb2f6359e26d1e76a2443c5a91a36d826aef61"
|
||||
"0xe4b924a6adb5959fccf769d5b7bb2f6359e26d1e76a2443c5a91a36d826aef61"
|
||||
```
|
||||
|
@@ -152,7 +152,7 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig,
|
||||
}
|
||||
vmConfig.Tracer = tracer
|
||||
vmConfig.Debug = (tracer != nil)
|
||||
statedb.Prepare(tx.Hash(), blockHash, txIndex)
|
||||
statedb.Prepare(tx.Hash(), txIndex)
|
||||
txContext := core.NewEVMTxContext(msg)
|
||||
snapshot := statedb.Snapshot()
|
||||
evm := vm.NewEVM(vmContext, txContext, statedb, chainConfig, vmConfig)
|
||||
@@ -197,7 +197,7 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig,
|
||||
}
|
||||
|
||||
// Set the receipt logs and create the bloom filter.
|
||||
receipt.Logs = statedb.GetLogs(tx.Hash())
|
||||
receipt.Logs = statedb.GetLogs(tx.Hash(), blockHash)
|
||||
receipt.Bloom = types.CreateBloom(types.Receipts{receipt})
|
||||
// These three are non-consensus fields:
|
||||
//receipt.BlockHash
|
||||
|
@@ -79,8 +79,10 @@ var (
|
||||
Value: "env.json",
|
||||
}
|
||||
InputTxsFlag = cli.StringFlag{
|
||||
Name: "input.txs",
|
||||
Usage: "`stdin` or file name of where to find the transactions to apply.",
|
||||
Name: "input.txs",
|
||||
Usage: "`stdin` or file name of where to find the transactions to apply. " +
|
||||
"If the file prefix is '.rlp', then the data is interpreted as an RLP list of signed transactions." +
|
||||
"The '.rlp' format is identical to the output.body format.",
|
||||
Value: "txs.json",
|
||||
}
|
||||
RewardFlag = cli.Int64Flag{
|
||||
|
@@ -16,11 +16,11 @@ var _ = (*stEnvMarshaling)(nil)
|
||||
// MarshalJSON marshals as JSON.
|
||||
func (s stEnv) MarshalJSON() ([]byte, error) {
|
||||
type stEnv struct {
|
||||
Coinbase common.UnprefixedAddress `json:"currentCoinbase" gencodec:"required"`
|
||||
Coinbase common.UnprefixedAddress `json:"currentCoinbase" gencodec:"required"`
|
||||
Difficulty *math.HexOrDecimal256 `json:"currentDifficulty" gencodec:"required"`
|
||||
GasLimit math.HexOrDecimal64 `json:"currentGasLimit" gencodec:"required"`
|
||||
Number math.HexOrDecimal64 `json:"currentNumber" gencodec:"required"`
|
||||
Timestamp math.HexOrDecimal64 `json:"currentTimestamp" gencodec:"required"`
|
||||
GasLimit math.HexOrDecimal64 `json:"currentGasLimit" gencodec:"required"`
|
||||
Number math.HexOrDecimal64 `json:"currentNumber" gencodec:"required"`
|
||||
Timestamp math.HexOrDecimal64 `json:"currentTimestamp" gencodec:"required"`
|
||||
BlockHashes map[math.HexOrDecimal64]common.Hash `json:"blockHashes,omitempty"`
|
||||
Ommers []ommer `json:"ommers,omitempty"`
|
||||
BaseFee *math.HexOrDecimal256 `json:"currentBaseFee,omitempty"`
|
||||
@@ -40,11 +40,11 @@ func (s stEnv) MarshalJSON() ([]byte, error) {
|
||||
// UnmarshalJSON unmarshals from JSON.
|
||||
func (s *stEnv) UnmarshalJSON(input []byte) error {
|
||||
type stEnv struct {
|
||||
Coinbase *common.UnprefixedAddress `json:"currentCoinbase" gencodec:"required"`
|
||||
Coinbase *common.UnprefixedAddress `json:"currentCoinbase" gencodec:"required"`
|
||||
Difficulty *math.HexOrDecimal256 `json:"currentDifficulty" gencodec:"required"`
|
||||
GasLimit *math.HexOrDecimal64 `json:"currentGasLimit" gencodec:"required"`
|
||||
Number *math.HexOrDecimal64 `json:"currentNumber" gencodec:"required"`
|
||||
Timestamp *math.HexOrDecimal64 `json:"currentTimestamp" gencodec:"required"`
|
||||
GasLimit *math.HexOrDecimal64 `json:"currentGasLimit" gencodec:"required"`
|
||||
Number *math.HexOrDecimal64 `json:"currentNumber" gencodec:"required"`
|
||||
Timestamp *math.HexOrDecimal64 `json:"currentTimestamp" gencodec:"required"`
|
||||
BlockHashes map[math.HexOrDecimal64]common.Hash `json:"blockHashes,omitempty"`
|
||||
Ommers []ommer `json:"ommers,omitempty"`
|
||||
BaseFee *math.HexOrDecimal256 `json:"currentBaseFee,omitempty"`
|
||||
|
@@ -25,6 +25,7 @@ import (
|
||||
"math/big"
|
||||
"os"
|
||||
"path"
|
||||
"strings"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||
@@ -72,6 +73,7 @@ type input struct {
|
||||
Alloc core.GenesisAlloc `json:"alloc,omitempty"`
|
||||
Env *stEnv `json:"env,omitempty"`
|
||||
Txs []*txWithKey `json:"txs,omitempty"`
|
||||
TxRlp string `json:"txsRlp,omitempty"`
|
||||
}
|
||||
|
||||
func Main(ctx *cli.Context) error {
|
||||
@@ -199,11 +201,44 @@ func Main(ctx *cli.Context) error {
|
||||
}
|
||||
defer inFile.Close()
|
||||
decoder := json.NewDecoder(inFile)
|
||||
if err := decoder.Decode(&txsWithKeys); err != nil {
|
||||
return NewError(ErrorJson, fmt.Errorf("failed unmarshaling txs-file: %v", err))
|
||||
if strings.HasSuffix(txStr, ".rlp") {
|
||||
var body hexutil.Bytes
|
||||
if err := decoder.Decode(&body); err != nil {
|
||||
return err
|
||||
}
|
||||
var txs types.Transactions
|
||||
if err := rlp.DecodeBytes(body, &txs); err != nil {
|
||||
return err
|
||||
}
|
||||
for _, tx := range txs {
|
||||
txsWithKeys = append(txsWithKeys, &txWithKey{
|
||||
key: nil,
|
||||
tx: tx,
|
||||
})
|
||||
}
|
||||
} else {
|
||||
if err := decoder.Decode(&txsWithKeys); err != nil {
|
||||
return NewError(ErrorJson, fmt.Errorf("failed unmarshaling txs-file: %v", err))
|
||||
}
|
||||
}
|
||||
} else {
|
||||
txsWithKeys = inputData.Txs
|
||||
if len(inputData.TxRlp) > 0 {
|
||||
// Decode the body of already signed transactions
|
||||
body := common.FromHex(inputData.TxRlp)
|
||||
var txs types.Transactions
|
||||
if err := rlp.DecodeBytes(body, &txs); err != nil {
|
||||
return err
|
||||
}
|
||||
for _, tx := range txs {
|
||||
txsWithKeys = append(txsWithKeys, &txWithKey{
|
||||
key: nil,
|
||||
tx: tx,
|
||||
})
|
||||
}
|
||||
} else {
|
||||
// JSON encoded transactions
|
||||
txsWithKeys = inputData.Txs
|
||||
}
|
||||
}
|
||||
// We may have to sign the transactions.
|
||||
signer := types.MakeSigner(chainConfig, big.NewInt(int64(prestate.Env.Number)))
|
||||
@@ -365,6 +400,7 @@ func dispatchOutput(ctx *cli.Context, baseDir string, result *ExecutionResult, a
|
||||
return NewError(ErrorJson, fmt.Errorf("failed marshalling output: %v", err))
|
||||
}
|
||||
os.Stdout.Write(b)
|
||||
os.Stdout.Write([]byte("\n"))
|
||||
}
|
||||
if len(stdErrObject) > 0 {
|
||||
b, err := json.MarshalIndent(stdErrObject, "", " ")
|
||||
@@ -372,6 +408,7 @@ func dispatchOutput(ctx *cli.Context, baseDir string, result *ExecutionResult, a
|
||||
return NewError(ErrorJson, fmt.Errorf("failed marshalling output: %v", err))
|
||||
}
|
||||
os.Stderr.Write(b)
|
||||
os.Stderr.Write([]byte("\n"))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
@@ -129,11 +129,6 @@ var (
|
||||
Name: "noreturndata",
|
||||
Usage: "disable return data output",
|
||||
}
|
||||
EVMInterpreterFlag = cli.StringFlag{
|
||||
Name: "vm.evm",
|
||||
Usage: "External EVM configuration (default = built-in interpreter)",
|
||||
Value: "",
|
||||
}
|
||||
)
|
||||
|
||||
var stateTransitionCommand = cli.Command{
|
||||
@@ -185,7 +180,6 @@ func init() {
|
||||
DisableStackFlag,
|
||||
DisableStorageFlag,
|
||||
DisableReturnDataFlag,
|
||||
EVMInterpreterFlag,
|
||||
}
|
||||
app.Commands = []cli.Command{
|
||||
compileCommand,
|
||||
|
@@ -211,9 +211,8 @@ func runCmd(ctx *cli.Context) error {
|
||||
Coinbase: genesisConfig.Coinbase,
|
||||
BlockNumber: new(big.Int).SetUint64(genesisConfig.Number),
|
||||
EVMConfig: vm.Config{
|
||||
Tracer: tracer,
|
||||
Debug: ctx.GlobalBool(DebugFlag.Name) || ctx.GlobalBool(MachineFlag.Name),
|
||||
EVMInterpreter: ctx.GlobalString(EVMInterpreterFlag.Name),
|
||||
Tracer: tracer,
|
||||
Debug: ctx.GlobalBool(DebugFlag.Name) || ctx.GlobalBool(MachineFlag.Name),
|
||||
},
|
||||
}
|
||||
|
||||
|
11
cmd/evm/testdata/12/alloc.json
vendored
Normal file
11
cmd/evm/testdata/12/alloc.json
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b" : {
|
||||
"balance" : "84000000",
|
||||
"code" : "0x",
|
||||
"nonce" : "0x00",
|
||||
"storage" : {
|
||||
"0x00" : "0x00"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
10
cmd/evm/testdata/12/env.json
vendored
Normal file
10
cmd/evm/testdata/12/env.json
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"currentCoinbase" : "0x2adc25665018aa1fe0e6bc666dac8fc2697ff9ba",
|
||||
"currentDifficulty" : "0x020000",
|
||||
"currentNumber" : "0x01",
|
||||
"currentTimestamp" : "0x03e8",
|
||||
"previousHash" : "0xfda4419b3660e99f37e536dae1ab081c180136bb38c837a93e93d9aab58553b2",
|
||||
"currentGasLimit" : "0x0f4240",
|
||||
"currentBaseFee" : "0x20"
|
||||
}
|
||||
|
40
cmd/evm/testdata/12/readme.md
vendored
Normal file
40
cmd/evm/testdata/12/readme.md
vendored
Normal file
@@ -0,0 +1,40 @@
|
||||
## Test 1559 balance + gasCap
|
||||
|
||||
This test contains an EIP-1559 consensus issue which happened on Ropsten, where
|
||||
`geth` did not properly account for the value transfer while doing the check on `max_fee_per_gas * gas_limit`.
|
||||
|
||||
Before the issue was fixed, this invocation allowed the transaction to pass into a block:
|
||||
```
|
||||
dir=./testdata/12 && ./evm t8n --state.fork=London --input.alloc=$dir/alloc.json --input.txs=$dir/txs.json --input.env=$dir/env.json --output.alloc=stdout --output.result=stdout
|
||||
```
|
||||
|
||||
With the fix applied, the result is:
|
||||
```
|
||||
dir=./testdata/12 && ./evm t8n --state.fork=London --input.alloc=$dir/alloc.json --input.txs=$dir/txs.json --input.env=$dir/env.json --output.alloc=stdout --output.result=stdout
|
||||
INFO [07-21|19:03:50.276] rejected tx index=0 hash=ccc996..d83435 from=0xa94f5374Fce5edBC8E2a8697C15331677e6EbF0B error="insufficient funds for gas * price + value: address 0xa94f5374Fce5edBC8E2a8697C15331677e6EbF0B have 84000000 want 84000032"
|
||||
INFO [07-21|19:03:50.276] Trie dumping started root=e05f81..6597a5
|
||||
INFO [07-21|19:03:50.276] Trie dumping complete accounts=1 elapsed="39.549µs"
|
||||
{
|
||||
"alloc": {
|
||||
"0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b": {
|
||||
"balance": "0x501bd00"
|
||||
}
|
||||
},
|
||||
"result": {
|
||||
"stateRoot": "0xe05f81f8244a76503ceec6f88abfcd03047a612a1001217f37d30984536597a5",
|
||||
"txRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
|
||||
"receiptRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
|
||||
"logsHash": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347",
|
||||
"logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
|
||||
"receipts": [],
|
||||
"rejected": [
|
||||
{
|
||||
"index": 0,
|
||||
"error": "insufficient funds for gas * price + value: address 0xa94f5374Fce5edBC8E2a8697C15331677e6EbF0B have 84000000 want 84000032"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
The transaction is rejected.
|
20
cmd/evm/testdata/12/txs.json
vendored
Normal file
20
cmd/evm/testdata/12/txs.json
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
[
|
||||
{
|
||||
"input" : "0x",
|
||||
"gas" : "0x5208",
|
||||
"nonce" : "0x0",
|
||||
"to" : "0x1111111111111111111111111111111111111111",
|
||||
"value" : "0x20",
|
||||
"v" : "0x0",
|
||||
"r" : "0x0",
|
||||
"s" : "0x0",
|
||||
"secretKey" : "0x45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8",
|
||||
"chainId" : "0x1",
|
||||
"type" : "0x2",
|
||||
"maxFeePerGas" : "0xfa0",
|
||||
"maxPriorityFeePerGas" : "0x20",
|
||||
"accessList" : [
|
||||
]
|
||||
}
|
||||
]
|
||||
|
23
cmd/evm/testdata/13/alloc.json
vendored
Normal file
23
cmd/evm/testdata/13/alloc.json
vendored
Normal file
@@ -0,0 +1,23 @@
|
||||
{
|
||||
"0x1111111111111111111111111111111111111111" : {
|
||||
"balance" : "0x010000000000",
|
||||
"code" : "0xfe",
|
||||
"nonce" : "0x01",
|
||||
"storage" : {
|
||||
}
|
||||
},
|
||||
"0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b" : {
|
||||
"balance" : "0x010000000000",
|
||||
"code" : "0x",
|
||||
"nonce" : "0x01",
|
||||
"storage" : {
|
||||
}
|
||||
},
|
||||
"0xd02d72e067e77158444ef2020ff2d325f929b363" : {
|
||||
"balance" : "0x01000000000000",
|
||||
"code" : "0x",
|
||||
"nonce" : "0x01",
|
||||
"storage" : {
|
||||
}
|
||||
}
|
||||
}
|
12
cmd/evm/testdata/13/env.json
vendored
Normal file
12
cmd/evm/testdata/13/env.json
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"currentCoinbase" : "0x2adc25665018aa1fe0e6bc666dac8fc2697ff9ba",
|
||||
"currentDifficulty" : "0x020000",
|
||||
"currentNumber" : "0x01",
|
||||
"currentTimestamp" : "0x079e",
|
||||
"previousHash" : "0xcb23ee65a163121f640673b41788ee94633941405f95009999b502eedfbbfd4f",
|
||||
"currentGasLimit" : "0x40000000",
|
||||
"currentBaseFee" : "0x036b",
|
||||
"blockHashes" : {
|
||||
"0" : "0xcb23ee65a163121f640673b41788ee94633941405f95009999b502eedfbbfd4f"
|
||||
}
|
||||
}
|
4
cmd/evm/testdata/13/readme.md
vendored
Normal file
4
cmd/evm/testdata/13/readme.md
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
## Input transactions in RLP form
|
||||
|
||||
This testdata folder is used to examplify how transaction input can be provided in rlp form.
|
||||
Please see the README in `evm` folder for how this is performed.
|
34
cmd/evm/testdata/13/txs.json
vendored
Normal file
34
cmd/evm/testdata/13/txs.json
vendored
Normal file
@@ -0,0 +1,34 @@
|
||||
[
|
||||
{
|
||||
"input" : "0x",
|
||||
"gas" : "0x84d0",
|
||||
"nonce" : "0x1",
|
||||
"to" : "0x1111111111111111111111111111111111111111",
|
||||
"value" : "0x0",
|
||||
"v" : "0x0",
|
||||
"r" : "0x0",
|
||||
"s" : "0x0",
|
||||
"secretKey" : "0x41f6e321b31e72173f8ff2e292359e1862f24fba42fe6f97efaf641980eff298",
|
||||
"chainId" : "0x1",
|
||||
"type" : "0x2",
|
||||
"maxFeePerGas" : "0xfa0",
|
||||
"maxPriorityFeePerGas" : "0x0",
|
||||
"accessList" : []
|
||||
},
|
||||
{
|
||||
"input" : "0x",
|
||||
"gas" : "0x84d0",
|
||||
"nonce" : "0x2",
|
||||
"to" : "0x1111111111111111111111111111111111111111",
|
||||
"value" : "0x0",
|
||||
"v" : "0x0",
|
||||
"r" : "0x0",
|
||||
"s" : "0x0",
|
||||
"secretKey" : "0x41f6e321b31e72173f8ff2e292359e1862f24fba42fe6f97efaf641980eff298",
|
||||
"chainId" : "0x1",
|
||||
"type" : "0x2",
|
||||
"maxFeePerGas" : "0xfa0",
|
||||
"maxPriorityFeePerGas" : "0x0",
|
||||
"accessList" : []
|
||||
}
|
||||
]
|
@@ -11,6 +11,8 @@ function showjson(){
|
||||
function demo(){
|
||||
echo "$ticks"
|
||||
echo "$1"
|
||||
$1
|
||||
echo ""
|
||||
echo "$ticks"
|
||||
echo ""
|
||||
}
|
||||
@@ -152,9 +154,7 @@ echo ""
|
||||
echo "The \`BLOCKHASH\` opcode requires blockhashes to be provided by the caller, inside the \`env\`."
|
||||
echo "If a required blockhash is not provided, the exit code should be \`4\`:"
|
||||
echo "Example where blockhashes are provided: "
|
||||
cmd="./evm t8n --input.alloc=./testdata/3/alloc.json --input.txs=./testdata/3/txs.json --input.env=./testdata/3/env.json --trace"
|
||||
tick && echo $cmd && tick
|
||||
$cmd 2>&1 >/dev/null
|
||||
demo "./evm --verbosity=1 t8n --input.alloc=./testdata/3/alloc.json --input.txs=./testdata/3/txs.json --input.env=./testdata/3/env.json --trace"
|
||||
cmd="cat trace-0-0x72fadbef39cd251a437eea619cfeda752271a5faaaa2147df012e112159ffb81.jsonl | grep BLOCKHASH -C2"
|
||||
tick && echo $cmd && tick
|
||||
echo "$ticks"
|
||||
@@ -164,13 +164,11 @@ echo ""
|
||||
|
||||
echo "In this example, the caller has not provided the required blockhash:"
|
||||
cmd="./evm t8n --input.alloc=./testdata/4/alloc.json --input.txs=./testdata/4/txs.json --input.env=./testdata/4/env.json --trace"
|
||||
tick && echo $cmd && tick
|
||||
tick
|
||||
$cmd
|
||||
tick && echo $cmd && $cmd
|
||||
errc=$?
|
||||
tick
|
||||
echo "Error code: $errc"
|
||||
|
||||
echo ""
|
||||
|
||||
echo "### Chaining"
|
||||
echo ""
|
||||
@@ -189,3 +187,28 @@ echo ""
|
||||
echo "In order to meaningfully chain invocations, one would need to provide meaningful new \`env\`, otherwise the"
|
||||
echo "actual blocknumber (exposed to the EVM) would not increase."
|
||||
echo ""
|
||||
|
||||
echo "### Transactions in RLP form"
|
||||
echo ""
|
||||
echo "It is possible to provide already-signed transactions as input to, using an \`input.txs\` which ends with the \`rlp\` suffix."
|
||||
echo "The input format for RLP-form transactions is _identical_ to the _output_ format for block bodies. Therefore, it's fully possible"
|
||||
echo "to use the evm to go from \`json\` input to \`rlp\` input."
|
||||
echo ""
|
||||
echo "The following command takes **json** the transactions in \`./testdata/13/txs.json\` and signs them. After execution, they are output to \`signed_txs.rlp\`.:"
|
||||
demo "./evm t8n --state.fork=London --input.alloc=./testdata/13/alloc.json --input.txs=./testdata/13/txs.json --input.env=./testdata/13/env.json --output.result=alloc_jsontx.json --output.body=signed_txs.rlp"
|
||||
echo "The \`output.body\` is the rlp-list of transactions, encoded in hex and placed in a string a'la \`json\` encoding rules:"
|
||||
demo "cat signed_txs.rlp"
|
||||
echo "We can use \`rlpdump\` to check what the contents are: "
|
||||
echo "$ticks"
|
||||
echo "rlpdump -hex \$(cat signed_txs.rlp | jq -r )"
|
||||
rlpdump -hex $(cat signed_txs.rlp | jq -r )
|
||||
echo "$ticks"
|
||||
echo "Now, we can now use those (or any other already signed transactions), as input, like so: "
|
||||
demo "./evm t8n --state.fork=London --input.alloc=./testdata/13/alloc.json --input.txs=./signed_txs.rlp --input.env=./testdata/13/env.json --output.result=alloc_rlptx.json"
|
||||
|
||||
echo "You might have noticed that the results from these two invocations were stored in two separate files. "
|
||||
echo "And we can now finally check that they match."
|
||||
echo "$ticks"
|
||||
echo "cat alloc_jsontx.json | jq .stateRoot && cat alloc_rlptx.json | jq .stateRoot"
|
||||
cat alloc_jsontx.json | jq .stateRoot && cat alloc_rlptx.json | jq .stateRoot
|
||||
echo "$ticks"
|
||||
|
@@ -68,7 +68,6 @@ It expects the genesis file as argument.`,
|
||||
utils.RopstenFlag,
|
||||
utils.RinkebyFlag,
|
||||
utils.GoerliFlag,
|
||||
utils.CalaverasFlag,
|
||||
},
|
||||
Category: "BLOCKCHAIN COMMANDS",
|
||||
Description: `
|
||||
@@ -92,11 +91,15 @@ The dumpgenesis command dumps the genesis block configuration in JSON format to
|
||||
utils.MetricsHTTPFlag,
|
||||
utils.MetricsPortFlag,
|
||||
utils.MetricsEnableInfluxDBFlag,
|
||||
utils.MetricsEnableInfluxDBV2Flag,
|
||||
utils.MetricsInfluxDBEndpointFlag,
|
||||
utils.MetricsInfluxDBDatabaseFlag,
|
||||
utils.MetricsInfluxDBUsernameFlag,
|
||||
utils.MetricsInfluxDBPasswordFlag,
|
||||
utils.MetricsInfluxDBTagsFlag,
|
||||
utils.MetricsInfluxDBTokenFlag,
|
||||
utils.MetricsInfluxDBBucketFlag,
|
||||
utils.MetricsInfluxDBOrganizationFlag,
|
||||
utils.TxLookupLimitFlag,
|
||||
},
|
||||
Category: "BLOCKCHAIN COMMANDS",
|
||||
|
@@ -31,6 +31,7 @@ import (
|
||||
"github.com/ethereum/go-ethereum/eth/catalyst"
|
||||
"github.com/ethereum/go-ethereum/eth/ethconfig"
|
||||
"github.com/ethereum/go-ethereum/internal/ethapi"
|
||||
"github.com/ethereum/go-ethereum/log"
|
||||
"github.com/ethereum/go-ethereum/metrics"
|
||||
"github.com/ethereum/go-ethereum/node"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
@@ -63,7 +64,12 @@ var tomlSettings = toml.Config{
|
||||
return field
|
||||
},
|
||||
MissingField: func(rt reflect.Type, field string) error {
|
||||
link := ""
|
||||
id := fmt.Sprintf("%s.%s", rt.String(), field)
|
||||
if deprecated(id) {
|
||||
log.Warn("Config field is deprecated and won't have an effect", "name", id)
|
||||
return nil
|
||||
}
|
||||
var link string
|
||||
if unicode.IsUpper(rune(rt.Name()[0])) && rt.PkgPath() != "main" {
|
||||
link = fmt.Sprintf(", see https://godoc.org/%s#%s for available fields", rt.PkgPath(), rt.Name())
|
||||
}
|
||||
@@ -227,4 +233,27 @@ func applyMetricConfig(ctx *cli.Context, cfg *gethConfig) {
|
||||
if ctx.GlobalIsSet(utils.MetricsInfluxDBTagsFlag.Name) {
|
||||
cfg.Metrics.InfluxDBTags = ctx.GlobalString(utils.MetricsInfluxDBTagsFlag.Name)
|
||||
}
|
||||
if ctx.GlobalIsSet(utils.MetricsEnableInfluxDBV2Flag.Name) {
|
||||
cfg.Metrics.EnableInfluxDBV2 = ctx.GlobalBool(utils.MetricsEnableInfluxDBV2Flag.Name)
|
||||
}
|
||||
if ctx.GlobalIsSet(utils.MetricsInfluxDBTokenFlag.Name) {
|
||||
cfg.Metrics.InfluxDBToken = ctx.GlobalString(utils.MetricsInfluxDBTokenFlag.Name)
|
||||
}
|
||||
if ctx.GlobalIsSet(utils.MetricsInfluxDBBucketFlag.Name) {
|
||||
cfg.Metrics.InfluxDBBucket = ctx.GlobalString(utils.MetricsInfluxDBBucketFlag.Name)
|
||||
}
|
||||
if ctx.GlobalIsSet(utils.MetricsInfluxDBOrganizationFlag.Name) {
|
||||
cfg.Metrics.InfluxDBOrganization = ctx.GlobalString(utils.MetricsInfluxDBOrganizationFlag.Name)
|
||||
}
|
||||
}
|
||||
|
||||
func deprecated(field string) bool {
|
||||
switch field {
|
||||
case "ethconfig.Config.EVMInterpreter":
|
||||
return true
|
||||
case "ethconfig.Config.EWASMInterpreter":
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
@@ -134,8 +134,6 @@ func remoteConsole(ctx *cli.Context) error {
|
||||
path = filepath.Join(path, "rinkeby")
|
||||
} else if ctx.GlobalBool(utils.GoerliFlag.Name) {
|
||||
path = filepath.Join(path, "goerli")
|
||||
} else if ctx.GlobalBool(utils.CalaverasFlag.Name) {
|
||||
path = filepath.Join(path, "calaveras")
|
||||
}
|
||||
}
|
||||
endpoint = fmt.Sprintf("%s/geth.ipc", path)
|
||||
|
@@ -75,7 +75,6 @@ Remove blockchain and state databases`,
|
||||
utils.RopstenFlag,
|
||||
utils.RinkebyFlag,
|
||||
utils.GoerliFlag,
|
||||
utils.CalaverasFlag,
|
||||
},
|
||||
Usage: "Inspect the storage size for each type of data in the database",
|
||||
Description: `This commands iterates the entire database. If the optional 'prefix' and 'start' arguments are provided, then the iteration is limited to the given subset of data.`,
|
||||
@@ -91,7 +90,6 @@ Remove blockchain and state databases`,
|
||||
utils.RopstenFlag,
|
||||
utils.RinkebyFlag,
|
||||
utils.GoerliFlag,
|
||||
utils.CalaverasFlag,
|
||||
},
|
||||
}
|
||||
dbCompactCmd = cli.Command{
|
||||
@@ -105,7 +103,6 @@ Remove blockchain and state databases`,
|
||||
utils.RopstenFlag,
|
||||
utils.RinkebyFlag,
|
||||
utils.GoerliFlag,
|
||||
utils.CalaverasFlag,
|
||||
utils.CacheFlag,
|
||||
utils.CacheDatabaseFlag,
|
||||
},
|
||||
@@ -125,7 +122,6 @@ corruption if it is aborted during execution'!`,
|
||||
utils.RopstenFlag,
|
||||
utils.RinkebyFlag,
|
||||
utils.GoerliFlag,
|
||||
utils.CalaverasFlag,
|
||||
},
|
||||
Description: "This command looks up the specified database key from the database.",
|
||||
}
|
||||
@@ -141,7 +137,6 @@ corruption if it is aborted during execution'!`,
|
||||
utils.RopstenFlag,
|
||||
utils.RinkebyFlag,
|
||||
utils.GoerliFlag,
|
||||
utils.CalaverasFlag,
|
||||
},
|
||||
Description: `This command deletes the specified database key from the database.
|
||||
WARNING: This is a low-level operation which may cause database corruption!`,
|
||||
@@ -158,7 +153,6 @@ WARNING: This is a low-level operation which may cause database corruption!`,
|
||||
utils.RopstenFlag,
|
||||
utils.RinkebyFlag,
|
||||
utils.GoerliFlag,
|
||||
utils.CalaverasFlag,
|
||||
},
|
||||
Description: `This command sets a given database key to the given value.
|
||||
WARNING: This is a low-level operation which may cause database corruption!`,
|
||||
@@ -175,7 +169,6 @@ WARNING: This is a low-level operation which may cause database corruption!`,
|
||||
utils.RopstenFlag,
|
||||
utils.RinkebyFlag,
|
||||
utils.GoerliFlag,
|
||||
utils.CalaverasFlag,
|
||||
},
|
||||
Description: "This command looks up the specified database key from the database.",
|
||||
}
|
||||
@@ -191,7 +184,6 @@ WARNING: This is a low-level operation which may cause database corruption!`,
|
||||
utils.RopstenFlag,
|
||||
utils.RinkebyFlag,
|
||||
utils.GoerliFlag,
|
||||
utils.CalaverasFlag,
|
||||
},
|
||||
Description: "This command displays information about the freezer index.",
|
||||
}
|
||||
|
@@ -137,14 +137,18 @@ func startGethWithIpc(t *testing.T, name string, args ...string) *gethrpc {
|
||||
name: name,
|
||||
geth: runGeth(t, args...),
|
||||
}
|
||||
// wait before we can attach to it. TODO: probe for it properly
|
||||
time.Sleep(1 * time.Second)
|
||||
var err error
|
||||
ipcpath := ipcEndpoint(ipcName, g.geth.Datadir)
|
||||
if g.rpc, err = rpc.Dial(ipcpath); err != nil {
|
||||
t.Fatalf("%v rpc connect to %v: %v", name, ipcpath, err)
|
||||
// We can't know exactly how long geth will take to start, so we try 10
|
||||
// times over a 5 second period.
|
||||
var err error
|
||||
for i := 0; i < 10; i++ {
|
||||
time.Sleep(500 * time.Millisecond)
|
||||
if g.rpc, err = rpc.Dial(ipcpath); err == nil {
|
||||
return g
|
||||
}
|
||||
}
|
||||
return g
|
||||
t.Fatalf("%v rpc connect to %v: %v", name, ipcpath, err)
|
||||
return nil
|
||||
}
|
||||
|
||||
func initGeth(t *testing.T) string {
|
||||
|
@@ -118,7 +118,7 @@ var (
|
||||
utils.MiningEnabledFlag,
|
||||
utils.MinerThreadsFlag,
|
||||
utils.MinerNotifyFlag,
|
||||
utils.MinerGasTargetFlag,
|
||||
utils.LegacyMinerGasTargetFlag,
|
||||
utils.MinerGasLimitFlag,
|
||||
utils.MinerGasPriceFlag,
|
||||
utils.MinerEtherbaseFlag,
|
||||
@@ -138,7 +138,6 @@ var (
|
||||
utils.RopstenFlag,
|
||||
utils.RinkebyFlag,
|
||||
utils.GoerliFlag,
|
||||
utils.CalaverasFlag,
|
||||
utils.VMEnableDebugFlag,
|
||||
utils.NetworkIdFlag,
|
||||
utils.EthStatsURLFlag,
|
||||
@@ -148,8 +147,6 @@ var (
|
||||
utils.GpoPercentileFlag,
|
||||
utils.GpoMaxGasPriceFlag,
|
||||
utils.GpoIgnoreGasPriceFlag,
|
||||
utils.EWASMInterpreterFlag,
|
||||
utils.EVMInterpreterFlag,
|
||||
utils.MinerNotifyFullFlag,
|
||||
configFileFlag,
|
||||
utils.CatalystFlag,
|
||||
@@ -197,6 +194,10 @@ var (
|
||||
utils.MetricsInfluxDBUsernameFlag,
|
||||
utils.MetricsInfluxDBPasswordFlag,
|
||||
utils.MetricsInfluxDBTagsFlag,
|
||||
utils.MetricsEnableInfluxDBV2Flag,
|
||||
utils.MetricsInfluxDBTokenFlag,
|
||||
utils.MetricsInfluxDBBucketFlag,
|
||||
utils.MetricsInfluxDBOrganizationFlag,
|
||||
}
|
||||
)
|
||||
|
||||
@@ -276,9 +277,6 @@ func prepare(ctx *cli.Context) {
|
||||
case ctx.GlobalIsSet(utils.GoerliFlag.Name):
|
||||
log.Info("Starting Geth on Görli testnet...")
|
||||
|
||||
case ctx.GlobalIsSet(utils.CalaverasFlag.Name):
|
||||
log.Info("Starting Geth on Calaveras testnet...")
|
||||
|
||||
case ctx.GlobalIsSet(utils.DeveloperFlag.Name):
|
||||
log.Info("Starting Geth in ephemeral dev mode...")
|
||||
|
||||
|
55
cmd/geth/testdata/vcheck/vulnerabilities.json
vendored
55
cmd/geth/testdata/vcheck/vulnerabilities.json
vendored
@@ -52,13 +52,16 @@
|
||||
"check": "Geth\\/v1\\.9\\.(7|8|9|10|11|12|13|14|15|16).*$"
|
||||
},
|
||||
{
|
||||
"name": "GethCrash",
|
||||
"name": "Geth DoS via MULMOD",
|
||||
"uid": "GETH-2020-04",
|
||||
"summary": "A denial-of-service issue can be used to crash Geth nodes during block processing",
|
||||
"description": "Full details to be disclosed at a later date",
|
||||
"description": "Affected versions suffer from a vulnerability which can be exploited through the `MULMOD` operation, by specifying a modulo of `0`: `mulmod(a,b,0)`, causing a `panic` in the underlying library. \nThe crash was in the `uint256` library, where a buffer [underflowed](https://github.com/holiman/uint256/blob/4ce82e695c10ddad57215bdbeafb68b8c5df2c30/uint256.go#L442).\n\n\tif `d == 0`, `dLen` remains `0`\n\nand https://github.com/holiman/uint256/blob/4ce82e695c10ddad57215bdbeafb68b8c5df2c30/uint256.go#L451 will try to access index `[-1]`.\n\nThe `uint256` library was first merged in this [commit](https://github.com/ethereum/go-ethereum/commit/cf6674539c589f80031f3371a71c6a80addbe454), on 2020-06-08. \nExploiting this vulnerabilty would cause all vulnerable nodes to drop off the network. \n\nThe issue was brought to our attention through a [bug report](https://github.com/ethereum/go-ethereum/issues/21367), showing a `panic` occurring on sync from genesis on the Ropsten network.\n \nIt was estimated that the least obvious way to fix this would be to merge the fix into `uint256`, make a new release of that library and then update the geth-dependency.\n",
|
||||
"links": [
|
||||
"https://blog.ethereum.org/2020/11/12/geth_security_release/",
|
||||
"https://github.com/ethereum/go-ethereum/security/advisories/GHSA-jm5c-rv3w-w83m"
|
||||
"https://github.com/ethereum/go-ethereum/security/advisories/GHSA-jm5c-rv3w-w83m",
|
||||
"https://github.com/holiman/uint256/releases/tag/v1.1.1",
|
||||
"https://github.com/holiman/uint256/pull/80",
|
||||
"https://github.com/ethereum/go-ethereum/pull/21368"
|
||||
],
|
||||
"introduced": "v1.9.16",
|
||||
"fixed": "v1.9.18",
|
||||
@@ -66,5 +69,51 @@
|
||||
"severity": "Critical",
|
||||
"CVE": "CVE-2020-26242",
|
||||
"check": "Geth\\/v1\\.9.(16|17).*$"
|
||||
},
|
||||
{
|
||||
"name": "LES Server DoS via GetProofsV2",
|
||||
"uid": "GETH-2020-05",
|
||||
"summary": "A DoS vulnerability can make a LES server crash.",
|
||||
"description": "A DoS vulnerability can make a LES server crash via malicious GetProofsV2 request from a connected LES client.\n\nThe vulnerability was patched in #21896.\n\nThis vulnerability only concern users explicitly running geth as a light server",
|
||||
"links": [
|
||||
"https://github.com/ethereum/go-ethereum/security/advisories/GHSA-r33q-22hv-j29q",
|
||||
"https://github.com/ethereum/go-ethereum/pull/21896"
|
||||
],
|
||||
"introduced": "v1.8.0",
|
||||
"fixed": "v1.9.25",
|
||||
"published": "2020-12-10",
|
||||
"severity": "Medium",
|
||||
"CVE": "CVE-2020-26264",
|
||||
"check": "(Geth\\/v1\\.8\\.*)|(Geth\\/v1\\.9\\.\\d-.*)|(Geth\\/v1\\.9\\.1\\d-.*)|(Geth\\/v1\\.9\\.(20|21|22|23|24)-.*)$"
|
||||
},
|
||||
{
|
||||
"name": "SELFDESTRUCT-recreate consensus flaw",
|
||||
"uid": "GETH-2020-06",
|
||||
"introduced": "v1.9.4",
|
||||
"fixed": "v1.9.20",
|
||||
"summary": "A consensus-vulnerability in Geth could cause a chain split, where vulnerable versions refuse to accept the canonical chain.",
|
||||
"description": "A flaw was repoted at 2020-08-11 by John Youngseok Yang (Software Platform Lab), where a particular sequence of transactions could cause a consensus failure.\n\n- Tx 1:\n - `sender` invokes `caller`.\n - `caller` invokes `0xaa`. `0xaa` has 3 wei, does a self-destruct-to-self\n - `caller` does a `1 wei` -call to `0xaa`, who thereby has 1 wei (the code in `0xaa` still executed, since the tx is still ongoing, but doesn't redo the selfdestruct, it takes a different path if callvalue is non-zero)\n\n-Tx 2:\n - `sender` does a 5-wei call to 0xaa. No exec (since no code). \n\nIn geth, the result would be that `0xaa` had `6 wei`, whereas OE reported (correctly) `5` wei. Furthermore, in geth, if the second tx was not executed, the `0xaa` would be destructed, resulting in `0 wei`. Thus obviously wrong. \n\nIt was determined that the root cause was this [commit](https://github.com/ethereum/go-ethereum/commit/223b950944f494a5b4e0957fd9f92c48b09037ad) from [this PR](https://github.com/ethereum/go-ethereum/pull/19953). The semantics of `createObject` was subtly changd, into returning a non-nil object (with `deleted=true`) where it previously did not if the account had been destructed. This return value caused the new object to inherit the old `balance`.\n",
|
||||
"links": [
|
||||
"https://github.com/ethereum/go-ethereum/security/advisories/GHSA-xw37-57qp-9mm4"
|
||||
],
|
||||
"published": "2020-12-10",
|
||||
"severity": "High",
|
||||
"CVE": "CVE-2020-26265",
|
||||
"check": "(Geth\\/v1\\.9\\.(4|5|6|7|8|9)-.*)|(Geth\\/v1\\.9\\.1\\d-.*)$"
|
||||
},
|
||||
{
|
||||
"name": "Not ready for London upgrade",
|
||||
"uid": "GETH-2021-01",
|
||||
"summary": "The client is not ready for the 'London' technical upgrade, and will deviate from the canonical chain when the London upgrade occurs (at block '12965000' around August 4, 2021.",
|
||||
"description": "At (or around) August 4, Ethereum will undergo a technical upgrade called 'London'. Clients not upgraded will fail to progress on the canonical chain.",
|
||||
"links": [
|
||||
"https://github.com/ethereum/eth1.0-specs/blob/master/network-upgrades/mainnet-upgrades/london.md",
|
||||
"https://notes.ethereum.org/@timbeiko/ropsten-postmortem"
|
||||
],
|
||||
"introduced": "v1.10.1",
|
||||
"fixed": "v1.10.6",
|
||||
"published": "2020-12-10",
|
||||
"severity": "High",
|
||||
"check": "(Geth\\/v1\\.10\\.(1|2|3|4|5)-.*)$"
|
||||
}
|
||||
]
|
||||
|
@@ -44,7 +44,6 @@ var AppHelpFlagGroups = []flags.FlagGroup{
|
||||
utils.MainnetFlag,
|
||||
utils.GoerliFlag,
|
||||
utils.RinkebyFlag,
|
||||
utils.CalaverasFlag,
|
||||
utils.RopstenFlag,
|
||||
utils.SyncModeFlag,
|
||||
utils.ExitWhenSyncedFlag,
|
||||
@@ -182,7 +181,6 @@ var AppHelpFlagGroups = []flags.FlagGroup{
|
||||
utils.MinerNotifyFlag,
|
||||
utils.MinerNotifyFullFlag,
|
||||
utils.MinerGasPriceFlag,
|
||||
utils.MinerGasTargetFlag,
|
||||
utils.MinerGasLimitFlag,
|
||||
utils.MinerEtherbaseFlag,
|
||||
utils.MinerExtraDataFlag,
|
||||
@@ -203,8 +201,6 @@ var AppHelpFlagGroups = []flags.FlagGroup{
|
||||
Name: "VIRTUAL MACHINE",
|
||||
Flags: []cli.Flag{
|
||||
utils.VMEnableDebugFlag,
|
||||
utils.EVMInterpreterFlag,
|
||||
utils.EWASMInterpreterFlag,
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -228,6 +224,7 @@ var AppHelpFlagGroups = []flags.FlagGroup{
|
||||
utils.LegacyRPCCORSDomainFlag,
|
||||
utils.LegacyRPCVirtualHostsFlag,
|
||||
utils.LegacyRPCApiFlag,
|
||||
utils.LegacyMinerGasTargetFlag,
|
||||
},
|
||||
},
|
||||
{
|
||||
|
@@ -482,7 +482,7 @@ ADD puppeth.png /dashboard/puppeth.png
|
||||
|
||||
EXPOSE 80
|
||||
|
||||
CMD ["node", "/server.js"]
|
||||
CMD ["node", "./server.js"]
|
||||
`
|
||||
|
||||
// dashboardComposefile is the docker-compose.yml file required to deploy and
|
||||
|
@@ -158,7 +158,7 @@ func checkEthstats(client *sshClient, network string) (*ethstatsInfos, error) {
|
||||
if port != 80 && port != 443 {
|
||||
config += fmt.Sprintf(":%d", port)
|
||||
}
|
||||
// Retrieve the IP blacklist
|
||||
// Retrieve the IP banned list
|
||||
banned := strings.Split(infos.envvars["BANNED"], ",")
|
||||
|
||||
// Run a sanity check to see if the port is reachable
|
||||
|
@@ -63,20 +63,20 @@ func (w *wizard) deployEthstats() {
|
||||
fmt.Printf("What should be the secret password for the API? (default = %s)\n", infos.secret)
|
||||
infos.secret = w.readDefaultString(infos.secret)
|
||||
}
|
||||
// Gather any blacklists to ban from reporting
|
||||
// Gather any banned lists to ban from reporting
|
||||
if existed {
|
||||
fmt.Println()
|
||||
fmt.Printf("Keep existing IP %v blacklist (y/n)? (default = yes)\n", infos.banned)
|
||||
fmt.Printf("Keep existing IP %v in the banned list (y/n)? (default = yes)\n", infos.banned)
|
||||
if !w.readDefaultYesNo(true) {
|
||||
// The user might want to clear the entire list, although generally probably not
|
||||
fmt.Println()
|
||||
fmt.Printf("Clear out blacklist and start over (y/n)? (default = no)\n")
|
||||
fmt.Printf("Clear out the banned list and start over (y/n)? (default = no)\n")
|
||||
if w.readDefaultYesNo(false) {
|
||||
infos.banned = nil
|
||||
}
|
||||
// Offer the user to explicitly add/remove certain IP addresses
|
||||
fmt.Println()
|
||||
fmt.Println("Which additional IP addresses should be blacklisted?")
|
||||
fmt.Println("Which additional IP addresses should be in the banned list?")
|
||||
for {
|
||||
if ip := w.readIPAddress(); ip != "" {
|
||||
infos.banned = append(infos.banned, ip)
|
||||
@@ -85,7 +85,7 @@ func (w *wizard) deployEthstats() {
|
||||
break
|
||||
}
|
||||
fmt.Println()
|
||||
fmt.Println("Which IP addresses should not be blacklisted?")
|
||||
fmt.Println("Which IP addresses should not be in the banned list?")
|
||||
for {
|
||||
if ip := w.readIPAddress(); ip != "" {
|
||||
for i, addr := range infos.banned {
|
||||
|
@@ -125,10 +125,6 @@ var (
|
||||
Name: "keystore",
|
||||
Usage: "Directory for the keystore (default = inside the datadir)",
|
||||
}
|
||||
NoUSBFlag = cli.BoolFlag{
|
||||
Name: "nousb",
|
||||
Usage: "Disables monitoring for and managing USB hardware wallets (deprecated)",
|
||||
}
|
||||
USBFlag = cli.BoolFlag{
|
||||
Name: "usb",
|
||||
Usage: "Enable monitoring and management of USB hardware wallets",
|
||||
@@ -151,10 +147,6 @@ var (
|
||||
Name: "goerli",
|
||||
Usage: "Görli network: pre-configured proof-of-authority test network",
|
||||
}
|
||||
CalaverasFlag = cli.BoolFlag{
|
||||
Name: "calaveras",
|
||||
Usage: "Calaveras network: pre-configured proof-of-authority shortlived test network.",
|
||||
}
|
||||
RinkebyFlag = cli.BoolFlag{
|
||||
Name: "rinkeby",
|
||||
Usage: "Rinkeby network: pre-configured proof-of-authority test network",
|
||||
@@ -444,11 +436,6 @@ var (
|
||||
Name: "miner.notify.full",
|
||||
Usage: "Notify with pending block headers instead of work packages",
|
||||
}
|
||||
MinerGasTargetFlag = cli.Uint64Flag{
|
||||
Name: "miner.gastarget",
|
||||
Usage: "Target gas floor for mined blocks",
|
||||
Value: ethconfig.Defaults.Miner.GasFloor,
|
||||
}
|
||||
MinerGasLimitFlag = cli.Uint64Flag{
|
||||
Name: "miner.gaslimit",
|
||||
Usage: "Target gas ceiling for mined blocks",
|
||||
@@ -760,15 +747,28 @@ var (
|
||||
Usage: "Comma-separated InfluxDB tags (key/values) attached to all measurements",
|
||||
Value: metrics.DefaultConfig.InfluxDBTags,
|
||||
}
|
||||
EWASMInterpreterFlag = cli.StringFlag{
|
||||
Name: "vm.ewasm",
|
||||
Usage: "External ewasm configuration (default = built-in interpreter)",
|
||||
Value: "",
|
||||
|
||||
MetricsEnableInfluxDBV2Flag = cli.BoolFlag{
|
||||
Name: "metrics.influxdbv2",
|
||||
Usage: "Enable metrics export/push to an external InfluxDB v2 database",
|
||||
}
|
||||
EVMInterpreterFlag = cli.StringFlag{
|
||||
Name: "vm.evm",
|
||||
Usage: "External EVM configuration (default = built-in interpreter)",
|
||||
Value: "",
|
||||
|
||||
MetricsInfluxDBTokenFlag = cli.StringFlag{
|
||||
Name: "metrics.influxdb.token",
|
||||
Usage: "Token to authorize access to the database (v2 only)",
|
||||
Value: metrics.DefaultConfig.InfluxDBToken,
|
||||
}
|
||||
|
||||
MetricsInfluxDBBucketFlag = cli.StringFlag{
|
||||
Name: "metrics.influxdb.bucket",
|
||||
Usage: "InfluxDB bucket name to push reported metrics to (v2 only)",
|
||||
Value: metrics.DefaultConfig.InfluxDBBucket,
|
||||
}
|
||||
|
||||
MetricsInfluxDBOrganizationFlag = cli.StringFlag{
|
||||
Name: "metrics.influxdb.organization",
|
||||
Usage: "InfluxDB organization name (v2 only)",
|
||||
Value: metrics.DefaultConfig.InfluxDBOrganization,
|
||||
}
|
||||
|
||||
CatalystFlag = cli.BoolFlag{
|
||||
@@ -793,9 +793,6 @@ func MakeDataDir(ctx *cli.Context) string {
|
||||
if ctx.GlobalBool(GoerliFlag.Name) {
|
||||
return filepath.Join(path, "goerli")
|
||||
}
|
||||
if ctx.GlobalBool(CalaverasFlag.Name) {
|
||||
return filepath.Join(path, "calaveras")
|
||||
}
|
||||
return path
|
||||
}
|
||||
Fatalf("Cannot determine default data directory, please set manually (--datadir)")
|
||||
@@ -848,8 +845,6 @@ func setBootstrapNodes(ctx *cli.Context, cfg *p2p.Config) {
|
||||
urls = params.RinkebyBootnodes
|
||||
case ctx.GlobalBool(GoerliFlag.Name):
|
||||
urls = params.GoerliBootnodes
|
||||
case ctx.GlobalBool(CalaverasFlag.Name):
|
||||
urls = params.CalaverasBootnodes
|
||||
case cfg.BootstrapNodes != nil:
|
||||
return // already set, don't apply defaults.
|
||||
}
|
||||
@@ -1293,8 +1288,6 @@ func setDataDir(ctx *cli.Context, cfg *node.Config) {
|
||||
cfg.DataDir = filepath.Join(node.DefaultDataDir(), "rinkeby")
|
||||
case ctx.GlobalBool(GoerliFlag.Name) && cfg.DataDir == node.DefaultDataDir():
|
||||
cfg.DataDir = filepath.Join(node.DefaultDataDir(), "goerli")
|
||||
case ctx.GlobalBool(CalaverasFlag.Name) && cfg.DataDir == node.DefaultDataDir():
|
||||
cfg.DataDir = filepath.Join(node.DefaultDataDir(), "calaveras")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1302,8 +1295,7 @@ func setGPO(ctx *cli.Context, cfg *gasprice.Config, light bool) {
|
||||
// If we are running the light client, apply another group
|
||||
// settings for gas oracle.
|
||||
if light {
|
||||
cfg.Blocks = ethconfig.LightClientGPO.Blocks
|
||||
cfg.Percentile = ethconfig.LightClientGPO.Percentile
|
||||
*cfg = ethconfig.LightClientGPO
|
||||
}
|
||||
if ctx.GlobalIsSet(GpoBlocksFlag.Name) {
|
||||
cfg.Blocks = ctx.GlobalInt(GpoBlocksFlag.Name)
|
||||
@@ -1397,9 +1389,6 @@ func setMiner(ctx *cli.Context, cfg *miner.Config) {
|
||||
if ctx.GlobalIsSet(MinerExtraDataFlag.Name) {
|
||||
cfg.ExtraData = []byte(ctx.GlobalString(MinerExtraDataFlag.Name))
|
||||
}
|
||||
if ctx.GlobalIsSet(MinerGasTargetFlag.Name) {
|
||||
cfg.GasFloor = ctx.GlobalUint64(MinerGasTargetFlag.Name)
|
||||
}
|
||||
if ctx.GlobalIsSet(MinerGasLimitFlag.Name) {
|
||||
cfg.GasCeil = ctx.GlobalUint64(MinerGasLimitFlag.Name)
|
||||
}
|
||||
@@ -1412,6 +1401,9 @@ func setMiner(ctx *cli.Context, cfg *miner.Config) {
|
||||
if ctx.GlobalIsSet(MinerNoVerfiyFlag.Name) {
|
||||
cfg.Noverify = ctx.GlobalBool(MinerNoVerfiyFlag.Name)
|
||||
}
|
||||
if ctx.GlobalIsSet(LegacyMinerGasTargetFlag.Name) {
|
||||
log.Warn("The generic --miner.gastarget flag is deprecated and will be removed in the future!")
|
||||
}
|
||||
}
|
||||
|
||||
func setWhitelist(ctx *cli.Context, cfg *ethconfig.Config) {
|
||||
@@ -1481,7 +1473,7 @@ func CheckExclusive(ctx *cli.Context, args ...interface{}) {
|
||||
// SetEthConfig applies eth-related command line flags to the config.
|
||||
func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *ethconfig.Config) {
|
||||
// Avoid conflicting network flags
|
||||
CheckExclusive(ctx, MainnetFlag, DeveloperFlag, RopstenFlag, RinkebyFlag, GoerliFlag, CalaverasFlag)
|
||||
CheckExclusive(ctx, MainnetFlag, DeveloperFlag, RopstenFlag, RinkebyFlag, GoerliFlag)
|
||||
CheckExclusive(ctx, LightServeFlag, SyncModeFlag, "light")
|
||||
CheckExclusive(ctx, DeveloperFlag, ExternalSignerFlag) // Can't use both ephemeral unlocked and external signer
|
||||
if ctx.GlobalString(GCModeFlag.Name) == "archive" && ctx.GlobalUint64(TxLookupLimitFlag.Name) != 0 {
|
||||
@@ -1587,13 +1579,6 @@ func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *ethconfig.Config) {
|
||||
cfg.EnablePreimageRecording = ctx.GlobalBool(VMEnableDebugFlag.Name)
|
||||
}
|
||||
|
||||
if ctx.GlobalIsSet(EWASMInterpreterFlag.Name) {
|
||||
cfg.EWASMInterpreter = ctx.GlobalString(EWASMInterpreterFlag.Name)
|
||||
}
|
||||
|
||||
if ctx.GlobalIsSet(EVMInterpreterFlag.Name) {
|
||||
cfg.EVMInterpreter = ctx.GlobalString(EVMInterpreterFlag.Name)
|
||||
}
|
||||
if ctx.GlobalIsSet(RPCGlobalGasCapFlag.Name) {
|
||||
cfg.RPCGasCap = ctx.GlobalUint64(RPCGlobalGasCapFlag.Name)
|
||||
}
|
||||
@@ -1641,11 +1626,6 @@ func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *ethconfig.Config) {
|
||||
}
|
||||
cfg.Genesis = core.DefaultGoerliGenesisBlock()
|
||||
SetDNSDiscoveryDefaults(cfg, params.GoerliGenesisHash)
|
||||
case ctx.GlobalBool(CalaverasFlag.Name):
|
||||
if !ctx.GlobalIsSet(NetworkIdFlag.Name) {
|
||||
cfg.NetworkId = 123 // https://gist.github.com/holiman/c5697b041b3dc18c50a5cdd382cbdd16
|
||||
}
|
||||
cfg.Genesis = core.DefaultCalaverasGenesisBlock()
|
||||
case ctx.GlobalBool(DeveloperFlag.Name):
|
||||
if !ctx.GlobalIsSet(NetworkIdFlag.Name) {
|
||||
cfg.NetworkId = 1337
|
||||
@@ -1762,11 +1742,36 @@ func SetupMetrics(ctx *cli.Context) {
|
||||
log.Info("Enabling metrics collection")
|
||||
|
||||
var (
|
||||
enableExport = ctx.GlobalBool(MetricsEnableInfluxDBFlag.Name)
|
||||
endpoint = ctx.GlobalString(MetricsInfluxDBEndpointFlag.Name)
|
||||
database = ctx.GlobalString(MetricsInfluxDBDatabaseFlag.Name)
|
||||
username = ctx.GlobalString(MetricsInfluxDBUsernameFlag.Name)
|
||||
password = ctx.GlobalString(MetricsInfluxDBPasswordFlag.Name)
|
||||
enableExport = ctx.GlobalBool(MetricsEnableInfluxDBFlag.Name)
|
||||
enableExportV2 = ctx.GlobalBool(MetricsEnableInfluxDBV2Flag.Name)
|
||||
)
|
||||
|
||||
if enableExport || enableExportV2 {
|
||||
CheckExclusive(ctx, MetricsEnableInfluxDBFlag, MetricsEnableInfluxDBV2Flag)
|
||||
|
||||
v1FlagIsSet := ctx.GlobalIsSet(MetricsInfluxDBUsernameFlag.Name) ||
|
||||
ctx.GlobalIsSet(MetricsInfluxDBPasswordFlag.Name)
|
||||
|
||||
v2FlagIsSet := ctx.GlobalIsSet(MetricsInfluxDBTokenFlag.Name) ||
|
||||
ctx.GlobalIsSet(MetricsInfluxDBOrganizationFlag.Name) ||
|
||||
ctx.GlobalIsSet(MetricsInfluxDBBucketFlag.Name)
|
||||
|
||||
if enableExport && v2FlagIsSet {
|
||||
Fatalf("Flags --influxdb.metrics.organization, --influxdb.metrics.token, --influxdb.metrics.bucket are only available for influxdb-v2")
|
||||
} else if enableExportV2 && v1FlagIsSet {
|
||||
Fatalf("Flags --influxdb.metrics.username, --influxdb.metrics.password are only available for influxdb-v1")
|
||||
}
|
||||
}
|
||||
|
||||
var (
|
||||
endpoint = ctx.GlobalString(MetricsInfluxDBEndpointFlag.Name)
|
||||
database = ctx.GlobalString(MetricsInfluxDBDatabaseFlag.Name)
|
||||
username = ctx.GlobalString(MetricsInfluxDBUsernameFlag.Name)
|
||||
password = ctx.GlobalString(MetricsInfluxDBPasswordFlag.Name)
|
||||
|
||||
token = ctx.GlobalString(MetricsInfluxDBTokenFlag.Name)
|
||||
bucket = ctx.GlobalString(MetricsInfluxDBBucketFlag.Name)
|
||||
organization = ctx.GlobalString(MetricsInfluxDBOrganizationFlag.Name)
|
||||
)
|
||||
|
||||
if enableExport {
|
||||
@@ -1775,6 +1780,12 @@ func SetupMetrics(ctx *cli.Context) {
|
||||
log.Info("Enabling metrics export to InfluxDB")
|
||||
|
||||
go influxdb.InfluxDBWithTags(metrics.DefaultRegistry, 10*time.Second, endpoint, database, username, password, "geth.", tagsMap)
|
||||
} else if enableExportV2 {
|
||||
tagsMap := SplitTagsFlag(ctx.GlobalString(MetricsInfluxDBTagsFlag.Name))
|
||||
|
||||
log.Info("Enabling metrics export to InfluxDB (v2)")
|
||||
|
||||
go influxdb.InfluxDBV2WithTags(metrics.DefaultRegistry, 10*time.Second, endpoint, token, bucket, organization, "geth.", tagsMap)
|
||||
}
|
||||
|
||||
if ctx.GlobalIsSet(MetricsHTTPFlag.Name) {
|
||||
@@ -1835,8 +1846,6 @@ func MakeGenesis(ctx *cli.Context) *core.Genesis {
|
||||
genesis = core.DefaultRinkebyGenesisBlock()
|
||||
case ctx.GlobalBool(GoerliFlag.Name):
|
||||
genesis = core.DefaultGoerliGenesisBlock()
|
||||
case ctx.GlobalBool(CalaverasFlag.Name):
|
||||
genesis = core.DefaultCalaverasGenesisBlock()
|
||||
case ctx.GlobalBool(DeveloperFlag.Name):
|
||||
Fatalf("Developer chains are ephemeral")
|
||||
}
|
||||
|
@@ -20,6 +20,7 @@ import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/ethereum/go-ethereum/eth/ethconfig"
|
||||
"github.com/ethereum/go-ethereum/node"
|
||||
"gopkg.in/urfave/cli.v1"
|
||||
)
|
||||
@@ -33,10 +34,17 @@ var ShowDeprecated = cli.Command{
|
||||
Description: "Show flags that have been deprecated and will soon be removed",
|
||||
}
|
||||
|
||||
var DeprecatedFlags = []cli.Flag{}
|
||||
var DeprecatedFlags = []cli.Flag{
|
||||
LegacyMinerGasTargetFlag,
|
||||
NoUSBFlag,
|
||||
}
|
||||
|
||||
var (
|
||||
// (Deprecated May 2020, shown in aliased flags section)
|
||||
NoUSBFlag = cli.BoolFlag{
|
||||
Name: "nousb",
|
||||
Usage: "Disables monitoring for and managing USB hardware wallets (deprecated)",
|
||||
}
|
||||
LegacyRPCEnabledFlag = cli.BoolFlag{
|
||||
Name: "rpc",
|
||||
Usage: "Enable the HTTP-RPC server (deprecated and will be removed June 2021, use --http)",
|
||||
@@ -66,6 +74,12 @@ var (
|
||||
Usage: "API's offered over the HTTP-RPC interface (deprecated and will be removed June 2021, use --http.api)",
|
||||
Value: "",
|
||||
}
|
||||
// (Deprecated July 2021, shown in aliased flags section)
|
||||
LegacyMinerGasTargetFlag = cli.Uint64Flag{
|
||||
Name: "miner.gastarget",
|
||||
Usage: "Target gas floor for mined blocks (deprecated)",
|
||||
Value: ethconfig.Defaults.Miner.GasFloor,
|
||||
}
|
||||
)
|
||||
|
||||
// showDeprecated displays deprecated flags that will be soon removed from the codebase.
|
||||
@@ -74,7 +88,8 @@ func showDeprecated(*cli.Context) {
|
||||
fmt.Println("The following flags are deprecated and will be removed in the future!")
|
||||
fmt.Println("--------------------------------------------------------------------")
|
||||
fmt.Println()
|
||||
// TODO remove when there are newly deprecated flags
|
||||
fmt.Println("no deprecated flags to show at this time")
|
||||
for _, flag := range DeprecatedFlags {
|
||||
fmt.Println(flag.String())
|
||||
}
|
||||
fmt.Println()
|
||||
}
|
||||
|
@@ -17,11 +17,14 @@
|
||||
package clique
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||
"github.com/ethereum/go-ethereum/consensus"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/rlp"
|
||||
"github.com/ethereum/go-ethereum/rpc"
|
||||
)
|
||||
|
||||
@@ -175,3 +178,51 @@ func (api *API) Status() (*status, error) {
|
||||
NumBlocks: numBlocks,
|
||||
}, nil
|
||||
}
|
||||
|
||||
type blockNumberOrHashOrRLP struct {
|
||||
*rpc.BlockNumberOrHash
|
||||
RLP hexutil.Bytes `json:"rlp,omitempty"`
|
||||
}
|
||||
|
||||
func (sb *blockNumberOrHashOrRLP) UnmarshalJSON(data []byte) error {
|
||||
bnOrHash := new(rpc.BlockNumberOrHash)
|
||||
// Try to unmarshal bNrOrHash
|
||||
if err := bnOrHash.UnmarshalJSON(data); err == nil {
|
||||
sb.BlockNumberOrHash = bnOrHash
|
||||
return nil
|
||||
}
|
||||
// Try to unmarshal RLP
|
||||
var input string
|
||||
if err := json.Unmarshal(data, &input); err != nil {
|
||||
return err
|
||||
}
|
||||
sb.RLP = hexutil.MustDecode(input)
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetSigner returns the signer for a specific clique block.
|
||||
// Can be called with either a blocknumber, blockhash or an rlp encoded blob.
|
||||
// The RLP encoded blob can either be a block or a header.
|
||||
func (api *API) GetSigner(rlpOrBlockNr *blockNumberOrHashOrRLP) (common.Address, error) {
|
||||
if len(rlpOrBlockNr.RLP) == 0 {
|
||||
blockNrOrHash := rlpOrBlockNr.BlockNumberOrHash
|
||||
var header *types.Header
|
||||
if blockNrOrHash == nil {
|
||||
header = api.chain.CurrentHeader()
|
||||
} else if hash, ok := blockNrOrHash.Hash(); ok {
|
||||
header = api.chain.GetHeaderByHash(hash)
|
||||
} else if number, ok := blockNrOrHash.Number(); ok {
|
||||
header = api.chain.GetHeaderByNumber(uint64(number.Int64()))
|
||||
}
|
||||
return api.clique.Author(header)
|
||||
}
|
||||
block := new(types.Block)
|
||||
if err := rlp.DecodeBytes(rlpOrBlockNr.RLP, block); err == nil {
|
||||
return api.clique.Author(block.Header())
|
||||
}
|
||||
header := new(types.Header)
|
||||
if err := rlp.DecodeBytes(rlpOrBlockNr.RLP, header); err != nil {
|
||||
return common.Address{}, err
|
||||
}
|
||||
return api.clique.Author(header)
|
||||
}
|
||||
|
@@ -710,7 +710,7 @@ func (c *Clique) APIs(chain consensus.ChainHeaderReader) []rpc.API {
|
||||
func SealHash(header *types.Header) (hash common.Hash) {
|
||||
hasher := sha3.NewLegacyKeccak256()
|
||||
encodeSigHeader(hasher, header)
|
||||
hasher.Sum(hash[:0])
|
||||
hasher.(crypto.KeccakState).Read(hash[:])
|
||||
return hash
|
||||
}
|
||||
|
||||
|
@@ -112,3 +112,16 @@ func TestReimportMirroredState(t *testing.T) {
|
||||
t.Fatalf("chain head mismatch: have %d, want %d", head, 3)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSealHash(t *testing.T) {
|
||||
have := SealHash(&types.Header{
|
||||
Difficulty: new(big.Int),
|
||||
Number: new(big.Int),
|
||||
Extra: make([]byte, 32+65),
|
||||
BaseFee: new(big.Int),
|
||||
})
|
||||
want := common.HexToHash("0xbd3d1fa43fbc4c5bfcc91b179ec92e2861df3654de60468beb908ff805359e8f")
|
||||
if have != want {
|
||||
t.Errorf("have %x, want %x", have, want)
|
||||
}
|
||||
}
|
||||
|
@@ -215,7 +215,7 @@ func (ethash *Ethash) VerifyUncles(chain consensus.ChainReader, block *types.Blo
|
||||
ancestors[parent] = ancestorHeader
|
||||
// If the ancestor doesn't have any uncles, we don't have to iterate them
|
||||
if ancestorHeader.UncleHash != types.EmptyUncleHash {
|
||||
// Need to add those uncles to the blacklist too
|
||||
// Need to add those uncles to the banned list too
|
||||
ancestor := chain.GetBlock(parent, number)
|
||||
if ancestor == nil {
|
||||
break
|
||||
|
@@ -140,8 +140,9 @@ func (ethash *Ethash) mine(block *types.Block, id int, seed uint64, abort chan s
|
||||
)
|
||||
// Start generating random nonces until we abort or find a good one
|
||||
var (
|
||||
attempts = int64(0)
|
||||
nonce = seed
|
||||
attempts = int64(0)
|
||||
nonce = seed
|
||||
powBuffer = new(big.Int)
|
||||
)
|
||||
logger := ethash.config.Log.New("miner", id)
|
||||
logger.Trace("Started ethash search for new nonces", "seed", seed)
|
||||
@@ -163,7 +164,7 @@ search:
|
||||
}
|
||||
// Compute the PoW value of this nonce
|
||||
digest, result := hashimotoFull(dataset.dataset, hash, nonce)
|
||||
if new(big.Int).SetBytes(result).Cmp(target) <= 0 {
|
||||
if powBuffer.SetBytes(result).Cmp(target) <= 0 {
|
||||
// Correct nonce found, create a new header with it
|
||||
header = types.CopyHeader(header)
|
||||
header.Nonce = types.EncodeNonce(nonce)
|
||||
|
@@ -44,7 +44,6 @@ func copyConfig(original *params.ChainConfig) *params.ChainConfig {
|
||||
MuirGlacierBlock: original.MuirGlacierBlock,
|
||||
BerlinBlock: original.BerlinBlock,
|
||||
LondonBlock: original.LondonBlock,
|
||||
EWASMBlock: original.EWASMBlock,
|
||||
CatalystBlock: original.CatalystBlock,
|
||||
Ethash: original.Ethash,
|
||||
Clique: original.Clique,
|
||||
|
@@ -103,44 +103,9 @@ func (v *BlockValidator) ValidateState(block *types.Block, statedb *state.StateD
|
||||
}
|
||||
|
||||
// CalcGasLimit computes the gas limit of the next block after parent. It aims
|
||||
// to keep the baseline gas above the provided floor, and increase it towards the
|
||||
// ceil if the blocks are full. If the ceil is exceeded, it will always decrease
|
||||
// the gas allowance.
|
||||
func CalcGasLimit(parentGasUsed, parentGasLimit, gasFloor, gasCeil uint64) uint64 {
|
||||
// contrib = (parentGasUsed * 3 / 2) / 1024
|
||||
contrib := (parentGasUsed + parentGasUsed/2) / params.GasLimitBoundDivisor
|
||||
|
||||
// decay = parentGasLimit / 1024 -1
|
||||
decay := parentGasLimit/params.GasLimitBoundDivisor - 1
|
||||
|
||||
/*
|
||||
strategy: gasLimit of block-to-mine is set based on parent's
|
||||
gasUsed value. if parentGasUsed > parentGasLimit * (2/3) then we
|
||||
increase it, otherwise lower it (or leave it unchanged if it's right
|
||||
at that usage) the amount increased/decreased depends on how far away
|
||||
from parentGasLimit * (2/3) parentGasUsed is.
|
||||
*/
|
||||
limit := parentGasLimit - decay + contrib
|
||||
if limit < params.MinGasLimit {
|
||||
limit = params.MinGasLimit
|
||||
}
|
||||
// If we're outside our allowed gas range, we try to hone towards them
|
||||
if limit < gasFloor {
|
||||
limit = parentGasLimit + decay
|
||||
if limit > gasFloor {
|
||||
limit = gasFloor
|
||||
}
|
||||
} else if limit > gasCeil {
|
||||
limit = parentGasLimit - decay
|
||||
if limit < gasCeil {
|
||||
limit = gasCeil
|
||||
}
|
||||
}
|
||||
return limit
|
||||
}
|
||||
|
||||
// CalcGasLimit1559 calculates the next block gas limit under 1559 rules.
|
||||
func CalcGasLimit1559(parentGasLimit, desiredLimit uint64) uint64 {
|
||||
// to keep the baseline gas close to the provided target, and increase it towards
|
||||
// the target if the baseline gas is lower.
|
||||
func CalcGasLimit(parentGasLimit, desiredLimit uint64) uint64 {
|
||||
delta := parentGasLimit/params.GasLimitBoundDivisor - 1
|
||||
limit := parentGasLimit
|
||||
if desiredLimit < params.MinGasLimit {
|
||||
|
@@ -198,8 +198,7 @@ func testHeaderConcurrentAbortion(t *testing.T, threads int) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestCalcGasLimit1559(t *testing.T) {
|
||||
|
||||
func TestCalcGasLimit(t *testing.T) {
|
||||
for i, tc := range []struct {
|
||||
pGasLimit uint64
|
||||
max uint64
|
||||
@@ -209,23 +208,23 @@ func TestCalcGasLimit1559(t *testing.T) {
|
||||
{40000000, 40039061, 39960939},
|
||||
} {
|
||||
// Increase
|
||||
if have, want := CalcGasLimit1559(tc.pGasLimit, 2*tc.pGasLimit), tc.max; have != want {
|
||||
if have, want := CalcGasLimit(tc.pGasLimit, 2*tc.pGasLimit), tc.max; have != want {
|
||||
t.Errorf("test %d: have %d want <%d", i, have, want)
|
||||
}
|
||||
// Decrease
|
||||
if have, want := CalcGasLimit1559(tc.pGasLimit, 0), tc.min; have != want {
|
||||
if have, want := CalcGasLimit(tc.pGasLimit, 0), tc.min; have != want {
|
||||
t.Errorf("test %d: have %d want >%d", i, have, want)
|
||||
}
|
||||
// Small decrease
|
||||
if have, want := CalcGasLimit1559(tc.pGasLimit, tc.pGasLimit-1), tc.pGasLimit-1; have != want {
|
||||
if have, want := CalcGasLimit(tc.pGasLimit, tc.pGasLimit-1), tc.pGasLimit-1; have != want {
|
||||
t.Errorf("test %d: have %d want %d", i, have, want)
|
||||
}
|
||||
// Small increase
|
||||
if have, want := CalcGasLimit1559(tc.pGasLimit, tc.pGasLimit+1), tc.pGasLimit+1; have != want {
|
||||
if have, want := CalcGasLimit(tc.pGasLimit, tc.pGasLimit+1), tc.pGasLimit+1; have != want {
|
||||
t.Errorf("test %d: have %d want %d", i, have, want)
|
||||
}
|
||||
// No change
|
||||
if have, want := CalcGasLimit1559(tc.pGasLimit, tc.pGasLimit), tc.pGasLimit; have != want {
|
||||
if have, want := CalcGasLimit(tc.pGasLimit, tc.pGasLimit), tc.pGasLimit; have != want {
|
||||
t.Errorf("test %d: have %d want %d", i, have, want)
|
||||
}
|
||||
}
|
||||
|
@@ -1785,8 +1785,8 @@ func (bc *BlockChain) insertChain(chain types.Blocks, verifySeals bool) (int, er
|
||||
}
|
||||
// If the header is a banned one, straight out abort
|
||||
if BadHashes[block.Hash()] {
|
||||
bc.reportBlock(block, nil, ErrBlacklistedHash)
|
||||
return it.index, ErrBlacklistedHash
|
||||
bc.reportBlock(block, nil, ErrBannedHash)
|
||||
return it.index, ErrBannedHash
|
||||
}
|
||||
// If the block is known (in the middle of the chain), it's a special case for
|
||||
// Clique blocks where they can share state among each other, so importing an
|
||||
@@ -2417,12 +2417,22 @@ func (bc *BlockChain) GetTdByHash(hash common.Hash) *big.Int {
|
||||
// GetHeader retrieves a block header from the database by hash and number,
|
||||
// caching it if found.
|
||||
func (bc *BlockChain) GetHeader(hash common.Hash, number uint64) *types.Header {
|
||||
// Blockchain might have cached the whole block, only if not go to headerchain
|
||||
if block, ok := bc.blockCache.Get(hash); ok {
|
||||
return block.(*types.Block).Header()
|
||||
}
|
||||
|
||||
return bc.hc.GetHeader(hash, number)
|
||||
}
|
||||
|
||||
// GetHeaderByHash retrieves a block header from the database by hash, caching it if
|
||||
// found.
|
||||
func (bc *BlockChain) GetHeaderByHash(hash common.Hash) *types.Header {
|
||||
// Blockchain might have cached the whole block, only if not go to headerchain
|
||||
if block, ok := bc.blockCache.Get(hash); ok {
|
||||
return block.(*types.Block).Header()
|
||||
}
|
||||
|
||||
return bc.hc.GetHeaderByHash(hash)
|
||||
}
|
||||
|
||||
|
@@ -473,8 +473,8 @@ func testBadHashes(t *testing.T, full bool) {
|
||||
|
||||
_, err = blockchain.InsertHeaderChain(headers, 1)
|
||||
}
|
||||
if !errors.Is(err, ErrBlacklistedHash) {
|
||||
t.Errorf("error mismatch: have: %v, want: %v", err, ErrBlacklistedHash)
|
||||
if !errors.Is(err, ErrBannedHash) {
|
||||
t.Errorf("error mismatch: have: %v, want: %v", err, ErrBannedHash)
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -102,7 +102,7 @@ func (b *BlockGen) AddTxWithChain(bc *BlockChain, tx *types.Transaction) {
|
||||
if b.gasPool == nil {
|
||||
b.SetCoinbase(common.Address{})
|
||||
}
|
||||
b.statedb.Prepare(tx.Hash(), common.Hash{}, len(b.txs))
|
||||
b.statedb.Prepare(tx.Hash(), len(b.txs))
|
||||
receipt, err := ApplyTransaction(b.config, bc, &b.header.Coinbase, b.gasPool, b.statedb, b.header, tx, &b.header.GasUsed, vm.Config{})
|
||||
if err != nil {
|
||||
panic(err)
|
||||
@@ -273,11 +273,10 @@ func makeHeader(chain consensus.ChainReader, parent *types.Block, state *state.S
|
||||
}
|
||||
if chain.Config().IsLondon(header.Number) {
|
||||
header.BaseFee = misc.CalcBaseFee(chain.Config(), parent.Header())
|
||||
parentGasLimit := parent.GasLimit()
|
||||
if !chain.Config().IsLondon(parent.Number()) {
|
||||
parentGasLimit = parent.GasLimit() * params.ElasticityMultiplier
|
||||
parentGasLimit := parent.GasLimit() * params.ElasticityMultiplier
|
||||
header.GasLimit = CalcGasLimit(parentGasLimit, parentGasLimit)
|
||||
}
|
||||
header.GasLimit = CalcGasLimit1559(parentGasLimit, parentGasLimit)
|
||||
}
|
||||
return header
|
||||
}
|
||||
|
@@ -26,8 +26,8 @@ var (
|
||||
// ErrKnownBlock is returned when a block to import is already known locally.
|
||||
ErrKnownBlock = errors.New("block already known")
|
||||
|
||||
// ErrBlacklistedHash is returned if a block to import is on the blacklist.
|
||||
ErrBlacklistedHash = errors.New("blacklisted hash")
|
||||
// ErrBannedHash is returned if a block to import is on the banned list.
|
||||
ErrBannedHash = errors.New("banned hash")
|
||||
|
||||
// ErrNoGenesis is returned when there is no Genesis Block.
|
||||
ErrNoGenesis = errors.New("genesis not found in chain")
|
||||
@@ -87,4 +87,7 @@ var (
|
||||
// ErrFeeCapTooLow is returned if the transaction fee cap is less than the
|
||||
// the base fee of the block.
|
||||
ErrFeeCapTooLow = errors.New("max fee per gas less than block base fee")
|
||||
|
||||
// ErrSenderNoEOA is returned if the sender of a transaction is a contract.
|
||||
ErrSenderNoEOA = errors.New("sender not an eoa")
|
||||
)
|
||||
|
@@ -61,8 +61,10 @@ func TestCreation(t *testing.T) {
|
||||
{9199999, ID{Hash: checksumToBytes(0x879d6e30), Next: 9200000}}, // Last Istanbul and first Muir Glacier block
|
||||
{9200000, ID{Hash: checksumToBytes(0xe029e991), Next: 12244000}}, // First Muir Glacier block
|
||||
{12243999, ID{Hash: checksumToBytes(0xe029e991), Next: 12244000}}, // Last Muir Glacier block
|
||||
{12244000, ID{Hash: checksumToBytes(0x0eb440f6), Next: 0}}, // First Berlin block
|
||||
{20000000, ID{Hash: checksumToBytes(0x0eb440f6), Next: 0}}, // Future Berlin block
|
||||
{12244000, ID{Hash: checksumToBytes(0x0eb440f6), Next: 12965000}}, // First Berlin block
|
||||
{12964999, ID{Hash: checksumToBytes(0x0eb440f6), Next: 12965000}}, // Last Berlin block
|
||||
{12965000, ID{Hash: checksumToBytes(0xb715077d), Next: 0}}, // First London block
|
||||
{20000000, ID{Hash: checksumToBytes(0xb715077d), Next: 0}}, // Future London block
|
||||
},
|
||||
},
|
||||
// Ropsten test cases
|
||||
@@ -203,11 +205,11 @@ func TestValidation(t *testing.T) {
|
||||
// Local is mainnet Petersburg, remote is Rinkeby Petersburg.
|
||||
{7987396, ID{Hash: checksumToBytes(0xafec6b27), Next: 0}, ErrLocalIncompatibleOrStale},
|
||||
|
||||
// Local is mainnet Berlin, far in the future. Remote announces Gopherium (non existing fork)
|
||||
// Local is mainnet London, far in the future. Remote announces Gopherium (non existing fork)
|
||||
// at some future block 88888888, for itself, but past block for local. Local is incompatible.
|
||||
//
|
||||
// This case detects non-upgraded nodes with majority hash power (typical Ropsten mess).
|
||||
{88888888, ID{Hash: checksumToBytes(0x0eb440f6), Next: 88888888}, ErrLocalIncompatibleOrStale},
|
||||
{88888888, ID{Hash: checksumToBytes(0xb715077d), Next: 88888888}, ErrLocalIncompatibleOrStale},
|
||||
|
||||
// Local is mainnet Byzantium. Remote is also in Byzantium, but announces Gopherium (non existing
|
||||
// fork) at block 7279999, before Petersburg. Local is incompatible.
|
||||
|
@@ -248,8 +248,6 @@ func (g *Genesis) configOrDefault(ghash common.Hash) *params.ChainConfig {
|
||||
return params.RinkebyChainConfig
|
||||
case ghash == params.GoerliGenesisHash:
|
||||
return params.GoerliChainConfig
|
||||
case ghash == params.CalaverasGenesisHash:
|
||||
return params.CalaverasChainConfig
|
||||
default:
|
||||
return params.AllEthashProtocolChanges
|
||||
}
|
||||
@@ -399,23 +397,14 @@ func DefaultGoerliGenesisBlock() *Genesis {
|
||||
}
|
||||
}
|
||||
|
||||
func DefaultCalaverasGenesisBlock() *Genesis {
|
||||
// Full genesis: https://gist.github.com/holiman/c6ed9269dce28304ad176314caa75e97
|
||||
return &Genesis{
|
||||
Config: params.CalaverasChainConfig,
|
||||
Timestamp: 0x60b3877f,
|
||||
ExtraData: hexutil.MustDecode("0x00000000000000000000000000000000000000000000000000000000000000005211cea3870c7ba7c6c44b185e62eecdb864cd8c560228ce57d31efbf64c200b2c200aacec78cf17a7148e784fe95a7a750335f8b9572ee28d72e7650000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"),
|
||||
GasLimit: 0x47b760,
|
||||
Difficulty: big.NewInt(1),
|
||||
Alloc: decodePrealloc(calaverasAllocData),
|
||||
}
|
||||
}
|
||||
|
||||
// DeveloperGenesisBlock returns the 'geth --dev' genesis block.
|
||||
func DeveloperGenesisBlock(period uint64, faucet common.Address) *Genesis {
|
||||
// Override the default period to the user requested one
|
||||
config := *params.AllCliqueProtocolChanges
|
||||
config.Clique.Period = period
|
||||
config.Clique = ¶ms.CliqueConfig{
|
||||
Period: period,
|
||||
Epoch: config.Clique.Epoch,
|
||||
}
|
||||
|
||||
// Assemble and return the genesis with the precompiles and faucet pre-funded
|
||||
return &Genesis{
|
||||
|
@@ -185,10 +185,6 @@ func TestGenesisHashes(t *testing.T) {
|
||||
genesis: DefaultRinkebyGenesisBlock(),
|
||||
hash: params.RinkebyGenesisHash,
|
||||
},
|
||||
{
|
||||
genesis: DefaultCalaverasGenesisBlock(),
|
||||
hash: params.CalaverasGenesisHash,
|
||||
},
|
||||
}
|
||||
for i, c := range cases {
|
||||
b := c.genesis.MustCommit(rawdb.NewMemoryDatabase())
|
||||
|
@@ -165,6 +165,7 @@ func (hc *HeaderChain) writeHeaders(headers []*types.Header) (result *headerWrit
|
||||
)
|
||||
|
||||
batch := hc.chainDb.NewBatch()
|
||||
parentKnown := true // Set to true to force hc.HasHeader check the first iteration
|
||||
for i, header := range headers {
|
||||
var hash common.Hash
|
||||
// The headers have already been validated at this point, so we already
|
||||
@@ -178,8 +179,10 @@ func (hc *HeaderChain) writeHeaders(headers []*types.Header) (result *headerWrit
|
||||
number := header.Number.Uint64()
|
||||
newTD.Add(newTD, header.Difficulty)
|
||||
|
||||
// If the parent was not present, store it
|
||||
// If the header is already known, skip it, otherwise store
|
||||
if !hc.HasHeader(hash, number) {
|
||||
alreadyKnown := parentKnown && hc.HasHeader(hash, number)
|
||||
if !alreadyKnown {
|
||||
// Irrelevant of the canonical status, write the TD and header to the database.
|
||||
rawdb.WriteTd(batch, hash, number, newTD)
|
||||
hc.tdCache.Add(hash, new(big.Int).Set(newTD))
|
||||
@@ -192,6 +195,7 @@ func (hc *HeaderChain) writeHeaders(headers []*types.Header) (result *headerWrit
|
||||
firstInserted = i
|
||||
}
|
||||
}
|
||||
parentKnown = alreadyKnown
|
||||
lastHeader, lastHash, lastNumber = header, hash, number
|
||||
}
|
||||
|
||||
@@ -311,11 +315,11 @@ func (hc *HeaderChain) ValidateHeaderChain(chain []*types.Header, checkFreq int)
|
||||
}
|
||||
// If the header is a banned one, straight out abort
|
||||
if BadHashes[chain[i].ParentHash] {
|
||||
return i - 1, ErrBlacklistedHash
|
||||
return i - 1, ErrBannedHash
|
||||
}
|
||||
// If it's the last header in the cunk, we need to check it too
|
||||
if i == len(chain)-1 && BadHashes[chain[i].Hash()] {
|
||||
return i, ErrBlacklistedHash
|
||||
return i, ErrBannedHash
|
||||
}
|
||||
}
|
||||
|
||||
@@ -570,7 +574,7 @@ func (hc *HeaderChain) SetHead(head uint64, updateFn UpdateHeadBlocksCallback, d
|
||||
if parent == nil {
|
||||
parent = hc.genesisHeader
|
||||
}
|
||||
parentHash = hdr.ParentHash
|
||||
parentHash = parent.Hash()
|
||||
|
||||
// Notably, since geth has the possibility for setting the head to a low
|
||||
// height which is even lower than ancient head.
|
||||
|
@@ -444,6 +444,7 @@ func TestAncientStorage(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create database with ancient backend")
|
||||
}
|
||||
defer db.Close()
|
||||
// Create a test block
|
||||
block := types.NewBlockWithHeader(&types.Header{
|
||||
Number: big.NewInt(0),
|
||||
|
@@ -106,7 +106,7 @@ func ReadTransaction(db ethdb.Reader, hash common.Hash) (*types.Transaction, com
|
||||
}
|
||||
body := ReadBody(db, blockHash, *blockNumber)
|
||||
if body == nil {
|
||||
log.Error("Transaction referenced missing", "number", blockNumber, "hash", blockHash)
|
||||
log.Error("Transaction referenced missing", "number", *blockNumber, "hash", blockHash)
|
||||
return nil, common.Hash{}, 0, 0
|
||||
}
|
||||
for txIndex, tx := range body.Transactions {
|
||||
@@ -114,7 +114,7 @@ func ReadTransaction(db ethdb.Reader, hash common.Hash) (*types.Transaction, com
|
||||
return tx, blockHash, *blockNumber, uint64(txIndex)
|
||||
}
|
||||
}
|
||||
log.Error("Transaction not found", "number", blockNumber, "hash", blockHash, "txhash", hash)
|
||||
log.Error("Transaction not found", "number", *blockNumber, "hash", blockHash, "txhash", hash)
|
||||
return nil, common.Hash{}, 0, 0
|
||||
}
|
||||
|
||||
@@ -137,7 +137,7 @@ func ReadReceipt(db ethdb.Reader, hash common.Hash, config *params.ChainConfig)
|
||||
return receipt, blockHash, *blockNumber, uint64(receiptIndex)
|
||||
}
|
||||
}
|
||||
log.Error("Receipt not found", "number", blockNumber, "hash", blockHash, "txhash", hash)
|
||||
log.Error("Receipt not found", "number", *blockNumber, "hash", blockHash, "txhash", hash)
|
||||
return nil, common.Hash{}, 0, 0
|
||||
}
|
||||
|
||||
|
@@ -89,6 +89,11 @@ func (db *nofreezedb) Ancient(kind string, number uint64) ([]byte, error) {
|
||||
return nil, errNotSupported
|
||||
}
|
||||
|
||||
// ReadAncients returns an error as we don't have a backing chain freezer.
|
||||
func (db *nofreezedb) ReadAncients(kind string, start, max, maxByteSize uint64) ([][]byte, error) {
|
||||
return nil, errNotSupported
|
||||
}
|
||||
|
||||
// Ancients returns an error as we don't have a backing chain freezer.
|
||||
func (db *nofreezedb) Ancients() (uint64, error) {
|
||||
return 0, errNotSupported
|
||||
|
@@ -180,6 +180,18 @@ func (f *freezer) Ancient(kind string, number uint64) ([]byte, error) {
|
||||
return nil, errUnknownTable
|
||||
}
|
||||
|
||||
// ReadAncients retrieves multiple items in sequence, starting from the index 'start'.
|
||||
// It will return
|
||||
// - at most 'max' items,
|
||||
// - at least 1 item (even if exceeding the maxByteSize), but will otherwise
|
||||
// return as many items as fit into maxByteSize.
|
||||
func (f *freezer) ReadAncients(kind string, start, count, maxBytes uint64) ([][]byte, error) {
|
||||
if table := f.tables[kind]; table != nil {
|
||||
return table.RetrieveItems(start, count, maxBytes)
|
||||
}
|
||||
return nil, errUnknownTable
|
||||
}
|
||||
|
||||
// Ancients returns the length of the frozen items.
|
||||
func (f *freezer) Ancients() (uint64, error) {
|
||||
return atomic.LoadUint64(&f.frozen), nil
|
||||
@@ -402,7 +414,7 @@ func (f *freezer) freeze(db ethdb.KeyValueStore) {
|
||||
}
|
||||
batch.Reset()
|
||||
|
||||
// Wipe out side chains also and track dangling side chians
|
||||
// Wipe out side chains also and track dangling side chains
|
||||
var dangling []common.Hash
|
||||
for number := first; number < f.frozen; number++ {
|
||||
// Always keep the genesis block in active database
|
||||
|
@@ -70,6 +70,19 @@ func (i *indexEntry) marshallBinary() []byte {
|
||||
return b
|
||||
}
|
||||
|
||||
// bounds returns the start- and end- offsets, and the file number of where to
|
||||
// read there data item marked by the two index entries. The two entries are
|
||||
// assumed to be sequential.
|
||||
func (start *indexEntry) bounds(end *indexEntry) (startOffset, endOffset, fileId uint32) {
|
||||
if start.filenum != end.filenum {
|
||||
// If a piece of data 'crosses' a data-file,
|
||||
// it's actually in one piece on the second data-file.
|
||||
// We return a zero-indexEntry for the second file as start
|
||||
return 0, end.offset, end.filenum
|
||||
}
|
||||
return start.offset, end.offset, end.filenum
|
||||
}
|
||||
|
||||
// freezerTable represents a single chained data table within the freezer (e.g. blocks).
|
||||
// It consists of a data file (snappy encoded arbitrary data blobs) and an indexEntry
|
||||
// file (uncompressed 64 bit indices into the data file).
|
||||
@@ -546,84 +559,183 @@ func (t *freezerTable) append(item uint64, encodedBlob []byte, wlock bool) (bool
|
||||
return false, nil
|
||||
}
|
||||
|
||||
// getBounds returns the indexes for the item
|
||||
// returns start, end, filenumber and error
|
||||
func (t *freezerTable) getBounds(item uint64) (uint32, uint32, uint32, error) {
|
||||
buffer := make([]byte, indexEntrySize)
|
||||
var startIdx, endIdx indexEntry
|
||||
// Read second index
|
||||
if _, err := t.index.ReadAt(buffer, int64((item+1)*indexEntrySize)); err != nil {
|
||||
return 0, 0, 0, err
|
||||
// getIndices returns the index entries for the given from-item, covering 'count' items.
|
||||
// N.B: The actual number of returned indices for N items will always be N+1 (unless an
|
||||
// error is returned).
|
||||
// OBS: This method assumes that the caller has already verified (and/or trimmed) the range
|
||||
// so that the items are within bounds. If this method is used to read out of bounds,
|
||||
// it will return error.
|
||||
func (t *freezerTable) getIndices(from, count uint64) ([]*indexEntry, error) {
|
||||
// Apply the table-offset
|
||||
from = from - uint64(t.itemOffset)
|
||||
// For reading N items, we need N+1 indices.
|
||||
buffer := make([]byte, (count+1)*indexEntrySize)
|
||||
if _, err := t.index.ReadAt(buffer, int64(from*indexEntrySize)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
endIdx.unmarshalBinary(buffer)
|
||||
// Read first index (unless it's the very first item)
|
||||
if item != 0 {
|
||||
if _, err := t.index.ReadAt(buffer, int64(item*indexEntrySize)); err != nil {
|
||||
return 0, 0, 0, err
|
||||
}
|
||||
startIdx.unmarshalBinary(buffer)
|
||||
} else {
|
||||
var (
|
||||
indices []*indexEntry
|
||||
offset int
|
||||
)
|
||||
for i := from; i <= from+count; i++ {
|
||||
index := new(indexEntry)
|
||||
index.unmarshalBinary(buffer[offset:])
|
||||
offset += indexEntrySize
|
||||
indices = append(indices, index)
|
||||
}
|
||||
if from == 0 {
|
||||
// Special case if we're reading the first item in the freezer. We assume that
|
||||
// the first item always start from zero(regarding the deletion, we
|
||||
// only support deletion by files, so that the assumption is held).
|
||||
// This means we can use the first item metadata to carry information about
|
||||
// the 'global' offset, for the deletion-case
|
||||
return 0, endIdx.offset, endIdx.filenum, nil
|
||||
indices[0].offset = 0
|
||||
indices[0].filenum = indices[1].filenum
|
||||
}
|
||||
if startIdx.filenum != endIdx.filenum {
|
||||
// If a piece of data 'crosses' a data-file,
|
||||
// it's actually in one piece on the second data-file.
|
||||
// We return a zero-indexEntry for the second file as start
|
||||
return 0, endIdx.offset, endIdx.filenum, nil
|
||||
}
|
||||
return startIdx.offset, endIdx.offset, endIdx.filenum, nil
|
||||
return indices, nil
|
||||
}
|
||||
|
||||
// Retrieve looks up the data offset of an item with the given number and retrieves
|
||||
// the raw binary blob from the data file.
|
||||
func (t *freezerTable) Retrieve(item uint64) ([]byte, error) {
|
||||
blob, err := t.retrieve(item)
|
||||
items, err := t.RetrieveItems(item, 1, 0)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if t.noCompression {
|
||||
return blob, nil
|
||||
}
|
||||
return snappy.Decode(nil, blob)
|
||||
return items[0], nil
|
||||
}
|
||||
|
||||
// retrieve looks up the data offset of an item with the given number and retrieves
|
||||
// the raw binary blob from the data file. OBS! This method does not decode
|
||||
// compressed data.
|
||||
func (t *freezerTable) retrieve(item uint64) ([]byte, error) {
|
||||
// RetrieveItems returns multiple items in sequence, starting from the index 'start'.
|
||||
// It will return at most 'max' items, but will abort earlier to respect the
|
||||
// 'maxBytes' argument. However, if the 'maxBytes' is smaller than the size of one
|
||||
// item, it _will_ return one element and possibly overflow the maxBytes.
|
||||
func (t *freezerTable) RetrieveItems(start, count, maxBytes uint64) ([][]byte, error) {
|
||||
// First we read the 'raw' data, which might be compressed.
|
||||
diskData, sizes, err := t.retrieveItems(start, count, maxBytes)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var (
|
||||
output = make([][]byte, 0, count)
|
||||
offset int // offset for reading
|
||||
outputSize int // size of uncompressed data
|
||||
)
|
||||
// Now slice up the data and decompress.
|
||||
for i, diskSize := range sizes {
|
||||
item := diskData[offset : offset+diskSize]
|
||||
offset += diskSize
|
||||
decompressedSize := diskSize
|
||||
if !t.noCompression {
|
||||
decompressedSize, _ = snappy.DecodedLen(item)
|
||||
}
|
||||
if i > 0 && uint64(outputSize+decompressedSize) > maxBytes {
|
||||
break
|
||||
}
|
||||
if !t.noCompression {
|
||||
data, err := snappy.Decode(nil, item)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
output = append(output, data)
|
||||
} else {
|
||||
output = append(output, item)
|
||||
}
|
||||
outputSize += decompressedSize
|
||||
}
|
||||
return output, nil
|
||||
}
|
||||
|
||||
// retrieveItems reads up to 'count' items from the table. It reads at least
|
||||
// one item, but otherwise avoids reading more than maxBytes bytes.
|
||||
// It returns the (potentially compressed) data, and the sizes.
|
||||
func (t *freezerTable) retrieveItems(start, count, maxBytes uint64) ([]byte, []int, error) {
|
||||
t.lock.RLock()
|
||||
defer t.lock.RUnlock()
|
||||
// Ensure the table and the item is accessible
|
||||
if t.index == nil || t.head == nil {
|
||||
return nil, errClosed
|
||||
return nil, nil, errClosed
|
||||
}
|
||||
if atomic.LoadUint64(&t.items) <= item {
|
||||
return nil, errOutOfBounds
|
||||
itemCount := atomic.LoadUint64(&t.items) // max number
|
||||
// Ensure the start is written, not deleted from the tail, and that the
|
||||
// caller actually wants something
|
||||
if itemCount <= start || uint64(t.itemOffset) > start || count == 0 {
|
||||
return nil, nil, errOutOfBounds
|
||||
}
|
||||
// Ensure the item was not deleted from the tail either
|
||||
if uint64(t.itemOffset) > item {
|
||||
return nil, errOutOfBounds
|
||||
if start+count > itemCount {
|
||||
count = itemCount - start
|
||||
}
|
||||
startOffset, endOffset, filenum, err := t.getBounds(item - uint64(t.itemOffset))
|
||||
var (
|
||||
output = make([]byte, maxBytes) // Buffer to read data into
|
||||
outputSize int // Used size of that buffer
|
||||
)
|
||||
// readData is a helper method to read a single data item from disk.
|
||||
readData := func(fileId, start uint32, length int) error {
|
||||
// In case a small limit is used, and the elements are large, may need to
|
||||
// realloc the read-buffer when reading the first (and only) item.
|
||||
if len(output) < length {
|
||||
output = make([]byte, length)
|
||||
}
|
||||
dataFile, exist := t.files[fileId]
|
||||
if !exist {
|
||||
return fmt.Errorf("missing data file %d", fileId)
|
||||
}
|
||||
if _, err := dataFile.ReadAt(output[outputSize:outputSize+length], int64(start)); err != nil {
|
||||
return err
|
||||
}
|
||||
outputSize += length
|
||||
return nil
|
||||
}
|
||||
// Read all the indexes in one go
|
||||
indices, err := t.getIndices(start, count)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, nil, err
|
||||
}
|
||||
dataFile, exist := t.files[filenum]
|
||||
if !exist {
|
||||
return nil, fmt.Errorf("missing data file %d", filenum)
|
||||
var (
|
||||
sizes []int // The sizes for each element
|
||||
totalSize = 0 // The total size of all data read so far
|
||||
readStart = indices[0].offset // Where, in the file, to start reading
|
||||
unreadSize = 0 // The size of the as-yet-unread data
|
||||
)
|
||||
|
||||
for i, firstIndex := range indices[:len(indices)-1] {
|
||||
secondIndex := indices[i+1]
|
||||
// Determine the size of the item.
|
||||
offset1, offset2, _ := firstIndex.bounds(secondIndex)
|
||||
size := int(offset2 - offset1)
|
||||
// Crossing a file boundary?
|
||||
if secondIndex.filenum != firstIndex.filenum {
|
||||
// If we have unread data in the first file, we need to do that read now.
|
||||
if unreadSize > 0 {
|
||||
if err := readData(firstIndex.filenum, readStart, unreadSize); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
unreadSize = 0
|
||||
}
|
||||
readStart = 0
|
||||
}
|
||||
if i > 0 && uint64(totalSize+size) > maxBytes {
|
||||
// About to break out due to byte limit being exceeded. We don't
|
||||
// read this last item, but we need to do the deferred reads now.
|
||||
if unreadSize > 0 {
|
||||
if err := readData(secondIndex.filenum, readStart, unreadSize); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
}
|
||||
break
|
||||
}
|
||||
// Defer the read for later
|
||||
unreadSize += size
|
||||
totalSize += size
|
||||
sizes = append(sizes, size)
|
||||
if i == len(indices)-2 || uint64(totalSize) > maxBytes {
|
||||
// Last item, need to do the read now
|
||||
if err := readData(secondIndex.filenum, readStart, unreadSize); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
// Retrieve the data itself, decompress and return
|
||||
blob := make([]byte, endOffset-startOffset)
|
||||
if _, err := dataFile.ReadAt(blob, int64(startOffset)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
t.readMeter.Mark(int64(len(blob) + 2*indexEntrySize))
|
||||
return blob, nil
|
||||
return output[:outputSize], sizes, nil
|
||||
}
|
||||
|
||||
// has returns an indicator whether the specified number data
|
||||
|
@@ -74,7 +74,7 @@ func TestFreezerBasics(t *testing.T) {
|
||||
exp := getChunk(15, y)
|
||||
got, err := f.Retrieve(uint64(y))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
t.Fatalf("reading item %d: %v", y, err)
|
||||
}
|
||||
if !bytes.Equal(got, exp) {
|
||||
t.Fatalf("test %d, got \n%x != \n%x", y, got, exp)
|
||||
@@ -692,3 +692,118 @@ func TestAppendTruncateParallel(t *testing.T) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TestSequentialRead does some basic tests on the RetrieveItems.
|
||||
func TestSequentialRead(t *testing.T) {
|
||||
rm, wm, sg := metrics.NewMeter(), metrics.NewMeter(), metrics.NewGauge()
|
||||
fname := fmt.Sprintf("batchread-%d", rand.Uint64())
|
||||
{ // Fill table
|
||||
f, err := newCustomTable(os.TempDir(), fname, rm, wm, sg, 50, true)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
// Write 15 bytes 30 times
|
||||
for x := 0; x < 30; x++ {
|
||||
data := getChunk(15, x)
|
||||
f.Append(uint64(x), data)
|
||||
}
|
||||
f.DumpIndex(0, 30)
|
||||
f.Close()
|
||||
}
|
||||
{ // Open it, iterate, verify iteration
|
||||
f, err := newCustomTable(os.TempDir(), fname, rm, wm, sg, 50, true)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
items, err := f.RetrieveItems(0, 10000, 100000)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if have, want := len(items), 30; have != want {
|
||||
t.Fatalf("want %d items, have %d ", want, have)
|
||||
}
|
||||
for i, have := range items {
|
||||
want := getChunk(15, i)
|
||||
if !bytes.Equal(want, have) {
|
||||
t.Fatalf("data corruption: have\n%x\n, want \n%x\n", have, want)
|
||||
}
|
||||
}
|
||||
f.Close()
|
||||
}
|
||||
{ // Open it, iterate, verify byte limit. The byte limit is less than item
|
||||
// size, so each lookup should only return one item
|
||||
f, err := newCustomTable(os.TempDir(), fname, rm, wm, sg, 40, true)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
items, err := f.RetrieveItems(0, 10000, 10)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if have, want := len(items), 1; have != want {
|
||||
t.Fatalf("want %d items, have %d ", want, have)
|
||||
}
|
||||
for i, have := range items {
|
||||
want := getChunk(15, i)
|
||||
if !bytes.Equal(want, have) {
|
||||
t.Fatalf("data corruption: have\n%x\n, want \n%x\n", have, want)
|
||||
}
|
||||
}
|
||||
f.Close()
|
||||
}
|
||||
}
|
||||
|
||||
// TestSequentialReadByteLimit does some more advanced tests on batch reads.
|
||||
// These tests check that when the byte limit hits, we correctly abort in time,
|
||||
// but also properly do all the deferred reads for the previous data, regardless
|
||||
// of whether the data crosses a file boundary or not.
|
||||
func TestSequentialReadByteLimit(t *testing.T) {
|
||||
rm, wm, sg := metrics.NewMeter(), metrics.NewMeter(), metrics.NewGauge()
|
||||
fname := fmt.Sprintf("batchread-2-%d", rand.Uint64())
|
||||
{ // Fill table
|
||||
f, err := newCustomTable(os.TempDir(), fname, rm, wm, sg, 100, true)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
// Write 10 bytes 30 times,
|
||||
// Splitting it at every 100 bytes (10 items)
|
||||
for x := 0; x < 30; x++ {
|
||||
data := getChunk(10, x)
|
||||
f.Append(uint64(x), data)
|
||||
}
|
||||
f.Close()
|
||||
}
|
||||
for i, tc := range []struct {
|
||||
items uint64
|
||||
limit uint64
|
||||
want int
|
||||
}{
|
||||
{9, 89, 8},
|
||||
{10, 99, 9},
|
||||
{11, 109, 10},
|
||||
{100, 89, 8},
|
||||
{100, 99, 9},
|
||||
{100, 109, 10},
|
||||
} {
|
||||
{
|
||||
f, err := newCustomTable(os.TempDir(), fname, rm, wm, sg, 100, true)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
items, err := f.RetrieveItems(0, tc.items, tc.limit)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if have, want := len(items), tc.want; have != want {
|
||||
t.Fatalf("test %d: want %d items, have %d ", i, want, have)
|
||||
}
|
||||
for ii, have := range items {
|
||||
want := getChunk(10, ii)
|
||||
if !bytes.Equal(want, have) {
|
||||
t.Fatalf("test %d: data corruption item %d: have\n%x\n, want \n%x\n", i, ii, have, want)
|
||||
}
|
||||
}
|
||||
f.Close()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -62,6 +62,12 @@ func (t *table) Ancient(kind string, number uint64) ([]byte, error) {
|
||||
return t.db.Ancient(kind, number)
|
||||
}
|
||||
|
||||
// ReadAncients is a noop passthrough that just forwards the request to the underlying
|
||||
// database.
|
||||
func (t *table) ReadAncients(kind string, start, count, maxBytes uint64) ([][]byte, error) {
|
||||
return t.db.ReadAncients(kind, start, count, maxBytes)
|
||||
}
|
||||
|
||||
// Ancients is a noop passthrough that just forwards the request to the underlying
|
||||
// database.
|
||||
func (t *table) Ancients() (uint64, error) {
|
||||
|
@@ -90,7 +90,7 @@ func (bloom *stateBloom) Commit(filename, tempname string) error {
|
||||
return err
|
||||
}
|
||||
// Ensure the file is synced to disk
|
||||
f, err := os.Open(tempname)
|
||||
f, err := os.OpenFile(tempname, os.O_RDWR, 0666)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@@ -169,11 +169,17 @@ type Tree struct {
|
||||
// store (with a number of memory layers from a journal), ensuring that the head
|
||||
// of the snapshot matches the expected one.
|
||||
//
|
||||
// If the snapshot is missing or the disk layer is broken, the entire is deleted
|
||||
// and will be reconstructed from scratch based on the tries in the key-value
|
||||
// store, on a background thread. If the memory layers from the journal is not
|
||||
// continuous with disk layer or the journal is missing, all diffs will be discarded
|
||||
// iff it's in "recovery" mode, otherwise rebuild is mandatory.
|
||||
// If the snapshot is missing or the disk layer is broken, the snapshot will be
|
||||
// reconstructed using both the existing data and the state trie.
|
||||
// The repair happens on a background thread.
|
||||
//
|
||||
// If the memory layers in the journal do not match the disk layer (e.g. there is
|
||||
// a gap) or the journal is missing, there are two repair cases:
|
||||
//
|
||||
// - if the 'recovery' parameter is true, all memory diff-layers will be discarded.
|
||||
// This case happens when the snapshot is 'ahead' of the state trie.
|
||||
// - otherwise, the entire snapshot is considered invalid and will be recreated on
|
||||
// a background thread.
|
||||
func New(diskdb ethdb.KeyValueStore, triedb *trie.Database, cache int, root common.Hash, async bool, rebuild bool, recovery bool) (*Tree, error) {
|
||||
// Create a new, empty snapshot tree
|
||||
snap := &Tree{
|
||||
@@ -469,7 +475,7 @@ func (t *Tree) cap(diff *diffLayer, layers int) *diskLayer {
|
||||
if flattened.memory < aggregatorMemoryLimit {
|
||||
// Accumulator layer is smaller than the limit, so we can abort, unless
|
||||
// there's a snapshot being generated currently. In that case, the trie
|
||||
// will move fron underneath the generator so we **must** merge all the
|
||||
// will move from underneath the generator so we **must** merge all the
|
||||
// partial data down into the snapshot and restart the generation.
|
||||
if flattened.parent.(*diskLayer).genAbort == nil {
|
||||
return nil
|
||||
|
@@ -450,9 +450,6 @@ func (s *stateObject) setBalance(amount *big.Int) {
|
||||
s.data.Balance = amount
|
||||
}
|
||||
|
||||
// Return the gas back to the origin. Used by the Virtual machine or Closures
|
||||
func (s *stateObject) ReturnGas(gas *big.Int) {}
|
||||
|
||||
func (s *stateObject) deepCopy(db *StateDB) *stateObject {
|
||||
stateObject := newObject(db, s.address, s.data)
|
||||
if s.trie != nil {
|
||||
|
@@ -89,10 +89,10 @@ type StateDB struct {
|
||||
// The refund counter, also used by state transitioning.
|
||||
refund uint64
|
||||
|
||||
thash, bhash common.Hash
|
||||
txIndex int
|
||||
logs map[common.Hash][]*types.Log
|
||||
logSize uint
|
||||
thash common.Hash
|
||||
txIndex int
|
||||
logs map[common.Hash][]*types.Log
|
||||
logSize uint
|
||||
|
||||
preimages map[common.Hash][]byte
|
||||
|
||||
@@ -186,15 +186,18 @@ func (s *StateDB) AddLog(log *types.Log) {
|
||||
s.journal.append(addLogChange{txhash: s.thash})
|
||||
|
||||
log.TxHash = s.thash
|
||||
log.BlockHash = s.bhash
|
||||
log.TxIndex = uint(s.txIndex)
|
||||
log.Index = s.logSize
|
||||
s.logs[s.thash] = append(s.logs[s.thash], log)
|
||||
s.logSize++
|
||||
}
|
||||
|
||||
func (s *StateDB) GetLogs(hash common.Hash) []*types.Log {
|
||||
return s.logs[hash]
|
||||
func (s *StateDB) GetLogs(hash common.Hash, blockHash common.Hash) []*types.Log {
|
||||
logs := s.logs[hash]
|
||||
for _, l := range logs {
|
||||
l.BlockHash = blockHash
|
||||
}
|
||||
return logs
|
||||
}
|
||||
|
||||
func (s *StateDB) Logs() []*types.Log {
|
||||
@@ -272,11 +275,6 @@ func (s *StateDB) TxIndex() int {
|
||||
return s.txIndex
|
||||
}
|
||||
|
||||
// BlockHash returns the current block hash set by Prepare.
|
||||
func (s *StateDB) BlockHash() common.Hash {
|
||||
return s.bhash
|
||||
}
|
||||
|
||||
func (s *StateDB) GetCode(addr common.Address) []byte {
|
||||
stateObject := s.getStateObject(addr)
|
||||
if stateObject != nil {
|
||||
@@ -333,17 +331,6 @@ func (s *StateDB) GetStorageProof(a common.Address, key common.Hash) ([][]byte,
|
||||
return proof, err
|
||||
}
|
||||
|
||||
// GetStorageProofByHash returns the Merkle proof for given storage slot.
|
||||
func (s *StateDB) GetStorageProofByHash(a common.Address, key common.Hash) ([][]byte, error) {
|
||||
var proof proofList
|
||||
trie := s.StorageTrie(a)
|
||||
if trie == nil {
|
||||
return proof, errors.New("storage trie for requested address does not exist")
|
||||
}
|
||||
err := trie.Prove(crypto.Keccak256(key.Bytes()), 0, &proof)
|
||||
return proof, err
|
||||
}
|
||||
|
||||
// GetCommittedState retrieves a value from the given account's committed storage trie.
|
||||
func (s *StateDB) GetCommittedState(addr common.Address, hash common.Hash) common.Hash {
|
||||
stateObject := s.getStateObject(addr)
|
||||
@@ -597,7 +584,6 @@ func (s *StateDB) createObject(addr common.Address) (newobj, prev *stateObject)
|
||||
}
|
||||
}
|
||||
newobj = newObject(s, addr, Account{})
|
||||
newobj.setNonce(0) // sets the object to dirty
|
||||
if prev == nil {
|
||||
s.journal.append(createObjectChange{account: &addr})
|
||||
} else {
|
||||
@@ -892,11 +878,10 @@ func (s *StateDB) IntermediateRoot(deleteEmptyObjects bool) common.Hash {
|
||||
return s.trie.Hash()
|
||||
}
|
||||
|
||||
// Prepare sets the current transaction hash and index and block hash which is
|
||||
// Prepare sets the current transaction hash and index which are
|
||||
// used when the EVM emits new state logs.
|
||||
func (s *StateDB) Prepare(thash, bhash common.Hash, ti int) {
|
||||
func (s *StateDB) Prepare(thash common.Hash, ti int) {
|
||||
s.thash = thash
|
||||
s.bhash = bhash
|
||||
s.txIndex = ti
|
||||
s.accessList = newAccessList()
|
||||
}
|
||||
|
@@ -463,9 +463,9 @@ func (test *snapshotTest) checkEqual(state, checkstate *StateDB) error {
|
||||
return fmt.Errorf("got GetRefund() == %d, want GetRefund() == %d",
|
||||
state.GetRefund(), checkstate.GetRefund())
|
||||
}
|
||||
if !reflect.DeepEqual(state.GetLogs(common.Hash{}), checkstate.GetLogs(common.Hash{})) {
|
||||
if !reflect.DeepEqual(state.GetLogs(common.Hash{}, common.Hash{}), checkstate.GetLogs(common.Hash{}, common.Hash{})) {
|
||||
return fmt.Errorf("got GetLogs(common.Hash{}) == %v, want GetLogs(common.Hash{}) == %v",
|
||||
state.GetLogs(common.Hash{}), checkstate.GetLogs(common.Hash{}))
|
||||
state.GetLogs(common.Hash{}, common.Hash{}), checkstate.GetLogs(common.Hash{}, common.Hash{}))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
@@ -312,12 +312,11 @@ func (sf *subfetcher) loop() {
|
||||
|
||||
default:
|
||||
// No termination request yet, prefetch the next entry
|
||||
taskid := string(task)
|
||||
if _, ok := sf.seen[taskid]; ok {
|
||||
if _, ok := sf.seen[string(task)]; ok {
|
||||
sf.dups++
|
||||
} else {
|
||||
sf.trie.TryGet(task)
|
||||
sf.seen[taskid] = struct{}{}
|
||||
sf.seen[string(task)] = struct{}{}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
110
core/state/trie_prefetcher_test.go
Normal file
110
core/state/trie_prefetcher_test.go
Normal file
@@ -0,0 +1,110 @@
|
||||
// Copyright 2021 The go-ethereum Authors
|
||||
// This file is part of the go-ethereum library.
|
||||
//
|
||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package state
|
||||
|
||||
import (
|
||||
"math/big"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/core/rawdb"
|
||||
)
|
||||
|
||||
func filledStateDB() *StateDB {
|
||||
state, _ := New(common.Hash{}, NewDatabase(rawdb.NewMemoryDatabase()), nil)
|
||||
|
||||
// Create an account and check if the retrieved balance is correct
|
||||
addr := common.HexToAddress("0xaffeaffeaffeaffeaffeaffeaffeaffeaffeaffe")
|
||||
skey := common.HexToHash("aaa")
|
||||
sval := common.HexToHash("bbb")
|
||||
|
||||
state.SetBalance(addr, big.NewInt(42)) // Change the account trie
|
||||
state.SetCode(addr, []byte("hello")) // Change an external metadata
|
||||
state.SetState(addr, skey, sval) // Change the storage trie
|
||||
for i := 0; i < 100; i++ {
|
||||
sk := common.BigToHash(big.NewInt(int64(i)))
|
||||
state.SetState(addr, sk, sk) // Change the storage trie
|
||||
}
|
||||
return state
|
||||
}
|
||||
|
||||
func TestCopyAndClose(t *testing.T) {
|
||||
db := filledStateDB()
|
||||
prefetcher := newTriePrefetcher(db.db, db.originalRoot, "")
|
||||
skey := common.HexToHash("aaa")
|
||||
prefetcher.prefetch(db.originalRoot, [][]byte{skey.Bytes()})
|
||||
prefetcher.prefetch(db.originalRoot, [][]byte{skey.Bytes()})
|
||||
time.Sleep(1 * time.Second)
|
||||
a := prefetcher.trie(db.originalRoot)
|
||||
prefetcher.prefetch(db.originalRoot, [][]byte{skey.Bytes()})
|
||||
b := prefetcher.trie(db.originalRoot)
|
||||
cpy := prefetcher.copy()
|
||||
cpy.prefetch(db.originalRoot, [][]byte{skey.Bytes()})
|
||||
cpy.prefetch(db.originalRoot, [][]byte{skey.Bytes()})
|
||||
c := cpy.trie(db.originalRoot)
|
||||
prefetcher.close()
|
||||
cpy2 := cpy.copy()
|
||||
cpy2.prefetch(db.originalRoot, [][]byte{skey.Bytes()})
|
||||
d := cpy2.trie(db.originalRoot)
|
||||
cpy.close()
|
||||
cpy2.close()
|
||||
if a.Hash() != b.Hash() || a.Hash() != c.Hash() || a.Hash() != d.Hash() {
|
||||
t.Fatalf("Invalid trie, hashes should be equal: %v %v %v %v", a.Hash(), b.Hash(), c.Hash(), d.Hash())
|
||||
}
|
||||
}
|
||||
|
||||
func TestUseAfterClose(t *testing.T) {
|
||||
db := filledStateDB()
|
||||
prefetcher := newTriePrefetcher(db.db, db.originalRoot, "")
|
||||
skey := common.HexToHash("aaa")
|
||||
prefetcher.prefetch(db.originalRoot, [][]byte{skey.Bytes()})
|
||||
a := prefetcher.trie(db.originalRoot)
|
||||
prefetcher.close()
|
||||
b := prefetcher.trie(db.originalRoot)
|
||||
if a == nil {
|
||||
t.Fatal("Prefetching before close should not return nil")
|
||||
}
|
||||
if b != nil {
|
||||
t.Fatal("Trie after close should return nil")
|
||||
}
|
||||
}
|
||||
|
||||
func TestCopyClose(t *testing.T) {
|
||||
db := filledStateDB()
|
||||
prefetcher := newTriePrefetcher(db.db, db.originalRoot, "")
|
||||
skey := common.HexToHash("aaa")
|
||||
prefetcher.prefetch(db.originalRoot, [][]byte{skey.Bytes()})
|
||||
cpy := prefetcher.copy()
|
||||
a := prefetcher.trie(db.originalRoot)
|
||||
b := cpy.trie(db.originalRoot)
|
||||
prefetcher.close()
|
||||
c := prefetcher.trie(db.originalRoot)
|
||||
d := cpy.trie(db.originalRoot)
|
||||
if a == nil {
|
||||
t.Fatal("Prefetching before close should not return nil")
|
||||
}
|
||||
if b == nil {
|
||||
t.Fatal("Copy trie should return nil")
|
||||
}
|
||||
if c != nil {
|
||||
t.Fatal("Trie after close should return nil")
|
||||
}
|
||||
if d == nil {
|
||||
t.Fatal("Copy trie should not return nil")
|
||||
}
|
||||
}
|
@@ -67,7 +67,7 @@ func (p *statePrefetcher) Prefetch(block *types.Block, statedb *state.StateDB, c
|
||||
if err != nil {
|
||||
return // Also invalid block, bail out
|
||||
}
|
||||
statedb.Prepare(tx.Hash(), block.Hash(), i)
|
||||
statedb.Prepare(tx.Hash(), i)
|
||||
if err := precacheTransaction(msg, p.config, gaspool, statedb, header, evm); err != nil {
|
||||
return // Ugh, something went horribly wrong, bail out
|
||||
}
|
||||
|
@@ -18,6 +18,7 @@ package core
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math/big"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/consensus"
|
||||
@@ -57,11 +58,13 @@ func NewStateProcessor(config *params.ChainConfig, bc *BlockChain, engine consen
|
||||
// transactions failed to execute due to insufficient gas it will return an error.
|
||||
func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg vm.Config) (types.Receipts, []*types.Log, uint64, error) {
|
||||
var (
|
||||
receipts types.Receipts
|
||||
usedGas = new(uint64)
|
||||
header = block.Header()
|
||||
allLogs []*types.Log
|
||||
gp = new(GasPool).AddGas(block.GasLimit())
|
||||
receipts types.Receipts
|
||||
usedGas = new(uint64)
|
||||
header = block.Header()
|
||||
blockHash = block.Hash()
|
||||
blockNumber = block.Number()
|
||||
allLogs []*types.Log
|
||||
gp = new(GasPool).AddGas(block.GasLimit())
|
||||
)
|
||||
// Mutate the block and state according to any hard-fork specs
|
||||
if p.config.DAOForkSupport && p.config.DAOForkBlock != nil && p.config.DAOForkBlock.Cmp(block.Number()) == 0 {
|
||||
@@ -75,8 +78,8 @@ func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg
|
||||
if err != nil {
|
||||
return nil, nil, 0, fmt.Errorf("could not apply tx %d [%v]: %w", i, tx.Hash().Hex(), err)
|
||||
}
|
||||
statedb.Prepare(tx.Hash(), block.Hash(), i)
|
||||
receipt, err := applyTransaction(msg, p.config, p.bc, nil, gp, statedb, header, tx, usedGas, vmenv)
|
||||
statedb.Prepare(tx.Hash(), i)
|
||||
receipt, err := applyTransaction(msg, p.config, p.bc, nil, gp, statedb, blockNumber, blockHash, tx, usedGas, vmenv)
|
||||
if err != nil {
|
||||
return nil, nil, 0, fmt.Errorf("could not apply tx %d [%v]: %w", i, tx.Hash().Hex(), err)
|
||||
}
|
||||
@@ -89,7 +92,7 @@ func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg
|
||||
return receipts, allLogs, *usedGas, nil
|
||||
}
|
||||
|
||||
func applyTransaction(msg types.Message, config *params.ChainConfig, bc ChainContext, author *common.Address, gp *GasPool, statedb *state.StateDB, header *types.Header, tx *types.Transaction, usedGas *uint64, evm *vm.EVM) (*types.Receipt, error) {
|
||||
func applyTransaction(msg types.Message, config *params.ChainConfig, bc ChainContext, author *common.Address, gp *GasPool, statedb *state.StateDB, blockNumber *big.Int, blockHash common.Hash, tx *types.Transaction, usedGas *uint64, evm *vm.EVM) (*types.Receipt, error) {
|
||||
// Create a new context to be used in the EVM environment.
|
||||
txContext := NewEVMTxContext(msg)
|
||||
evm.Reset(txContext, statedb)
|
||||
@@ -102,10 +105,10 @@ func applyTransaction(msg types.Message, config *params.ChainConfig, bc ChainCon
|
||||
|
||||
// Update the state with pending changes.
|
||||
var root []byte
|
||||
if config.IsByzantium(header.Number) {
|
||||
if config.IsByzantium(blockNumber) {
|
||||
statedb.Finalise(true)
|
||||
} else {
|
||||
root = statedb.IntermediateRoot(config.IsEIP158(header.Number)).Bytes()
|
||||
root = statedb.IntermediateRoot(config.IsEIP158(blockNumber)).Bytes()
|
||||
}
|
||||
*usedGas += result.UsedGas
|
||||
|
||||
@@ -126,10 +129,10 @@ func applyTransaction(msg types.Message, config *params.ChainConfig, bc ChainCon
|
||||
}
|
||||
|
||||
// Set the receipt logs and create the bloom filter.
|
||||
receipt.Logs = statedb.GetLogs(tx.Hash())
|
||||
receipt.Logs = statedb.GetLogs(tx.Hash(), blockHash)
|
||||
receipt.Bloom = types.CreateBloom(types.Receipts{receipt})
|
||||
receipt.BlockHash = statedb.BlockHash()
|
||||
receipt.BlockNumber = header.Number
|
||||
receipt.BlockHash = blockHash
|
||||
receipt.BlockNumber = blockNumber
|
||||
receipt.TransactionIndex = uint(statedb.TxIndex())
|
||||
return receipt, err
|
||||
}
|
||||
@@ -146,5 +149,5 @@ func ApplyTransaction(config *params.ChainConfig, bc ChainContext, author *commo
|
||||
// Create a new context to be used in the EVM environment
|
||||
blockContext := NewEVMBlockContext(header, bc, author)
|
||||
vmenv := vm.NewEVM(blockContext, vm.TxContext{}, statedb, config, cfg)
|
||||
return applyTransaction(msg, config, bc, author, gp, statedb, header, tx, usedGas, vmenv)
|
||||
return applyTransaction(msg, config, bc, author, gp, statedb, header.Number, header.Hash(), tx, usedGas, vmenv)
|
||||
}
|
||||
|
@@ -118,7 +118,7 @@ func TestStateProcessorErrors(t *testing.T) {
|
||||
txs: []*types.Transaction{
|
||||
makeTx(0, common.Address{}, big.NewInt(1000000000000000000), params.TxGas, big.NewInt(875000000), nil),
|
||||
},
|
||||
want: "could not apply tx 0 [0x98c796b470f7fcab40aaef5c965a602b0238e1034cce6fb73823042dd0638d74]: insufficient funds for transfer: address 0x71562b71999873DB5b286dF957af199Ec94617F7",
|
||||
want: "could not apply tx 0 [0x98c796b470f7fcab40aaef5c965a602b0238e1034cce6fb73823042dd0638d74]: insufficient funds for gas * price + value: address 0x71562b71999873DB5b286dF957af199Ec94617F7 have 1000000000000000000 want 1000018375000000000",
|
||||
},
|
||||
{ // ErrInsufficientFunds
|
||||
txs: []*types.Transaction{
|
||||
@@ -195,7 +195,7 @@ func TestStateProcessorErrors(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// One final error is ErrTxTypeNotSupported. For this, we need an older chain
|
||||
// ErrTxTypeNotSupported, For this, we need an older chain
|
||||
{
|
||||
var (
|
||||
db = rawdb.NewMemoryDatabase()
|
||||
@@ -244,6 +244,46 @@ func TestStateProcessorErrors(t *testing.T) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ErrSenderNoEOA, for this we need the sender to have contract code
|
||||
{
|
||||
var (
|
||||
db = rawdb.NewMemoryDatabase()
|
||||
gspec = &Genesis{
|
||||
Config: config,
|
||||
Alloc: GenesisAlloc{
|
||||
common.HexToAddress("0x71562b71999873DB5b286dF957af199Ec94617F7"): GenesisAccount{
|
||||
Balance: big.NewInt(1000000000000000000), // 1 ether
|
||||
Nonce: 0,
|
||||
Code: common.FromHex("0xB0B0FACE"),
|
||||
},
|
||||
},
|
||||
}
|
||||
genesis = gspec.MustCommit(db)
|
||||
blockchain, _ = NewBlockChain(db, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil, nil)
|
||||
)
|
||||
defer blockchain.Stop()
|
||||
for i, tt := range []struct {
|
||||
txs []*types.Transaction
|
||||
want string
|
||||
}{
|
||||
{ // ErrSenderNoEOA
|
||||
txs: []*types.Transaction{
|
||||
mkDynamicTx(0, common.Address{}, params.TxGas-1000, big.NewInt(0), big.NewInt(0)),
|
||||
},
|
||||
want: "could not apply tx 0 [0x88626ac0d53cb65308f2416103c62bb1f18b805573d4f96a3640bbbfff13c14f]: sender not an eoa: address 0x71562b71999873DB5b286dF957af199Ec94617F7, codehash: 0x9280914443471259d4570a8661015ae4a5b80186dbc619658fb494bebc3da3d1",
|
||||
},
|
||||
} {
|
||||
block := GenerateBadBlock(genesis, ethash.NewFaker(), tt.txs, gspec.Config)
|
||||
_, err := blockchain.InsertChain(types.Blocks{block})
|
||||
if err == nil {
|
||||
t.Fatal("block imported without errors")
|
||||
}
|
||||
if have, want := err.Error(), tt.want; have != want {
|
||||
t.Errorf("test %d:\nhave \"%v\"\nwant \"%v\"\n", i, have, want)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// GenerateBadBlock constructs a "block" which contains the transactions. The transactions are not expected to be
|
||||
|
@@ -25,9 +25,12 @@ import (
|
||||
cmath "github.com/ethereum/go-ethereum/common/math"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/core/vm"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
)
|
||||
|
||||
var emptyCodeHash = crypto.Keccak256Hash(nil)
|
||||
|
||||
/*
|
||||
The State Transitioning Model
|
||||
|
||||
@@ -71,7 +74,7 @@ type Message interface {
|
||||
Value() *big.Int
|
||||
|
||||
Nonce() uint64
|
||||
CheckNonce() bool
|
||||
IsFake() bool
|
||||
Data() []byte
|
||||
AccessList() types.AccessList
|
||||
}
|
||||
@@ -193,6 +196,7 @@ func (st *StateTransition) buyGas() error {
|
||||
if st.gasFeeCap != nil {
|
||||
balanceCheck = new(big.Int).SetUint64(st.msg.Gas())
|
||||
balanceCheck = balanceCheck.Mul(balanceCheck, st.gasFeeCap)
|
||||
balanceCheck.Add(balanceCheck, st.value)
|
||||
}
|
||||
if have, want := st.state.GetBalance(st.msg.From()), balanceCheck; have.Cmp(want) < 0 {
|
||||
return fmt.Errorf("%w: address %v have %v want %v", ErrInsufficientFunds, st.msg.From().Hex(), have, want)
|
||||
@@ -208,8 +212,9 @@ func (st *StateTransition) buyGas() error {
|
||||
}
|
||||
|
||||
func (st *StateTransition) preCheck() error {
|
||||
// Make sure this transaction's nonce is correct.
|
||||
if st.msg.CheckNonce() {
|
||||
// Only check transactions that are not fake
|
||||
if !st.msg.IsFake() {
|
||||
// Make sure this transaction's nonce is correct.
|
||||
stNonce := st.state.GetNonce(st.msg.From())
|
||||
if msgNonce := st.msg.Nonce(); stNonce < msgNonce {
|
||||
return fmt.Errorf("%w: address %v, tx: %d state: %d", ErrNonceTooHigh,
|
||||
@@ -218,6 +223,11 @@ func (st *StateTransition) preCheck() error {
|
||||
return fmt.Errorf("%w: address %v, tx: %d state: %d", ErrNonceTooLow,
|
||||
st.msg.From().Hex(), msgNonce, stNonce)
|
||||
}
|
||||
// Make sure the sender is an EOA
|
||||
if codeHash := st.state.GetCodeHash(st.msg.From()); codeHash != emptyCodeHash && codeHash != (common.Hash{}) {
|
||||
return fmt.Errorf("%w: address %v, codehash: %s", ErrSenderNoEOA,
|
||||
st.msg.From().Hex(), codeHash)
|
||||
}
|
||||
}
|
||||
// Make sure that transaction gasFeeCap is greater than the baseFee (post london)
|
||||
if st.evm.ChainConfig().IsLondon(st.evm.Context.BlockNumber) {
|
||||
@@ -278,6 +288,7 @@ func (st *StateTransition) TransitionDb() (*ExecutionResult, error) {
|
||||
sender := vm.AccountRef(msg.From())
|
||||
homestead := st.evm.ChainConfig().IsHomestead(st.evm.Context.BlockNumber)
|
||||
istanbul := st.evm.ChainConfig().IsIstanbul(st.evm.Context.BlockNumber)
|
||||
london := st.evm.ChainConfig().IsLondon(st.evm.Context.BlockNumber)
|
||||
contractCreation := msg.To() == nil
|
||||
|
||||
// Check clauses 4-5, subtract intrinsic gas if everything is correct
|
||||
@@ -310,7 +321,8 @@ func (st *StateTransition) TransitionDb() (*ExecutionResult, error) {
|
||||
st.state.SetNonce(msg.From(), st.state.GetNonce(sender.Address())+1)
|
||||
ret, st.gas, vmerr = st.evm.Call(sender, st.to(), st.data, st.gas, st.value)
|
||||
}
|
||||
if !st.evm.ChainConfig().IsLondon(st.evm.Context.BlockNumber) {
|
||||
|
||||
if !london {
|
||||
// Before EIP-3529: refunds were capped to gasUsed / 2
|
||||
st.refundGas(params.RefundQuotient)
|
||||
} else {
|
||||
@@ -318,7 +330,7 @@ func (st *StateTransition) TransitionDb() (*ExecutionResult, error) {
|
||||
st.refundGas(params.RefundQuotientEIP3529)
|
||||
}
|
||||
effectiveTip := st.gasPrice
|
||||
if st.evm.ChainConfig().IsLondon(st.evm.Context.BlockNumber) {
|
||||
if london {
|
||||
effectiveTip = cmath.BigMin(st.gasTipCap, new(big.Int).Sub(st.gasFeeCap, st.evm.Context.BaseFee))
|
||||
}
|
||||
st.state.AddBalance(st.evm.Context.Coinbase, new(big.Int).Mul(new(big.Int).SetUint64(st.gasUsed()), effectiveTip))
|
||||
|
@@ -138,7 +138,7 @@ func (journal *txJournal) rotate(all map[common.Address]types.Transactions) erro
|
||||
journal.writer = nil
|
||||
}
|
||||
// Generate a new journal with the contents of the current pool
|
||||
replacement, err := os.OpenFile(journal.path+".new", os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0755)
|
||||
replacement, err := os.OpenFile(journal.path+".new", os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0644)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -158,7 +158,7 @@ func (journal *txJournal) rotate(all map[common.Address]types.Transactions) erro
|
||||
if err = os.Rename(journal.path+".new", journal.path); err != nil {
|
||||
return err
|
||||
}
|
||||
sink, err := os.OpenFile(journal.path, os.O_WRONLY|os.O_APPEND, 0755)
|
||||
sink, err := os.OpenFile(journal.path, os.O_WRONLY|os.O_APPEND, 0644)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@@ -494,6 +494,23 @@ func (pool *TxPool) Content() (map[common.Address]types.Transactions, map[common
|
||||
return pending, queued
|
||||
}
|
||||
|
||||
// ContentFrom retrieves the data content of the transaction pool, returning the
|
||||
// pending as well as queued transactions of this address, grouped by nonce.
|
||||
func (pool *TxPool) ContentFrom(addr common.Address) (types.Transactions, types.Transactions) {
|
||||
pool.mu.RLock()
|
||||
defer pool.mu.RUnlock()
|
||||
|
||||
var pending types.Transactions
|
||||
if list, ok := pool.pending[addr]; ok {
|
||||
pending = list.Flatten()
|
||||
}
|
||||
var queued types.Transactions
|
||||
if list, ok := pool.queue[addr]; ok {
|
||||
queued = list.Flatten()
|
||||
}
|
||||
return pending, queued
|
||||
}
|
||||
|
||||
// Pending retrieves all currently processable transactions, grouped by origin
|
||||
// account and sorted by nonce. The returned transaction set is a copy and can be
|
||||
// freely modified by calling code.
|
||||
@@ -618,8 +635,8 @@ func (pool *TxPool) validateTx(tx *types.Transaction, local bool) error {
|
||||
// pending or queued one, it overwrites the previous transaction if its price is higher.
|
||||
//
|
||||
// If a newly added transaction is marked as local, its sending account will be
|
||||
// whitelisted, preventing any associated transaction from being dropped out of the pool
|
||||
// due to pricing constraints.
|
||||
// be added to the allowlist, preventing any associated transaction from being dropped
|
||||
// out of the pool due to pricing constraints.
|
||||
func (pool *TxPool) add(tx *types.Transaction, local bool) (replaced bool, err error) {
|
||||
// If the transaction is already known, discard it
|
||||
hash := tx.Hash()
|
||||
|
@@ -129,6 +129,11 @@ func (h *Header) SanityCheck() error {
|
||||
if eLen := len(h.Extra); eLen > 100*1024 {
|
||||
return fmt.Errorf("too large block extradata: size %d", eLen)
|
||||
}
|
||||
if h.BaseFee != nil {
|
||||
if bfLen := h.BaseFee.BitLen(); bfLen > 256 {
|
||||
return fmt.Errorf("too large base fee: bitlen %d", bfLen)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@@ -71,6 +71,18 @@ type rlpLog struct {
|
||||
// rlpStorageLog is the storage encoding of a log.
|
||||
type rlpStorageLog rlpLog
|
||||
|
||||
// legacyRlpStorageLog is the previous storage encoding of a log including some redundant fields.
|
||||
type legacyRlpStorageLog struct {
|
||||
Address common.Address
|
||||
Topics []common.Hash
|
||||
Data []byte
|
||||
BlockNumber uint64
|
||||
TxHash common.Hash
|
||||
TxIndex uint
|
||||
BlockHash common.Hash
|
||||
Index uint
|
||||
}
|
||||
|
||||
// EncodeRLP implements rlp.Encoder.
|
||||
func (l *Log) EncodeRLP(w io.Writer) error {
|
||||
return rlp.Encode(w, rlpLog{Address: l.Address, Topics: l.Topics, Data: l.Data})
|
||||
@@ -103,10 +115,29 @@ func (l *LogForStorage) EncodeRLP(w io.Writer) error {
|
||||
//
|
||||
// Note some redundant fields(e.g. block number, tx hash etc) will be assembled later.
|
||||
func (l *LogForStorage) DecodeRLP(s *rlp.Stream) error {
|
||||
var dec rlpStorageLog
|
||||
if err := s.Decode(&dec); err != nil {
|
||||
blob, err := s.Raw()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*l = LogForStorage{Address: dec.Address, Topics: dec.Topics, Data: dec.Data}
|
||||
return nil
|
||||
var dec rlpStorageLog
|
||||
err = rlp.DecodeBytes(blob, &dec)
|
||||
if err == nil {
|
||||
*l = LogForStorage{
|
||||
Address: dec.Address,
|
||||
Topics: dec.Topics,
|
||||
Data: dec.Data,
|
||||
}
|
||||
} else {
|
||||
// Try to decode log with previous definition.
|
||||
var dec legacyRlpStorageLog
|
||||
err = rlp.DecodeBytes(blob, &dec)
|
||||
if err == nil {
|
||||
*l = LogForStorage{
|
||||
Address: dec.Address,
|
||||
Topics: dec.Topics,
|
||||
Data: dec.Data,
|
||||
}
|
||||
}
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
@@ -97,6 +97,27 @@ type storedReceiptRLP struct {
|
||||
Logs []*LogForStorage
|
||||
}
|
||||
|
||||
// v4StoredReceiptRLP is the storage encoding of a receipt used in database version 4.
|
||||
type v4StoredReceiptRLP struct {
|
||||
PostStateOrStatus []byte
|
||||
CumulativeGasUsed uint64
|
||||
TxHash common.Hash
|
||||
ContractAddress common.Address
|
||||
Logs []*LogForStorage
|
||||
GasUsed uint64
|
||||
}
|
||||
|
||||
// v3StoredReceiptRLP is the original storage encoding of a receipt including some unnecessary fields.
|
||||
type v3StoredReceiptRLP struct {
|
||||
PostStateOrStatus []byte
|
||||
CumulativeGasUsed uint64
|
||||
Bloom Bloom
|
||||
TxHash common.Hash
|
||||
ContractAddress common.Address
|
||||
Logs []*LogForStorage
|
||||
GasUsed uint64
|
||||
}
|
||||
|
||||
// NewReceipt creates a barebone transaction receipt, copying the init fields.
|
||||
// Deprecated: create receipts using a struct literal instead.
|
||||
func NewReceipt(root []byte, failed bool, cumulativeGasUsed uint64) *Receipt {
|
||||
@@ -212,7 +233,8 @@ func (r *Receipt) Size() common.StorageSize {
|
||||
// entire content of a receipt, as opposed to only the consensus fields originally.
|
||||
type ReceiptForStorage Receipt
|
||||
|
||||
// EncodeRLP implements rlp.Encoder.
|
||||
// EncodeRLP implements rlp.Encoder, and flattens all content fields of a receipt
|
||||
// into an RLP stream.
|
||||
func (r *ReceiptForStorage) EncodeRLP(w io.Writer) error {
|
||||
enc := &storedReceiptRLP{
|
||||
PostStateOrStatus: (*Receipt)(r).statusEncoding(),
|
||||
@@ -225,10 +247,29 @@ func (r *ReceiptForStorage) EncodeRLP(w io.Writer) error {
|
||||
return rlp.Encode(w, enc)
|
||||
}
|
||||
|
||||
// DecodeRLP implements rlp.Decoder.
|
||||
// DecodeRLP implements rlp.Decoder, and loads both consensus and implementation
|
||||
// fields of a receipt from an RLP stream.
|
||||
func (r *ReceiptForStorage) DecodeRLP(s *rlp.Stream) error {
|
||||
// Retrieve the entire receipt blob as we need to try multiple decoders
|
||||
blob, err := s.Raw()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// Try decoding from the newest format for future proofness, then the older one
|
||||
// for old nodes that just upgraded. V4 was an intermediate unreleased format so
|
||||
// we do need to decode it, but it's not common (try last).
|
||||
if err := decodeStoredReceiptRLP(r, blob); err == nil {
|
||||
return nil
|
||||
}
|
||||
if err := decodeV3StoredReceiptRLP(r, blob); err == nil {
|
||||
return nil
|
||||
}
|
||||
return decodeV4StoredReceiptRLP(r, blob)
|
||||
}
|
||||
|
||||
func decodeStoredReceiptRLP(r *ReceiptForStorage, blob []byte) error {
|
||||
var stored storedReceiptRLP
|
||||
if err := s.Decode(&stored); err != nil {
|
||||
if err := rlp.DecodeBytes(blob, &stored); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := (*Receipt)(r).setStatus(stored.PostStateOrStatus); err != nil {
|
||||
@@ -240,6 +281,48 @@ func (r *ReceiptForStorage) DecodeRLP(s *rlp.Stream) error {
|
||||
r.Logs[i] = (*Log)(log)
|
||||
}
|
||||
r.Bloom = CreateBloom(Receipts{(*Receipt)(r)})
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func decodeV4StoredReceiptRLP(r *ReceiptForStorage, blob []byte) error {
|
||||
var stored v4StoredReceiptRLP
|
||||
if err := rlp.DecodeBytes(blob, &stored); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := (*Receipt)(r).setStatus(stored.PostStateOrStatus); err != nil {
|
||||
return err
|
||||
}
|
||||
r.CumulativeGasUsed = stored.CumulativeGasUsed
|
||||
r.TxHash = stored.TxHash
|
||||
r.ContractAddress = stored.ContractAddress
|
||||
r.GasUsed = stored.GasUsed
|
||||
r.Logs = make([]*Log, len(stored.Logs))
|
||||
for i, log := range stored.Logs {
|
||||
r.Logs[i] = (*Log)(log)
|
||||
}
|
||||
r.Bloom = CreateBloom(Receipts{(*Receipt)(r)})
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func decodeV3StoredReceiptRLP(r *ReceiptForStorage, blob []byte) error {
|
||||
var stored v3StoredReceiptRLP
|
||||
if err := rlp.DecodeBytes(blob, &stored); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := (*Receipt)(r).setStatus(stored.PostStateOrStatus); err != nil {
|
||||
return err
|
||||
}
|
||||
r.CumulativeGasUsed = stored.CumulativeGasUsed
|
||||
r.Bloom = stored.Bloom
|
||||
r.TxHash = stored.TxHash
|
||||
r.ContractAddress = stored.ContractAddress
|
||||
r.GasUsed = stored.GasUsed
|
||||
r.Logs = make([]*Log, len(stored.Logs))
|
||||
for i, log := range stored.Logs {
|
||||
r.Logs[i] = (*Log)(log)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@@ -20,6 +20,7 @@ import (
|
||||
"bytes"
|
||||
"math"
|
||||
"math/big"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
@@ -37,6 +38,128 @@ func TestDecodeEmptyTypedReceipt(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestLegacyReceiptDecoding(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
encode func(*Receipt) ([]byte, error)
|
||||
}{
|
||||
{
|
||||
"StoredReceiptRLP",
|
||||
encodeAsStoredReceiptRLP,
|
||||
},
|
||||
{
|
||||
"V4StoredReceiptRLP",
|
||||
encodeAsV4StoredReceiptRLP,
|
||||
},
|
||||
{
|
||||
"V3StoredReceiptRLP",
|
||||
encodeAsV3StoredReceiptRLP,
|
||||
},
|
||||
}
|
||||
|
||||
tx := NewTransaction(1, common.HexToAddress("0x1"), big.NewInt(1), 1, big.NewInt(1), nil)
|
||||
receipt := &Receipt{
|
||||
Status: ReceiptStatusFailed,
|
||||
CumulativeGasUsed: 1,
|
||||
Logs: []*Log{
|
||||
{
|
||||
Address: common.BytesToAddress([]byte{0x11}),
|
||||
Topics: []common.Hash{common.HexToHash("dead"), common.HexToHash("beef")},
|
||||
Data: []byte{0x01, 0x00, 0xff},
|
||||
},
|
||||
{
|
||||
Address: common.BytesToAddress([]byte{0x01, 0x11}),
|
||||
Topics: []common.Hash{common.HexToHash("dead"), common.HexToHash("beef")},
|
||||
Data: []byte{0x01, 0x00, 0xff},
|
||||
},
|
||||
},
|
||||
TxHash: tx.Hash(),
|
||||
ContractAddress: common.BytesToAddress([]byte{0x01, 0x11, 0x11}),
|
||||
GasUsed: 111111,
|
||||
}
|
||||
receipt.Bloom = CreateBloom(Receipts{receipt})
|
||||
|
||||
for _, tc := range tests {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
enc, err := tc.encode(receipt)
|
||||
if err != nil {
|
||||
t.Fatalf("Error encoding receipt: %v", err)
|
||||
}
|
||||
var dec ReceiptForStorage
|
||||
if err := rlp.DecodeBytes(enc, &dec); err != nil {
|
||||
t.Fatalf("Error decoding RLP receipt: %v", err)
|
||||
}
|
||||
// Check whether all consensus fields are correct.
|
||||
if dec.Status != receipt.Status {
|
||||
t.Fatalf("Receipt status mismatch, want %v, have %v", receipt.Status, dec.Status)
|
||||
}
|
||||
if dec.CumulativeGasUsed != receipt.CumulativeGasUsed {
|
||||
t.Fatalf("Receipt CumulativeGasUsed mismatch, want %v, have %v", receipt.CumulativeGasUsed, dec.CumulativeGasUsed)
|
||||
}
|
||||
if dec.Bloom != receipt.Bloom {
|
||||
t.Fatalf("Bloom data mismatch, want %v, have %v", receipt.Bloom, dec.Bloom)
|
||||
}
|
||||
if len(dec.Logs) != len(receipt.Logs) {
|
||||
t.Fatalf("Receipt log number mismatch, want %v, have %v", len(receipt.Logs), len(dec.Logs))
|
||||
}
|
||||
for i := 0; i < len(dec.Logs); i++ {
|
||||
if dec.Logs[i].Address != receipt.Logs[i].Address {
|
||||
t.Fatalf("Receipt log %d address mismatch, want %v, have %v", i, receipt.Logs[i].Address, dec.Logs[i].Address)
|
||||
}
|
||||
if !reflect.DeepEqual(dec.Logs[i].Topics, receipt.Logs[i].Topics) {
|
||||
t.Fatalf("Receipt log %d topics mismatch, want %v, have %v", i, receipt.Logs[i].Topics, dec.Logs[i].Topics)
|
||||
}
|
||||
if !bytes.Equal(dec.Logs[i].Data, receipt.Logs[i].Data) {
|
||||
t.Fatalf("Receipt log %d data mismatch, want %v, have %v", i, receipt.Logs[i].Data, dec.Logs[i].Data)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func encodeAsStoredReceiptRLP(want *Receipt) ([]byte, error) {
|
||||
stored := &storedReceiptRLP{
|
||||
PostStateOrStatus: want.statusEncoding(),
|
||||
CumulativeGasUsed: want.CumulativeGasUsed,
|
||||
Logs: make([]*LogForStorage, len(want.Logs)),
|
||||
}
|
||||
for i, log := range want.Logs {
|
||||
stored.Logs[i] = (*LogForStorage)(log)
|
||||
}
|
||||
return rlp.EncodeToBytes(stored)
|
||||
}
|
||||
|
||||
func encodeAsV4StoredReceiptRLP(want *Receipt) ([]byte, error) {
|
||||
stored := &v4StoredReceiptRLP{
|
||||
PostStateOrStatus: want.statusEncoding(),
|
||||
CumulativeGasUsed: want.CumulativeGasUsed,
|
||||
TxHash: want.TxHash,
|
||||
ContractAddress: want.ContractAddress,
|
||||
Logs: make([]*LogForStorage, len(want.Logs)),
|
||||
GasUsed: want.GasUsed,
|
||||
}
|
||||
for i, log := range want.Logs {
|
||||
stored.Logs[i] = (*LogForStorage)(log)
|
||||
}
|
||||
return rlp.EncodeToBytes(stored)
|
||||
}
|
||||
|
||||
func encodeAsV3StoredReceiptRLP(want *Receipt) ([]byte, error) {
|
||||
stored := &v3StoredReceiptRLP{
|
||||
PostStateOrStatus: want.statusEncoding(),
|
||||
CumulativeGasUsed: want.CumulativeGasUsed,
|
||||
Bloom: want.Bloom,
|
||||
TxHash: want.TxHash,
|
||||
ContractAddress: want.ContractAddress,
|
||||
Logs: make([]*LogForStorage, len(want.Logs)),
|
||||
GasUsed: want.GasUsed,
|
||||
}
|
||||
for i, log := range want.Logs {
|
||||
stored.Logs[i] = (*LogForStorage)(log)
|
||||
}
|
||||
return rlp.EncodeToBytes(stored)
|
||||
}
|
||||
|
||||
// Tests that receipt data can be correctly derived from the contextual infos
|
||||
func TestDeriveFields(t *testing.T) {
|
||||
// Create a few transactions to have receipts for
|
||||
|
@@ -67,7 +67,7 @@ func NewTx(inner TxData) *Transaction {
|
||||
|
||||
// TxData is the underlying data of a transaction.
|
||||
//
|
||||
// This is implemented by LegacyTx and AccessListTx.
|
||||
// This is implemented by DynamicFeeTx, LegacyTx and AccessListTx.
|
||||
type TxData interface {
|
||||
txType() byte // returns the type ID
|
||||
copy() TxData // creates a deep copy and initializes all fields
|
||||
@@ -579,10 +579,10 @@ type Message struct {
|
||||
gasTipCap *big.Int
|
||||
data []byte
|
||||
accessList AccessList
|
||||
checkNonce bool
|
||||
isFake bool
|
||||
}
|
||||
|
||||
func NewMessage(from common.Address, to *common.Address, nonce uint64, amount *big.Int, gasLimit uint64, gasPrice, gasFeeCap, gasTipCap *big.Int, data []byte, accessList AccessList, checkNonce bool) Message {
|
||||
func NewMessage(from common.Address, to *common.Address, nonce uint64, amount *big.Int, gasLimit uint64, gasPrice, gasFeeCap, gasTipCap *big.Int, data []byte, accessList AccessList, isFake bool) Message {
|
||||
return Message{
|
||||
from: from,
|
||||
to: to,
|
||||
@@ -594,7 +594,7 @@ func NewMessage(from common.Address, to *common.Address, nonce uint64, amount *b
|
||||
gasTipCap: gasTipCap,
|
||||
data: data,
|
||||
accessList: accessList,
|
||||
checkNonce: checkNonce,
|
||||
isFake: isFake,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -610,7 +610,7 @@ func (tx *Transaction) AsMessage(s Signer, baseFee *big.Int) (Message, error) {
|
||||
amount: tx.Value(),
|
||||
data: tx.Data(),
|
||||
accessList: tx.AccessList(),
|
||||
checkNonce: true,
|
||||
isFake: false,
|
||||
}
|
||||
// If baseFee provided, set gasPrice to effectiveGasPrice.
|
||||
if baseFee != nil {
|
||||
@@ -631,4 +631,4 @@ func (m Message) Gas() uint64 { return m.gasLimit }
|
||||
func (m Message) Nonce() uint64 { return m.nonce }
|
||||
func (m Message) Data() []byte { return m.data }
|
||||
func (m Message) AccessList() AccessList { return m.accessList }
|
||||
func (m Message) CheckNonce() bool { return m.checkNonce }
|
||||
func (m Message) IsFake() bool { return m.isFake }
|
||||
|
@@ -96,7 +96,7 @@ func (al accessList) equal(other accessList) bool {
|
||||
func (al accessList) accessList() types.AccessList {
|
||||
acl := make(types.AccessList, 0, len(al))
|
||||
for addr, slots := range al {
|
||||
tuple := types.AccessTuple{Address: addr}
|
||||
tuple := types.AccessTuple{Address: addr, StorageKeys: []common.Hash{}}
|
||||
for slot := range slots {
|
||||
tuple.StorageKeys = append(tuple.StorageKeys, slot)
|
||||
}
|
||||
|
@@ -16,17 +16,49 @@
|
||||
|
||||
package vm
|
||||
|
||||
const (
|
||||
set2BitsMask = uint16(0b1100_0000_0000_0000)
|
||||
set3BitsMask = uint16(0b1110_0000_0000_0000)
|
||||
set4BitsMask = uint16(0b1111_0000_0000_0000)
|
||||
set5BitsMask = uint16(0b1111_1000_0000_0000)
|
||||
set6BitsMask = uint16(0b1111_1100_0000_0000)
|
||||
set7BitsMask = uint16(0b1111_1110_0000_0000)
|
||||
)
|
||||
|
||||
// bitvec is a bit vector which maps bytes in a program.
|
||||
// An unset bit means the byte is an opcode, a set bit means
|
||||
// it's data (i.e. argument of PUSHxx).
|
||||
type bitvec []byte
|
||||
|
||||
func (bits *bitvec) set(pos uint64) {
|
||||
(*bits)[pos/8] |= 0x80 >> (pos % 8)
|
||||
var lookup = [8]byte{
|
||||
0x80, 0x40, 0x20, 0x10, 0x8, 0x4, 0x2, 0x1,
|
||||
}
|
||||
func (bits *bitvec) set8(pos uint64) {
|
||||
(*bits)[pos/8] |= 0xFF >> (pos % 8)
|
||||
(*bits)[pos/8+1] |= ^(0xFF >> (pos % 8))
|
||||
|
||||
func (bits bitvec) set1(pos uint64) {
|
||||
bits[pos/8] |= lookup[pos%8]
|
||||
}
|
||||
|
||||
func (bits bitvec) setN(flag uint16, pos uint64) {
|
||||
a := flag >> (pos % 8)
|
||||
bits[pos/8] |= byte(a >> 8)
|
||||
if b := byte(a); b != 0 {
|
||||
// If the bit-setting affects the neighbouring byte, we can assign - no need to OR it,
|
||||
// since it's the first write to that byte
|
||||
bits[pos/8+1] = b
|
||||
}
|
||||
}
|
||||
|
||||
func (bits bitvec) set8(pos uint64) {
|
||||
a := byte(0xFF >> (pos % 8))
|
||||
bits[pos/8] |= a
|
||||
bits[pos/8+1] = ^a
|
||||
}
|
||||
|
||||
func (bits bitvec) set16(pos uint64) {
|
||||
a := byte(0xFF >> (pos % 8))
|
||||
bits[pos/8] |= a
|
||||
bits[pos/8+1] = 0xFF
|
||||
bits[pos/8+2] = ^a
|
||||
}
|
||||
|
||||
// codeSegment checks if the position is in a code segment.
|
||||
@@ -40,22 +72,52 @@ func codeBitmap(code []byte) bitvec {
|
||||
// ends with a PUSH32, the algorithm will push zeroes onto the
|
||||
// bitvector outside the bounds of the actual code.
|
||||
bits := make(bitvec, len(code)/8+1+4)
|
||||
return codeBitmapInternal(code, bits)
|
||||
}
|
||||
|
||||
// codeBitmapInternal is the internal implementation of codeBitmap.
|
||||
// It exists for the purpose of being able to run benchmark tests
|
||||
// without dynamic allocations affecting the results.
|
||||
func codeBitmapInternal(code, bits bitvec) bitvec {
|
||||
for pc := uint64(0); pc < uint64(len(code)); {
|
||||
op := OpCode(code[pc])
|
||||
|
||||
if op >= PUSH1 && op <= PUSH32 {
|
||||
numbits := op - PUSH1 + 1
|
||||
pc++
|
||||
pc++
|
||||
if op < PUSH1 || op > PUSH32 {
|
||||
continue
|
||||
}
|
||||
numbits := op - PUSH1 + 1
|
||||
if numbits >= 8 {
|
||||
for ; numbits >= 16; numbits -= 16 {
|
||||
bits.set16(pc)
|
||||
pc += 16
|
||||
}
|
||||
for ; numbits >= 8; numbits -= 8 {
|
||||
bits.set8(pc) // 8
|
||||
bits.set8(pc)
|
||||
pc += 8
|
||||
}
|
||||
for ; numbits > 0; numbits-- {
|
||||
bits.set(pc)
|
||||
pc++
|
||||
}
|
||||
} else {
|
||||
pc++
|
||||
}
|
||||
switch numbits {
|
||||
case 1:
|
||||
bits.set1(pc)
|
||||
pc += 1
|
||||
case 2:
|
||||
bits.setN(set2BitsMask, pc)
|
||||
pc += 2
|
||||
case 3:
|
||||
bits.setN(set3BitsMask, pc)
|
||||
pc += 3
|
||||
case 4:
|
||||
bits.setN(set4BitsMask, pc)
|
||||
pc += 4
|
||||
case 5:
|
||||
bits.setN(set5BitsMask, pc)
|
||||
pc += 5
|
||||
case 6:
|
||||
bits.setN(set6BitsMask, pc)
|
||||
pc += 6
|
||||
case 7:
|
||||
bits.setN(set7BitsMask, pc)
|
||||
pc += 7
|
||||
}
|
||||
}
|
||||
return bits
|
||||
|
@@ -47,10 +47,10 @@ func TestJumpDestAnalysis(t *testing.T) {
|
||||
{[]byte{byte(PUSH32)}, 0xFF, 1},
|
||||
{[]byte{byte(PUSH32)}, 0xFF, 2},
|
||||
}
|
||||
for _, test := range tests {
|
||||
for i, test := range tests {
|
||||
ret := codeBitmap(test.code)
|
||||
if ret[test.which] != test.exp {
|
||||
t.Fatalf("expected %x, got %02x", test.exp, ret[test.which])
|
||||
t.Fatalf("test %d: expected %x, got %02x", i, test.exp, ret[test.which])
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -73,3 +73,23 @@ func BenchmarkJumpdestHashing_1200k(bench *testing.B) {
|
||||
}
|
||||
bench.StopTimer()
|
||||
}
|
||||
|
||||
func BenchmarkJumpdestOpAnalysis(bench *testing.B) {
|
||||
var op OpCode
|
||||
bencher := func(b *testing.B) {
|
||||
code := make([]byte, 32*b.N)
|
||||
for i := range code {
|
||||
code[i] = byte(op)
|
||||
}
|
||||
bits := make(bitvec, len(code)/8+1+4)
|
||||
b.ResetTimer()
|
||||
codeBitmapInternal(code, bits)
|
||||
}
|
||||
for op = PUSH1; op <= PUSH32; op++ {
|
||||
bench.Run(op.String(), bencher)
|
||||
}
|
||||
op = JUMPDEST
|
||||
bench.Run(op.String(), bencher)
|
||||
op = STOP
|
||||
bench.Run(op.String(), bencher)
|
||||
}
|
||||
|
@@ -17,7 +17,6 @@
|
||||
package vm
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"math/big"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
@@ -58,24 +57,6 @@ func (evm *EVM) precompile(addr common.Address) (PrecompiledContract, bool) {
|
||||
return p, ok
|
||||
}
|
||||
|
||||
// run runs the given contract and takes care of running precompiles with a fallback to the byte code interpreter.
|
||||
func run(evm *EVM, contract *Contract, input []byte, readOnly bool) ([]byte, error) {
|
||||
for _, interpreter := range evm.interpreters {
|
||||
if interpreter.CanRun(contract.Code) {
|
||||
if evm.interpreter != interpreter {
|
||||
// Ensure that the interpreter pointer is set back
|
||||
// to its current value upon return.
|
||||
defer func(i Interpreter) {
|
||||
evm.interpreter = i
|
||||
}(evm.interpreter)
|
||||
evm.interpreter = interpreter
|
||||
}
|
||||
return interpreter.Run(contract, input, readOnly)
|
||||
}
|
||||
}
|
||||
return nil, errors.New("no compatible interpreter")
|
||||
}
|
||||
|
||||
// BlockContext provides the EVM with auxiliary information. Once provided
|
||||
// it shouldn't be modified.
|
||||
type BlockContext struct {
|
||||
@@ -131,8 +112,7 @@ type EVM struct {
|
||||
Config Config
|
||||
// global (to this context) ethereum virtual machine
|
||||
// used throughout the execution of the tx.
|
||||
interpreters []Interpreter
|
||||
interpreter Interpreter
|
||||
interpreter *EVMInterpreter
|
||||
// abort is used to abort the EVM calling operations
|
||||
// NOTE: must be set atomically
|
||||
abort int32
|
||||
@@ -146,36 +126,14 @@ type EVM struct {
|
||||
// only ever be used *once*.
|
||||
func NewEVM(blockCtx BlockContext, txCtx TxContext, statedb StateDB, chainConfig *params.ChainConfig, config Config) *EVM {
|
||||
evm := &EVM{
|
||||
Context: blockCtx,
|
||||
TxContext: txCtx,
|
||||
StateDB: statedb,
|
||||
Config: config,
|
||||
chainConfig: chainConfig,
|
||||
chainRules: chainConfig.Rules(blockCtx.BlockNumber),
|
||||
interpreters: make([]Interpreter, 0, 1),
|
||||
Context: blockCtx,
|
||||
TxContext: txCtx,
|
||||
StateDB: statedb,
|
||||
Config: config,
|
||||
chainConfig: chainConfig,
|
||||
chainRules: chainConfig.Rules(blockCtx.BlockNumber),
|
||||
}
|
||||
|
||||
if chainConfig.IsEWASM(blockCtx.BlockNumber) {
|
||||
// to be implemented by EVM-C and Wagon PRs.
|
||||
// if vmConfig.EWASMInterpreter != "" {
|
||||
// extIntOpts := strings.Split(vmConfig.EWASMInterpreter, ":")
|
||||
// path := extIntOpts[0]
|
||||
// options := []string{}
|
||||
// if len(extIntOpts) > 1 {
|
||||
// options = extIntOpts[1..]
|
||||
// }
|
||||
// evm.interpreters = append(evm.interpreters, NewEVMVCInterpreter(evm, vmConfig, options))
|
||||
// } else {
|
||||
// evm.interpreters = append(evm.interpreters, NewEWASMInterpreter(evm, vmConfig))
|
||||
// }
|
||||
panic("No supported ewasm interpreter yet.")
|
||||
}
|
||||
|
||||
// vmConfig.EVMInterpreter will be used by EVM-C, it won't be checked here
|
||||
// as we always want to have the built-in EVM as the failover option.
|
||||
evm.interpreters = append(evm.interpreters, NewEVMInterpreter(evm, config))
|
||||
evm.interpreter = evm.interpreters[0]
|
||||
|
||||
evm.interpreter = NewEVMInterpreter(evm, config)
|
||||
return evm
|
||||
}
|
||||
|
||||
@@ -198,7 +156,7 @@ func (evm *EVM) Cancelled() bool {
|
||||
}
|
||||
|
||||
// Interpreter returns the current interpreter
|
||||
func (evm *EVM) Interpreter() Interpreter {
|
||||
func (evm *EVM) Interpreter() *EVMInterpreter {
|
||||
return evm.interpreter
|
||||
}
|
||||
|
||||
@@ -256,7 +214,7 @@ func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas
|
||||
// The depth-check is already done, and precompiles handled above
|
||||
contract := NewContract(caller, AccountRef(addrCopy), value, gas)
|
||||
contract.SetCallCode(&addrCopy, evm.StateDB.GetCodeHash(addrCopy), code)
|
||||
ret, err = run(evm, contract, input, false)
|
||||
ret, err = evm.interpreter.Run(contract, input, false)
|
||||
gas = contract.Gas
|
||||
}
|
||||
}
|
||||
@@ -308,7 +266,7 @@ func (evm *EVM) CallCode(caller ContractRef, addr common.Address, input []byte,
|
||||
// The contract is a scoped environment for this execution context only.
|
||||
contract := NewContract(caller, AccountRef(caller.Address()), value, gas)
|
||||
contract.SetCallCode(&addrCopy, evm.StateDB.GetCodeHash(addrCopy), evm.StateDB.GetCode(addrCopy))
|
||||
ret, err = run(evm, contract, input, false)
|
||||
ret, err = evm.interpreter.Run(contract, input, false)
|
||||
gas = contract.Gas
|
||||
}
|
||||
if err != nil {
|
||||
@@ -343,7 +301,7 @@ func (evm *EVM) DelegateCall(caller ContractRef, addr common.Address, input []by
|
||||
// Initialise a new contract and make initialise the delegate values
|
||||
contract := NewContract(caller, AccountRef(caller.Address()), nil, gas).AsDelegate()
|
||||
contract.SetCallCode(&addrCopy, evm.StateDB.GetCodeHash(addrCopy), evm.StateDB.GetCode(addrCopy))
|
||||
ret, err = run(evm, contract, input, false)
|
||||
ret, err = evm.interpreter.Run(contract, input, false)
|
||||
gas = contract.Gas
|
||||
}
|
||||
if err != nil {
|
||||
@@ -394,7 +352,7 @@ func (evm *EVM) StaticCall(caller ContractRef, addr common.Address, input []byte
|
||||
// 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
|
||||
// when we're in Homestead this also counts for code storage gas errors.
|
||||
ret, err = run(evm, contract, input, true)
|
||||
ret, err = evm.interpreter.Run(contract, input, true)
|
||||
gas = contract.Gas
|
||||
}
|
||||
if err != nil {
|
||||
@@ -462,7 +420,7 @@ func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64,
|
||||
}
|
||||
start := time.Now()
|
||||
|
||||
ret, err := run(evm, contract, nil, false)
|
||||
ret, err := evm.interpreter.Run(contract, nil, false)
|
||||
|
||||
// Check whether the max code size has been exceeded, assign err if the case.
|
||||
if err == nil && evm.chainRules.IsEIP158 && len(ret) > params.MaxCodeSize {
|
||||
|
@@ -4,11 +4,11 @@ package vm
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"math/big"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||
"github.com/ethereum/go-ethereum/common/math"
|
||||
"github.com/holiman/uint256"
|
||||
)
|
||||
|
||||
var _ = (*structLogMarshaling)(nil)
|
||||
@@ -22,8 +22,7 @@ func (s StructLog) MarshalJSON() ([]byte, error) {
|
||||
GasCost math.HexOrDecimal64 `json:"gasCost"`
|
||||
Memory hexutil.Bytes `json:"memory"`
|
||||
MemorySize int `json:"memSize"`
|
||||
Stack []*math.HexOrDecimal256 `json:"stack"`
|
||||
ReturnStack []math.HexOrDecimal64 `json:"returnStack"`
|
||||
Stack []uint256.Int `json:"stack"`
|
||||
ReturnData hexutil.Bytes `json:"returnData"`
|
||||
Storage map[common.Hash]common.Hash `json:"-"`
|
||||
Depth int `json:"depth"`
|
||||
@@ -39,12 +38,7 @@ func (s StructLog) MarshalJSON() ([]byte, error) {
|
||||
enc.GasCost = math.HexOrDecimal64(s.GasCost)
|
||||
enc.Memory = s.Memory
|
||||
enc.MemorySize = s.MemorySize
|
||||
if s.Stack != nil {
|
||||
enc.Stack = make([]*math.HexOrDecimal256, len(s.Stack))
|
||||
for k, v := range s.Stack {
|
||||
enc.Stack[k] = (*math.HexOrDecimal256)(v)
|
||||
}
|
||||
}
|
||||
enc.Stack = s.Stack
|
||||
enc.ReturnData = s.ReturnData
|
||||
enc.Storage = s.Storage
|
||||
enc.Depth = s.Depth
|
||||
@@ -64,7 +58,7 @@ func (s *StructLog) UnmarshalJSON(input []byte) error {
|
||||
GasCost *math.HexOrDecimal64 `json:"gasCost"`
|
||||
Memory *hexutil.Bytes `json:"memory"`
|
||||
MemorySize *int `json:"memSize"`
|
||||
Stack []*math.HexOrDecimal256 `json:"stack"`
|
||||
Stack []uint256.Int `json:"stack"`
|
||||
ReturnData *hexutil.Bytes `json:"returnData"`
|
||||
Storage map[common.Hash]common.Hash `json:"-"`
|
||||
Depth *int `json:"depth"`
|
||||
@@ -94,10 +88,7 @@ func (s *StructLog) UnmarshalJSON(input []byte) error {
|
||||
s.MemorySize = *dec.MemorySize
|
||||
}
|
||||
if dec.Stack != nil {
|
||||
s.Stack = make([]*big.Int, len(dec.Stack))
|
||||
for k, v := range dec.Stack {
|
||||
s.Stack[k] = (*big.Int)(v)
|
||||
}
|
||||
s.Stack = dec.Stack
|
||||
}
|
||||
if dec.ReturnData != nil {
|
||||
s.ReturnData = *dec.ReturnData
|
||||
|
@@ -669,6 +669,7 @@ func opCall(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byt
|
||||
}
|
||||
stack.push(&temp)
|
||||
if err == nil || err == ErrExecutionReverted {
|
||||
ret = common.CopyBytes(ret)
|
||||
scope.Memory.Set(retOffset.Uint64(), retSize.Uint64(), ret)
|
||||
}
|
||||
scope.Contract.Gas += returnGas
|
||||
@@ -703,6 +704,7 @@ func opCallCode(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([
|
||||
}
|
||||
stack.push(&temp)
|
||||
if err == nil || err == ErrExecutionReverted {
|
||||
ret = common.CopyBytes(ret)
|
||||
scope.Memory.Set(retOffset.Uint64(), retSize.Uint64(), ret)
|
||||
}
|
||||
scope.Contract.Gas += returnGas
|
||||
@@ -730,6 +732,7 @@ func opDelegateCall(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext
|
||||
}
|
||||
stack.push(&temp)
|
||||
if err == nil || err == ErrExecutionReverted {
|
||||
ret = common.CopyBytes(ret)
|
||||
scope.Memory.Set(retOffset.Uint64(), retSize.Uint64(), ret)
|
||||
}
|
||||
scope.Contract.Gas += returnGas
|
||||
@@ -757,6 +760,7 @@ func opStaticCall(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext)
|
||||
}
|
||||
stack.push(&temp)
|
||||
if err == nil || err == ErrExecutionReverted {
|
||||
ret = common.CopyBytes(ret)
|
||||
scope.Memory.Set(retOffset.Uint64(), retSize.Uint64(), ret)
|
||||
}
|
||||
scope.Contract.Gas += returnGas
|
||||
|
@@ -96,7 +96,7 @@ func testTwoOperandOp(t *testing.T, tests []TwoOperandTestcase, opFn executionFu
|
||||
env = NewEVM(BlockContext{}, TxContext{}, nil, params.TestChainConfig, Config{})
|
||||
stack = newstack()
|
||||
pc = uint64(0)
|
||||
evmInterpreter = env.interpreter.(*EVMInterpreter)
|
||||
evmInterpreter = env.interpreter
|
||||
)
|
||||
|
||||
for i, test := range tests {
|
||||
@@ -234,7 +234,7 @@ func getResult(args []*twoOperandParams, opFn executionFunc) []TwoOperandTestcas
|
||||
env = NewEVM(BlockContext{}, TxContext{}, nil, params.TestChainConfig, Config{})
|
||||
stack = newstack()
|
||||
pc = uint64(0)
|
||||
interpreter = env.interpreter.(*EVMInterpreter)
|
||||
interpreter = env.interpreter
|
||||
)
|
||||
result := make([]TwoOperandTestcase, len(args))
|
||||
for i, param := range args {
|
||||
|
@@ -35,34 +35,9 @@ type Config struct {
|
||||
|
||||
JumpTable [256]*operation // EVM instruction table, automatically populated if unset
|
||||
|
||||
EWASMInterpreter string // External EWASM interpreter options
|
||||
EVMInterpreter string // External EVM interpreter options
|
||||
|
||||
ExtraEips []int // Additional EIPS that are to be enabled
|
||||
}
|
||||
|
||||
// Interpreter is used to run Ethereum based contracts and will utilise the
|
||||
// passed environment to query external sources for state information.
|
||||
// The Interpreter will run the byte code VM based on the passed
|
||||
// configuration.
|
||||
type Interpreter interface {
|
||||
// Run loops and evaluates the contract's code with the given input data and returns
|
||||
// the return byte-slice and an error if one occurred.
|
||||
Run(contract *Contract, input []byte, static bool) ([]byte, error)
|
||||
// CanRun tells if the contract, passed as an argument, can be
|
||||
// run by the current interpreter. This is meant so that the
|
||||
// caller can do something like:
|
||||
//
|
||||
// ```golang
|
||||
// for _, interpreter := range interpreters {
|
||||
// if interpreter.CanRun(contract.code) {
|
||||
// interpreter.Run(contract.code, input)
|
||||
// }
|
||||
// }
|
||||
// ```
|
||||
CanRun([]byte) bool
|
||||
}
|
||||
|
||||
// ScopeContext contains the things that are per-call, such as stack and memory,
|
||||
// but not transients like pc and gas
|
||||
type ScopeContext struct {
|
||||
@@ -287,7 +262,7 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) (
|
||||
// if the operation clears the return data (e.g. it has returning data)
|
||||
// set the last return to the result of the operation.
|
||||
if operation.returns {
|
||||
in.returnData = common.CopyBytes(res)
|
||||
in.returnData = res
|
||||
}
|
||||
|
||||
switch {
|
||||
@@ -303,9 +278,3 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) (
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// CanRun tells if the contract, passed as an argument, can be
|
||||
// run by the current interpreter.
|
||||
func (in *EVMInterpreter) CanRun(code []byte) bool {
|
||||
return true
|
||||
}
|
||||
|
@@ -29,6 +29,7 @@ import (
|
||||
"github.com/ethereum/go-ethereum/common/math"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
"github.com/holiman/uint256"
|
||||
)
|
||||
|
||||
// Storage represents a contract's storage.
|
||||
@@ -66,7 +67,7 @@ type StructLog struct {
|
||||
GasCost uint64 `json:"gasCost"`
|
||||
Memory []byte `json:"memory"`
|
||||
MemorySize int `json:"memSize"`
|
||||
Stack []*big.Int `json:"stack"`
|
||||
Stack []uint256.Int `json:"stack"`
|
||||
ReturnData []byte `json:"returnData"`
|
||||
Storage map[common.Hash]common.Hash `json:"-"`
|
||||
Depth int `json:"depth"`
|
||||
@@ -76,7 +77,6 @@ type StructLog struct {
|
||||
|
||||
// overrides for gencodec
|
||||
type structLogMarshaling struct {
|
||||
Stack []*math.HexOrDecimal256
|
||||
Gas math.HexOrDecimal64
|
||||
GasCost math.HexOrDecimal64
|
||||
Memory hexutil.Bytes
|
||||
@@ -135,6 +135,14 @@ func NewStructLogger(cfg *LogConfig) *StructLogger {
|
||||
return logger
|
||||
}
|
||||
|
||||
// Reset clears the data held by the logger.
|
||||
func (l *StructLogger) Reset() {
|
||||
l.storage = make(map[common.Address]Storage)
|
||||
l.output = make([]byte, 0)
|
||||
l.logs = l.logs[:0]
|
||||
l.err = nil
|
||||
}
|
||||
|
||||
// CaptureStart implements the Tracer interface to initialize the tracing operation.
|
||||
func (l *StructLogger) CaptureStart(env *EVM, from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int) {
|
||||
}
|
||||
@@ -157,16 +165,16 @@ func (l *StructLogger) CaptureState(env *EVM, pc uint64, op OpCode, gas, cost ui
|
||||
copy(mem, memory.Data())
|
||||
}
|
||||
// Copy a snapshot of the current stack state to a new buffer
|
||||
var stck []*big.Int
|
||||
var stck []uint256.Int
|
||||
if !l.cfg.DisableStack {
|
||||
stck = make([]*big.Int, len(stack.Data()))
|
||||
stck = make([]uint256.Int, len(stack.Data()))
|
||||
for i, item := range stack.Data() {
|
||||
stck[i] = new(big.Int).Set(item.ToBig())
|
||||
stck[i] = item
|
||||
}
|
||||
}
|
||||
// Copy a snapshot of the current storage to a new container
|
||||
var storage Storage
|
||||
if !l.cfg.DisableStorage {
|
||||
if !l.cfg.DisableStorage && (op == SLOAD || op == SSTORE) {
|
||||
// initialise new changed values storage container for this contract
|
||||
// if not present.
|
||||
if l.storage[contract.Address()] == nil {
|
||||
@@ -179,16 +187,16 @@ func (l *StructLogger) CaptureState(env *EVM, pc uint64, op OpCode, gas, cost ui
|
||||
value = env.StateDB.GetState(contract.Address(), address)
|
||||
)
|
||||
l.storage[contract.Address()][address] = value
|
||||
}
|
||||
// capture SSTORE opcodes and record the written entry in the local storage.
|
||||
if op == SSTORE && stack.len() >= 2 {
|
||||
storage = l.storage[contract.Address()].Copy()
|
||||
} else if op == SSTORE && stack.len() >= 2 {
|
||||
// capture SSTORE opcodes and record the written entry in the local storage.
|
||||
var (
|
||||
value = common.Hash(stack.data[stack.len()-2].Bytes32())
|
||||
address = common.Hash(stack.data[stack.len()-1].Bytes32())
|
||||
)
|
||||
l.storage[contract.Address()][address] = value
|
||||
storage = l.storage[contract.Address()].Copy()
|
||||
}
|
||||
storage = l.storage[contract.Address()].Copy()
|
||||
}
|
||||
var rdata []byte
|
||||
if !l.cfg.DisableReturnData {
|
||||
@@ -238,7 +246,7 @@ func WriteTrace(writer io.Writer, logs []StructLog) {
|
||||
if len(log.Stack) > 0 {
|
||||
fmt.Fprintln(writer, "Stack:")
|
||||
for i := len(log.Stack) - 1; i >= 0; i-- {
|
||||
fmt.Fprintf(writer, "%08d %x\n", len(log.Stack)-i-1, math.PaddedBigBytes(log.Stack[i], 32))
|
||||
fmt.Fprintf(writer, "%08d %s\n", len(log.Stack)-i-1, log.Stack[i].Hex())
|
||||
}
|
||||
}
|
||||
if len(log.Memory) > 0 {
|
||||
@@ -314,7 +322,7 @@ func (t *mdLogger) CaptureState(env *EVM, pc uint64, op OpCode, gas, cost uint64
|
||||
// format stack
|
||||
var a []string
|
||||
for _, elem := range stack.data {
|
||||
a = append(a, fmt.Sprintf("%v", elem.String()))
|
||||
a = append(a, elem.Hex())
|
||||
}
|
||||
b := fmt.Sprintf("[%v]", strings.Join(a, ","))
|
||||
fmt.Fprintf(t.out, "%10v |", b)
|
||||
|
@@ -57,7 +57,6 @@ func (l *JSONLogger) CaptureState(env *EVM, pc uint64, op OpCode, gas, cost uint
|
||||
Gas: gas,
|
||||
GasCost: cost,
|
||||
MemorySize: memory.Len(),
|
||||
Storage: nil,
|
||||
Depth: depth,
|
||||
RefundCounter: env.StateDB.GetRefund(),
|
||||
Err: err,
|
||||
@@ -66,12 +65,7 @@ func (l *JSONLogger) CaptureState(env *EVM, pc uint64, op OpCode, gas, cost uint
|
||||
log.Memory = memory.Data()
|
||||
}
|
||||
if !l.cfg.DisableStack {
|
||||
//TODO(@holiman) improve this
|
||||
logstack := make([]*big.Int, len(stack.Data()))
|
||||
for i, item := range stack.Data() {
|
||||
logstack[i] = item.ToBig()
|
||||
}
|
||||
log.Stack = logstack
|
||||
log.Stack = stack.data
|
||||
}
|
||||
if !l.cfg.DisableReturnData {
|
||||
log.ReturnData = rData
|
||||
|
@@ -30,7 +30,6 @@ type dummyContractRef struct {
|
||||
calledForEach bool
|
||||
}
|
||||
|
||||
func (dummyContractRef) ReturnGas(*big.Int) {}
|
||||
func (dummyContractRef) Address() common.Address { return common.Address{} }
|
||||
func (dummyContractRef) Value() *big.Int { return new(big.Int) }
|
||||
func (dummyContractRef) SetCode(common.Hash, []byte) {}
|
||||
|
@@ -2,7 +2,8 @@
|
||||
// Use of this source code is governed by a BSD-style license that can be found in
|
||||
// the LICENSE file.
|
||||
|
||||
// +build !gofuzz cgo
|
||||
// +build !gofuzz
|
||||
// +build cgo
|
||||
|
||||
package secp256k1
|
||||
|
||||
|
@@ -2,7 +2,8 @@
|
||||
// Use of this source code is governed by a BSD-style license that can be found in
|
||||
// the LICENSE file.
|
||||
|
||||
// +build !gofuzz cgo
|
||||
// +build !gofuzz
|
||||
// +build cgo
|
||||
|
||||
package secp256k1
|
||||
|
||||
|
@@ -2,7 +2,8 @@
|
||||
// Use of this source code is governed by a BSD-style license that can be found in
|
||||
// the LICENSE file.
|
||||
|
||||
// +build !gofuzz cgo
|
||||
// +build !gofuzz
|
||||
// +build cgo
|
||||
|
||||
// Package secp256k1 wraps the bitcoin secp256k1 C library.
|
||||
package secp256k1
|
||||
|
@@ -14,7 +14,7 @@
|
||||
// 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/>.
|
||||
|
||||
// +build !nacl,!js,cgo
|
||||
// +build !nacl,!js,cgo,!gofuzz
|
||||
|
||||
package crypto
|
||||
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user