Skip to content

Commit

Permalink
jaipur fork (#269)
Browse files Browse the repository at this point in the history
* jaipur fork

* add check

* review comments

* test case

* fix few changes

* Change condition

* review comments

* type conversion

* Update mumbai.go

* Update config.go

* Update config.go

Co-authored-by: Ferran Borreguero <ferranbt@protonmail.com>
  • Loading branch information
2 people authored and Victor Castell committed Jan 11, 2022
1 parent 467e53b commit a10f79d
Show file tree
Hide file tree
Showing 9 changed files with 168 additions and 31 deletions.
12 changes: 8 additions & 4 deletions command/server/chains/mainnet.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,14 @@ var mainnetBor = &Chain{
MuirGlacierBlock: big.NewInt(3395000),
BerlinBlock: big.NewInt(14750000),
Bor: &params.BorConfig{
Period: 2,
ProducerDelay: 6,
Sprint: 64,
BackupMultiplier: 2,
Period: map[string]uint64{
"0": 2,
},
ProducerDelay: 6,
Sprint: 64,
BackupMultiplier: map[string]uint64{
"0": 2,
},
ValidatorContract: "0x0000000000000000000000000000000000001000",
StateReceiverContract: "0x0000000000000000000000000000000000001001",
OverrideStateSyncRecords: map[string]int{
Expand Down
26 changes: 22 additions & 4 deletions command/server/chains/mumbai.go

Large diffs are not rendered by default.

35 changes: 20 additions & 15 deletions consensus/bor/bor.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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
}
Expand All @@ -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,
Expand All @@ -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())
}
}
Expand All @@ -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()
}

Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -578,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
}
Expand Down Expand Up @@ -844,7 +849,7 @@ func (c *Bor) Seal(chain consensus.ChainHeaderReader, block *types.Block, result
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
}
Expand Down Expand Up @@ -876,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
Expand All @@ -895,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
Expand Down
36 changes: 36 additions & 0 deletions consensus/bor/bor_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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, &params.BorConfig{JaipurBlock: 10})
assert.Equal(t, hash, hashWithoutBaseFee)

// Jaipur enabled (Jaipur=0) and BaseFee not set
hash = SealHash(h, &params.BorConfig{JaipurBlock: 0})
assert.Equal(t, hash, hashWithoutBaseFee)

h.BaseFee = big.NewInt(2)

// Jaipur enabled (Jaipur=Header block) and BaseFee set
hash = SealHash(h, &params.BorConfig{JaipurBlock: 1})
assert.Equal(t, hash, hashWithBaseFee)

// Jaipur NOT enabled and BaseFee set
hash = SealHash(h, &params.BorConfig{JaipurBlock: 10})
assert.Equal(t, hash, hashWithoutBaseFee)
}
2 changes: 1 addition & 1 deletion consensus/bor/snapshot.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
Expand Down
8 changes: 7 additions & 1 deletion params/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -276,6 +276,7 @@ var (
BerlinBlock: big.NewInt(13996000),
LondonBlock: big.NewInt(22640000),
Bor: &BorConfig{
JaipurBlock: 22770000,
Period: map[string]uint64{
"0": 2,
},
Expand Down Expand Up @@ -486,7 +487,8 @@ type BorConfig struct {
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
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.
Expand All @@ -502,6 +504,10 @@ 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 {
Expand Down
62 changes: 60 additions & 2 deletions tests/bor/bor_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package bor
import (
"encoding/hex"
"encoding/json"
"io"
"math/big"
"testing"
"time"
Expand All @@ -16,9 +17,11 @@ import (
"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 (
Expand Down Expand Up @@ -192,15 +195,15 @@ 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,
*err.(*bor.WrongDifficultyError),
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)
Expand Down Expand Up @@ -502,3 +505,58 @@ func TestEIP1559Transition(t *testing.T) {
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())
}
}
15 changes: 12 additions & 3 deletions tests/bor/helper.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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)
Expand All @@ -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)
}
Expand Down
3 changes: 2 additions & 1 deletion tests/bor/testdata/genesis.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,9 @@
"istanbulBlock": 0,
"muirGlacierBlock": 0,
"berlinBlock": 0,
"londonBlock": 10,
"londonBlock": 1,
"bor": {
"jaipurBlock": 2,
"period": {
"0": 1
},
Expand Down

0 comments on commit a10f79d

Please sign in to comment.