Skip to content

Commit

Permalink
Merge pull request #24 from binance-chain/develop
Browse files Browse the repository at this point in the history
[R4R] prepare for release 1.0.1-beta
  • Loading branch information
unclezoro authored Aug 11, 2020
2 parents 5cc893a + f2e4ef2 commit d9a0607
Show file tree
Hide file tree
Showing 20 changed files with 494 additions and 90 deletions.
14 changes: 14 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,19 @@
# Changelog

## v1.0.1-beta

IMPROVEMENT
* [\#22](https://github.com/binance-chain/bsc/pull/22) resolve best practice advice

FEATURES
* [\#23](https://github.com/binance-chain/bsc/pull/23) enforce backoff time for out-turn validator

BUGFIX
* [\#25](https://github.com/binance-chain/bsc/pull/25) minor fix for ramanujan upgrade

UPGRADE
* [\#26](https://github.com/binance-chain/bsc/pull/26) update chapel network config for ramanujan fork

## v1.0.0-beta.0

FEATURES
Expand Down
8 changes: 8 additions & 0 deletions cmd/geth/retesteth.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ import (
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/rawdb"
"github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/systemcontracts"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/crypto"
Expand Down Expand Up @@ -133,6 +134,7 @@ type CParamsParams struct {
ConstantinopleForkBlock *math.HexOrDecimal64 `json:"constantinopleForkBlock"`
ConstantinopleFixForkBlock *math.HexOrDecimal64 `json:"constantinopleFixForkBlock"`
IstanbulBlock *math.HexOrDecimal64 `json:"istanbulForkBlock"`
RamanujanForkBlock *math.HexOrDecimal64 `json:"ramanujanForkBlock"`
ChainID *math.HexOrDecimal256 `json:"chainID"`
MaximumExtraDataSize math.HexOrDecimal64 `json:"maximumExtraDataSize"`
TieBreakingGas bool `json:"tieBreakingGas"`
Expand Down Expand Up @@ -322,6 +324,7 @@ func (api *RetestethAPI) SetChainParams(ctx context.Context, chainParams ChainPa
constantinopleBlock *big.Int
petersburgBlock *big.Int
istanbulBlock *big.Int
ramanujanBlock *big.Int
)
if chainParams.Params.HomesteadForkBlock != nil {
homesteadBlock = big.NewInt(int64(*chainParams.Params.HomesteadForkBlock))
Expand Down Expand Up @@ -351,6 +354,9 @@ func (api *RetestethAPI) SetChainParams(ctx context.Context, chainParams ChainPa
if chainParams.Params.IstanbulBlock != nil {
istanbulBlock = big.NewInt(int64(*chainParams.Params.IstanbulBlock))
}
if chainParams.Params.RamanujanForkBlock != nil {
ramanujanBlock = big.NewInt(int64(*chainParams.Params.RamanujanForkBlock))
}

genesis := &core.Genesis{
Config: &params.ChainConfig{
Expand All @@ -365,6 +371,7 @@ func (api *RetestethAPI) SetChainParams(ctx context.Context, chainParams ChainPa
ConstantinopleBlock: constantinopleBlock,
PetersburgBlock: petersburgBlock,
IstanbulBlock: istanbulBlock,
RamanujanBlock: ramanujanBlock,
},
Nonce: uint64(chainParams.Genesis.Nonce),
Timestamp: uint64(chainParams.Genesis.Timestamp),
Expand Down Expand Up @@ -501,6 +508,7 @@ func (api *RetestethAPI) mineBlock() error {
if api.chainConfig.DAOForkSupport && api.chainConfig.DAOForkBlock != nil && api.chainConfig.DAOForkBlock.Cmp(header.Number) == 0 {
misc.ApplyDAOHardFork(statedb)
}
systemcontracts.UpgradeBuildInSystemContract(api.chainConfig, header.Number, statedb)
gasPool := new(core.GasPool).AddGas(header.GasLimit)
txCount := 0
var txs []*types.Transaction
Expand Down
114 changes: 69 additions & 45 deletions consensus/parlia/parlia.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import (
"github.com/ethereum/go-ethereum/consensus/misc"
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/systemcontracts"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/crypto"
Expand All @@ -48,21 +49,11 @@ const (
extraSeal = 65 // Fixed number of extra-data suffix bytes reserved for signer seal

validatorBytesLength = common.AddressLength
wiggleTime = 500 * time.Millisecond // Random delay (per signer) to allow concurrent signers
fixedBackOffTime = 200 * time.Millisecond
wiggleTime = uint64(1) // second, Random delay (per signer) to allow concurrent signers
initialBackOffTime = uint64(1) // second

systemRewardPercent = 4 // it means 1/2^4 = 1/16 percentage of gas fee incoming will be distributed to system

// genesis contracts
ValidatorContract = "0x0000000000000000000000000000000000001000"
SlashContract = "0x0000000000000000000000000000000000001001"
SystemRewardContract = "0x0000000000000000000000000000000000001002"
LightClientContract = "0x0000000000000000000000000000000000001003"
TokenHubContract = "0x0000000000000000000000000000000000001004"
RelayerIncentivizeContract = "0x0000000000000000000000000000000000001005"
RelayerHubContract = "0x0000000000000000000000000000000000001006"
GovHubContract = "0x0000000000000000000000000000000000001007"
CrossChainContract = "0x0000000000000000000000000000000000002000"
)

var (
Expand All @@ -73,15 +64,15 @@ var (
maxSystemBalance = new(big.Int).Mul(big.NewInt(100), big.NewInt(params.Ether))

systemContracts = map[common.Address]bool{
common.HexToAddress(ValidatorContract): true,
common.HexToAddress(SlashContract): true,
common.HexToAddress(SystemRewardContract): true,
common.HexToAddress(LightClientContract): true,
common.HexToAddress(RelayerHubContract): true,
common.HexToAddress(GovHubContract): true,
common.HexToAddress(TokenHubContract): true,
common.HexToAddress(RelayerIncentivizeContract): true,
common.HexToAddress(CrossChainContract): true,
common.HexToAddress(systemcontracts.ValidatorContract): true,
common.HexToAddress(systemcontracts.SlashContract): true,
common.HexToAddress(systemcontracts.SystemRewardContract): true,
common.HexToAddress(systemcontracts.LightClientContract): true,
common.HexToAddress(systemcontracts.RelayerHubContract): true,
common.HexToAddress(systemcontracts.GovHubContract): true,
common.HexToAddress(systemcontracts.TokenHubContract): true,
common.HexToAddress(systemcontracts.RelayerIncentivizeContract): true,
common.HexToAddress(systemcontracts.CrossChainContract): true,
}
)

Expand Down Expand Up @@ -395,10 +386,20 @@ func (p *Parlia) verifyCascadingFields(chain consensus.ChainReader, header *type
return consensus.ErrUnknownAncestor
}

snap, err := p.snapshot(chain, number-1, header.ParentHash, parents)
if err != nil {
return err
}

err = p.blockTimeVerifyForRamanujanFork(snap, header, parent)
if err != nil {
return err
}

// 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)
capacity := uint64(0x7fffffffffffffff)
if header.GasLimit > capacity {
return fmt.Errorf("invalid gasLimit: have %v, max %v", header.GasLimit, capacity)
}
// Verify that the gasUsed is <= gasLimit
if header.GasUsed > header.GasLimit {
Expand Down Expand Up @@ -570,7 +571,7 @@ func (p *Parlia) verifySeal(chain consensus.ChainReader, header *types.Header, p

// Ensure that the difficulty corresponds to the turn-ness of the signer
if !p.fakeDiff {
inturn := snap.inturn(header.Number.Uint64(), signer)
inturn := snap.inturn(signer)
if inturn && header.Difficulty.Cmp(diffInTurn) != 0 {
return errWrongDifficulty
}
Expand Down Expand Up @@ -626,8 +627,7 @@ func (p *Parlia) Prepare(chain consensus.ChainReader, header *types.Header) erro
if parent == nil {
return consensus.ErrUnknownAncestor
}

header.Time = parent.Time + p.config.Period
header.Time = p.blockTimeForRamanujanFork(snap, header, parent)
if header.Time < uint64(time.Now().Unix()) {
header.Time = uint64(time.Now().Unix())
}
Expand Down Expand Up @@ -809,14 +809,7 @@ func (p *Parlia) Seal(chain consensus.ChainReader, block *types.Block, results c
}

// 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
if header.Difficulty.Cmp(diffNoTurn) == 0 {
// It's not our turn explicitly to sign, delay it a bit
wiggle := time.Duration(len(snap.Validators)/2+1) * wiggleTime
delay += time.Duration(fixedBackOffTime) + time.Duration(rand.Int63n(int64(wiggle)))

log.Trace("Out-of-turn signing requested", "wiggle", common.PrettyDuration(wiggle))
}
delay := p.delayForRamanujanFork(snap, header)

log.Info("Sealing block with", "number", number, "delay", delay, "headerDifficulty", header.Difficulty, "val", val.Hex())

Expand Down Expand Up @@ -861,7 +854,7 @@ func (p *Parlia) CalcDifficulty(chain consensus.ChainReader, time uint64, parent
// that a new block should have based on the previous blocks in the chain and the
// current signer.
func CalcDifficulty(snap *Snapshot, signer common.Address) *big.Int {
if snap.inturn(snap.Number+1, signer) {
if snap.inturn(signer) {
return new(big.Int).Set(diffInTurn)
}
return new(big.Int).Set(diffNoTurn)
Expand Down Expand Up @@ -907,7 +900,7 @@ func (p *Parlia) getCurrentValidators(blockHash common.Hash) ([]common.Address,
}
// call
msgData := (hexutil.Bytes)(data)
toAddress := common.HexToAddress(ValidatorContract)
toAddress := common.HexToAddress(systemcontracts.ValidatorContract)
gas := (hexutil.Uint64)(uint64(math.MaxUint64 / 2))
result, err := p.ethAPI.Call(ctx, ethapi.CallArgs{
Gas: &gas,
Expand Down Expand Up @@ -945,7 +938,7 @@ func (p *Parlia) distributeIncoming(val common.Address, state *state.StateDB, he
state.SetBalance(consensus.SystemAddress, big.NewInt(0))
state.AddBalance(coinbase, balance)

doDistributeSysReward := state.GetBalance(common.HexToAddress(SystemRewardContract)).Cmp(maxSystemBalance) < 0
doDistributeSysReward := state.GetBalance(common.HexToAddress(systemcontracts.SystemRewardContract)).Cmp(maxSystemBalance) < 0
if doDistributeSysReward {
var rewards = new(big.Int)
rewards = rewards.Rsh(balance, systemRewardPercent)
Expand Down Expand Up @@ -977,7 +970,7 @@ func (p *Parlia) slash(spoiledVal common.Address, state *state.StateDB, header *
return err
}
// get system message
msg := p.getSystemMessage(header.Coinbase, common.HexToAddress(SlashContract), data, common.Big0)
msg := p.getSystemMessage(header.Coinbase, common.HexToAddress(systemcontracts.SlashContract), data, common.Big0)
// apply message
return p.applyTransaction(msg, state, header, chain, txs, receipts, receivedTxs, usedGas, mining)
}
Expand All @@ -988,7 +981,15 @@ func (p *Parlia) initContract(state *state.StateDB, header *types.Header, chain
// method
method := "init"
// contracts
contracts := []string{ValidatorContract, SlashContract, LightClientContract, RelayerHubContract, TokenHubContract, RelayerIncentivizeContract, CrossChainContract}
contracts := []string{
systemcontracts.ValidatorContract,
systemcontracts.SlashContract,
systemcontracts.LightClientContract,
systemcontracts.RelayerHubContract,
systemcontracts.TokenHubContract,
systemcontracts.RelayerIncentivizeContract,
systemcontracts.CrossChainContract,
}
// get packed data
data, err := p.validatorSetABI.Pack(method)
if err != nil {
Expand All @@ -1010,7 +1011,7 @@ func (p *Parlia) initContract(state *state.StateDB, header *types.Header, chain
func (p *Parlia) distributeToSystem(amount *big.Int, state *state.StateDB, header *types.Header, chain core.ChainContext,
txs *[]*types.Transaction, receipts *[]*types.Receipt, receivedTxs *[]*types.Transaction, usedGas *uint64, mining bool) error {
// get system message
msg := p.getSystemMessage(header.Coinbase, common.HexToAddress(SystemRewardContract), nil, amount)
msg := p.getSystemMessage(header.Coinbase, common.HexToAddress(systemcontracts.SystemRewardContract), nil, amount)
// apply message
return p.applyTransaction(msg, state, header, chain, txs, receipts, receivedTxs, usedGas, mining)
}
Expand All @@ -1031,7 +1032,7 @@ func (p *Parlia) distributeToValidator(amount *big.Int, validator common.Address
return err
}
// get system message
msg := p.getSystemMessage(header.Coinbase, common.HexToAddress(ValidatorContract), data, amount)
msg := p.getSystemMessage(header.Coinbase, common.HexToAddress(systemcontracts.ValidatorContract), data, amount)
// apply message
return p.applyTransaction(msg, state, header, chain, txs, receipts, receivedTxs, usedGas, mining)
}
Expand Down Expand Up @@ -1072,8 +1073,8 @@ func (p *Parlia) applyTransaction(
return errors.New("supposed to get a actual transaction, but get none")
}
actualTx := (*receivedTxs)[0]
if bytes.Compare(p.signer.Hash(actualTx).Bytes(), expectedHash.Bytes()) != 0 {
return errors.New(fmt.Sprintf("expected tx hash %v, get %v", expectedHash.String(), actualTx.Hash().String()))
if !bytes.Equal(p.signer.Hash(actualTx).Bytes(), expectedHash.Bytes()) {
return fmt.Errorf("expected tx hash %v, get %v", expectedHash.String(), actualTx.Hash().String())
}
expectedTx = actualTx
// move to next
Expand Down Expand Up @@ -1140,6 +1141,30 @@ func encodeSigHeader(w io.Writer, header *types.Header, chainId *big.Int) {
}
}

func backOffTime(snap *Snapshot, val common.Address) uint64 {
if snap.inturn(val) {
return 0
} else {
idx := snap.indexOfVal(val)
if idx < 0 {
// The backOffTime does not matter when a validator is not authorized.
return 0
}
s := rand.NewSource(int64(snap.Number))
r := rand.New(s)
n := len(snap.Validators)
backOffSteps := make([]uint64, 0, n)
for idx := uint64(0); idx < uint64(n); idx++ {
backOffSteps = append(backOffSteps, idx)
}
r.Shuffle(n, func(i, j int) {
backOffSteps[i], backOffSteps[j] = backOffSteps[j], backOffSteps[i]
})
delay := initialBackOffTime + backOffSteps[idx]*wiggleTime
return delay
}
}

// chain context
type chainContext struct {
Chain consensus.ChainReader
Expand Down Expand Up @@ -1194,4 +1219,3 @@ func applyMessage(
}
return msg.Gas() - returnGas, err
}

41 changes: 25 additions & 16 deletions consensus/parlia/parlia_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import (
"fmt"
"math/rand"
"testing"
"time"

"github.com/ethereum/go-ethereum/common"
)
Expand Down Expand Up @@ -57,7 +56,7 @@ func simulateValidatorOutOfService(totalValidators int, downValidators int) {
return validators[idx]
}

downDelay := time.Duration(0)
downDelay := uint64(0)
for h := 1; h <= downBlocks; h++ {
if limit := uint64(totalValidators/2 + 1); uint64(h) >= limit {
delete(recents, uint64(h)-limit)
Expand All @@ -73,21 +72,21 @@ func simulateValidatorOutOfService(totalValidators int, downValidators int) {
if len(candidates) == 0 {
panic("can not test such case")
}
idx, delay := producerBlockDelay(candidates, totalValidators)
idx, delay := producerBlockDelay(candidates, h, totalValidators)
downDelay = downDelay + delay
recents[uint64(h)] = idx
} else {
recents[uint64(h)] = proposer
}
}
fmt.Printf("average delay is %v when there is %d validators and %d is down \n",
downDelay/time.Duration(downBlocks), totalValidators, downValidators)
downDelay/uint64(downBlocks), totalValidators, downValidators)

for i := 0; i < downValidators; i++ {
validators[down[i]] = true
}

recoverDelay := time.Duration(0)
recoverDelay := uint64(0)
lastseen := downBlocks
for h := downBlocks + 1; h <= downBlocks+recoverBlocks; h++ {
if limit := uint64(totalValidators/2 + 1); uint64(h) >= limit {
Expand All @@ -105,7 +104,7 @@ func simulateValidatorOutOfService(totalValidators int, downValidators int) {
if len(candidates) == 0 {
panic("can not test such case")
}
idx, delay := producerBlockDelay(candidates, totalValidators)
idx, delay := producerBlockDelay(candidates, h, totalValidators)
recoverDelay = recoverDelay + delay
recents[uint64(h)] = idx
} else {
Expand All @@ -116,18 +115,28 @@ func simulateValidatorOutOfService(totalValidators int, downValidators int) {
recoverDelay, downValidators, lastseen)
}

func producerBlockDelay(candidates map[int]bool, numOfValidators int) (int, time.Duration) {
minDur := time.Duration(0)
minIdx := 0
wiggle := time.Duration(numOfValidators/2+1) * wiggleTime
for idx := range candidates {
sleepTime := rand.Int63n(int64(wiggle))
if int64(minDur) < sleepTime {
minDur = time.Duration(rand.Int63n(int64(wiggle)))
minIdx = idx
func producerBlockDelay(candidates map[int]bool, height, numOfValidators int) (int, uint64) {

s := rand.NewSource(int64(height))
r := rand.New(s)
n := numOfValidators
backOffSteps := make([]int, 0, n)
for idx := 0; idx < n; idx++ {
backOffSteps = append(backOffSteps, idx)
}
r.Shuffle(n, func(i, j int) {
backOffSteps[i], backOffSteps[j] = backOffSteps[j], backOffSteps[i]
})
minDelay := numOfValidators
minCandidate := 0
for c := range candidates {
if minDelay > backOffSteps[c] {
minDelay = backOffSteps[c]
minCandidate = c
}
}
return minIdx, minDur
delay := initialBackOffTime + uint64(minDelay)*wiggleTime
return minCandidate, delay
}

func randomAddress() common.Address {
Expand Down
Loading

0 comments on commit d9a0607

Please sign in to comment.