mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-07-27 23:26:44 +00:00
Merge pull request #294 from maticnetwork/v0.2.13-candidate
v0.2.13 candidate
This commit is contained in:
commit
0978123079
16 changed files with 637 additions and 64 deletions
29
.github/workflows/dockerimage.yml
vendored
Normal file
29
.github/workflows/dockerimage.yml
vendored
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
name: Bor Docker Image CI
|
||||
|
||||
on:
|
||||
push:
|
||||
branches-ignore:
|
||||
- '**'
|
||||
tags:
|
||||
- 'v*.*.*'
|
||||
# to be used by fork patch-releases ^^
|
||||
- 'v*.*.*-*'
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Build the Bor Docker image
|
||||
env:
|
||||
DOCKERHUB: ${{ secrets.DOCKERHUB }}
|
||||
DOCKERHUB_KEY: ${{ secrets.DOCKERHUB_KEY }}
|
||||
run: |
|
||||
ls -l
|
||||
echo "Docker login"
|
||||
docker login -u $DOCKERHUB -p $DOCKERHUB_KEY
|
||||
echo "running build"
|
||||
docker build -f Dockerfile.classic -t maticnetwork/bor:${GITHUB_REF/refs\/tags\//} .
|
||||
echo "pushing image"
|
||||
docker push maticnetwork/bor:${GITHUB_REF/refs\/tags\//}
|
||||
echo "DONE!"
|
||||
18
Dockerfile.classic
Normal file
18
Dockerfile.classic
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
# Build Geth in a stock Go builder container
|
||||
FROM golang:1.17-alpine as builder
|
||||
|
||||
RUN apk add --no-cache make gcc musl-dev linux-headers git bash
|
||||
|
||||
ADD . /bor
|
||||
RUN cd /bor && make bor-all
|
||||
|
||||
CMD ["/bin/bash"]
|
||||
|
||||
# Pull Bor into a second stage deploy alpine container
|
||||
FROM alpine:latest
|
||||
|
||||
RUN apk add --no-cache ca-certificates
|
||||
COPY --from=builder /bor/build/bin/bor /usr/local/bin/
|
||||
COPY --from=builder /bor/build/bin/bootnode /usr/local/bin/
|
||||
|
||||
EXPOSE 8545 8546 8547 30303 30303/udp
|
||||
4
Makefile
4
Makefile
|
|
@ -50,8 +50,8 @@ ios:
|
|||
|
||||
test: all
|
||||
# $(GORUN) build/ci.go test
|
||||
go test github.com/ethereum/go-ethereum/consensus/bor
|
||||
go test github.com/ethereum/go-ethereum/tests/bor
|
||||
go test github.com/ethereum/go-ethereum/consensus/bor -v
|
||||
go test github.com/ethereum/go-ethereum/tests/bor -v
|
||||
|
||||
lint: ## Run linters.
|
||||
$(GORUN) build/ci.go lint
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
|
@ -123,7 +123,7 @@ var (
|
|||
type SignerFn func(accounts.Account, string, []byte) ([]byte, error)
|
||||
|
||||
// ecrecover extracts the Ethereum account address from a signed header.
|
||||
func ecrecover(header *types.Header, sigcache *lru.ARCCache) (common.Address, error) {
|
||||
func ecrecover(header *types.Header, sigcache *lru.ARCCache, c *params.BorConfig) (common.Address, error) {
|
||||
// If the signature's already cached, return that
|
||||
hash := header.Hash()
|
||||
if address, known := sigcache.Get(hash); known {
|
||||
|
|
@ -136,7 +136,7 @@ func ecrecover(header *types.Header, sigcache *lru.ARCCache) (common.Address, er
|
|||
signature := header.Extra[len(header.Extra)-extraSeal:]
|
||||
|
||||
// Recover the public key and the Ethereum address
|
||||
pubkey, err := crypto.Ecrecover(SealHash(header).Bytes(), signature)
|
||||
pubkey, err := crypto.Ecrecover(SealHash(header, c).Bytes(), signature)
|
||||
if err != nil {
|
||||
return common.Address{}, err
|
||||
}
|
||||
|
|
@ -148,15 +148,15 @@ func ecrecover(header *types.Header, sigcache *lru.ARCCache) (common.Address, er
|
|||
}
|
||||
|
||||
// SealHash returns the hash of a block prior to it being sealed.
|
||||
func SealHash(header *types.Header) (hash common.Hash) {
|
||||
func SealHash(header *types.Header, c *params.BorConfig) (hash common.Hash) {
|
||||
hasher := sha3.NewLegacyKeccak256()
|
||||
encodeSigHeader(hasher, header)
|
||||
encodeSigHeader(hasher, header, c)
|
||||
hasher.Sum(hash[:0])
|
||||
return hash
|
||||
}
|
||||
|
||||
func encodeSigHeader(w io.Writer, header *types.Header) {
|
||||
err := rlp.Encode(w, []interface{}{
|
||||
func encodeSigHeader(w io.Writer, header *types.Header, c *params.BorConfig) {
|
||||
enc := []interface{}{
|
||||
header.ParentHash,
|
||||
header.UncleHash,
|
||||
header.Coinbase,
|
||||
|
|
@ -172,8 +172,13 @@ func encodeSigHeader(w io.Writer, header *types.Header) {
|
|||
header.Extra[:len(header.Extra)-65], // Yes, this will panic if extra is too short
|
||||
header.MixDigest,
|
||||
header.Nonce,
|
||||
})
|
||||
if err != nil {
|
||||
}
|
||||
if c.IsJaipur(header.Number.Uint64()) {
|
||||
if header.BaseFee != nil {
|
||||
enc = append(enc, header.BaseFee)
|
||||
}
|
||||
}
|
||||
if err := rlp.Encode(w, enc); err != nil {
|
||||
panic("can't encode: " + err.Error())
|
||||
}
|
||||
}
|
||||
|
|
@ -182,12 +187,12 @@ func encodeSigHeader(w io.Writer, header *types.Header) {
|
|||
func CalcProducerDelay(number uint64, succession int, c *params.BorConfig) uint64 {
|
||||
// When the block is the first block of the sprint, it is expected to be delayed by `producerDelay`.
|
||||
// That is to allow time for block propagation in the last sprint
|
||||
delay := c.Period
|
||||
delay := c.CalculatePeriod(number)
|
||||
if number%c.Sprint == 0 {
|
||||
delay = c.ProducerDelay
|
||||
}
|
||||
if succession > 0 {
|
||||
delay += uint64(succession) * c.BackupMultiplier
|
||||
delay += uint64(succession) * c.CalculateBackupMultiplier(number)
|
||||
}
|
||||
return delay
|
||||
}
|
||||
|
|
@ -199,9 +204,9 @@ func CalcProducerDelay(number uint64, succession int, c *params.BorConfig) uint6
|
|||
// Note, the method requires the extra data to be at least 65 bytes, otherwise it
|
||||
// panics. This is done to avoid accidentally using both forms (signature present
|
||||
// or not), which could be abused to produce different hashes for the same header.
|
||||
func BorRLP(header *types.Header) []byte {
|
||||
func BorRLP(header *types.Header, c *params.BorConfig) []byte {
|
||||
b := new(bytes.Buffer)
|
||||
encodeSigHeader(b, header)
|
||||
encodeSigHeader(b, header, c)
|
||||
return b.Bytes()
|
||||
}
|
||||
|
||||
|
|
@ -280,7 +285,7 @@ func New(
|
|||
// Author implements consensus.Engine, returning the Ethereum address recovered
|
||||
// from the signature in the header's extra-data section.
|
||||
func (c *Bor) Author(header *types.Header) (common.Address, error) {
|
||||
return ecrecover(header, c.signatures)
|
||||
return ecrecover(header, c.signatures, c.config)
|
||||
}
|
||||
|
||||
// VerifyHeader checks whether a header conforms to the consensus rules.
|
||||
|
|
@ -353,6 +358,11 @@ func (c *Bor) verifyHeader(chain consensus.ChainHeaderReader, header *types.Head
|
|||
return errInvalidDifficulty
|
||||
}
|
||||
}
|
||||
// Verify that the gas limit is <= 2^63-1
|
||||
cap := uint64(0x7fffffffffffffff)
|
||||
if header.GasLimit > cap {
|
||||
return fmt.Errorf("invalid gasLimit: have %v, max %v", header.GasLimit, cap)
|
||||
}
|
||||
// If all checks passed, validate any special fields for hard forks
|
||||
if err := misc.VerifyForkHashes(chain.Config(), header, false); err != nil {
|
||||
return err
|
||||
|
|
@ -396,7 +406,24 @@ func (c *Bor) verifyCascadingFields(chain consensus.ChainHeaderReader, header *t
|
|||
return consensus.ErrUnknownAncestor
|
||||
}
|
||||
|
||||
if parent.Time+c.config.Period > header.Time {
|
||||
// Verify that the gasUsed is <= gasLimit
|
||||
if header.GasUsed > header.GasLimit {
|
||||
return fmt.Errorf("invalid gasUsed: have %d, gasLimit %d", header.GasUsed, header.GasLimit)
|
||||
}
|
||||
if !chain.Config().IsLondon(header.Number) {
|
||||
// Verify BaseFee not present before EIP-1559 fork.
|
||||
if header.BaseFee != nil {
|
||||
return fmt.Errorf("invalid baseFee before fork: have %d, want <nil>", header.BaseFee)
|
||||
}
|
||||
if err := misc.VerifyGaslimit(parent.GasLimit, header.GasLimit); err != nil {
|
||||
return err
|
||||
}
|
||||
} else if err := misc.VerifyEip1559Header(chain.Config(), parent, header); err != nil {
|
||||
// Verify the header's EIP-1559 attributes.
|
||||
return err
|
||||
}
|
||||
|
||||
if parent.Time+c.config.CalculatePeriod(number) > header.Time {
|
||||
return ErrInvalidTimestamp
|
||||
}
|
||||
|
||||
|
|
@ -556,7 +583,7 @@ func (c *Bor) verifySeal(chain consensus.ChainHeaderReader, header *types.Header
|
|||
}
|
||||
|
||||
// Resolve the authorization key and check against signers
|
||||
signer, err := ecrecover(header, c.signatures)
|
||||
signer, err := ecrecover(header, c.signatures, c.config)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
@ -791,7 +818,7 @@ func (c *Bor) Seal(chain consensus.ChainHeaderReader, block *types.Block, result
|
|||
return errUnknownBlock
|
||||
}
|
||||
// For 0-period chains, refuse to seal empty blocks (no reward but would spin sealing)
|
||||
if c.config.Period == 0 && len(block.Transactions()) == 0 {
|
||||
if c.config.CalculatePeriod(number) == 0 && len(block.Transactions()) == 0 {
|
||||
log.Info("Sealing paused, waiting for transactions")
|
||||
return nil
|
||||
}
|
||||
|
|
@ -819,10 +846,10 @@ func (c *Bor) Seal(chain consensus.ChainHeaderReader, block *types.Block, result
|
|||
// Sweet, the protocol permits us to sign the block, wait for our time
|
||||
delay := time.Unix(int64(header.Time), 0).Sub(time.Now()) // nolint: gosimple
|
||||
// wiggle was already accounted for in header.Time, this is just for logging
|
||||
wiggle := time.Duration(successionNumber) * time.Duration(c.config.BackupMultiplier) * time.Second
|
||||
wiggle := time.Duration(successionNumber) * time.Duration(c.config.CalculateBackupMultiplier(number)) * time.Second
|
||||
|
||||
// Sign all the things!
|
||||
sighash, err := signFn(accounts.Account{Address: signer}, accounts.MimetypeBor, BorRLP(header))
|
||||
sighash, err := signFn(accounts.Account{Address: signer}, accounts.MimetypeBor, BorRLP(header, c.config))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
@ -854,7 +881,7 @@ func (c *Bor) Seal(chain consensus.ChainHeaderReader, block *types.Block, result
|
|||
select {
|
||||
case results <- block.WithSeal(header):
|
||||
default:
|
||||
log.Warn("Sealing result was not read by miner", "number", number, "sealhash", SealHash(header))
|
||||
log.Warn("Sealing result was not read by miner", "number", number, "sealhash", SealHash(header, c.config))
|
||||
}
|
||||
}()
|
||||
return nil
|
||||
|
|
@ -873,7 +900,7 @@ func (c *Bor) CalcDifficulty(chain consensus.ChainHeaderReader, time uint64, par
|
|||
|
||||
// SealHash returns the hash of a block prior to it being sealed.
|
||||
func (c *Bor) SealHash(header *types.Header) common.Hash {
|
||||
return SealHash(header)
|
||||
return SealHash(header, c.config)
|
||||
}
|
||||
|
||||
// APIs implements consensus.Engine, returning the user facing RPC API to allow
|
||||
|
|
|
|||
|
|
@ -99,3 +99,39 @@ func TestGenesisContractChange(t *testing.T) {
|
|||
// make sure balance change DOES NOT take effect
|
||||
assert.Equal(t, statedb.GetBalance(addr0), big.NewInt(0))
|
||||
}
|
||||
|
||||
func TestEncodeSigHeaderJaipur(t *testing.T) {
|
||||
// As part of the EIP-1559 fork in mumbai, an incorrect seal hash
|
||||
// was used for Bor that did not included the BaseFee. The Jaipur
|
||||
// block is a hard fork to fix that.
|
||||
h := &types.Header{
|
||||
Difficulty: new(big.Int),
|
||||
Number: big.NewInt(1),
|
||||
Extra: make([]byte, 32+65),
|
||||
}
|
||||
|
||||
var (
|
||||
// hash for the block without the BaseFee
|
||||
hashWithoutBaseFee = common.HexToHash("0x1be13e83939b3c4701ee57a34e10c9290ce07b0e53af0fe90b812c6881826e36")
|
||||
// hash for the block with the baseFee
|
||||
hashWithBaseFee = common.HexToHash("0xc55b0cac99161f71bde1423a091426b1b5b4d7598e5981ad802cce712771965b")
|
||||
)
|
||||
|
||||
// Jaipur NOT enabled and BaseFee not set
|
||||
hash := SealHash(h, ¶ms.BorConfig{JaipurBlock: 10})
|
||||
assert.Equal(t, hash, hashWithoutBaseFee)
|
||||
|
||||
// Jaipur enabled (Jaipur=0) and BaseFee not set
|
||||
hash = SealHash(h, ¶ms.BorConfig{JaipurBlock: 0})
|
||||
assert.Equal(t, hash, hashWithoutBaseFee)
|
||||
|
||||
h.BaseFee = big.NewInt(2)
|
||||
|
||||
// Jaipur enabled (Jaipur=Header block) and BaseFee set
|
||||
hash = SealHash(h, ¶ms.BorConfig{JaipurBlock: 1})
|
||||
assert.Equal(t, hash, hashWithBaseFee)
|
||||
|
||||
// Jaipur NOT enabled and BaseFee set
|
||||
hash = SealHash(h, ¶ms.BorConfig{JaipurBlock: 10})
|
||||
assert.Equal(t, hash, hashWithoutBaseFee)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -131,7 +131,7 @@ func (s *Snapshot) apply(headers []*types.Header) (*Snapshot, error) {
|
|||
}
|
||||
|
||||
// Resolve the authorization key and check against signers
|
||||
signer, err := ecrecover(header, s.sigcache)
|
||||
signer, err := ecrecover(header, s.sigcache, s.config)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
|
|||
|
|
@ -337,6 +337,11 @@ func (st *StateTransition) TransitionDb() (*ExecutionResult, error) {
|
|||
effectiveTip = cmath.BigMin(st.gasTipCap, new(big.Int).Sub(st.gasFeeCap, st.evm.Context.BaseFee))
|
||||
}
|
||||
amount := new(big.Int).Mul(new(big.Int).SetUint64(st.gasUsed()), effectiveTip)
|
||||
if london {
|
||||
burntContractAddress := common.HexToAddress(st.evm.ChainConfig().Bor.CalculateBurntContract(st.evm.Context.BlockNumber.Uint64()))
|
||||
burnAmount := new(big.Int).Mul(new(big.Int).SetUint64(st.gasUsed()), st.evm.Context.BaseFee)
|
||||
st.state.AddBalance(burntContractAddress, burnAmount)
|
||||
}
|
||||
st.state.AddBalance(st.evm.Context.Coinbase, amount)
|
||||
output1 := new(big.Int).SetBytes(input1.Bytes())
|
||||
output2 := new(big.Int).SetBytes(input2.Bytes())
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@ func (s *PublicBlockChainAPI) appendRPCMarshalBorTransaction(ctx context.Context
|
|||
if borTx != nil {
|
||||
formattedTxs := fields["transactions"].([]interface{})
|
||||
if fullTx {
|
||||
marshalledTx := newRPCTransaction(borTx, blockHash, blockNumber, txIndex, nil, s.b.ChainConfig())
|
||||
marshalledTx := newRPCTransaction(borTx, blockHash, blockNumber, txIndex, block.BaseFee(), s.b.ChainConfig())
|
||||
// newRPCTransaction calculates hash based on RLP of the transaction data.
|
||||
// In case of bor block tx, we need simple derived tx hash (same as function argument) instead of RLP hash
|
||||
marshalledTx.Hash = txHash
|
||||
|
|
|
|||
129
params/config.go
129
params/config.go
|
|
@ -20,6 +20,8 @@ import (
|
|||
"encoding/binary"
|
||||
"fmt"
|
||||
"math/big"
|
||||
"sort"
|
||||
"strconv"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"golang.org/x/crypto/sha3"
|
||||
|
|
@ -222,6 +224,40 @@ var (
|
|||
Threshold: 2,
|
||||
}
|
||||
|
||||
// BorTestChainConfig contains the chain parameters to run a node on the Test network.
|
||||
BorTestChainConfig = &ChainConfig{
|
||||
ChainID: big.NewInt(80001),
|
||||
HomesteadBlock: big.NewInt(0),
|
||||
DAOForkBlock: nil,
|
||||
DAOForkSupport: true,
|
||||
EIP150Hash: common.HexToHash("0x0000000000000000000000000000000000000000000000000000000000000000"),
|
||||
EIP150Block: big.NewInt(0),
|
||||
EIP155Block: big.NewInt(0),
|
||||
EIP158Block: big.NewInt(0),
|
||||
ByzantiumBlock: big.NewInt(0),
|
||||
ConstantinopleBlock: big.NewInt(0),
|
||||
PetersburgBlock: big.NewInt(0),
|
||||
IstanbulBlock: big.NewInt(0),
|
||||
MuirGlacierBlock: big.NewInt(0),
|
||||
BerlinBlock: big.NewInt(0),
|
||||
LondonBlock: big.NewInt(0),
|
||||
Bor: &BorConfig{
|
||||
Period: map[string]uint64{
|
||||
"0": 2,
|
||||
},
|
||||
ProducerDelay: 6,
|
||||
Sprint: 64,
|
||||
BackupMultiplier: map[string]uint64{
|
||||
"0": 2,
|
||||
},
|
||||
ValidatorContract: "0x0000000000000000000000000000000000001000",
|
||||
StateReceiverContract: "0x0000000000000000000000000000000000001001",
|
||||
BurntContract: map[string]string{
|
||||
"0": "0x00000000000000000000000000000000000000000",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
// MumbaiChainConfig contains the chain parameters to run a node on the Mumbai test network.
|
||||
MumbaiChainConfig = &ChainConfig{
|
||||
ChainID: big.NewInt(80001),
|
||||
|
|
@ -238,13 +274,22 @@ var (
|
|||
IstanbulBlock: big.NewInt(2722000),
|
||||
MuirGlacierBlock: big.NewInt(2722000),
|
||||
BerlinBlock: big.NewInt(13996000),
|
||||
LondonBlock: big.NewInt(22640000),
|
||||
Bor: &BorConfig{
|
||||
Period: 2,
|
||||
ProducerDelay: 6,
|
||||
Sprint: 64,
|
||||
BackupMultiplier: 2,
|
||||
JaipurBlock: 22770000,
|
||||
Period: map[string]uint64{
|
||||
"0": 2,
|
||||
},
|
||||
ProducerDelay: 6,
|
||||
Sprint: 64,
|
||||
BackupMultiplier: map[string]uint64{
|
||||
"0": 2,
|
||||
},
|
||||
ValidatorContract: "0x0000000000000000000000000000000000001000",
|
||||
StateReceiverContract: "0x0000000000000000000000000000000000001001",
|
||||
BurntContract: map[string]string{
|
||||
"22640000": "0x70bcA57F4579f58670aB2d18Ef16e02C17553C38",
|
||||
},
|
||||
BlockAlloc: map[string]interface{}{
|
||||
// write as interface since that is how it is decoded in genesis
|
||||
"22244000": map[string]interface{}{
|
||||
|
|
@ -272,11 +317,17 @@ var (
|
|||
IstanbulBlock: big.NewInt(3395000),
|
||||
MuirGlacierBlock: big.NewInt(3395000),
|
||||
BerlinBlock: big.NewInt(14750000),
|
||||
LondonBlock: big.NewInt(23850000),
|
||||
Bor: &BorConfig{
|
||||
Period: 2,
|
||||
ProducerDelay: 6,
|
||||
Sprint: 64,
|
||||
BackupMultiplier: 2,
|
||||
JaipurBlock: 23850000,
|
||||
Period: map[string]uint64{
|
||||
"0": 2,
|
||||
},
|
||||
ProducerDelay: 6,
|
||||
Sprint: 64,
|
||||
BackupMultiplier: map[string]uint64{
|
||||
"0": 2,
|
||||
},
|
||||
ValidatorContract: "0x0000000000000000000000000000000000001000",
|
||||
StateReceiverContract: "0x0000000000000000000000000000000000001001",
|
||||
OverrideStateSyncRecords: map[string]int{
|
||||
|
|
@ -290,6 +341,9 @@ var (
|
|||
"14953792": 0,
|
||||
"14953856": 0,
|
||||
},
|
||||
BurntContract: map[string]string{
|
||||
"23850000": "0x70bca57f4579f58670ab2d18ef16e02c17553c38",
|
||||
},
|
||||
BlockAlloc: map[string]interface{}{
|
||||
// write as interface since that is how it is decoded in genesis
|
||||
"22156660": map[string]interface{}{
|
||||
|
|
@ -426,15 +480,16 @@ func (c *CliqueConfig) String() string {
|
|||
|
||||
// BorConfig is the consensus engine configs for Matic bor based sealing.
|
||||
type BorConfig struct {
|
||||
Period uint64 `json:"period"` // Number of seconds between blocks to enforce
|
||||
ProducerDelay uint64 `json:"producerDelay"` // Number of seconds delay between two producer interval
|
||||
Sprint uint64 `json:"sprint"` // Epoch length to proposer
|
||||
BackupMultiplier uint64 `json:"backupMultiplier"` // Backup multiplier to determine the wiggle time
|
||||
ValidatorContract string `json:"validatorContract"` // Validator set contract
|
||||
StateReceiverContract string `json:"stateReceiverContract"` // State receiver contract
|
||||
|
||||
Period map[string]uint64 `json:"period"` // Number of seconds between blocks to enforce
|
||||
ProducerDelay uint64 `json:"producerDelay"` // Number of seconds delay between two producer interval
|
||||
Sprint uint64 `json:"sprint"` // Epoch length to proposer
|
||||
BackupMultiplier map[string]uint64 `json:"backupMultiplier"` // Backup multiplier to determine the wiggle time
|
||||
ValidatorContract string `json:"validatorContract"` // Validator set contract
|
||||
StateReceiverContract string `json:"stateReceiverContract"` // State receiver contract
|
||||
OverrideStateSyncRecords map[string]int `json:"overrideStateSyncRecords"` // override state records count
|
||||
BlockAlloc map[string]interface{} `json:"blockAlloc"`
|
||||
BurntContract map[string]string `json:"burntContract"` // governance contract where the token will be sent to and burnt in london fork
|
||||
JaipurBlock uint64 `json:"jaipurBlock"` // Jaipur switch block (nil = no fork, 0 = already on jaipur)
|
||||
}
|
||||
|
||||
// String implements the stringer interface, returning the consensus engine details.
|
||||
|
|
@ -442,6 +497,50 @@ func (b *BorConfig) String() string {
|
|||
return "bor"
|
||||
}
|
||||
|
||||
func (c *BorConfig) CalculateBackupMultiplier(number uint64) uint64 {
|
||||
return c.calculateBorConfigHelper(c.BackupMultiplier, number)
|
||||
}
|
||||
|
||||
func (c *BorConfig) CalculatePeriod(number uint64) uint64 {
|
||||
return c.calculateBorConfigHelper(c.Period, number)
|
||||
}
|
||||
|
||||
func (c *BorConfig) IsJaipur(number uint64) bool {
|
||||
return number >= c.JaipurBlock
|
||||
}
|
||||
|
||||
func (c *BorConfig) calculateBorConfigHelper(field map[string]uint64, number uint64) uint64 {
|
||||
keys := make([]string, 0, len(field))
|
||||
for k := range field {
|
||||
keys = append(keys, k)
|
||||
}
|
||||
sort.Strings(keys)
|
||||
for i := 0; i < len(keys)-1; i++ {
|
||||
valUint, _ := strconv.ParseUint(keys[i], 10, 64)
|
||||
valUintNext, _ := strconv.ParseUint(keys[i+1], 10, 64)
|
||||
if number > valUint && number < valUintNext {
|
||||
return field[keys[i]]
|
||||
}
|
||||
}
|
||||
return field[keys[len(keys)-1]]
|
||||
}
|
||||
|
||||
func (c *BorConfig) CalculateBurntContract(number uint64) string {
|
||||
keys := make([]string, 0, len(c.BurntContract))
|
||||
for k := range c.BurntContract {
|
||||
keys = append(keys, k)
|
||||
}
|
||||
sort.Strings(keys)
|
||||
for i := 0; i < len(keys)-1; i++ {
|
||||
valUint, _ := strconv.ParseUint(keys[i], 10, 64)
|
||||
valUintNext, _ := strconv.ParseUint(keys[i+1], 10, 64)
|
||||
if number > valUint && number < valUintNext {
|
||||
return c.BurntContract[keys[i]]
|
||||
}
|
||||
}
|
||||
return c.BurntContract[keys[len(keys)-1]]
|
||||
}
|
||||
|
||||
// String implements the fmt.Stringer interface.
|
||||
func (c *ChainConfig) String() string {
|
||||
var engine interface{}
|
||||
|
|
|
|||
|
|
@ -21,9 +21,9 @@ import (
|
|||
)
|
||||
|
||||
const (
|
||||
VersionMajor = 0 // Major version component of the current release
|
||||
VersionMinor = 2 // Minor version component of the current release
|
||||
VersionPatch = 12 // Patch version component of the current release
|
||||
VersionMajor = 0 // Major version component of the current release
|
||||
VersionMinor = 2 // Minor version component of the current release
|
||||
VersionPatch = 13 // Patch version component of the current release
|
||||
VersionMeta = "stable" // Version metadata to append to the version string
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -3,18 +3,25 @@ package bor
|
|||
import (
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"io"
|
||||
"math/big"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/mock"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/consensus/bor"
|
||||
"github.com/ethereum/go-ethereum/consensus/ethash"
|
||||
"github.com/ethereum/go-ethereum/core"
|
||||
"github.com/ethereum/go-ethereum/core/rawdb"
|
||||
"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"
|
||||
"github.com/ethereum/go-ethereum/rlp"
|
||||
"github.com/ethereum/go-ethereum/tests/bor/mocks"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/mock"
|
||||
"golang.org/x/crypto/sha3"
|
||||
)
|
||||
|
||||
var (
|
||||
|
|
@ -188,7 +195,7 @@ func TestOutOfTurnSigning(t *testing.T) {
|
|||
header := block.Header()
|
||||
header.Time += (bor.CalcProducerDelay(header.Number.Uint64(), expectedSuccessionNumber, init.genesis.Config.Bor) -
|
||||
bor.CalcProducerDelay(header.Number.Uint64(), 0, init.genesis.Config.Bor))
|
||||
sign(t, header, signerKey)
|
||||
sign(t, header, signerKey, init.genesis.Config.Bor)
|
||||
block = types.NewBlockWithHeader(header)
|
||||
_, err = chain.InsertChain([]*types.Block{block})
|
||||
assert.Equal(t,
|
||||
|
|
@ -196,7 +203,7 @@ func TestOutOfTurnSigning(t *testing.T) {
|
|||
bor.WrongDifficultyError{Number: spanSize, Expected: expectedDifficulty, Actual: 3, Signer: addr.Bytes()})
|
||||
|
||||
header.Difficulty = new(big.Int).SetUint64(expectedDifficulty)
|
||||
sign(t, header, signerKey)
|
||||
sign(t, header, signerKey, init.genesis.Config.Bor)
|
||||
block = types.NewBlockWithHeader(header)
|
||||
_, err = chain.InsertChain([]*types.Block{block})
|
||||
assert.Nil(t, err)
|
||||
|
|
@ -268,3 +275,288 @@ func getSampleEventRecord(t *testing.T) *bor.EventRecordWithTime {
|
|||
_eventRecords[0].Time = time.Unix(1, 0)
|
||||
return _eventRecords[0]
|
||||
}
|
||||
|
||||
// TestEIP1559Transition tests the following:
|
||||
//
|
||||
// 1. A transaction whose gasFeeCap is greater than the baseFee is valid.
|
||||
// 2. Gas accounting for access lists on EIP-1559 transactions is correct.
|
||||
// 3. Only the transaction's tip will be received by the coinbase.
|
||||
// 4. The transaction sender pays for both the tip and baseFee.
|
||||
// 5. The coinbase receives only the partially realized tip when
|
||||
// gasFeeCap - gasTipCap < baseFee.
|
||||
// 6. Legacy transaction behave as expected (e.g. gasPrice = gasFeeCap = gasTipCap).
|
||||
func TestEIP1559Transition(t *testing.T) {
|
||||
var (
|
||||
aa = common.HexToAddress("0x000000000000000000000000000000000000aaaa")
|
||||
|
||||
// Generate a canonical chain to act as the main dataset
|
||||
db = rawdb.NewMemoryDatabase()
|
||||
engine = ethash.NewFaker()
|
||||
|
||||
// A sender who makes transactions, has some funds
|
||||
key1, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
|
||||
key2, _ = crypto.HexToECDSA("8a1f9a8f95be41cd7ccb6168179afb4504aefe388d1e14474d32c45c72ce7b7a")
|
||||
key3, _ = crypto.HexToECDSA("225171aed3793cba1c029832886d69785b7e77a54a44211226b447aa2d16b058")
|
||||
|
||||
addr1 = crypto.PubkeyToAddress(key1.PublicKey)
|
||||
addr2 = crypto.PubkeyToAddress(key2.PublicKey)
|
||||
addr3 = crypto.PubkeyToAddress(key3.PublicKey)
|
||||
funds = new(big.Int).Mul(common.Big1, big.NewInt(params.Ether))
|
||||
gspec = &core.Genesis{
|
||||
Config: params.BorTestChainConfig,
|
||||
Alloc: core.GenesisAlloc{
|
||||
addr1: {Balance: funds},
|
||||
addr2: {Balance: funds},
|
||||
addr3: {Balance: funds},
|
||||
// The address 0xAAAA sloads 0x00 and 0x01
|
||||
aa: {
|
||||
Code: []byte{
|
||||
byte(vm.PC),
|
||||
byte(vm.PC),
|
||||
byte(vm.SLOAD),
|
||||
byte(vm.SLOAD),
|
||||
},
|
||||
Nonce: 0,
|
||||
Balance: big.NewInt(0),
|
||||
},
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
gspec.Config.BerlinBlock = common.Big0
|
||||
gspec.Config.LondonBlock = common.Big0
|
||||
genesis := gspec.MustCommit(db)
|
||||
signer := types.LatestSigner(gspec.Config)
|
||||
|
||||
blocks, _ := core.GenerateChain(gspec.Config, genesis, engine, db, 1, func(i int, b *core.BlockGen) {
|
||||
b.SetCoinbase(common.Address{1})
|
||||
// One transaction to 0xAAAA
|
||||
accesses := types.AccessList{types.AccessTuple{
|
||||
Address: aa,
|
||||
StorageKeys: []common.Hash{{0}},
|
||||
}}
|
||||
|
||||
txdata := &types.DynamicFeeTx{
|
||||
ChainID: gspec.Config.ChainID,
|
||||
Nonce: 0,
|
||||
To: &aa,
|
||||
Gas: 30000,
|
||||
GasFeeCap: newGwei(5),
|
||||
GasTipCap: big.NewInt(2),
|
||||
AccessList: accesses,
|
||||
Data: []byte{},
|
||||
}
|
||||
tx := types.NewTx(txdata)
|
||||
tx, _ = types.SignTx(tx, signer, key1)
|
||||
|
||||
b.AddTx(tx)
|
||||
})
|
||||
|
||||
diskdb := rawdb.NewMemoryDatabase()
|
||||
gspec.MustCommit(diskdb)
|
||||
|
||||
chain, err := core.NewBlockChain(diskdb, nil, gspec.Config, engine, vm.Config{}, nil, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create tester chain: %v", err)
|
||||
}
|
||||
if n, err := chain.InsertChain(blocks); err != nil {
|
||||
t.Fatalf("block %d: failed to insert into chain: %v", n, err)
|
||||
}
|
||||
|
||||
block := chain.GetBlockByNumber(1)
|
||||
|
||||
// 1+2: Ensure EIP-1559 access lists are accounted for via gas usage.
|
||||
expectedGas := params.TxGas + params.TxAccessListAddressGas + params.TxAccessListStorageKeyGas +
|
||||
vm.GasQuickStep*2 + params.WarmStorageReadCostEIP2929 + params.ColdSloadCostEIP2929
|
||||
if block.GasUsed() != expectedGas {
|
||||
t.Fatalf("incorrect amount of gas spent: expected %d, got %d", expectedGas, block.GasUsed())
|
||||
}
|
||||
|
||||
state, _ := chain.State()
|
||||
|
||||
// 3: Ensure that miner received only the tx's tip.
|
||||
actual := state.GetBalance(block.Coinbase())
|
||||
expected := new(big.Int).Add(
|
||||
new(big.Int).SetUint64(block.GasUsed()*block.Transactions()[0].GasTipCap().Uint64()),
|
||||
ethash.ConstantinopleBlockReward,
|
||||
)
|
||||
if actual.Cmp(expected) != 0 {
|
||||
t.Fatalf("miner balance incorrect: expected %d, got %d", expected, actual)
|
||||
}
|
||||
|
||||
// check burnt contract balance
|
||||
actual = state.GetBalance(common.HexToAddress(params.BorTestChainConfig.Bor.CalculateBurntContract(block.NumberU64())))
|
||||
expected = new(big.Int).Mul(new(big.Int).SetUint64(block.GasUsed()), block.BaseFee())
|
||||
burntContractBalance := expected
|
||||
if actual.Cmp(expected) != 0 {
|
||||
t.Fatalf("burnt contract balance incorrect: expected %d, got %d", expected, actual)
|
||||
}
|
||||
|
||||
// 4: Ensure the tx sender paid for the gasUsed * (tip + block baseFee).
|
||||
actual = new(big.Int).Sub(funds, state.GetBalance(addr1))
|
||||
expected = new(big.Int).SetUint64(block.GasUsed() * (block.Transactions()[0].GasTipCap().Uint64() + block.BaseFee().Uint64()))
|
||||
if actual.Cmp(expected) != 0 {
|
||||
t.Fatalf("sender balance incorrect: expected %d, got %d", expected, actual)
|
||||
}
|
||||
|
||||
blocks, _ = core.GenerateChain(gspec.Config, block, engine, db, 1, func(i int, b *core.BlockGen) {
|
||||
b.SetCoinbase(common.Address{2})
|
||||
|
||||
txdata := &types.LegacyTx{
|
||||
Nonce: 0,
|
||||
To: &aa,
|
||||
Gas: 30000,
|
||||
GasPrice: newGwei(5),
|
||||
}
|
||||
tx := types.NewTx(txdata)
|
||||
tx, _ = types.SignTx(tx, signer, key2)
|
||||
|
||||
b.AddTx(tx)
|
||||
})
|
||||
|
||||
if n, err := chain.InsertChain(blocks); err != nil {
|
||||
t.Fatalf("block %d: failed to insert into chain: %v", n, err)
|
||||
}
|
||||
|
||||
block = chain.GetBlockByNumber(2)
|
||||
state, _ = chain.State()
|
||||
effectiveTip := block.Transactions()[0].GasTipCap().Uint64() - block.BaseFee().Uint64()
|
||||
|
||||
// 6+5: Ensure that miner received only the tx's effective tip.
|
||||
actual = state.GetBalance(block.Coinbase())
|
||||
expected = new(big.Int).Add(
|
||||
new(big.Int).SetUint64(block.GasUsed()*effectiveTip),
|
||||
ethash.ConstantinopleBlockReward,
|
||||
)
|
||||
if actual.Cmp(expected) != 0 {
|
||||
t.Fatalf("miner balance incorrect: expected %d, got %d", expected, actual)
|
||||
}
|
||||
|
||||
// check burnt contract balance
|
||||
actual = state.GetBalance(common.HexToAddress(params.BorTestChainConfig.Bor.CalculateBurntContract(block.NumberU64())))
|
||||
expected = new(big.Int).Add(burntContractBalance, new(big.Int).Mul(new(big.Int).SetUint64(block.GasUsed()), block.BaseFee()))
|
||||
burntContractBalance = expected
|
||||
if actual.Cmp(expected) != 0 {
|
||||
t.Fatalf("burnt contract balance incorrect: expected %d, got %d", expected, actual)
|
||||
}
|
||||
|
||||
// 4: Ensure the tx sender paid for the gasUsed * (effectiveTip + block baseFee).
|
||||
actual = new(big.Int).Sub(funds, state.GetBalance(addr2))
|
||||
expected = new(big.Int).SetUint64(block.GasUsed() * (effectiveTip + block.BaseFee().Uint64()))
|
||||
if actual.Cmp(expected) != 0 {
|
||||
t.Fatalf("sender balance incorrect: expected %d, got %d", expected, actual)
|
||||
}
|
||||
|
||||
blocks, _ = core.GenerateChain(gspec.Config, block, engine, db, 1, func(i int, b *core.BlockGen) {
|
||||
b.SetCoinbase(common.Address{3})
|
||||
|
||||
txdata := &types.LegacyTx{
|
||||
Nonce: 0,
|
||||
To: &aa,
|
||||
Gas: 30000,
|
||||
GasPrice: newGwei(5),
|
||||
}
|
||||
tx := types.NewTx(txdata)
|
||||
tx, _ = types.SignTx(tx, signer, key3)
|
||||
|
||||
b.AddTx(tx)
|
||||
|
||||
accesses := types.AccessList{types.AccessTuple{
|
||||
Address: aa,
|
||||
StorageKeys: []common.Hash{{0}},
|
||||
}}
|
||||
|
||||
txdata2 := &types.DynamicFeeTx{
|
||||
ChainID: gspec.Config.ChainID,
|
||||
Nonce: 1,
|
||||
To: &aa,
|
||||
Gas: 30000,
|
||||
GasFeeCap: newGwei(5),
|
||||
GasTipCap: big.NewInt(2),
|
||||
AccessList: accesses,
|
||||
Data: []byte{},
|
||||
}
|
||||
tx = types.NewTx(txdata2)
|
||||
tx, _ = types.SignTx(tx, signer, key3)
|
||||
|
||||
b.AddTx(tx)
|
||||
|
||||
})
|
||||
|
||||
if n, err := chain.InsertChain(blocks); err != nil {
|
||||
t.Fatalf("block %d: failed to insert into chain: %v", n, err)
|
||||
}
|
||||
|
||||
block = chain.GetBlockByNumber(3)
|
||||
state, _ = chain.State()
|
||||
|
||||
// check burnt contract balance
|
||||
actual = state.GetBalance(common.HexToAddress(params.BorTestChainConfig.Bor.CalculateBurntContract(block.NumberU64())))
|
||||
burntAmount := new(big.Int).Mul(
|
||||
block.BaseFee(),
|
||||
big.NewInt(int64(block.GasUsed())),
|
||||
)
|
||||
expected = new(big.Int).Add(burntContractBalance, burntAmount)
|
||||
if actual.Cmp(expected) != 0 {
|
||||
t.Fatalf("burnt contract balance incorrect: expected %d, got %d", expected, actual)
|
||||
}
|
||||
}
|
||||
|
||||
func newGwei(n int64) *big.Int {
|
||||
return new(big.Int).Mul(big.NewInt(n), big.NewInt(params.GWei))
|
||||
}
|
||||
|
||||
func TestJaipurFork(t *testing.T) {
|
||||
init := buildEthereumInstance(t, rawdb.NewMemoryDatabase())
|
||||
chain := init.ethereum.BlockChain()
|
||||
engine := init.ethereum.Engine()
|
||||
_bor := engine.(*bor.Bor)
|
||||
db := init.ethereum.ChainDb()
|
||||
block := init.genesis.ToBlock(db)
|
||||
for i := uint64(1); i < sprintSize; i++ {
|
||||
block = buildNextBlock(t, _bor, chain, block, nil, init.genesis.Config.Bor)
|
||||
insertNewBlock(t, chain, block)
|
||||
if block.Number().Uint64() == init.genesis.Config.Bor.JaipurBlock-1 {
|
||||
assert.Equal(t, testSealHash(block.Header(), init.genesis.Config.Bor), bor.SealHash(block.Header(), init.genesis.Config.Bor))
|
||||
}
|
||||
if block.Number().Uint64() == init.genesis.Config.Bor.JaipurBlock {
|
||||
assert.Equal(t, testSealHash(block.Header(), init.genesis.Config.Bor), bor.SealHash(block.Header(), init.genesis.Config.Bor))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// SealHash returns the hash of a block prior to it being sealed.
|
||||
func testSealHash(header *types.Header, c *params.BorConfig) (hash common.Hash) {
|
||||
hasher := sha3.NewLegacyKeccak256()
|
||||
testEncodeSigHeader(hasher, header, c)
|
||||
hasher.Sum(hash[:0])
|
||||
return hash
|
||||
}
|
||||
|
||||
func testEncodeSigHeader(w io.Writer, header *types.Header, c *params.BorConfig) {
|
||||
enc := []interface{}{
|
||||
header.ParentHash,
|
||||
header.UncleHash,
|
||||
header.Coinbase,
|
||||
header.Root,
|
||||
header.TxHash,
|
||||
header.ReceiptHash,
|
||||
header.Bloom,
|
||||
header.Difficulty,
|
||||
header.Number,
|
||||
header.GasLimit,
|
||||
header.GasUsed,
|
||||
header.Time,
|
||||
header.Extra[:len(header.Extra)-65], // Yes, this will panic if extra is too short
|
||||
header.MixDigest,
|
||||
header.Nonce,
|
||||
}
|
||||
if c.IsJaipur(header.Number.Uint64()) {
|
||||
if header.BaseFee != nil {
|
||||
enc = append(enc, header.BaseFee)
|
||||
}
|
||||
}
|
||||
if err := rlp.Encode(w, enc); err != nil {
|
||||
panic("can't encode: " + err.Error())
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ import (
|
|||
"github.com/ethereum/go-ethereum/cmd/utils"
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/consensus/bor"
|
||||
"github.com/ethereum/go-ethereum/consensus/misc"
|
||||
"github.com/ethereum/go-ethereum/core"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
|
|
@ -111,6 +112,14 @@ func buildNextBlock(t *testing.T, _bor *bor.Bor, chain *core.BlockChain, block *
|
|||
copy(header.Extra[32:], validatorBytes)
|
||||
}
|
||||
|
||||
if chain.Config().IsLondon(header.Number) {
|
||||
header.BaseFee = misc.CalcBaseFee(chain.Config(), block.Header())
|
||||
if !chain.Config().IsLondon(block.Number()) {
|
||||
parentGasLimit := block.GasLimit() * params.ElasticityMultiplier
|
||||
header.GasLimit = core.CalcGasLimit(parentGasLimit, parentGasLimit)
|
||||
}
|
||||
}
|
||||
|
||||
state, err := chain.State()
|
||||
if err != nil {
|
||||
t.Fatalf("%s", err)
|
||||
|
|
@ -119,12 +128,12 @@ func buildNextBlock(t *testing.T, _bor *bor.Bor, chain *core.BlockChain, block *
|
|||
if err != nil {
|
||||
t.Fatalf("%s", err)
|
||||
}
|
||||
sign(t, header, signer)
|
||||
sign(t, header, signer, borConfig)
|
||||
return types.NewBlockWithHeader(header)
|
||||
}
|
||||
|
||||
func sign(t *testing.T, header *types.Header, signer []byte) {
|
||||
sig, err := secp256k1.Sign(crypto.Keccak256(bor.BorRLP(header)), signer)
|
||||
func sign(t *testing.T, header *types.Header, signer []byte, c *params.BorConfig) {
|
||||
sig, err := secp256k1.Sign(crypto.Keccak256(bor.BorRLP(header, c)), signer)
|
||||
if err != nil {
|
||||
t.Fatalf("%s", err)
|
||||
}
|
||||
|
|
|
|||
19
tests/bor/testdata/genesis.json
vendored
19
tests/bor/testdata/genesis.json
vendored
|
|
@ -8,13 +8,26 @@
|
|||
"eip158Block": 0,
|
||||
"byzantiumBlock": 0,
|
||||
"constantinopleBlock": 0,
|
||||
"petersburgBlock": 0,
|
||||
"istanbulBlock": 0,
|
||||
"muirGlacierBlock": 0,
|
||||
"berlinBlock": 0,
|
||||
"londonBlock": 1,
|
||||
"bor": {
|
||||
"period": 1,
|
||||
"jaipurBlock": 2,
|
||||
"period": {
|
||||
"0": 1
|
||||
},
|
||||
"producerDelay": 4,
|
||||
"sprint": 4,
|
||||
"backupMultiplier": 1,
|
||||
"backupMultiplier": {
|
||||
"0": 1
|
||||
},
|
||||
"validatorContract": "0x0000000000000000000000000000000000001000",
|
||||
"stateReceiverContract": "0x0000000000000000000000000000000000001001"
|
||||
"stateReceiverContract": "0x0000000000000000000000000000000000001001",
|
||||
"burntContract": {
|
||||
"0": "0x0000000000000000000000000000000000000000"
|
||||
}
|
||||
}
|
||||
},
|
||||
"nonce": "0x0",
|
||||
|
|
|
|||
Loading…
Reference in a new issue