mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-07-31 17:13:57 +00:00
Merge pull request #70 from maticnetwork/develop
Merge develop (Apr 20 - May 27) to master
This commit is contained in:
commit
555e67a551
53 changed files with 1255 additions and 588 deletions
19
.github/workflows/linuxpackage.yml
vendored
19
.github/workflows/linuxpackage.yml
vendored
|
|
@ -3,7 +3,7 @@ name: Linux package
|
|||
on:
|
||||
push:
|
||||
tags:
|
||||
- 'v*.*.*'
|
||||
- "v*.*.*"
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
|
|
@ -30,7 +30,7 @@ jobs:
|
|||
sudo apt-get -yqq install libpq-dev build-essential
|
||||
gem install --no-document fpm
|
||||
fpm --version
|
||||
|
||||
|
||||
make bor
|
||||
|
||||
cat > bor.service <<- "EOF"
|
||||
|
|
@ -39,8 +39,7 @@ jobs:
|
|||
[Service]
|
||||
WorkingDirectory=/etc/bor/
|
||||
EnvironmentFile=/etc/bor/metadata
|
||||
ExecStartPre=/bin/mkdir -p /var/log/matic-logs/
|
||||
ExecStart=/bin/bash -c "/usr/bin/bor --datadir /etc/bor/dataDir --port '30303' --rpc --rpcaddr '0.0.0.0' --rpcvhosts '*' --rpccorsdomain '*' --rpcport '8545' --ipcpath /etc/bor/geth.ipc --rpcapi 'bor,db,eth,net,web3,txpool' --networkid ${NETWORK_ID} --gasprice '0' --keystore /etc/bor/dataDir/keystore --unlock ${VALIDATOR_ADDRESS} --password /etc/bor/dataDir/password.txt --allow-insecure-unlock --maxpeers 150 --mine > /var/log/matic-logs/bor.log 2>&1"
|
||||
ExecStart=/bin/bash -c "/usr/bin/bor --datadir /etc/bor/dataDir --port '30303' --rpc --rpcaddr '0.0.0.0' --rpcvhosts '*' --rpccorsdomain '*' --rpcport '8545' --ipcpath /etc/bor/bor.ipc --rpcapi 'db,eth,net,web3,txpool,bor' --networkid ${NETWORK_ID} --miner.gaslimit '20000000' --txpool.nolocals --txpool.accountslots '128' --txpool.globalslots '20000' --txpool.lifetime '0h16m0s' --keystore /etc/bor/dataDir/keystore --unlock ${VALIDATOR_ADDRESS} --password /etc/bor/dataDir/password.txt --allow-insecure-unlock --maxpeers 150 --mine"
|
||||
Type=simple
|
||||
User=root
|
||||
EOF
|
||||
|
|
@ -60,7 +59,7 @@ jobs:
|
|||
bor.service=/etc/systemd/system/ \
|
||||
build/bin/bor=/usr/bin/ \
|
||||
metadata=/etc/bor/
|
||||
|
||||
|
||||
mkdir packages-v${{ env.RELEASE_VERSION }}
|
||||
|
||||
mv matic-bor_${{ env.RELEASE_VERSION }}_amd64.deb packages-v${{ env.RELEASE_VERSION }}/
|
||||
|
|
@ -75,14 +74,14 @@ jobs:
|
|||
AWS_S3_BUCKET: ${{ secrets.AWS_S3_BUCKET }}
|
||||
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
|
||||
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
|
||||
AWS_REGION: 'us-east-1' # optional: defaults to us-east-1
|
||||
SOURCE_DIR: 'packages-v${{ env.RELEASE_VERSION }}'
|
||||
DEST_DIR: 'v${{ env.RELEASE_VERSION }}'
|
||||
AWS_REGION: "us-east-1" # optional: defaults to us-east-1
|
||||
SOURCE_DIR: "packages-v${{ env.RELEASE_VERSION }}"
|
||||
DEST_DIR: "v${{ env.RELEASE_VERSION }}"
|
||||
|
||||
- name: Slack Notification
|
||||
uses: rtCamp/action-slack-notify@v2.0.0
|
||||
env:
|
||||
SLACK_CHANNEL: code-releases
|
||||
SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK }}
|
||||
SLACK_TITLE: 'New linux package for Bor v${{ env.RELEASE_VERSION }} just got released'
|
||||
SLACK_MESSAGE: 'Package has been uploaded to S3 bucket for public use and available at https://matic-public.s3.amazonaws.com/v${{ env.RELEASE_VERSION }}/matic-bor_${{ env.RELEASE_VERSION }}_amd64.deb'
|
||||
SLACK_TITLE: "New linux package for Bor v${{ env.RELEASE_VERSION }} just got released"
|
||||
SLACK_MESSAGE: "Package has been uploaded to S3 bucket for public use and available at https://matic-public.s3.amazonaws.com/v${{ env.RELEASE_VERSION }}/matic-bor_${{ env.RELEASE_VERSION }}_amd64.deb"
|
||||
|
|
|
|||
|
|
@ -41,11 +41,11 @@ import (
|
|||
"sync"
|
||||
"time"
|
||||
|
||||
pcsc "github.com/gballet/go-libpcsclite"
|
||||
"github.com/maticnetwork/bor/accounts"
|
||||
"github.com/maticnetwork/bor/common"
|
||||
"github.com/maticnetwork/bor/event"
|
||||
"github.com/maticnetwork/bor/log"
|
||||
pcsc "github.com/gballet/go-libpcsclite"
|
||||
)
|
||||
|
||||
// Scheme is the URI prefix for smartcard wallets.
|
||||
|
|
|
|||
|
|
@ -25,8 +25,8 @@ import (
|
|||
"crypto/sha512"
|
||||
"fmt"
|
||||
|
||||
"github.com/maticnetwork/bor/crypto"
|
||||
pcsc "github.com/gballet/go-libpcsclite"
|
||||
"github.com/maticnetwork/bor/crypto"
|
||||
"github.com/wsddn/go-ecdh"
|
||||
"golang.org/x/crypto/pbkdf2"
|
||||
"golang.org/x/text/unicode/norm"
|
||||
|
|
|
|||
|
|
@ -33,13 +33,13 @@ import (
|
|||
"sync"
|
||||
"time"
|
||||
|
||||
pcsc "github.com/gballet/go-libpcsclite"
|
||||
ethereum "github.com/maticnetwork/bor"
|
||||
"github.com/maticnetwork/bor/accounts"
|
||||
"github.com/maticnetwork/bor/common"
|
||||
"github.com/maticnetwork/bor/core/types"
|
||||
"github.com/maticnetwork/bor/crypto"
|
||||
"github.com/maticnetwork/bor/log"
|
||||
pcsc "github.com/gballet/go-libpcsclite"
|
||||
"github.com/status-im/keycard-go/derivationpath"
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -23,10 +23,10 @@ import (
|
|||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"github.com/karalabe/usb"
|
||||
"github.com/maticnetwork/bor/accounts"
|
||||
"github.com/maticnetwork/bor/event"
|
||||
"github.com/maticnetwork/bor/log"
|
||||
"github.com/karalabe/usb"
|
||||
)
|
||||
|
||||
// LedgerScheme is the protocol scheme prefixing account and wallet URLs.
|
||||
|
|
|
|||
|
|
@ -27,13 +27,13 @@ import (
|
|||
"io"
|
||||
"math/big"
|
||||
|
||||
"github.com/golang/protobuf/proto"
|
||||
"github.com/maticnetwork/bor/accounts"
|
||||
"github.com/maticnetwork/bor/accounts/usbwallet/trezor"
|
||||
"github.com/maticnetwork/bor/common"
|
||||
"github.com/maticnetwork/bor/common/hexutil"
|
||||
"github.com/maticnetwork/bor/core/types"
|
||||
"github.com/maticnetwork/bor/log"
|
||||
"github.com/golang/protobuf/proto"
|
||||
)
|
||||
|
||||
// ErrTrezorPINNeeded is returned if opening the trezor requires a PIN code. In
|
||||
|
|
|
|||
|
|
@ -25,13 +25,13 @@ import (
|
|||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/karalabe/usb"
|
||||
ethereum "github.com/maticnetwork/bor"
|
||||
"github.com/maticnetwork/bor/accounts"
|
||||
"github.com/maticnetwork/bor/common"
|
||||
"github.com/maticnetwork/bor/core/types"
|
||||
"github.com/maticnetwork/bor/crypto"
|
||||
"github.com/maticnetwork/bor/log"
|
||||
"github.com/karalabe/usb"
|
||||
)
|
||||
|
||||
// Maximum time between wallet health checks to detect USB unplugs.
|
||||
|
|
|
|||
|
|
@ -64,6 +64,7 @@ It expects the genesis file as argument.`,
|
|||
ArgsUsage: "<filename> (<filename 2> ... <filename N>) <genesisPath>",
|
||||
Flags: []cli.Flag{
|
||||
utils.DataDirFlag,
|
||||
utils.HeimdallURLFlag,
|
||||
utils.CacheFlag,
|
||||
utils.SyncModeFlag,
|
||||
utils.GCModeFlag,
|
||||
|
|
|
|||
|
|
@ -151,7 +151,7 @@ func enableWhisper(ctx *cli.Context) bool {
|
|||
|
||||
func makeFullNode(ctx *cli.Context) *node.Node {
|
||||
stack, cfg := makeConfigNode(ctx)
|
||||
cfg.Eth.HeimdallURL = ctx.String(utils.HeimdallURLFlag.Name)
|
||||
cfg.Eth.HeimdallURL = ctx.GlobalString(utils.HeimdallURLFlag.Name)
|
||||
log.Info("Connecting to heimdall service on...", "heimdallURL", cfg.Eth.HeimdallURL)
|
||||
utils.RegisterEthService(stack, &cfg.Eth)
|
||||
|
||||
|
|
|
|||
|
|
@ -111,6 +111,7 @@ func (w *wizard) makeGenesis() {
|
|||
Period: 1,
|
||||
ProducerDelay: 5,
|
||||
Sprint: 60,
|
||||
BackupMultiplier: 1,
|
||||
ValidatorContract: "0x0000000000000000000000000000000000001000",
|
||||
StateReceiverContract: "0x0000000000000000000000000000000000001001",
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1694,7 +1694,7 @@ func MakeChain(ctx *cli.Context, stack *node.Node) (chain *core.BlockChain, chai
|
|||
if config.Clique != nil {
|
||||
engine = clique.New(config.Clique, chainDb)
|
||||
} else if config.Bor != nil {
|
||||
cfg := ð.Config{Genesis: genesis}
|
||||
cfg := ð.Config{Genesis: genesis, HeimdallURL: ctx.GlobalString(HeimdallURLFlag.Name)}
|
||||
workspace, err := ioutil.TempDir("", "console-tester-")
|
||||
if err != nil {
|
||||
Fatalf("failed to create temporary keystore: %v", err)
|
||||
|
|
|
|||
|
|
@ -17,17 +17,33 @@
|
|||
package bor
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"math"
|
||||
"math/big"
|
||||
"strconv"
|
||||
"sync"
|
||||
|
||||
lru "github.com/hashicorp/golang-lru"
|
||||
"github.com/maticnetwork/bor/common"
|
||||
"github.com/maticnetwork/bor/consensus"
|
||||
"github.com/maticnetwork/bor/core/types"
|
||||
"github.com/maticnetwork/bor/crypto"
|
||||
"github.com/maticnetwork/bor/rpc"
|
||||
"github.com/xsleonard/go-merkle"
|
||||
"golang.org/x/crypto/sha3"
|
||||
)
|
||||
|
||||
var (
|
||||
// MaxCheckpointLength is the maximum number of blocks that can be requested for constructing a checkpoint root hash
|
||||
MaxCheckpointLength = uint64(math.Pow(2, 15))
|
||||
)
|
||||
|
||||
// API is a user facing RPC API to allow controlling the signer and voting
|
||||
// mechanisms of the proof-of-authority scheme.
|
||||
type API struct {
|
||||
chain consensus.ChainReader
|
||||
bor *Bor
|
||||
chain consensus.ChainReader
|
||||
bor *Bor
|
||||
rootHashCache *lru.ARCCache
|
||||
}
|
||||
|
||||
// GetSnapshot retrieves the state snapshot at a given block.
|
||||
|
|
@ -105,3 +121,71 @@ func (api *API) GetCurrentValidators() ([]*Validator, error) {
|
|||
}
|
||||
return snap.ValidatorSet.Validators, nil
|
||||
}
|
||||
|
||||
// GetRootHash returns the merkle root of the start to end block headers
|
||||
func (api *API) GetRootHash(start uint64, end uint64) (string, error) {
|
||||
if err := api.initializeRootHashCache(); err != nil {
|
||||
return "", err
|
||||
}
|
||||
key := getRootHashKey(start, end)
|
||||
if root, known := api.rootHashCache.Get(key); known {
|
||||
return root.(string), nil
|
||||
}
|
||||
length := uint64(end - start + 1)
|
||||
if length > MaxCheckpointLength {
|
||||
return "", &MaxCheckpointLengthExceededError{start, end}
|
||||
}
|
||||
currentHeaderNumber := api.chain.CurrentHeader().Number.Uint64()
|
||||
if start > end || end > currentHeaderNumber {
|
||||
return "", &InvalidStartEndBlockError{start, end, currentHeaderNumber}
|
||||
}
|
||||
blockHeaders := make([]*types.Header, end-start+1)
|
||||
wg := new(sync.WaitGroup)
|
||||
concurrent := make(chan bool, 20)
|
||||
for i := start; i <= end; i++ {
|
||||
wg.Add(1)
|
||||
concurrent <- true
|
||||
go func(number uint64) {
|
||||
blockHeaders[number-start] = api.chain.GetHeaderByNumber(uint64(number))
|
||||
<-concurrent
|
||||
wg.Done()
|
||||
}(i)
|
||||
}
|
||||
wg.Wait()
|
||||
close(concurrent)
|
||||
|
||||
headers := make([][32]byte, nextPowerOfTwo(length))
|
||||
for i := 0; i < len(blockHeaders); i++ {
|
||||
blockHeader := blockHeaders[i]
|
||||
header := crypto.Keccak256(appendBytes32(
|
||||
blockHeader.Number.Bytes(),
|
||||
new(big.Int).SetUint64(blockHeader.Time).Bytes(),
|
||||
blockHeader.TxHash.Bytes(),
|
||||
blockHeader.ReceiptHash.Bytes(),
|
||||
))
|
||||
|
||||
var arr [32]byte
|
||||
copy(arr[:], header)
|
||||
headers[i] = arr
|
||||
}
|
||||
|
||||
tree := merkle.NewTreeWithOpts(merkle.TreeOptions{EnableHashSorting: false, DisableHashLeaves: true})
|
||||
if err := tree.Generate(convert(headers), sha3.NewLegacyKeccak256()); err != nil {
|
||||
return "", err
|
||||
}
|
||||
root := hex.EncodeToString(tree.Root().Hash)
|
||||
api.rootHashCache.Add(key, root)
|
||||
return root, nil
|
||||
}
|
||||
|
||||
func (api *API) initializeRootHashCache() error {
|
||||
var err error
|
||||
if api.rootHashCache == nil {
|
||||
api.rootHashCache, err = lru.NewARC(10)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func getRootHashKey(start uint64, end uint64) string {
|
||||
return strconv.FormatUint(start, 10) + "-" + strconv.FormatUint(end, 10)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,7 +12,6 @@ import (
|
|||
"math/big"
|
||||
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
|
@ -41,9 +40,6 @@ import (
|
|||
"golang.org/x/crypto/sha3"
|
||||
)
|
||||
|
||||
const validatorsetABI = `[{"constant":true,"inputs":[{"name":"span","type":"uint256"}],"name":"getSpan","outputs":[{"name":"number","type":"uint256"},{"name":"startBlock","type":"uint256"},{"name":"endBlock","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"number","type":"uint256"}],"name":"getBorValidators","outputs":[{"name":"","type":"address[]"},{"name":"","type":"uint256[]"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"span","type":"uint256"},{"name":"signer","type":"address"}],"name":"isProducer","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"newSpan","type":"uint256"},{"name":"startBlock","type":"uint256"},{"name":"endBlock","type":"uint256"},{"name":"validatorBytes","type":"bytes"},{"name":"producerBytes","type":"bytes"}],"name":"commitSpan","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"span","type":"uint256"},{"name":"signer","type":"address"}],"name":"isValidator","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[],"name":"proposeSpan","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"currentSpanNumber","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"getNextSpan","outputs":[{"name":"number","type":"uint256"},{"name":"startBlock","type":"uint256"},{"name":"endBlock","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"getInitialValidators","outputs":[{"name":"","type":"address[]"},{"name":"","type":"uint256[]"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"spanProposalPending","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"getCurrentSpan","outputs":[{"name":"number","type":"uint256"},{"name":"startBlock","type":"uint256"},{"name":"endBlock","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"number","type":"uint256"}],"name":"getSpanByBlock","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"getValidators","outputs":[{"name":"","type":"address[]"},{"name":"","type":"uint256[]"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"vote","type":"bytes"},{"name":"sigs","type":"bytes"},{"name":"txBytes","type":"bytes"},{"name":"proof","type":"bytes"}],"name":"validateValidatorSet","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"}]`
|
||||
const stateReceiverABI = `[{"constant":true,"inputs":[{"name":"","type":"uint256"}],"name":"states","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"recordBytes","type":"bytes"}],"name":"commitState","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"getPendingStates","outputs":[{"name":"","type":"uint256[]"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"SYSTEM_ADDRESS","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"validatorSet","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"vote","type":"bytes"},{"name":"sigs","type":"bytes"},{"name":"txBytes","type":"bytes"},{"name":"proof","type":"bytes"}],"name":"validateValidatorSet","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"isValidatorSetContract","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"stateId","type":"uint256"}],"name":"proposeState","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"signer","type":"address"}],"name":"isProducer","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"signer","type":"address"}],"name":"isValidator","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"}]`
|
||||
|
||||
const (
|
||||
checkpointInterval = 1024 // Number of blocks after which to save the vote snapshot to the database
|
||||
inmemorySnapshots = 128 // Number of recent vote snapshots to keep in memory
|
||||
|
|
@ -105,10 +101,6 @@ var (
|
|||
// invalid list of validators (i.e. non divisible by 40 bytes).
|
||||
errInvalidSpanValidators = errors.New("invalid validator list on sprint end block")
|
||||
|
||||
// errMismatchingSprintValidators is returned if a sprint block contains a
|
||||
// list of validators different than the one the local node calculated.
|
||||
errMismatchingSprintValidators = errors.New("mismatching validator list on sprint block")
|
||||
|
||||
// errInvalidMixDigest is returned if a block's mix digest is non-zero.
|
||||
errInvalidMixDigest = errors.New("non-zero mix digest")
|
||||
|
||||
|
|
@ -118,10 +110,6 @@ var (
|
|||
// errInvalidDifficulty is returned if the difficulty of a block neither 1 or 2.
|
||||
errInvalidDifficulty = errors.New("invalid difficulty")
|
||||
|
||||
// errWrongDifficulty is returned if the difficulty of a block doesn't match the
|
||||
// turn of the signer.
|
||||
errWrongDifficulty = errors.New("wrong difficulty")
|
||||
|
||||
// ErrInvalidTimestamp is returned if the timestamp of a block is lower than
|
||||
// the previous block's timestamp + the minimum block period.
|
||||
ErrInvalidTimestamp = errors.New("invalid timestamp")
|
||||
|
|
@ -129,13 +117,6 @@ var (
|
|||
// errOutOfRangeChain is returned if an authorization list is attempted to
|
||||
// be modified via out-of-range or non-contiguous headers.
|
||||
errOutOfRangeChain = errors.New("out of range or non-contiguous chain")
|
||||
|
||||
// errUnauthorizedSigner is returned if a header is signed by a non-authorized entity.
|
||||
errUnauthorizedSigner = errors.New("unauthorized signer")
|
||||
|
||||
// errRecentlySigned is returned if a header is signed by an authorized entity
|
||||
// that already signed a header recently, thus is temporarily not allowed to.
|
||||
errRecentlySigned = errors.New("recently signed")
|
||||
)
|
||||
|
||||
// SignerFn is a signer callback function to request a header to be signed by a
|
||||
|
|
@ -198,21 +179,18 @@ func encodeSigHeader(w io.Writer, header *types.Header) {
|
|||
}
|
||||
}
|
||||
|
||||
// CalcDifficulty is the difficulty adjustment algorithm. It returns the difficulty
|
||||
// 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, epoch uint64) *big.Int {
|
||||
return big.NewInt(0).SetUint64(snap.inturn(snap.Number+1, signer, epoch))
|
||||
}
|
||||
|
||||
// CalcProducerDelay is the producer delay algorithm based on block time.
|
||||
func CalcProducerDelay(snap *Snapshot, signer common.Address, period uint64, epoch uint64, producerDelay uint64) uint64 {
|
||||
// if block is epoch start block, proposer will be inturn signer
|
||||
if (snap.Number+1)%epoch == 0 {
|
||||
return producerDelay
|
||||
// CalcProducerDelay is the block delay algorithm based on block time, period, producerDelay and turn-ness of a signer
|
||||
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
|
||||
if number%c.Sprint == 0 {
|
||||
delay = c.ProducerDelay
|
||||
}
|
||||
|
||||
return period
|
||||
if succession > 0 {
|
||||
delay += uint64(succession) * c.BackupMultiplier
|
||||
}
|
||||
return delay
|
||||
}
|
||||
|
||||
// BorRLP returns the rlp bytes which needs to be signed for the bor
|
||||
|
|
@ -241,10 +219,11 @@ type Bor struct {
|
|||
signFn SignerFn // Signer function to authorize hashes with
|
||||
lock sync.RWMutex // Protects the signer fields
|
||||
|
||||
ethAPI *ethapi.PublicBlockChainAPI
|
||||
validatorSetABI abi.ABI
|
||||
stateReceiverABI abi.ABI
|
||||
HeimdallClient IHeimdallClient
|
||||
ethAPI *ethapi.PublicBlockChainAPI
|
||||
GenesisContractsClient *GenesisContractsClient
|
||||
validatorSetABI abi.ABI
|
||||
stateReceiverABI abi.ABI
|
||||
HeimdallClient IHeimdallClient
|
||||
|
||||
stateDataFeed event.Feed
|
||||
scope event.SubscriptionScope
|
||||
|
|
@ -273,17 +252,18 @@ func New(
|
|||
vABI, _ := abi.JSON(strings.NewReader(validatorsetABI))
|
||||
sABI, _ := abi.JSON(strings.NewReader(stateReceiverABI))
|
||||
heimdallClient, _ := NewHeimdallClient(heimdallURL)
|
||||
|
||||
genesisContractsClient := NewGenesisContractsClient(chainConfig, borConfig.ValidatorContract, borConfig.StateReceiverContract, ethAPI)
|
||||
c := &Bor{
|
||||
chainConfig: chainConfig,
|
||||
config: borConfig,
|
||||
db: db,
|
||||
ethAPI: ethAPI,
|
||||
recents: recents,
|
||||
signatures: signatures,
|
||||
validatorSetABI: vABI,
|
||||
stateReceiverABI: sABI,
|
||||
HeimdallClient: heimdallClient,
|
||||
chainConfig: chainConfig,
|
||||
config: borConfig,
|
||||
db: db,
|
||||
ethAPI: ethAPI,
|
||||
recents: recents,
|
||||
signatures: signatures,
|
||||
validatorSetABI: vABI,
|
||||
stateReceiverABI: sABI,
|
||||
GenesisContractsClient: genesisContractsClient,
|
||||
HeimdallClient: heimdallClient,
|
||||
}
|
||||
|
||||
return c
|
||||
|
|
@ -335,12 +315,9 @@ func (c *Bor) verifyHeader(chain consensus.ChainReader, header *types.Header, pa
|
|||
if header.Time > uint64(time.Now().Unix()) {
|
||||
return consensus.ErrFutureBlock
|
||||
}
|
||||
// Check that the extra-data contains both the vanity and signature
|
||||
if len(header.Extra) < extraVanity {
|
||||
return errMissingVanity
|
||||
}
|
||||
if len(header.Extra) < extraVanity+extraSeal {
|
||||
return errMissingSignature
|
||||
|
||||
if err := validateHeaderExtraField(header.Extra); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// check extr adata
|
||||
|
|
@ -376,6 +353,18 @@ func (c *Bor) verifyHeader(chain consensus.ChainReader, header *types.Header, pa
|
|||
return c.verifyCascadingFields(chain, header, parents)
|
||||
}
|
||||
|
||||
// validateHeaderExtraField validates that the extra-data contains both the vanity and signature.
|
||||
// header.Extra = header.Vanity + header.ProducerBytes (optional) + header.Seal
|
||||
func validateHeaderExtraField(extraBytes []byte) error {
|
||||
if len(extraBytes) < extraVanity {
|
||||
return errMissingVanity
|
||||
}
|
||||
if len(extraBytes) < extraVanity+extraSeal {
|
||||
return errMissingSignature
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// verifyCascadingFields verifies all the header fields that are not standalone,
|
||||
// rather depend on a batch of previous headers. The caller may optionally pass
|
||||
// in a batch of parents (ascending order) to avoid looking those up from the
|
||||
|
|
@ -409,8 +398,9 @@ func (c *Bor) verifyCascadingFields(chain consensus.ChainReader, header *types.H
|
|||
return err
|
||||
}
|
||||
|
||||
// If the block is a sprint end block, verify the validator list
|
||||
if number%c.config.Sprint == 0 {
|
||||
// verify the validator list in the last sprint block
|
||||
if isSprintStart(number, c.config.Sprint) {
|
||||
parentValidatorBytes := parent.Extra[extraVanity : len(parent.Extra)-extraSeal]
|
||||
validatorsBytes := make([]byte, len(snap.ValidatorSet.Validators)*validatorHeaderBytesLength)
|
||||
|
||||
currentValidators := snap.ValidatorSet.Copy().Validators
|
||||
|
|
@ -419,14 +409,9 @@ func (c *Bor) verifyCascadingFields(chain consensus.ChainReader, header *types.H
|
|||
for i, validator := range currentValidators {
|
||||
copy(validatorsBytes[i*validatorHeaderBytesLength:], validator.HeaderBytes())
|
||||
}
|
||||
|
||||
extraSuffix := len(header.Extra) - extraSeal
|
||||
|
||||
// fmt.Println("validatorsBytes ==> verify seal ==> ", hex.EncodeToString(validatorsBytes))
|
||||
// fmt.Println("header.Extra ==> verify seal ==> ", hex.EncodeToString(header.Extra[extraVanity:extraSuffix]))
|
||||
|
||||
if !bytes.Equal(header.Extra[extraVanity:extraSuffix], validatorsBytes) {
|
||||
// return errMismatchingSprintValidators
|
||||
// len(header.Extra) >= extraVanity+extraSeal has already been validated in validateHeaderExtraField, so this won't result in a panic
|
||||
if !bytes.Equal(parentValidatorBytes, validatorsBytes) {
|
||||
return &MismatchingValidatorsError{number - 1, validatorsBytes, parentValidatorBytes}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -463,7 +448,7 @@ func (c *Bor) snapshot(chain consensus.ChainReader, number uint64, hash common.H
|
|||
// up more headers than allowed to be reorged (chain reinit from a freezer),
|
||||
// consider the checkpoint trusted and snapshot it.
|
||||
// TODO fix this
|
||||
if number == 0 /* || (number%c.config.Sprint == 0 && (len(headers) > params.ImmutabilityThreshold || chain.GetHeaderByNumber(number-1) == nil)) */ {
|
||||
if number == 0 {
|
||||
checkpoint := chain.GetHeaderByNumber(number)
|
||||
if checkpoint != nil {
|
||||
// get checkpoint data
|
||||
|
|
@ -568,37 +553,31 @@ func (c *Bor) verifySeal(chain consensus.ChainReader, header *types.Header, pare
|
|||
return err
|
||||
}
|
||||
if !snap.ValidatorSet.HasAddress(signer.Bytes()) {
|
||||
return errUnauthorizedSigner
|
||||
// Check the UnauthorizedSignerError.Error() msg to see why we pass number-1
|
||||
return &UnauthorizedSignerError{number - 1, signer.Bytes()}
|
||||
}
|
||||
|
||||
// check if signer is correct
|
||||
validators := snap.ValidatorSet.Validators
|
||||
// proposer will be the last signer if block is not epoch block
|
||||
proposer := snap.ValidatorSet.GetProposer().Address
|
||||
if number%c.config.Sprint != 0 {
|
||||
// proposer = snap.Recents[number-1]
|
||||
succession, err := snap.GetSignerSuccessionNumber(signer)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
proposerIndex, _ := snap.ValidatorSet.GetByAddress(proposer)
|
||||
signerIndex, _ := snap.ValidatorSet.GetByAddress(signer)
|
||||
limit := len(validators)/2 + 1
|
||||
|
||||
// temp index
|
||||
tempIndex := signerIndex
|
||||
if proposerIndex != tempIndex && limit > 0 {
|
||||
if tempIndex < proposerIndex {
|
||||
tempIndex = tempIndex + len(validators)
|
||||
}
|
||||
var parent *types.Header
|
||||
if len(parents) > 0 { // if parents is nil, len(parents) is zero
|
||||
parent = parents[len(parents)-1]
|
||||
} else if number > 0 {
|
||||
parent = chain.GetHeader(header.ParentHash, number-1)
|
||||
}
|
||||
|
||||
if tempIndex-proposerIndex > limit {
|
||||
return errRecentlySigned
|
||||
}
|
||||
if parent != nil && header.Time < parent.Time+CalcProducerDelay(number, succession, c.config) {
|
||||
return &BlockTooSoonError{number, succession}
|
||||
}
|
||||
|
||||
// Ensure that the difficulty corresponds to the turn-ness of the signer
|
||||
if !c.fakeDiff {
|
||||
difficulty := snap.inturn(header.Number.Uint64(), signer, c.config.Sprint)
|
||||
difficulty := snap.Difficulty(signer)
|
||||
if header.Difficulty.Uint64() != difficulty {
|
||||
return errWrongDifficulty
|
||||
return &WrongDifficultyError{number, difficulty, header.Difficulty.Uint64(), signer.Bytes()}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -620,7 +599,7 @@ func (c *Bor) Prepare(chain consensus.ChainReader, header *types.Header) error {
|
|||
}
|
||||
|
||||
// Set the correct difficulty
|
||||
header.Difficulty = CalcDifficulty(snap, c.signer, c.config.Sprint)
|
||||
header.Difficulty = new(big.Int).SetUint64(snap.Difficulty(c.signer))
|
||||
|
||||
// Ensure the extra data has all it's components
|
||||
if len(header.Extra) < extraVanity {
|
||||
|
|
@ -654,7 +633,16 @@ func (c *Bor) Prepare(chain consensus.ChainReader, header *types.Header) error {
|
|||
return consensus.ErrUnknownAncestor
|
||||
}
|
||||
|
||||
header.Time = parent.Time + CalcProducerDelay(snap, c.signer, c.config.Period, c.config.Sprint, c.config.ProducerDelay)
|
||||
var succession int
|
||||
// if signer is not empty
|
||||
if bytes.Compare(c.signer.Bytes(), common.Address{}.Bytes()) != 0 {
|
||||
succession, err = snap.GetSignerSuccessionNumber(c.signer)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
header.Time = parent.Time + CalcProducerDelay(number, succession, c.config)
|
||||
if header.Time < uint64(time.Now().Unix()) {
|
||||
header.Time = uint64(time.Now().Unix())
|
||||
}
|
||||
|
|
@ -664,9 +652,7 @@ func (c *Bor) Prepare(chain consensus.ChainReader, header *types.Header) error {
|
|||
// Finalize implements consensus.Engine, ensuring no uncles are set, nor block
|
||||
// rewards given.
|
||||
func (c *Bor) Finalize(chain consensus.ChainReader, header *types.Header, state *state.StateDB, txs []*types.Transaction, uncles []*types.Header) {
|
||||
// commit span
|
||||
headerNumber := header.Number.Uint64()
|
||||
|
||||
if headerNumber%c.config.Sprint == 0 {
|
||||
cx := chainContext{Chain: chain, Bor: c}
|
||||
// check and commit span
|
||||
|
|
@ -674,6 +660,7 @@ func (c *Bor) Finalize(chain consensus.ChainReader, header *types.Header, state
|
|||
log.Error("Error while committing span", "error", err)
|
||||
return
|
||||
}
|
||||
|
||||
// commit statees
|
||||
if err := c.CommitStates(state, header, cx); err != nil {
|
||||
log.Error("Error while committing states", "error", err)
|
||||
|
|
@ -689,8 +676,8 @@ func (c *Bor) Finalize(chain consensus.ChainReader, header *types.Header, state
|
|||
// FinalizeAndAssemble implements consensus.Engine, ensuring no uncles are set,
|
||||
// nor block rewards given, and returns the final block.
|
||||
func (c *Bor) FinalizeAndAssemble(chain consensus.ChainReader, header *types.Header, state *state.StateDB, txs []*types.Transaction, uncles []*types.Header, receipts []*types.Receipt) (*types.Block, error) {
|
||||
// commit span
|
||||
if header.Number.Uint64()%c.config.Sprint == 0 {
|
||||
headerNumber := header.Number.Uint64()
|
||||
if headerNumber%c.config.Sprint == 0 {
|
||||
cx := chainContext{Chain: chain, Bor: c}
|
||||
|
||||
// check and commit span
|
||||
|
|
@ -703,7 +690,7 @@ func (c *Bor) FinalizeAndAssemble(chain consensus.ChainReader, header *types.Hea
|
|||
// commit statees
|
||||
if err := c.CommitStates(state, header, cx); err != nil {
|
||||
log.Error("Error while committing states", "error", err)
|
||||
// return nil, err
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -752,38 +739,19 @@ func (c *Bor) Seal(chain consensus.ChainReader, block *types.Block, results chan
|
|||
|
||||
// Bail out if we're unauthorized to sign a block
|
||||
if !snap.ValidatorSet.HasAddress(signer.Bytes()) {
|
||||
return errUnauthorizedSigner
|
||||
// Check the UnauthorizedSignerError.Error() msg to see why we pass number-1
|
||||
return &UnauthorizedSignerError{number - 1, signer.Bytes()}
|
||||
}
|
||||
|
||||
validators := snap.ValidatorSet.Validators
|
||||
// proposer will be the last signer if block is not epoch block
|
||||
proposer := snap.ValidatorSet.GetProposer().Address
|
||||
if number%c.config.Sprint != 0 {
|
||||
// proposer = snap.Recents[number-1]
|
||||
}
|
||||
|
||||
proposerIndex, _ := snap.ValidatorSet.GetByAddress(proposer)
|
||||
signerIndex, _ := snap.ValidatorSet.GetByAddress(signer)
|
||||
limit := len(validators)/2 + 1
|
||||
|
||||
// temp index
|
||||
tempIndex := signerIndex
|
||||
if tempIndex < proposerIndex {
|
||||
tempIndex = tempIndex + len(validators)
|
||||
}
|
||||
|
||||
if limit > 0 && tempIndex-proposerIndex > limit {
|
||||
log.Info("Signed recently, must wait for others")
|
||||
return nil
|
||||
successionNumber, err := snap.GetSignerSuccessionNumber(signer)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 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 := time.Duration(2*c.config.Period) * time.Second * time.Duration(tempIndex-proposerIndex)
|
||||
delay += wiggle
|
||||
|
||||
log.Info("Out-of-turn signing requested", "wiggle", common.PrettyDuration(wiggle))
|
||||
log.Info("Sealing block with", "number", number, "delay", delay, "headerDifficulty", header.Difficulty, "signer", signer.Hex(), "proposer", proposer.Hex())
|
||||
// wiggle was already accounted for in header.Time, this is just for logging
|
||||
wiggle := time.Duration(successionNumber) * time.Duration(c.config.BackupMultiplier) * time.Second
|
||||
|
||||
// Sign all the things!
|
||||
sighash, err := signFn(accounts.Account{Address: signer}, accounts.MimetypeBor, BorRLP(header))
|
||||
|
|
@ -797,17 +765,30 @@ func (c *Bor) Seal(chain consensus.ChainReader, block *types.Block, results chan
|
|||
go func() {
|
||||
select {
|
||||
case <-stop:
|
||||
log.Debug("Discarding sealing operation for block", "number", number)
|
||||
return
|
||||
case <-time.After(delay):
|
||||
if wiggle > 0 {
|
||||
log.Info(
|
||||
"Sealing out-of-turn",
|
||||
"number", number,
|
||||
"wiggle", common.PrettyDuration(wiggle),
|
||||
"in-turn-signer", snap.ValidatorSet.GetProposer().Address.Hex(),
|
||||
)
|
||||
}
|
||||
log.Info(
|
||||
"Sealing successful",
|
||||
"number", number,
|
||||
"delay", delay,
|
||||
"headerDifficulty", header.Difficulty,
|
||||
)
|
||||
}
|
||||
|
||||
select {
|
||||
case results <- block.WithSeal(header):
|
||||
default:
|
||||
log.Warn("Sealing result is not read by miner", "sealhash", SealHash(header))
|
||||
log.Warn("Sealing result was not read by miner", "number", number, "sealhash", SealHash(header))
|
||||
}
|
||||
}()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
@ -819,7 +800,7 @@ func (c *Bor) CalcDifficulty(chain consensus.ChainReader, time uint64, parent *t
|
|||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
return CalcDifficulty(snap, c.signer, c.config.Sprint)
|
||||
return new(big.Int).SetUint64(snap.Difficulty(c.signer))
|
||||
}
|
||||
|
||||
// SealHash returns the hash of a block prior to it being sealed.
|
||||
|
|
@ -843,42 +824,6 @@ func (c *Bor) Close() error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// Checks if new span is pending
|
||||
func (c *Bor) isSpanPending(snapshotNumber uint64) (bool, error) {
|
||||
blockNr := rpc.BlockNumber(snapshotNumber)
|
||||
method := "spanProposalPending"
|
||||
|
||||
// get packed data
|
||||
data, err := c.validatorSetABI.Pack(method)
|
||||
if err != nil {
|
||||
log.Error("Unable to pack tx for spanProposalPending", "error", err)
|
||||
return false, err
|
||||
}
|
||||
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel() // cancel when we are finished consuming integers
|
||||
|
||||
// call
|
||||
msgData := (hexutil.Bytes)(data)
|
||||
toAddress := common.HexToAddress(c.config.ValidatorContract)
|
||||
gas := (hexutil.Uint64)(uint64(math.MaxUint64 / 2))
|
||||
result, err := c.ethAPI.Call(ctx, ethapi.CallArgs{
|
||||
Gas: &gas,
|
||||
To: &toAddress,
|
||||
Data: &msgData,
|
||||
}, blockNr)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
var ret0 = new(bool)
|
||||
if err := c.validatorSetABI.Unpack(ret0, method, result); err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
return *ret0, nil
|
||||
}
|
||||
|
||||
// GetCurrentSpan get current span from contract
|
||||
func (c *Bor) GetCurrentSpan(snapshotNumber uint64) (*Span, error) {
|
||||
// block
|
||||
|
|
@ -893,14 +838,10 @@ func (c *Bor) GetCurrentSpan(snapshotNumber uint64) (*Span, error) {
|
|||
return nil, err
|
||||
}
|
||||
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel() // cancel when we are finished consuming integers
|
||||
|
||||
// call
|
||||
msgData := (hexutil.Bytes)(data)
|
||||
toAddress := common.HexToAddress(c.config.ValidatorContract)
|
||||
gas := (hexutil.Uint64)(uint64(math.MaxUint64 / 2))
|
||||
result, err := c.ethAPI.Call(ctx, ethapi.CallArgs{
|
||||
result, err := c.ethAPI.Call(context.Background(), ethapi.CallArgs{
|
||||
Gas: &gas,
|
||||
To: &toAddress,
|
||||
Data: &msgData,
|
||||
|
|
@ -943,14 +884,11 @@ func (c *Bor) GetCurrentValidators(snapshotNumber uint64, blockNumber uint64) ([
|
|||
return nil, err
|
||||
}
|
||||
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel() // cancel when we are finished consuming integers
|
||||
|
||||
// call
|
||||
msgData := (hexutil.Bytes)(data)
|
||||
toAddress := common.HexToAddress(c.config.ValidatorContract)
|
||||
gas := (hexutil.Uint64)(uint64(math.MaxUint64 / 2))
|
||||
result, err := c.ethAPI.Call(ctx, ethapi.CallArgs{
|
||||
result, err := c.ethAPI.Call(context.Background(), ethapi.CallArgs{
|
||||
Gas: &gas,
|
||||
To: &toAddress,
|
||||
Data: &msgData,
|
||||
|
|
@ -990,37 +928,14 @@ func (c *Bor) checkAndCommitSpan(
|
|||
chain core.ChainContext,
|
||||
) error {
|
||||
headerNumber := header.Number.Uint64()
|
||||
pending := false
|
||||
var span *Span = nil
|
||||
errors := make(chan error)
|
||||
go func() {
|
||||
var err error
|
||||
pending, err = c.isSpanPending(headerNumber - 1)
|
||||
errors <- err
|
||||
}()
|
||||
|
||||
go func() {
|
||||
var err error
|
||||
span, err = c.GetCurrentSpan(headerNumber - 1)
|
||||
errors <- err
|
||||
}()
|
||||
|
||||
var err error
|
||||
for i := 0; i < 2; i++ {
|
||||
err = <-errors
|
||||
if err != nil {
|
||||
close(errors)
|
||||
return err
|
||||
}
|
||||
span, err := c.GetCurrentSpan(headerNumber - 1)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
close(errors)
|
||||
|
||||
// commit span if there is new span pending or span is ending or end block is not set
|
||||
if pending || c.needToCommitSpan(span, headerNumber) {
|
||||
if c.needToCommitSpan(span, headerNumber) {
|
||||
err := c.fetchAndCommitSpan(span.ID+1, state, header, chain)
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
@ -1049,7 +964,7 @@ func (c *Bor) fetchAndCommitSpan(
|
|||
header *types.Header,
|
||||
chain core.ChainContext,
|
||||
) error {
|
||||
response, err := c.HeimdallClient.FetchWithRetry("bor", "span", strconv.FormatUint(newSpanID, 10))
|
||||
response, err := c.HeimdallClient.FetchWithRetry(fmt.Sprintf("bor/span/%d", newSpanID), "")
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
|
|
@ -1133,13 +1048,10 @@ func (c *Bor) GetPendingStateProposals(snapshotNumber uint64) ([]*big.Int, error
|
|||
return nil, err
|
||||
}
|
||||
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel() // cancel when we are finished consuming integers
|
||||
|
||||
msgData := (hexutil.Bytes)(data)
|
||||
toAddress := common.HexToAddress(c.config.StateReceiverContract)
|
||||
gas := (hexutil.Uint64)(uint64(math.MaxUint64 / 2))
|
||||
result, err := c.ethAPI.Call(ctx, ethapi.CallArgs{
|
||||
result, err := c.ethAPI.Call(context.Background(), ethapi.CallArgs{
|
||||
Gas: &gas,
|
||||
To: &toAddress,
|
||||
Data: &msgData,
|
||||
|
|
@ -1160,83 +1072,55 @@ func (c *Bor) GetPendingStateProposals(snapshotNumber uint64) ([]*big.Int, error
|
|||
func (c *Bor) CommitStates(
|
||||
state *state.StateDB,
|
||||
header *types.Header,
|
||||
chain core.ChainContext,
|
||||
chain chainContext,
|
||||
) error {
|
||||
// get pending state proposals
|
||||
stateIds, err := c.GetPendingStateProposals(header.Number.Uint64() - 1)
|
||||
number := header.Number.Uint64()
|
||||
_lastStateID, err := c.GenesisContractsClient.LastStateId(number - 1)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// state ids
|
||||
if len(stateIds) > 0 {
|
||||
log.Debug("Found new proposed states", "numberOfStates", len(stateIds))
|
||||
}
|
||||
to := time.Unix(int64(chain.Chain.GetHeaderByNumber(number-c.config.Sprint).Time), 0)
|
||||
lastStateID := _lastStateID.Uint64()
|
||||
log.Info(
|
||||
"Fetching state updates from Heimdall",
|
||||
"fromID", lastStateID+1,
|
||||
"to", to.Format(time.RFC3339))
|
||||
eventRecords, err := c.HeimdallClient.FetchStateSyncEvents(lastStateID+1, to.Unix())
|
||||
|
||||
method := "commitState"
|
||||
|
||||
// itereate through state ids
|
||||
for _, stateID := range stateIds {
|
||||
// fetch from heimdall
|
||||
response, err := c.HeimdallClient.FetchWithRetry("clerk", "event-record", strconv.FormatUint(stateID.Uint64(), 10))
|
||||
if err != nil {
|
||||
return err
|
||||
chainID := c.chainConfig.ChainID.String()
|
||||
for _, eventRecord := range eventRecords {
|
||||
if eventRecord.ID <= lastStateID {
|
||||
continue
|
||||
}
|
||||
if err := validateEventRecord(eventRecord, number, to, lastStateID, chainID); err != nil {
|
||||
log.Error(err.Error())
|
||||
break
|
||||
}
|
||||
|
||||
// get event record
|
||||
var eventRecord EventRecord
|
||||
if err := json.Unmarshal(response.Result, &eventRecord); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// check if chain id matches with event record
|
||||
if eventRecord.ChainID != "" && eventRecord.ChainID != c.chainConfig.ChainID.String() {
|
||||
return fmt.Errorf(
|
||||
"Chain id proposed state in span, %s, and bor chain id, %s, doesn't match",
|
||||
eventRecord.ChainID,
|
||||
c.chainConfig.ChainID,
|
||||
)
|
||||
}
|
||||
|
||||
log.Info("→ committing new state",
|
||||
"id", eventRecord.ID,
|
||||
"contract", eventRecord.Contract,
|
||||
"data", hex.EncodeToString(eventRecord.Data),
|
||||
"txHash", eventRecord.TxHash,
|
||||
"chainID", eventRecord.ChainID,
|
||||
)
|
||||
stateData := types.StateData{
|
||||
Did: eventRecord.ID,
|
||||
Contract: eventRecord.Contract,
|
||||
Data: hex.EncodeToString(eventRecord.Data),
|
||||
TxHash: eventRecord.TxHash,
|
||||
}
|
||||
|
||||
go func() {
|
||||
c.stateDataFeed.Send(core.NewStateChangeEvent{StateData: &stateData})
|
||||
}()
|
||||
|
||||
recordBytes, err := rlp.EncodeToBytes(eventRecord)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// get packed data for commit state
|
||||
data, err := c.stateReceiverABI.Pack(method, recordBytes)
|
||||
if err != nil {
|
||||
log.Error("Unable to pack tx for commitState", "error", err)
|
||||
return err
|
||||
}
|
||||
|
||||
// get system message
|
||||
msg := getSystemMessage(common.HexToAddress(c.config.StateReceiverContract), data)
|
||||
|
||||
// apply message
|
||||
if err := applyMessage(msg, state, header, c.chainConfig, chain); err != nil {
|
||||
if err := c.GenesisContractsClient.CommitState(eventRecord, state, header, chain); err != nil {
|
||||
return err
|
||||
}
|
||||
lastStateID++
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func validateEventRecord(eventRecord *EventRecordWithTime, number uint64, to time.Time, lastStateID uint64, chainID string) error {
|
||||
// event id should be sequential and event.Time should lie in the range [from, to)
|
||||
if lastStateID+1 != eventRecord.ID || eventRecord.ChainID != chainID || !eventRecord.Time.Before(to) {
|
||||
return &InvalidStateReceivedError{number, lastStateID, &to, eventRecord}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
@ -1249,48 +1133,6 @@ func (c *Bor) SetHeimdallClient(h IHeimdallClient) {
|
|||
c.HeimdallClient = h
|
||||
}
|
||||
|
||||
func (c *Bor) IsValidatorAction(chain consensus.ChainReader, from common.Address, tx *types.Transaction) bool {
|
||||
header := chain.CurrentHeader()
|
||||
validators, err := c.GetCurrentValidators(header.Number.Uint64(), header.Number.Uint64()+1)
|
||||
if err != nil {
|
||||
log.Error("Failed fetching snapshot", err)
|
||||
return false
|
||||
}
|
||||
|
||||
isValidator := false
|
||||
for _, validator := range validators {
|
||||
if bytes.Compare(validator.Address.Bytes(), from.Bytes()) == 0 {
|
||||
isValidator = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return isValidator && (isProposeSpanAction(tx, chain.Config().Bor.ValidatorContract) ||
|
||||
isProposeStateAction(tx, chain.Config().Bor.StateReceiverContract))
|
||||
}
|
||||
|
||||
func isProposeSpanAction(tx *types.Transaction, validatorContract string) bool {
|
||||
// keccak256('proposeSpan()').slice(0, 4)
|
||||
proposeSpanSig, _ := hex.DecodeString("4b0e4d17")
|
||||
if tx.Data() == nil || len(tx.Data()) < 4 {
|
||||
return false
|
||||
}
|
||||
|
||||
return bytes.Compare(proposeSpanSig, tx.Data()[:4]) == 0 &&
|
||||
tx.To().String() == validatorContract
|
||||
}
|
||||
|
||||
func isProposeStateAction(tx *types.Transaction, stateReceiverContract string) bool {
|
||||
// keccak256('proposeState(uint256)').slice(0, 4)
|
||||
proposeStateSig, _ := hex.DecodeString("ede01f17")
|
||||
if tx.Data() == nil || len(tx.Data()) < 4 {
|
||||
return false
|
||||
}
|
||||
|
||||
return bytes.Compare(proposeStateSig, tx.Data()[:4]) == 0 &&
|
||||
tx.To().String() == stateReceiverContract
|
||||
}
|
||||
|
||||
//
|
||||
// Private methods
|
||||
//
|
||||
|
|
@ -1403,3 +1245,7 @@ func getUpdatedValidatorSet(oldValidatorSet *ValidatorSet, newVals []*Validator)
|
|||
v.UpdateWithChangeSet(changes)
|
||||
return v
|
||||
}
|
||||
|
||||
func isSprintStart(number, sprint uint64) bool {
|
||||
return number%sprint == 0
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,130 +2,269 @@ package bortest
|
|||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"math/big"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/mock"
|
||||
|
||||
"github.com/maticnetwork/bor/common"
|
||||
"github.com/maticnetwork/bor/consensus/bor"
|
||||
"github.com/maticnetwork/bor/core/rawdb"
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
"github.com/maticnetwork/bor/core/types"
|
||||
|
||||
"github.com/maticnetwork/bor/crypto"
|
||||
"github.com/maticnetwork/bor/mocks"
|
||||
)
|
||||
|
||||
func TestCommitSpan(t *testing.T) {
|
||||
var (
|
||||
spanPath = "bor/span/1"
|
||||
clerkPath = "clerk/event-record/list"
|
||||
clerkQueryParams = "from-time=%d&to-time=%d&page=%d&limit=50"
|
||||
)
|
||||
|
||||
func TestInsertingSpanSizeBlocks(t *testing.T) {
|
||||
init := buildEthereumInstance(t, rawdb.NewMemoryDatabase())
|
||||
chain := init.ethereum.BlockChain()
|
||||
engine := init.ethereum.Engine()
|
||||
_bor := engine.(*bor.Bor)
|
||||
|
||||
// Mock HeimdallClient.FetchWithRetry to return span data from span.json
|
||||
res, heimdallSpan := loadSpanFromFile(t)
|
||||
h := &mocks.IHeimdallClient{}
|
||||
h.On("FetchWithRetry", "bor", "span", "1").
|
||||
Return(res, nil).
|
||||
Times(2) // both FinalizeAndAssemble and chain.InsertChain call HeimdallClient.FetchWithRetry. @todo Investigate this in depth
|
||||
h, heimdallSpan := getMockedHeimdallClient(t)
|
||||
_bor.SetHeimdallClient(h)
|
||||
|
||||
db := init.ethereum.ChainDb()
|
||||
block := init.genesis.ToBlock(db)
|
||||
// Build 1st block's header
|
||||
header := buildMinimalNextHeader(t, block, init.genesis.Config.Bor.Period)
|
||||
// to := int64(block.Header().Time)
|
||||
|
||||
statedb, err := chain.State()
|
||||
// Insert sprintSize # of blocks so that span is fetched at the start of a new sprint
|
||||
for i := uint64(1); i <= spanSize; i++ {
|
||||
block = buildNextBlock(t, _bor, chain, block, nil, init.genesis.Config.Bor)
|
||||
insertNewBlock(t, chain, block)
|
||||
}
|
||||
|
||||
assert.True(t, h.AssertCalled(t, "FetchWithRetry", spanPath, ""))
|
||||
validators, err := _bor.GetCurrentValidators(sprintSize, spanSize) // check validator set at the first block of new span
|
||||
if err != nil {
|
||||
t.Fatalf("%s", err)
|
||||
}
|
||||
|
||||
_key, _ := hex.DecodeString(privKey)
|
||||
insertNewBlock(t, _bor, chain, header, statedb, _key)
|
||||
|
||||
assert.True(t, h.AssertNumberOfCalls(t, "FetchWithRetry", 2))
|
||||
validators, err := _bor.GetCurrentValidators(1, 256) // new span starts at 256
|
||||
if err != nil {
|
||||
t.Fatalf("%s", err)
|
||||
}
|
||||
|
||||
assert.Equal(t, len(validators), 3)
|
||||
assert.Equal(t, 3, len(validators))
|
||||
for i, validator := range validators {
|
||||
assert.Equal(t, validator.Address.Bytes(), heimdallSpan.SelectedProducers[i].Address.Bytes())
|
||||
assert.Equal(t, validator.VotingPower, heimdallSpan.SelectedProducers[i].VotingPower)
|
||||
}
|
||||
}
|
||||
|
||||
func TestIsValidatorAction(t *testing.T) {
|
||||
func TestFetchStateSyncEvents(t *testing.T) {
|
||||
init := buildEthereumInstance(t, rawdb.NewMemoryDatabase())
|
||||
chain := init.ethereum.BlockChain()
|
||||
engine := init.ethereum.Engine()
|
||||
_bor := engine.(*bor.Bor)
|
||||
|
||||
proposeStateData, _ := hex.DecodeString("ede01f170000000000000000000000000000000000000000000000000000000000000000")
|
||||
proposeSpanData, _ := hex.DecodeString("4b0e4d17")
|
||||
var tx *types.Transaction
|
||||
tx = types.NewTransaction(
|
||||
0,
|
||||
common.HexToAddress(chain.Config().Bor.StateReceiverContract),
|
||||
big.NewInt(0), 0, big.NewInt(0),
|
||||
proposeStateData,
|
||||
)
|
||||
assert.True(t, _bor.IsValidatorAction(chain, addr, tx))
|
||||
// A. Insert blocks for 0th sprint
|
||||
db := init.ethereum.ChainDb()
|
||||
block := init.genesis.ToBlock(db)
|
||||
// Insert sprintSize # of blocks so that span is fetched at the start of a new sprint
|
||||
for i := uint64(1); i < sprintSize; i++ {
|
||||
block = buildNextBlock(t, _bor, chain, block, nil, init.genesis.Config.Bor)
|
||||
insertNewBlock(t, chain, block)
|
||||
}
|
||||
|
||||
tx = types.NewTransaction(
|
||||
0,
|
||||
common.HexToAddress(chain.Config().Bor.ValidatorContract),
|
||||
big.NewInt(0), 0, big.NewInt(0),
|
||||
proposeSpanData,
|
||||
)
|
||||
assert.True(t, _bor.IsValidatorAction(chain, addr, tx))
|
||||
|
||||
res, heimdallSpan := loadSpanFromFile(t)
|
||||
// B. Before inserting 1st block of the next sprint, mock heimdall deps
|
||||
// B.1 Mock /bor/span/1
|
||||
res, _ := loadSpanFromFile(t)
|
||||
h := &mocks.IHeimdallClient{}
|
||||
h.On("FetchWithRetry", "bor", "span", "1").Return(res, nil)
|
||||
h.On("FetchWithRetry", spanPath, "").Return(res, nil)
|
||||
|
||||
// B.2 Mock State Sync events
|
||||
fromID := uint64(1)
|
||||
// at # sprintSize, events are fetched for [fromID, (block-sprint).Time)
|
||||
to := int64(chain.GetHeaderByNumber(0).Time)
|
||||
eventCount := 50
|
||||
|
||||
sample := getSampleEventRecord(t)
|
||||
sample.Time = time.Unix(to-int64(eventCount+1), 0) // last event.Time will be just < to
|
||||
eventRecords := generateFakeStateSyncEvents(sample, eventCount)
|
||||
h.On("FetchStateSyncEvents", fromID, to).Return(eventRecords, nil)
|
||||
_bor.SetHeimdallClient(h)
|
||||
|
||||
block = buildNextBlock(t, _bor, chain, block, nil, init.genesis.Config.Bor)
|
||||
insertNewBlock(t, chain, block)
|
||||
|
||||
assert.True(t, h.AssertCalled(t, "FetchWithRetry", spanPath, ""))
|
||||
assert.True(t, h.AssertCalled(t, "FetchStateSyncEvents", fromID, to))
|
||||
}
|
||||
|
||||
func TestFetchStateSyncEvents_2(t *testing.T) {
|
||||
init := buildEthereumInstance(t, rawdb.NewMemoryDatabase())
|
||||
chain := init.ethereum.BlockChain()
|
||||
engine := init.ethereum.Engine()
|
||||
_bor := engine.(*bor.Bor)
|
||||
|
||||
// Mock /bor/span/1
|
||||
res, _ := loadSpanFromFile(t)
|
||||
h := &mocks.IHeimdallClient{}
|
||||
h.On("FetchWithRetry", spanPath, "").Return(res, nil)
|
||||
|
||||
// Mock State Sync events
|
||||
// at # sprintSize, events are fetched for [fromID, (block-sprint).Time)
|
||||
fromID := uint64(1)
|
||||
to := int64(chain.GetHeaderByNumber(0).Time)
|
||||
sample := getSampleEventRecord(t)
|
||||
|
||||
// First query will be from [id=1, (block-sprint).Time]
|
||||
// Insert 5 events in this time range
|
||||
eventRecords := []*bor.EventRecordWithTime{
|
||||
buildStateEvent(sample, 1, 3), // id = 1, time = 1
|
||||
buildStateEvent(sample, 2, 1), // id = 2, time = 3
|
||||
buildStateEvent(sample, 3, 2), // id = 3, time = 2
|
||||
// event with id 5 is missing
|
||||
buildStateEvent(sample, 4, 5), // id = 4, time = 5
|
||||
buildStateEvent(sample, 6, 4), // id = 6, time = 4
|
||||
}
|
||||
h.On("FetchStateSyncEvents", fromID, to).Return(eventRecords, nil)
|
||||
_bor.SetHeimdallClient(h)
|
||||
|
||||
// Insert blocks for 0th sprint
|
||||
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)
|
||||
}
|
||||
assert.True(t, h.AssertCalled(t, "FetchWithRetry", spanPath, ""))
|
||||
assert.True(t, h.AssertCalled(t, "FetchStateSyncEvents", fromID, to))
|
||||
lastStateID, _ := _bor.GenesisContractsClient.LastStateId(sprintSize)
|
||||
// state 6 was not written
|
||||
assert.Equal(t, uint64(4), lastStateID.Uint64())
|
||||
|
||||
//
|
||||
fromID = uint64(5)
|
||||
to = int64(chain.GetHeaderByNumber(sprintSize).Time)
|
||||
eventRecords = []*bor.EventRecordWithTime{
|
||||
buildStateEvent(sample, 5, 7),
|
||||
buildStateEvent(sample, 6, 4),
|
||||
}
|
||||
h.On("FetchStateSyncEvents", fromID, to).Return(eventRecords, nil)
|
||||
for i := sprintSize + 1; i <= spanSize; i++ {
|
||||
block = buildNextBlock(t, _bor, chain, block, nil, init.genesis.Config.Bor)
|
||||
insertNewBlock(t, chain, block)
|
||||
}
|
||||
assert.True(t, h.AssertCalled(t, "FetchStateSyncEvents", fromID, to))
|
||||
lastStateID, _ = _bor.GenesisContractsClient.LastStateId(spanSize)
|
||||
assert.Equal(t, uint64(6), lastStateID.Uint64())
|
||||
}
|
||||
|
||||
func TestOutOfTurnSigning(t *testing.T) {
|
||||
init := buildEthereumInstance(t, rawdb.NewMemoryDatabase())
|
||||
chain := init.ethereum.BlockChain()
|
||||
engine := init.ethereum.Engine()
|
||||
_bor := engine.(*bor.Bor)
|
||||
h, _ := getMockedHeimdallClient(t)
|
||||
_bor.SetHeimdallClient(h)
|
||||
|
||||
// Build 1st block's header
|
||||
db := init.ethereum.ChainDb()
|
||||
block := init.genesis.ToBlock(db)
|
||||
|
||||
header := buildMinimalNextHeader(t, block, init.genesis.Config.Bor.Period)
|
||||
statedb, err := chain.State()
|
||||
if err != nil {
|
||||
t.Fatalf("%s", err)
|
||||
for i := uint64(1); i < spanSize; i++ {
|
||||
block = buildNextBlock(t, _bor, chain, block, nil, init.genesis.Config.Bor)
|
||||
insertNewBlock(t, chain, block)
|
||||
}
|
||||
|
||||
_key, _ := hex.DecodeString(privKey)
|
||||
insertNewBlock(t, _bor, chain, header, statedb, _key)
|
||||
// insert spanSize-th block
|
||||
// This account is one the out-of-turn validators for 1st (0-indexed) span
|
||||
signer := "c8deb0bea5c41afe8e37b4d1bd84e31adff11b09c8c96ff4b605003cce067cd9"
|
||||
signerKey, _ := hex.DecodeString(signer)
|
||||
key, _ = crypto.HexToECDSA(signer)
|
||||
addr = crypto.PubkeyToAddress(key.PublicKey)
|
||||
expectedSuccessionNumber := 2
|
||||
|
||||
block = buildNextBlock(t, _bor, chain, block, signerKey, init.genesis.Config.Bor)
|
||||
_, err := chain.InsertChain([]*types.Block{block})
|
||||
assert.Equal(t,
|
||||
*err.(*bor.BlockTooSoonError),
|
||||
bor.BlockTooSoonError{Number: spanSize, Succession: expectedSuccessionNumber})
|
||||
|
||||
expectedDifficulty := uint64(3 - expectedSuccessionNumber) // len(validators) - succession
|
||||
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)
|
||||
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()})
|
||||
|
||||
var headers []*types.Header
|
||||
for i := int64(2); i <= 255; i++ {
|
||||
header := buildMinimalNextHeader(t, block, init.genesis.Config.Bor.Period)
|
||||
headers = append(headers, header)
|
||||
block = types.NewBlockWithHeader(header)
|
||||
header.Difficulty = new(big.Int).SetUint64(expectedDifficulty)
|
||||
sign(t, header, signerKey)
|
||||
block = types.NewBlockWithHeader(header)
|
||||
_, err = chain.InsertChain([]*types.Block{block})
|
||||
assert.Nil(t, err)
|
||||
}
|
||||
|
||||
func TestSignerNotFound(t *testing.T) {
|
||||
init := buildEthereumInstance(t, rawdb.NewMemoryDatabase())
|
||||
chain := init.ethereum.BlockChain()
|
||||
engine := init.ethereum.Engine()
|
||||
_bor := engine.(*bor.Bor)
|
||||
h, _ := getMockedHeimdallClient(t)
|
||||
_bor.SetHeimdallClient(h)
|
||||
|
||||
db := init.ethereum.ChainDb()
|
||||
block := init.genesis.ToBlock(db)
|
||||
|
||||
// random signer account that is not a part of the validator set
|
||||
signer := "3714d99058cd64541433d59c6b391555b2fd9b54629c2b717a6c9c00d1127b6b"
|
||||
signerKey, _ := hex.DecodeString(signer)
|
||||
key, _ = crypto.HexToECDSA(signer)
|
||||
addr = crypto.PubkeyToAddress(key.PublicKey)
|
||||
|
||||
block = buildNextBlock(t, _bor, chain, block, signerKey, init.genesis.Config.Bor)
|
||||
_, err := chain.InsertChain([]*types.Block{block})
|
||||
assert.Equal(t,
|
||||
*err.(*bor.UnauthorizedSignerError),
|
||||
bor.UnauthorizedSignerError{Number: 0, Signer: addr.Bytes()})
|
||||
}
|
||||
|
||||
func getMockedHeimdallClient(t *testing.T) (*mocks.IHeimdallClient, *bor.HeimdallSpan) {
|
||||
res, heimdallSpan := loadSpanFromFile(t)
|
||||
h := &mocks.IHeimdallClient{}
|
||||
h.On("FetchWithRetry", "bor/span/1", "").Return(res, nil)
|
||||
h.On(
|
||||
"FetchStateSyncEvents",
|
||||
mock.AnythingOfType("uint64"),
|
||||
mock.AnythingOfType("int64")).Return([]*bor.EventRecordWithTime{getSampleEventRecord(t)}, nil)
|
||||
return h, heimdallSpan
|
||||
}
|
||||
|
||||
func generateFakeStateSyncEvents(sample *bor.EventRecordWithTime, count int) []*bor.EventRecordWithTime {
|
||||
events := make([]*bor.EventRecordWithTime, count)
|
||||
event := *sample
|
||||
event.ID = 1
|
||||
events[0] = &bor.EventRecordWithTime{}
|
||||
*events[0] = event
|
||||
for i := 1; i < count; i++ {
|
||||
event.ID = uint64(i)
|
||||
event.Time = event.Time.Add(1 * time.Second)
|
||||
events[i] = &bor.EventRecordWithTime{}
|
||||
*events[i] = event
|
||||
}
|
||||
t.Logf("inserting %v headers", len(headers))
|
||||
if _, err := chain.InsertHeaderChain(headers, 0); err != nil {
|
||||
return events
|
||||
}
|
||||
|
||||
func buildStateEvent(sample *bor.EventRecordWithTime, id uint64, timeStamp int64) *bor.EventRecordWithTime {
|
||||
event := *sample
|
||||
event.ID = id
|
||||
event.Time = time.Unix(timeStamp, 0)
|
||||
return &event
|
||||
}
|
||||
|
||||
func getSampleEventRecord(t *testing.T) *bor.EventRecordWithTime {
|
||||
res := stateSyncEventsPayload(t)
|
||||
var _eventRecords []*bor.EventRecordWithTime
|
||||
if err := json.Unmarshal(res.Result, &_eventRecords); err != nil {
|
||||
t.Fatalf("%s", err)
|
||||
}
|
||||
|
||||
for _, validator := range heimdallSpan.SelectedProducers {
|
||||
_addr := validator.Address
|
||||
tx = types.NewTransaction(
|
||||
0,
|
||||
common.HexToAddress(chain.Config().Bor.StateReceiverContract),
|
||||
big.NewInt(0), 0, big.NewInt(0),
|
||||
proposeStateData,
|
||||
)
|
||||
assert.True(t, _bor.IsValidatorAction(chain, _addr, tx))
|
||||
|
||||
tx = types.NewTransaction(
|
||||
0,
|
||||
common.HexToAddress(chain.Config().Bor.ValidatorContract),
|
||||
big.NewInt(0), 0, big.NewInt(0),
|
||||
proposeSpanData,
|
||||
)
|
||||
assert.True(t, _bor.IsValidatorAction(chain, _addr, tx))
|
||||
}
|
||||
_eventRecords[0].Time = time.Unix(1, 0)
|
||||
return _eventRecords[0]
|
||||
}
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
|
|
@ -5,24 +5,38 @@ import (
|
|||
"encoding/json"
|
||||
"io/ioutil"
|
||||
"math/big"
|
||||
"sort"
|
||||
"testing"
|
||||
|
||||
"github.com/maticnetwork/bor/common"
|
||||
"github.com/maticnetwork/bor/consensus/bor"
|
||||
"github.com/maticnetwork/bor/core"
|
||||
"github.com/maticnetwork/bor/core/state"
|
||||
"github.com/maticnetwork/bor/core/types"
|
||||
"github.com/maticnetwork/bor/crypto"
|
||||
"github.com/maticnetwork/bor/crypto/secp256k1"
|
||||
"github.com/maticnetwork/bor/eth"
|
||||
"github.com/maticnetwork/bor/ethdb"
|
||||
"github.com/maticnetwork/bor/node"
|
||||
"github.com/maticnetwork/bor/params"
|
||||
)
|
||||
|
||||
var (
|
||||
// The genesis for tests was generated with following parameters
|
||||
extraSeal = 65 // Fixed number of extra-data suffix bytes reserved for signer seal
|
||||
privKey = "b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291"
|
||||
key, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
|
||||
addr = crypto.PubkeyToAddress(key.PublicKey) // 0x71562b71999873DB5b286dF957af199Ec94617F7
|
||||
|
||||
// Only this account is a validator for the 0th span
|
||||
privKey = "b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291"
|
||||
key, _ = crypto.HexToECDSA(privKey)
|
||||
addr = crypto.PubkeyToAddress(key.PublicKey) // 0x71562b71999873DB5b286dF957af199Ec94617F7
|
||||
|
||||
// This account is one the validators for 1st span (0-indexed)
|
||||
privKey2 = "9b28f36fbd67381120752d6172ecdcf10e06ab2d9a1367aac00cdcd6ac7855d3"
|
||||
key2, _ = crypto.HexToECDSA(privKey2)
|
||||
addr2 = crypto.PubkeyToAddress(key2.PublicKey) // 0x9fB29AAc15b9A4B7F17c3385939b007540f4d791
|
||||
|
||||
validatorHeaderBytesLength = common.AddressLength + 20 // address + power
|
||||
sprintSize uint64 = 4
|
||||
spanSize uint64 = 8
|
||||
)
|
||||
|
||||
type initializeData struct {
|
||||
|
|
@ -81,37 +95,77 @@ func buildEthereumInstance(t *testing.T, db ethdb.Database) *initializeData {
|
|||
}
|
||||
}
|
||||
|
||||
func insertNewBlock(t *testing.T, _bor *bor.Bor, chain *core.BlockChain, header *types.Header, statedb *state.StateDB, privKey []byte) {
|
||||
_, err := _bor.FinalizeAndAssemble(chain, header, statedb, nil, nil, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("%s", err)
|
||||
}
|
||||
|
||||
sig, err := secp256k1.Sign(crypto.Keccak256(bor.BorRLP(header)), privKey)
|
||||
if err != nil {
|
||||
t.Fatalf("%s", err)
|
||||
}
|
||||
copy(header.Extra[len(header.Extra)-extraSeal:], sig)
|
||||
|
||||
block := types.NewBlockWithHeader(header)
|
||||
func insertNewBlock(t *testing.T, chain *core.BlockChain, block *types.Block) {
|
||||
if _, err := chain.InsertChain([]*types.Block{block}); err != nil {
|
||||
t.Fatalf("%s", err)
|
||||
}
|
||||
}
|
||||
|
||||
func buildMinimalNextHeader(t *testing.T, block *types.Block, period uint64) *types.Header {
|
||||
func buildNextBlock(t *testing.T, _bor *bor.Bor, chain *core.BlockChain, block *types.Block, signer []byte, borConfig *params.BorConfig) *types.Block {
|
||||
header := block.Header()
|
||||
header.Number.Add(header.Number, big.NewInt(1))
|
||||
number := header.Number.Uint64()
|
||||
|
||||
if signer == nil {
|
||||
signer = getSignerKey(header.Number.Uint64())
|
||||
}
|
||||
|
||||
header.ParentHash = block.Hash()
|
||||
header.Time += (period + 1)
|
||||
header.Extra = make([]byte, 97) // vanity (32) + extraSeal (65)
|
||||
_key, _ := hex.DecodeString(privKey)
|
||||
sig, err := secp256k1.Sign(crypto.Keccak256(bor.BorRLP(header)), _key)
|
||||
header.Time += bor.CalcProducerDelay(header.Number.Uint64(), 0, borConfig)
|
||||
header.Extra = make([]byte, 32+65) // vanity + extraSeal
|
||||
|
||||
currentValidators := []*bor.Validator{bor.NewValidator(addr, 10)}
|
||||
|
||||
isSpanEnd := (number+1)%spanSize == 0
|
||||
isSpanStart := number%spanSize == 0
|
||||
isSprintEnd := (header.Number.Uint64()+1)%sprintSize == 0
|
||||
if isSpanEnd {
|
||||
_, heimdallSpan := loadSpanFromFile(t)
|
||||
// this is to stash the validator bytes in the header
|
||||
currentValidators = heimdallSpan.ValidatorSet.Validators
|
||||
} else if isSpanStart {
|
||||
header.Difficulty = new(big.Int).SetInt64(3)
|
||||
}
|
||||
if isSprintEnd {
|
||||
sort.Sort(bor.ValidatorsByAddress(currentValidators))
|
||||
validatorBytes := make([]byte, len(currentValidators)*validatorHeaderBytesLength)
|
||||
header.Extra = make([]byte, 32+len(validatorBytes)+65) // vanity + validatorBytes + extraSeal
|
||||
for i, val := range currentValidators {
|
||||
copy(validatorBytes[i*validatorHeaderBytesLength:], val.HeaderBytes())
|
||||
}
|
||||
copy(header.Extra[32:], validatorBytes)
|
||||
}
|
||||
|
||||
state, err := chain.State()
|
||||
if err != nil {
|
||||
t.Fatalf("%s", err)
|
||||
}
|
||||
_, err = _bor.FinalizeAndAssemble(chain, header, state, nil, nil, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("%s", err)
|
||||
}
|
||||
sign(t, header, signer)
|
||||
return types.NewBlockWithHeader(header)
|
||||
}
|
||||
|
||||
func sign(t *testing.T, header *types.Header, signer []byte) {
|
||||
sig, err := secp256k1.Sign(crypto.Keccak256(bor.BorRLP(header)), signer)
|
||||
if err != nil {
|
||||
t.Fatalf("%s", err)
|
||||
}
|
||||
copy(header.Extra[len(header.Extra)-extraSeal:], sig)
|
||||
return header
|
||||
}
|
||||
|
||||
func stateSyncEventsPayload(t *testing.T) *bor.ResponseWithHeight {
|
||||
stateData, err := ioutil.ReadFile("states.json")
|
||||
if err != nil {
|
||||
t.Fatalf("%s", err)
|
||||
}
|
||||
res := &bor.ResponseWithHeight{}
|
||||
if err := json.Unmarshal(stateData, res); err != nil {
|
||||
t.Fatalf("%s", err)
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
func loadSpanFromFile(t *testing.T) (*bor.ResponseWithHeight, *bor.HeimdallSpan) {
|
||||
|
|
@ -130,3 +184,14 @@ func loadSpanFromFile(t *testing.T) (*bor.ResponseWithHeight, *bor.HeimdallSpan)
|
|||
}
|
||||
return res, heimdallSpan
|
||||
}
|
||||
|
||||
func getSignerKey(number uint64) []byte {
|
||||
signerKey := privKey
|
||||
isSpanStart := number%spanSize == 0
|
||||
if isSpanStart {
|
||||
// validator set in the new span has changed
|
||||
signerKey = privKey2
|
||||
}
|
||||
_key, _ := hex.DecodeString(signerKey)
|
||||
return _key
|
||||
}
|
||||
|
|
|
|||
125
consensus/bor/bor_test/snapshot_test.go
Normal file
125
consensus/bor/bor_test/snapshot_test.go
Normal file
|
|
@ -0,0 +1,125 @@
|
|||
package bortest
|
||||
|
||||
import (
|
||||
"math/rand"
|
||||
"sort"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
"github.com/maticnetwork/bor/common"
|
||||
"github.com/maticnetwork/bor/consensus/bor"
|
||||
)
|
||||
|
||||
const (
|
||||
numVals = 100
|
||||
)
|
||||
|
||||
func TestGetSignerSuccessionNumber_ProposerIsSigner(t *testing.T) {
|
||||
validators := buildRandomValidatorSet(numVals)
|
||||
validatorSet := bor.NewValidatorSet(validators)
|
||||
snap := bor.Snapshot{
|
||||
ValidatorSet: validatorSet,
|
||||
}
|
||||
|
||||
// proposer is signer
|
||||
signer := validatorSet.Proposer.Address
|
||||
successionNumber, err := snap.GetSignerSuccessionNumber(signer)
|
||||
if err != nil {
|
||||
t.Fatalf("%s", err)
|
||||
}
|
||||
assert.Equal(t, 0, successionNumber)
|
||||
}
|
||||
|
||||
func TestGetSignerSuccessionNumber_SignerIndexIsLarger(t *testing.T) {
|
||||
validators := buildRandomValidatorSet(numVals)
|
||||
|
||||
// sort validators by address, which is what bor.NewValidatorSet also does
|
||||
sort.Sort(bor.ValidatorsByAddress(validators))
|
||||
proposerIndex := 32
|
||||
signerIndex := 56
|
||||
// give highest ProposerPriority to a particular val, so that they become the proposer
|
||||
validators[proposerIndex].VotingPower = 200
|
||||
snap := bor.Snapshot{
|
||||
ValidatorSet: bor.NewValidatorSet(validators),
|
||||
}
|
||||
|
||||
// choose a signer at an index greater than proposer index
|
||||
signer := snap.ValidatorSet.Validators[signerIndex].Address
|
||||
successionNumber, err := snap.GetSignerSuccessionNumber(signer)
|
||||
if err != nil {
|
||||
t.Fatalf("%s", err)
|
||||
}
|
||||
assert.Equal(t, signerIndex-proposerIndex, successionNumber)
|
||||
}
|
||||
|
||||
func TestGetSignerSuccessionNumber_SignerIndexIsSmaller(t *testing.T) {
|
||||
validators := buildRandomValidatorSet(numVals)
|
||||
proposerIndex := 98
|
||||
signerIndex := 11
|
||||
// give highest ProposerPriority to a particular val, so that they become the proposer
|
||||
validators[proposerIndex].VotingPower = 200
|
||||
snap := bor.Snapshot{
|
||||
ValidatorSet: bor.NewValidatorSet(validators),
|
||||
}
|
||||
|
||||
// choose a signer at an index greater than proposer index
|
||||
signer := snap.ValidatorSet.Validators[signerIndex].Address
|
||||
successionNumber, err := snap.GetSignerSuccessionNumber(signer)
|
||||
if err != nil {
|
||||
t.Fatalf("%s", err)
|
||||
}
|
||||
assert.Equal(t, signerIndex+numVals-proposerIndex, successionNumber)
|
||||
}
|
||||
|
||||
func TestGetSignerSuccessionNumber_ProposerNotFound(t *testing.T) {
|
||||
validators := buildRandomValidatorSet(numVals)
|
||||
snap := bor.Snapshot{
|
||||
ValidatorSet: bor.NewValidatorSet(validators),
|
||||
}
|
||||
dummyProposerAddress := randomAddress()
|
||||
snap.ValidatorSet.Proposer = &bor.Validator{Address: dummyProposerAddress}
|
||||
// choose any signer
|
||||
signer := snap.ValidatorSet.Validators[3].Address
|
||||
_, err := snap.GetSignerSuccessionNumber(signer)
|
||||
assert.NotNil(t, err)
|
||||
e, ok := err.(*bor.UnauthorizedProposerError)
|
||||
assert.True(t, ok)
|
||||
assert.Equal(t, dummyProposerAddress.Bytes(), e.Proposer)
|
||||
}
|
||||
|
||||
func TestGetSignerSuccessionNumber_SignerNotFound(t *testing.T) {
|
||||
validators := buildRandomValidatorSet(numVals)
|
||||
snap := bor.Snapshot{
|
||||
ValidatorSet: bor.NewValidatorSet(validators),
|
||||
}
|
||||
dummySignerAddress := randomAddress()
|
||||
_, err := snap.GetSignerSuccessionNumber(dummySignerAddress)
|
||||
assert.NotNil(t, err)
|
||||
e, ok := err.(*bor.UnauthorizedSignerError)
|
||||
assert.True(t, ok)
|
||||
assert.Equal(t, dummySignerAddress.Bytes(), e.Signer)
|
||||
}
|
||||
|
||||
func buildRandomValidatorSet(numVals int) []*bor.Validator {
|
||||
rand.Seed(time.Now().Unix())
|
||||
validators := make([]*bor.Validator, numVals)
|
||||
for i := 0; i < numVals; i++ {
|
||||
validators[i] = &bor.Validator{
|
||||
Address: randomAddress(),
|
||||
// cannot process validators with voting power 0, hence +1
|
||||
VotingPower: int64(rand.Intn(99) + 1),
|
||||
}
|
||||
}
|
||||
|
||||
// sort validators by address, which is what bor.NewValidatorSet also does
|
||||
sort.Sort(bor.ValidatorsByAddress(validators))
|
||||
return validators
|
||||
}
|
||||
|
||||
func randomAddress() common.Address {
|
||||
bytes := make([]byte, 32)
|
||||
rand.Read(bytes)
|
||||
return common.BytesToAddress(bytes)
|
||||
}
|
||||
|
|
@ -6,85 +6,57 @@
|
|||
"end_block": 6655,
|
||||
"validator_set": {
|
||||
"validators": [{
|
||||
"ID": 3,
|
||||
"startEpoch": 0,
|
||||
"endEpoch": 0,
|
||||
"power": 10000,
|
||||
"pubKey": "0x046434e10a34ade13c4fea917346a9fd1473eac2138a0b4e2a36426871918be63188fde4edbf598457592c9a49fe3b0036dd5497079495d132e5045bf499c4bdb1",
|
||||
"signer": "0xc787af4624cb3e80ee23ae7faac0f2acea2be34c",
|
||||
"last_updated": 0,
|
||||
"accum": -40000
|
||||
}, {
|
||||
"ID": 4,
|
||||
"startEpoch": 0,
|
||||
"endEpoch": 0,
|
||||
"power": 10000,
|
||||
"pubKey": "0x04d9d09f2afc9da3cccc164e8112eb6911a63f5ede10169768f800df83cf99c73f944411e9d4fac3543b11c5f84a82e56b36cfcd34f1d065855c1e2b27af8b5247",
|
||||
"signer": "0x461295d3d9249215e758e939a150ab180950720b",
|
||||
"last_updated": 0,
|
||||
"accum": 10000
|
||||
}, {
|
||||
"ID": 5,
|
||||
"startEpoch": 0,
|
||||
"endEpoch": 0,
|
||||
"power": 10000,
|
||||
"power": 30,
|
||||
"pubKey": "0x04a36f6ed1f93acb0a38f4cacbe2467c72458ac41ce3b12b34d758205b2bc5d930a4e059462da7a0976c32fce766e1f7e8d73933ae72ac2af231fe161187743932",
|
||||
"signer": "0x836fe3e3dd0a5f77d9d5b0f67e48048aaafcd5a0",
|
||||
"signer": "0x9fB29AAc15b9A4B7F17c3385939b007540f4d791",
|
||||
"last_updated": 0,
|
||||
"accum": 10000
|
||||
}, {
|
||||
"ID": 1,
|
||||
"startEpoch": 0,
|
||||
"endEpoch": 0,
|
||||
"power": 10000,
|
||||
"power": 20,
|
||||
"pubKey": "0x04a312814042a6655c8e5ecf0c52cba0b6a6f3291c87cc42260a3c0222410c0d0d59b9139d1c56542e5df0ce2fce3a86ce13e93bd9bde0dc8ff664f8dd5294dead",
|
||||
"signer": "0x925a91f8003aaeabea6037103123b93c50b86ca3",
|
||||
"signer": "0x96C42C56fdb78294F96B0cFa33c92bed7D75F96a",
|
||||
"last_updated": 0,
|
||||
"accum": 10000
|
||||
}, {
|
||||
"ID": 2,
|
||||
"startEpoch": 0,
|
||||
"endEpoch": 0,
|
||||
"power": 10000,
|
||||
"power": 10,
|
||||
"pubKey": "0x0469536ae98030a7e83ec5ef3baffed2d05a32e31d978e58486f6bdb0fbbf240293838325116090190c0639db03f9cbd8b9aecfd269d016f46e3a2287fbf9ad232",
|
||||
"signer": "0x1c4f0f054a0d6a1415382dc0fd83c6535188b220",
|
||||
"signer": "0xc787af4624cb3e80ee23ae7faac0f2acea2be34c",
|
||||
"last_updated": 0,
|
||||
"accum": 10000
|
||||
}],
|
||||
"proposer": {
|
||||
"ID": 3,
|
||||
"startEpoch": 0,
|
||||
"endEpoch": 0,
|
||||
"power": 10000,
|
||||
"pubKey": "0x046434e10a34ade13c4fea917346a9fd1473eac2138a0b4e2a36426871918be63188fde4edbf598457592c9a49fe3b0036dd5497079495d132e5045bf499c4bdb1",
|
||||
"signer": "0x1c4f0f054a0d6a1415382dc0fd83c6535188b220",
|
||||
"last_updated": 0,
|
||||
"accum": -40000
|
||||
}
|
||||
"accum": 5000
|
||||
}]
|
||||
},
|
||||
"selected_producers": [{
|
||||
"ID": 5,
|
||||
"startEpoch": 0,
|
||||
"endEpoch": 0,
|
||||
"power": 1,
|
||||
"power": 30,
|
||||
"pubKey": "0x04a36f6ed1f93acb0a38f4cacbe2467c72458ac41ce3b12b34d758205b2bc5d930a4e059462da7a0976c32fce766e1f7e8d73933ae72ac2af231fe161187743932",
|
||||
"signer": "0x836fe3e3dd0a5f77d9d5b0f67e48048aaafcd5a0",
|
||||
"signer": "0x9fB29AAc15b9A4B7F17c3385939b007540f4d791",
|
||||
"last_updated": 0,
|
||||
"accum": 10000
|
||||
}, {
|
||||
"ID": 1,
|
||||
"startEpoch": 0,
|
||||
"endEpoch": 0,
|
||||
"power": 1,
|
||||
"power": 20,
|
||||
"pubKey": "0x04a312814042a6655c8e5ecf0c52cba0b6a6f3291c87cc42260a3c0222410c0d0d59b9139d1c56542e5df0ce2fce3a86ce13e93bd9bde0dc8ff664f8dd5294dead",
|
||||
"signer": "0x925a91f8003aaeabea6037103123b93c50b86ca3",
|
||||
"signer": "0x96C42C56fdb78294F96B0cFa33c92bed7D75F96a",
|
||||
"last_updated": 0,
|
||||
"accum": 10000
|
||||
}, {
|
||||
"ID": 2,
|
||||
"startEpoch": 0,
|
||||
"endEpoch": 0,
|
||||
"power": 2,
|
||||
"power": 10,
|
||||
"pubKey": "0x0469536ae98030a7e83ec5ef3baffed2d05a32e31d978e58486f6bdb0fbbf240293838325116090190c0639db03f9cbd8b9aecfd269d016f46e3a2287fbf9ad232",
|
||||
"signer": "0xc787af4624cb3e80ee23ae7faac0f2acea2be34c",
|
||||
"last_updated": 0,
|
||||
|
|
|
|||
23
consensus/bor/bor_test/states.json
Normal file
23
consensus/bor/bor_test/states.json
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
{
|
||||
"height": "0",
|
||||
"result": [
|
||||
{
|
||||
"id": 1,
|
||||
"contract": "0xb55969a6d60413a63291a1de572269875df541e3",
|
||||
"data": "0x00000000000000000000000048aa8d4af32551892fcf08ad63be7dd206d46f6500000000000000000000000048aa8d4af32551892fcf08ad63be7dd206d46f65000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000014",
|
||||
"tx_hash": "0x7b113e09d98b6d4be1dedfbc0746e34876de767f2cb8b58ff00160a160811dd6",
|
||||
"log_index": 0,
|
||||
"bor_chain_id": "15001",
|
||||
"record_time": "2020-05-15T13:36:38.580995Z"
|
||||
},
|
||||
{
|
||||
"id": 2,
|
||||
"contract": "0xb55969a6d60413a63291a1de572269875df541e3",
|
||||
"data": "0x00000000000000000000000048aa8d4af32551892fcf08ad63be7dd206d46f6500000000000000000000000048aa8d4af32551892fcf08ad63be7dd206d46f65000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000015",
|
||||
"tx_hash": "0xb72358aff8e4d61f4de97a37a40ddda986c081e0de8036e0a78c4b61b067cba9",
|
||||
"log_index": 0,
|
||||
"bor_chain_id": "15001",
|
||||
"record_time": "2020-05-15T13:42:37.319058Z"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -1,6 +1,9 @@
|
|||
package bor
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/maticnetwork/bor/common"
|
||||
"github.com/maticnetwork/bor/common/hexutil"
|
||||
)
|
||||
|
|
@ -14,3 +17,33 @@ type EventRecord struct {
|
|||
LogIndex uint64 `json:"log_index" yaml:"log_index"`
|
||||
ChainID string `json:"bor_chain_id" yaml:"bor_chain_id"`
|
||||
}
|
||||
|
||||
type EventRecordWithTime struct {
|
||||
EventRecord
|
||||
Time time.Time `json:"record_time" yaml:"record_time"`
|
||||
}
|
||||
|
||||
// String returns the string representatin of span
|
||||
func (e *EventRecordWithTime) String() string {
|
||||
return fmt.Sprintf(
|
||||
"id %v, contract %v, data: %v, txHash: %v, logIndex: %v, chainId: %v, time %s",
|
||||
e.ID,
|
||||
e.Contract.String(),
|
||||
e.Data.String(),
|
||||
e.TxHash.Hex(),
|
||||
e.LogIndex,
|
||||
e.ChainID,
|
||||
e.Time.Format(time.RFC3339),
|
||||
)
|
||||
}
|
||||
|
||||
func (e *EventRecordWithTime) BuildEventRecord() *EventRecord {
|
||||
return &EventRecord{
|
||||
ID: e.ID,
|
||||
Contract: e.Contract,
|
||||
Data: e.Data,
|
||||
TxHash: e.TxHash,
|
||||
LogIndex: e.LogIndex,
|
||||
ChainID: e.ChainID,
|
||||
}
|
||||
}
|
||||
|
|
|
|||
143
consensus/bor/errors.go
Normal file
143
consensus/bor/errors.go
Normal file
|
|
@ -0,0 +1,143 @@
|
|||
package bor
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
)
|
||||
|
||||
// TotalVotingPowerExceededError is returned when the maximum allowed total voting power is exceeded
|
||||
type TotalVotingPowerExceededError struct {
|
||||
Sum int64
|
||||
Validators []*Validator
|
||||
}
|
||||
|
||||
func (e *TotalVotingPowerExceededError) Error() string {
|
||||
return fmt.Sprintf(
|
||||
"Total voting power should be guarded to not exceed %v; got: %v; for validator set: %v",
|
||||
MaxTotalVotingPower,
|
||||
e.Sum,
|
||||
e.Validators,
|
||||
)
|
||||
}
|
||||
|
||||
type InvalidStartEndBlockError struct {
|
||||
Start uint64
|
||||
End uint64
|
||||
CurrentHeader uint64
|
||||
}
|
||||
|
||||
func (e *InvalidStartEndBlockError) Error() string {
|
||||
return fmt.Sprintf(
|
||||
"Invalid parameters start: %d and end block: %d params",
|
||||
e.Start,
|
||||
e.End,
|
||||
)
|
||||
}
|
||||
|
||||
type MaxCheckpointLengthExceededError struct {
|
||||
Start uint64
|
||||
End uint64
|
||||
}
|
||||
|
||||
func (e *MaxCheckpointLengthExceededError) Error() string {
|
||||
return fmt.Sprintf(
|
||||
"Start: %d and end block: %d exceed max allowed checkpoint length: %d",
|
||||
e.Start,
|
||||
e.End,
|
||||
MaxCheckpointLength,
|
||||
)
|
||||
}
|
||||
|
||||
// MismatchingValidatorsError is returned if a last block in sprint contains a
|
||||
// list of validators different from the one that local node calculated
|
||||
type MismatchingValidatorsError struct {
|
||||
Number uint64
|
||||
ValidatorSetSnap []byte
|
||||
ValidatorSetHeader []byte
|
||||
}
|
||||
|
||||
func (e *MismatchingValidatorsError) Error() string {
|
||||
return fmt.Sprintf(
|
||||
"Mismatching validators at block %d\nValidatorBytes from snapshot: 0x%x\nValidatorBytes in Header: 0x%x\n",
|
||||
e.Number,
|
||||
e.ValidatorSetSnap,
|
||||
e.ValidatorSetHeader,
|
||||
)
|
||||
}
|
||||
|
||||
type BlockTooSoonError struct {
|
||||
Number uint64
|
||||
Succession int
|
||||
}
|
||||
|
||||
func (e *BlockTooSoonError) Error() string {
|
||||
return fmt.Sprintf(
|
||||
"Block %d was created too soon. Signer turn-ness number is %d\n",
|
||||
e.Number,
|
||||
e.Succession,
|
||||
)
|
||||
}
|
||||
|
||||
// UnauthorizedProposerError is returned if a header is [being] signed by an unauthorized entity.
|
||||
type UnauthorizedProposerError struct {
|
||||
Number uint64
|
||||
Proposer []byte
|
||||
}
|
||||
|
||||
func (e *UnauthorizedProposerError) Error() string {
|
||||
return fmt.Sprintf(
|
||||
"Proposer 0x%x is not a part of the producer set at block %d",
|
||||
e.Proposer,
|
||||
e.Number,
|
||||
)
|
||||
}
|
||||
|
||||
// UnauthorizedSignerError is returned if a header is [being] signed by an unauthorized entity.
|
||||
type UnauthorizedSignerError struct {
|
||||
Number uint64
|
||||
Signer []byte
|
||||
}
|
||||
|
||||
func (e *UnauthorizedSignerError) Error() string {
|
||||
return fmt.Sprintf(
|
||||
"Signer 0x%x is not a part of the producer set at block %d",
|
||||
e.Signer,
|
||||
e.Number,
|
||||
)
|
||||
}
|
||||
|
||||
// WrongDifficultyError is returned if the difficulty of a block doesn't match the
|
||||
// turn of the signer.
|
||||
type WrongDifficultyError struct {
|
||||
Number uint64
|
||||
Expected uint64
|
||||
Actual uint64
|
||||
Signer []byte
|
||||
}
|
||||
|
||||
func (e *WrongDifficultyError) Error() string {
|
||||
return fmt.Sprintf(
|
||||
"Wrong difficulty at block %d, expected: %d, actual %d. Signer was %x\n",
|
||||
e.Number,
|
||||
e.Expected,
|
||||
e.Actual,
|
||||
e.Signer,
|
||||
)
|
||||
}
|
||||
|
||||
type InvalidStateReceivedError struct {
|
||||
Number uint64
|
||||
LastStateID uint64
|
||||
To *time.Time
|
||||
Event *EventRecordWithTime
|
||||
}
|
||||
|
||||
func (e *InvalidStateReceivedError) Error() string {
|
||||
return fmt.Sprintf(
|
||||
"Received invalid event %s at block %d. Requested events until %s. Last state id was %d",
|
||||
e.Event,
|
||||
e.Number,
|
||||
e.To.Format(time.RFC3339),
|
||||
e.LastStateID,
|
||||
)
|
||||
}
|
||||
102
consensus/bor/genesis_contracts_client.go
Normal file
102
consensus/bor/genesis_contracts_client.go
Normal file
File diff suppressed because one or more lines are too long
48
consensus/bor/merkle.go
Normal file
48
consensus/bor/merkle.go
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
package bor
|
||||
|
||||
func appendBytes32(data ...[]byte) []byte {
|
||||
var result []byte
|
||||
for _, v := range data {
|
||||
paddedV, err := convertTo32(v)
|
||||
if err == nil {
|
||||
result = append(result, paddedV[:]...)
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func nextPowerOfTwo(n uint64) uint64 {
|
||||
if n == 0 {
|
||||
return 1
|
||||
}
|
||||
// http://graphics.stanford.edu/~seander/bithacks.html#RoundUpPowerOf2
|
||||
n--
|
||||
n |= n >> 1
|
||||
n |= n >> 2
|
||||
n |= n >> 4
|
||||
n |= n >> 8
|
||||
n |= n >> 16
|
||||
n |= n >> 32
|
||||
n++
|
||||
return n
|
||||
}
|
||||
|
||||
func convertTo32(input []byte) (output [32]byte, err error) {
|
||||
l := len(input)
|
||||
if l > 32 || l == 0 {
|
||||
return
|
||||
}
|
||||
copy(output[32-l:], input[:])
|
||||
return
|
||||
}
|
||||
|
||||
func convert(input []([32]byte)) [][]byte {
|
||||
var output [][]byte
|
||||
for _, in := range input {
|
||||
newInput := make([]byte, len(in[:]))
|
||||
copy(newInput, in[:])
|
||||
output = append(output, newInput)
|
||||
|
||||
}
|
||||
return output
|
||||
}
|
||||
|
|
@ -6,12 +6,16 @@ import (
|
|||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"path"
|
||||
"sort"
|
||||
"time"
|
||||
|
||||
"github.com/maticnetwork/bor/log"
|
||||
)
|
||||
|
||||
var (
|
||||
stateFetchLimit = 50
|
||||
)
|
||||
|
||||
// ResponseWithHeight defines a response object type that wraps an original
|
||||
// response with a height.
|
||||
type ResponseWithHeight struct {
|
||||
|
|
@ -20,8 +24,9 @@ type ResponseWithHeight struct {
|
|||
}
|
||||
|
||||
type IHeimdallClient interface {
|
||||
Fetch(paths ...string) (*ResponseWithHeight, error)
|
||||
FetchWithRetry(paths ...string) (*ResponseWithHeight, error)
|
||||
Fetch(path string, query string) (*ResponseWithHeight, error)
|
||||
FetchWithRetry(path string, query string) (*ResponseWithHeight, error)
|
||||
FetchStateSyncEvents(fromID uint64, to int64) ([]*EventRecordWithTime, error)
|
||||
}
|
||||
|
||||
type HeimdallClient struct {
|
||||
|
|
@ -39,33 +44,57 @@ func NewHeimdallClient(urlString string) (*HeimdallClient, error) {
|
|||
return h, nil
|
||||
}
|
||||
|
||||
func (h *HeimdallClient) Fetch(paths ...string) (*ResponseWithHeight, error) {
|
||||
func (h *HeimdallClient) FetchStateSyncEvents(fromID uint64, to int64) ([]*EventRecordWithTime, error) {
|
||||
eventRecords := make([]*EventRecordWithTime, 0)
|
||||
for {
|
||||
queryParams := fmt.Sprintf("from-id=%d&to-time=%d&limit=%d", fromID, to, stateFetchLimit)
|
||||
log.Info("Fetching state sync events", "queryParams", queryParams)
|
||||
response, err := h.FetchWithRetry("clerk/event-record/list", queryParams)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var _eventRecords []*EventRecordWithTime
|
||||
if response.Result == nil { // status 204
|
||||
break
|
||||
}
|
||||
if err := json.Unmarshal(response.Result, &_eventRecords); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
eventRecords = append(eventRecords, _eventRecords...)
|
||||
if len(_eventRecords) < stateFetchLimit {
|
||||
break
|
||||
}
|
||||
fromID += uint64(stateFetchLimit)
|
||||
}
|
||||
|
||||
sort.SliceStable(eventRecords, func(i, j int) bool {
|
||||
return eventRecords[i].ID < eventRecords[j].ID
|
||||
})
|
||||
return eventRecords, nil
|
||||
}
|
||||
|
||||
// Fetch fetches response from heimdall
|
||||
func (h *HeimdallClient) Fetch(rawPath string, rawQuery string) (*ResponseWithHeight, error) {
|
||||
u, err := url.Parse(h.urlString)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, e := range paths {
|
||||
if e != "" {
|
||||
u.Path = path.Join(u.Path, e)
|
||||
}
|
||||
}
|
||||
u.Path = rawPath
|
||||
u.RawQuery = rawQuery
|
||||
|
||||
return h.internalFetch(u)
|
||||
}
|
||||
|
||||
// FetchWithRetry returns data from heimdall with retry
|
||||
func (h *HeimdallClient) FetchWithRetry(paths ...string) (*ResponseWithHeight, error) {
|
||||
func (h *HeimdallClient) FetchWithRetry(rawPath string, rawQuery string) (*ResponseWithHeight, error) {
|
||||
u, err := url.Parse(h.urlString)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, e := range paths {
|
||||
if e != "" {
|
||||
u.Path = path.Join(u.Path, e)
|
||||
}
|
||||
}
|
||||
u.Path = rawPath
|
||||
u.RawQuery = rawQuery
|
||||
|
||||
for {
|
||||
res, err := h.internalFetch(u)
|
||||
|
|
@ -86,18 +115,22 @@ func (h *HeimdallClient) internalFetch(u *url.URL) (*ResponseWithHeight, error)
|
|||
defer res.Body.Close()
|
||||
|
||||
// check status code
|
||||
if res.StatusCode != 200 {
|
||||
if res.StatusCode != 200 && res.StatusCode != 204 {
|
||||
return nil, fmt.Errorf("Error while fetching data from Heimdall")
|
||||
}
|
||||
|
||||
// unmarshall data from buffer
|
||||
var response ResponseWithHeight
|
||||
if res.StatusCode == 204 {
|
||||
return &response, nil
|
||||
}
|
||||
|
||||
// get response
|
||||
body, err := ioutil.ReadAll(res.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// unmarshall data from buffer
|
||||
var response ResponseWithHeight
|
||||
if err := json.Unmarshal(body, &response); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
|
|||
|
|
@ -26,7 +26,6 @@ import (
|
|||
"github.com/maticnetwork/bor/core/types"
|
||||
"github.com/maticnetwork/bor/ethdb"
|
||||
"github.com/maticnetwork/bor/internal/ethapi"
|
||||
"github.com/maticnetwork/bor/log"
|
||||
"github.com/maticnetwork/bor/params"
|
||||
)
|
||||
|
||||
|
|
@ -87,7 +86,9 @@ func loadSnapshot(config *params.BorConfig, sigcache *lru.ARCCache, db ethdb.Dat
|
|||
snap.ethAPI = ethAPI
|
||||
|
||||
// update total voting power
|
||||
snap.ValidatorSet.updateTotalVotingPower()
|
||||
if err := snap.ValidatorSet.updateTotalVotingPower(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return snap, nil
|
||||
}
|
||||
|
|
@ -153,31 +154,11 @@ func (s *Snapshot) apply(headers []*types.Header) (*Snapshot, error) {
|
|||
|
||||
// check if signer is in validator set
|
||||
if !snap.ValidatorSet.HasAddress(signer.Bytes()) {
|
||||
return nil, errUnauthorizedSigner
|
||||
return nil, &UnauthorizedSignerError{number, signer.Bytes()}
|
||||
}
|
||||
|
||||
//
|
||||
// Check validator
|
||||
//
|
||||
|
||||
validators := snap.ValidatorSet.Validators
|
||||
// proposer will be the last signer if block is not epoch block
|
||||
proposer := snap.ValidatorSet.GetProposer().Address
|
||||
proposerIndex, _ := snap.ValidatorSet.GetByAddress(proposer)
|
||||
signerIndex, _ := snap.ValidatorSet.GetByAddress(signer)
|
||||
limit := len(validators)/2 + 1
|
||||
|
||||
// temp index
|
||||
tempIndex := signerIndex
|
||||
if proposerIndex != tempIndex && limit > 0 {
|
||||
if tempIndex < proposerIndex {
|
||||
tempIndex = tempIndex + len(validators)
|
||||
}
|
||||
|
||||
if tempIndex-proposerIndex > limit {
|
||||
log.Info("Invalid signer: error while applying headers", "proposerIndex", validators[proposerIndex].Address.Hex(), "signerIndex", validators[signerIndex].Address.Hex())
|
||||
return nil, errRecentlySigned
|
||||
}
|
||||
if _, err = snap.GetSignerSuccessionNumber(signer); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// add recents
|
||||
|
|
@ -185,6 +166,9 @@ func (s *Snapshot) apply(headers []*types.Header) (*Snapshot, error) {
|
|||
|
||||
// change validator set and change proposer
|
||||
if number > 0 && (number+1)%s.config.Sprint == 0 {
|
||||
if err := validateHeaderExtraField(header.Extra); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
validatorBytes := header.Extra[extraVanity : len(header.Extra)-extraSeal]
|
||||
|
||||
// get validators from headers and use that for new validator set
|
||||
|
|
@ -200,6 +184,28 @@ func (s *Snapshot) apply(headers []*types.Header) (*Snapshot, error) {
|
|||
return snap, nil
|
||||
}
|
||||
|
||||
// GetSignerSuccessionNumber returns the relative position of signer in terms of the in-turn proposer
|
||||
func (s *Snapshot) GetSignerSuccessionNumber(signer common.Address) (int, error) {
|
||||
validators := s.ValidatorSet.Validators
|
||||
proposer := s.ValidatorSet.GetProposer().Address
|
||||
proposerIndex, _ := s.ValidatorSet.GetByAddress(proposer)
|
||||
if proposerIndex == -1 {
|
||||
return -1, &UnauthorizedProposerError{s.Number, proposer.Bytes()}
|
||||
}
|
||||
signerIndex, _ := s.ValidatorSet.GetByAddress(signer)
|
||||
if signerIndex == -1 {
|
||||
return -1, &UnauthorizedSignerError{s.Number, signer.Bytes()}
|
||||
}
|
||||
|
||||
tempIndex := signerIndex
|
||||
if proposerIndex != tempIndex {
|
||||
if tempIndex < proposerIndex {
|
||||
tempIndex = tempIndex + len(validators)
|
||||
}
|
||||
}
|
||||
return tempIndex - proposerIndex, nil
|
||||
}
|
||||
|
||||
// signers retrieves the list of authorized signers in ascending order.
|
||||
func (s *Snapshot) signers() []common.Address {
|
||||
sigs := make([]common.Address, 0, len(s.ValidatorSet.Validators))
|
||||
|
|
@ -209,8 +215,8 @@ func (s *Snapshot) signers() []common.Address {
|
|||
return sigs
|
||||
}
|
||||
|
||||
// inturn returns if a signer at a given block height is in-turn or not.
|
||||
func (s *Snapshot) inturn(number uint64, signer common.Address, epoch uint64) uint64 {
|
||||
// Difficulty returns the difficulty for a particular signer at the current snapshot number
|
||||
func (s *Snapshot) Difficulty(signer common.Address) uint64 {
|
||||
// if signer is empty
|
||||
if bytes.Compare(signer.Bytes(), common.Address{}.Bytes()) == 0 {
|
||||
return 1
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ package bor
|
|||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
// "encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"math/big"
|
||||
|
|
@ -13,8 +13,6 @@ import (
|
|||
)
|
||||
|
||||
// Validator represets Volatile state for each Validator
|
||||
// NOTE: The ProposerPriority is not included in Validator.Hash();
|
||||
// make sure to update that method if changes are made here
|
||||
type Validator struct {
|
||||
ID uint64 `json:"ID"`
|
||||
Address common.Address `json:"signer"`
|
||||
|
|
@ -31,18 +29,23 @@ func NewValidator(address common.Address, votingPower int64) *Validator {
|
|||
}
|
||||
}
|
||||
|
||||
// Creates a new copy of the validator so we can mutate ProposerPriority.
|
||||
// Copy creates a new copy of the validator so we can mutate ProposerPriority.
|
||||
// Panics if the validator is nil.
|
||||
func (v *Validator) Copy() *Validator {
|
||||
vCopy := *v
|
||||
return &vCopy
|
||||
}
|
||||
|
||||
// Returns the one with higher ProposerPriority.
|
||||
func (v *Validator) CompareProposerPriority(other *Validator) *Validator {
|
||||
// Cmp returns the one validator with a higher ProposerPriority.
|
||||
// If ProposerPriority is same, it returns the validator with lexicographically smaller address
|
||||
func (v *Validator) Cmp(other *Validator) *Validator {
|
||||
// if both of v and other are nil, nil will be returned and that could possibly lead to nil pointer dereference bubbling up the stack
|
||||
if v == nil {
|
||||
return other
|
||||
}
|
||||
if other == nil {
|
||||
return v
|
||||
}
|
||||
if v.ProposerPriority > other.ProposerPriority {
|
||||
return v
|
||||
} else if v.ProposerPriority < other.ProposerPriority {
|
||||
|
|
@ -55,7 +58,6 @@ func (v *Validator) CompareProposerPriority(other *Validator) *Validator {
|
|||
return other
|
||||
} else {
|
||||
panic("Cannot compare identical validators")
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -80,18 +82,6 @@ func ValidatorListString(vals []*Validator) string {
|
|||
return strings.Join(chunks, ",")
|
||||
}
|
||||
|
||||
// Bytes computes the unique encoding of a validator with a given voting power.
|
||||
// These are the bytes that gets hashed in consensus. It excludes address
|
||||
// as its redundant with the pubkey. This also excludes ProposerPriority
|
||||
// which changes every round.
|
||||
func (v *Validator) Bytes() []byte {
|
||||
b, err := json.Marshal(v)
|
||||
if err != nil {
|
||||
return b
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// HeaderBytes return header bytes
|
||||
func (v *Validator) HeaderBytes() []byte {
|
||||
result := make([]byte, 40)
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ import (
|
|||
"strings"
|
||||
|
||||
"github.com/maticnetwork/bor/common"
|
||||
"github.com/maticnetwork/bor/log"
|
||||
)
|
||||
|
||||
// MaxTotalVotingPower - the maximum allowed total voting power.
|
||||
|
|
@ -182,7 +183,7 @@ func computeMaxMinPriorityDiff(vals *ValidatorSet) int64 {
|
|||
func (vals *ValidatorSet) getValWithMostPriority() *Validator {
|
||||
var res *Validator
|
||||
for _, val := range vals.Validators {
|
||||
res = res.CompareProposerPriority(val)
|
||||
res = res.Cmp(val)
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
|
@ -256,28 +257,29 @@ func (vals *ValidatorSet) Size() int {
|
|||
}
|
||||
|
||||
// Force recalculation of the set's total voting power.
|
||||
func (vals *ValidatorSet) updateTotalVotingPower() {
|
||||
func (vals *ValidatorSet) updateTotalVotingPower() error {
|
||||
|
||||
sum := int64(0)
|
||||
for _, val := range vals.Validators {
|
||||
// mind overflow
|
||||
sum = safeAddClip(sum, val.VotingPower)
|
||||
if sum > MaxTotalVotingPower {
|
||||
panic(fmt.Sprintf(
|
||||
"Total voting power should be guarded to not exceed %v; got: %v",
|
||||
MaxTotalVotingPower,
|
||||
sum))
|
||||
return &TotalVotingPowerExceededError{sum, vals.Validators}
|
||||
}
|
||||
}
|
||||
|
||||
vals.totalVotingPower = sum
|
||||
return nil
|
||||
}
|
||||
|
||||
// TotalVotingPower returns the sum of the voting powers of all validators.
|
||||
// It recomputes the total voting power if required.
|
||||
func (vals *ValidatorSet) TotalVotingPower() int64 {
|
||||
if vals.totalVotingPower == 0 {
|
||||
vals.updateTotalVotingPower()
|
||||
log.Info("invoking updateTotalVotingPower before returning it")
|
||||
if err := vals.updateTotalVotingPower(); err != nil {
|
||||
// Can/should we do better?
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
return vals.totalVotingPower
|
||||
}
|
||||
|
|
@ -298,7 +300,7 @@ func (vals *ValidatorSet) findProposer() *Validator {
|
|||
var proposer *Validator
|
||||
for _, val := range vals.Validators {
|
||||
if proposer == nil || !bytes.Equal(val.Address.Bytes(), proposer.Address.Bytes()) {
|
||||
proposer = proposer.CompareProposerPriority(val)
|
||||
proposer = proposer.Cmp(val)
|
||||
}
|
||||
}
|
||||
return proposer
|
||||
|
|
@ -562,7 +564,9 @@ func (vals *ValidatorSet) updateWithChangeSet(changes []*Validator, allowDeletes
|
|||
vals.applyUpdates(updates)
|
||||
vals.applyRemovals(deletes)
|
||||
|
||||
vals.updateTotalVotingPower()
|
||||
if err := vals.updateTotalVotingPower(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Scale and center.
|
||||
vals.RescalePriorities(PriorityWindowSizeFactor * vals.TotalVotingPower())
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@ import (
|
|||
"sync"
|
||||
"time"
|
||||
|
||||
lru "github.com/hashicorp/golang-lru"
|
||||
"github.com/maticnetwork/bor/accounts"
|
||||
"github.com/maticnetwork/bor/common"
|
||||
"github.com/maticnetwork/bor/common/hexutil"
|
||||
|
|
@ -39,7 +40,6 @@ import (
|
|||
"github.com/maticnetwork/bor/params"
|
||||
"github.com/maticnetwork/bor/rlp"
|
||||
"github.com/maticnetwork/bor/rpc"
|
||||
lru "github.com/hashicorp/golang-lru"
|
||||
"golang.org/x/crypto/sha3"
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -22,12 +22,12 @@ import (
|
|||
"sort"
|
||||
"time"
|
||||
|
||||
lru "github.com/hashicorp/golang-lru"
|
||||
"github.com/maticnetwork/bor/common"
|
||||
"github.com/maticnetwork/bor/core/types"
|
||||
"github.com/maticnetwork/bor/ethdb"
|
||||
"github.com/maticnetwork/bor/log"
|
||||
"github.com/maticnetwork/bor/params"
|
||||
lru "github.com/hashicorp/golang-lru"
|
||||
)
|
||||
|
||||
// Vote represents a single vote that an authorized signer made to modify the
|
||||
|
|
|
|||
|
|
@ -31,6 +31,9 @@ var (
|
|||
// to the current node.
|
||||
ErrFutureBlock = errors.New("block in the future")
|
||||
|
||||
// ErrBlockTooSoon is returned when the period / producerDelay values in the genesis were not respected
|
||||
ErrBlockTooSoon = errors.New("block was produced sooner than expected")
|
||||
|
||||
// ErrInvalidNumber is returned if a block's number doesn't equal it's parent's
|
||||
// plus one.
|
||||
ErrInvalidNumber = errors.New("invalid block number")
|
||||
|
|
|
|||
|
|
@ -34,13 +34,13 @@ import (
|
|||
"unsafe"
|
||||
|
||||
mmap "github.com/edsrzf/mmap-go"
|
||||
"github.com/hashicorp/golang-lru/simplelru"
|
||||
"github.com/maticnetwork/bor/common"
|
||||
"github.com/maticnetwork/bor/consensus"
|
||||
"github.com/maticnetwork/bor/core/types"
|
||||
"github.com/maticnetwork/bor/log"
|
||||
"github.com/maticnetwork/bor/metrics"
|
||||
"github.com/maticnetwork/bor/rpc"
|
||||
"github.com/hashicorp/golang-lru/simplelru"
|
||||
)
|
||||
|
||||
var ErrInvalidDumpMagic = errors.New("invalid dump magic")
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@ import (
|
|||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
lru "github.com/hashicorp/golang-lru"
|
||||
"github.com/maticnetwork/bor/common"
|
||||
"github.com/maticnetwork/bor/consensus"
|
||||
"github.com/maticnetwork/bor/core/rawdb"
|
||||
|
|
@ -33,7 +34,6 @@ import (
|
|||
"github.com/maticnetwork/bor/ethdb"
|
||||
"github.com/maticnetwork/bor/log"
|
||||
"github.com/maticnetwork/bor/params"
|
||||
lru "github.com/hashicorp/golang-lru"
|
||||
)
|
||||
|
||||
const (
|
||||
|
|
|
|||
|
|
@ -26,10 +26,10 @@ import (
|
|||
"sync"
|
||||
"sync/atomic"
|
||||
|
||||
"github.com/golang/snappy"
|
||||
"github.com/maticnetwork/bor/common"
|
||||
"github.com/maticnetwork/bor/log"
|
||||
"github.com/maticnetwork/bor/metrics"
|
||||
"github.com/golang/snappy"
|
||||
)
|
||||
|
||||
var (
|
||||
|
|
|
|||
|
|
@ -19,10 +19,10 @@ package state
|
|||
import (
|
||||
"fmt"
|
||||
|
||||
lru "github.com/hashicorp/golang-lru"
|
||||
"github.com/maticnetwork/bor/common"
|
||||
"github.com/maticnetwork/bor/ethdb"
|
||||
"github.com/maticnetwork/bor/trie"
|
||||
lru "github.com/hashicorp/golang-lru"
|
||||
)
|
||||
|
||||
const (
|
||||
|
|
|
|||
|
|
@ -117,12 +117,6 @@ const (
|
|||
TxStatusIncluded
|
||||
)
|
||||
|
||||
// bor acts as a way to be able to type cast consensus.Engine;
|
||||
// since importing "github.com/maticnetwork/bor/consensus/bor" results in a cyclic dependency
|
||||
type bor interface {
|
||||
IsValidatorAction(chain consensus.ChainReader, from common.Address, tx *types.Transaction) bool
|
||||
}
|
||||
|
||||
// blockChain provides the state of blockchain and current gas limit to do
|
||||
// some pre checks in tx pool and event subscribers.
|
||||
type blockChain interface {
|
||||
|
|
@ -537,7 +531,6 @@ func (pool *TxPool) validateTx(tx *types.Transaction, local bool) error {
|
|||
// Drop non-local transactions under our own minimal accepted gas price
|
||||
local = local || pool.locals.contains(from) // account may be local even if the transaction arrived from the network
|
||||
if !local &&
|
||||
!pool.chain.Engine().(bor).IsValidatorAction(pool.chain.(consensus.ChainReader), from, tx) &&
|
||||
pool.gasPrice.Cmp(tx.GasPrice()) > 0 {
|
||||
return ErrUnderpriced
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1075,7 +1075,7 @@ after@0.8.2:
|
|||
resolved "https://registry.yarnpkg.com/after/-/after-0.8.2.tgz#fedb394f9f0e02aa9768e702bda23b505fae7e1f"
|
||||
integrity sha1-/ts5T58OAqqXaOcCvaI7UF+ufh8=
|
||||
|
||||
agent-base@4, agent-base@^4.1.0:
|
||||
agent-base@4, agent-base@^4.3.0:
|
||||
version "4.3.0"
|
||||
resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-4.3.0.tgz#8165f01c436009bccad0b1d122f05ed770efc6ee"
|
||||
integrity sha512-salcGninV0nPrwpGNn4VTXBb1SOuXQBiqbrNXoeizJsHrsL6ERFM2Ne3JUSBWRE6aeNJI2ROP/WEEIDUiDe3cg==
|
||||
|
|
@ -3650,11 +3650,11 @@ https-browserify@^1.0.0:
|
|||
integrity sha1-7AbBDgo0wPL68Zn3/X/Hj//QPHM=
|
||||
|
||||
https-proxy-agent@^2.2.0:
|
||||
version "2.2.1"
|
||||
resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-2.2.1.tgz#51552970fa04d723e04c56d04178c3f92592bbc0"
|
||||
integrity sha512-HPCTS1LW51bcyMYbxUIOO4HEOlQ1/1qRaFWcyxvwaqUS9TY88aoEuHUY33kuAh1YhVVaDQhLZsnPd+XNARWZlQ==
|
||||
version "2.2.4"
|
||||
resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-2.2.4.tgz#4ee7a737abd92678a293d9b34a1af4d0d08c787b"
|
||||
integrity sha512-OmvfoQ53WLjtA9HeYP9RNrWMJzzAz1JGaSFr1nijg0PVR1JaD/xbJq1mdEIIlxGpXp9eSe/O2LgU9DJmTPd0Eg==
|
||||
dependencies:
|
||||
agent-base "^4.1.0"
|
||||
agent-base "^4.3.0"
|
||||
debug "^3.1.0"
|
||||
|
||||
humps@^2.0.1:
|
||||
|
|
|
|||
|
|
@ -248,3 +248,17 @@ func (b *EthAPIBackend) ServiceFilter(ctx context.Context, session *bloombits.Ma
|
|||
go session.Multiplex(bloomRetrievalBatch, bloomRetrievalWait, b.eth.bloomRequests)
|
||||
}
|
||||
}
|
||||
|
||||
func (b *EthAPIBackend) GetRootHash(ctx context.Context, starBlockNr uint64, endBlockNr uint64) (string, error) {
|
||||
var api *bor.API
|
||||
for _, _api := range b.eth.Engine().APIs(b.eth.BlockChain()) {
|
||||
if _api.Namespace == "bor" {
|
||||
api = _api.Service.(*bor.API)
|
||||
}
|
||||
}
|
||||
root, err := api.GetRootHash(starBlockNr, endBlockNr)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return root, nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -522,6 +522,15 @@ func (ec *Client) SendTransaction(ctx context.Context, tx *types.Transaction) er
|
|||
return ec.c.CallContext(ctx, nil, "eth_sendRawTransaction", common.ToHex(data))
|
||||
}
|
||||
|
||||
// GetRootHash returns the merkle root of the block headers
|
||||
func (ec *Client) GetRootHash(ctx context.Context, startBlockNumber uint64, endBlockNumber uint64) (string, error) {
|
||||
var rootHash string
|
||||
if err := ec.c.CallContext(ctx, &rootHash, "eth_getRootHash", startBlockNumber, endBlockNumber); err != nil {
|
||||
return "", err
|
||||
}
|
||||
return rootHash, nil
|
||||
}
|
||||
|
||||
func toCallArg(msg ethereum.CallMsg) interface{} {
|
||||
arg := map[string]interface{}{
|
||||
"from": msg.From,
|
||||
|
|
|
|||
1
go.mod
1
go.mod
|
|
@ -60,6 +60,7 @@ require (
|
|||
github.com/syndtr/goleveldb v1.0.1-0.20190923125748-758128399b1d
|
||||
github.com/tyler-smith/go-bip39 v1.0.1-0.20181017060643-dbb3b84ba2ef
|
||||
github.com/wsddn/go-ecdh v0.0.0-20161211032359-48726bab9208
|
||||
github.com/xsleonard/go-merkle v1.1.0
|
||||
golang.org/x/crypto v0.0.0-20200311171314-f7b00557c8c4
|
||||
golang.org/x/net v0.0.0-20200301022130-244492dfa37a
|
||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e
|
||||
|
|
|
|||
2
go.sum
2
go.sum
|
|
@ -176,6 +176,8 @@ github.com/tyler-smith/go-bip39 v1.0.1-0.20181017060643-dbb3b84ba2ef h1:wHSqTBrZ
|
|||
github.com/tyler-smith/go-bip39 v1.0.1-0.20181017060643-dbb3b84ba2ef/go.mod h1:sJ5fKU0s6JVwZjjcUEX2zFOnvq0ASQ2K9Zr6cf67kNs=
|
||||
github.com/wsddn/go-ecdh v0.0.0-20161211032359-48726bab9208 h1:1cngl9mPEoITZG8s8cVcUy5CeIBYhEESkOB7m6Gmkrk=
|
||||
github.com/wsddn/go-ecdh v0.0.0-20161211032359-48726bab9208/go.mod h1:IotVbo4F+mw0EzQ08zFqg7pK3FebNXpaMsRy2RT+Ees=
|
||||
github.com/xsleonard/go-merkle v1.1.0 h1:fHe1fuhJjGH22ZzVTAH0jqHLhTGhOq3wQjJN+8P0jQg=
|
||||
github.com/xsleonard/go-merkle v1.1.0/go.mod h1:cW4z+UZ/4f2n9IJgIiyDCdYguchoDyDAPmpuOWGxdGg=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2 h1:VklqNMn3ovrHsnt90PveolxSbWFaJdECFbxSq0Mqo2M=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20200311171314-f7b00557c8c4 h1:QmwruyY+bKbDDL0BaglrbZABEali68eoMFhTZpCjYVA=
|
||||
|
|
|
|||
|
|
@ -21,12 +21,12 @@ import (
|
|||
"net"
|
||||
"net/http"
|
||||
|
||||
"github.com/graph-gophers/graphql-go"
|
||||
"github.com/graph-gophers/graphql-go/relay"
|
||||
"github.com/maticnetwork/bor/internal/ethapi"
|
||||
"github.com/maticnetwork/bor/log"
|
||||
"github.com/maticnetwork/bor/p2p"
|
||||
"github.com/maticnetwork/bor/rpc"
|
||||
"github.com/graph-gophers/graphql-go"
|
||||
"github.com/graph-gophers/graphql-go/relay"
|
||||
)
|
||||
|
||||
// Service encapsulates a GraphQL service.
|
||||
|
|
|
|||
|
|
@ -24,10 +24,10 @@ import (
|
|||
"os"
|
||||
"runtime"
|
||||
|
||||
"github.com/fjl/memsize/memsizeui"
|
||||
"github.com/maticnetwork/bor/log"
|
||||
"github.com/maticnetwork/bor/metrics"
|
||||
"github.com/maticnetwork/bor/metrics/exp"
|
||||
"github.com/fjl/memsize/memsizeui"
|
||||
colorable "github.com/mattn/go-colorable"
|
||||
"github.com/mattn/go-isatty"
|
||||
"gopkg.in/urfave/cli.v1"
|
||||
|
|
|
|||
|
|
@ -716,6 +716,14 @@ func (s *PublicBlockChainAPI) GetStorageAt(ctx context.Context, address common.A
|
|||
return res[:], state.Error()
|
||||
}
|
||||
|
||||
func (s *PublicBlockChainAPI) GetRootHash(ctx context.Context, starBlockNr uint64, endBlockNr uint64) (string, error) {
|
||||
root, err := s.b.GetRootHash(ctx, starBlockNr, endBlockNr)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return root, nil
|
||||
}
|
||||
|
||||
// CallArgs represents the arguments for a call.
|
||||
type CallArgs struct {
|
||||
From *common.Address `json:"from"`
|
||||
|
|
|
|||
|
|
@ -62,6 +62,7 @@ type Backend interface {
|
|||
SubscribeStateEvent(ch chan<- core.NewStateChangeEvent) event.Subscription
|
||||
SubscribeChainHeadEvent(ch chan<- core.ChainHeadEvent) event.Subscription
|
||||
SubscribeChainSideEvent(ch chan<- core.ChainSideEvent) event.Subscription
|
||||
GetRootHash(ctx context.Context, starBlockNr uint64, endBlockNr uint64) (string, error)
|
||||
|
||||
// Transaction pool API
|
||||
SendTx(ctx context.Context, signedTx *types.Transaction) error
|
||||
|
|
|
|||
|
|
@ -149,6 +149,11 @@ web3._extend({
|
|||
call: 'bor_getCurrentValidators',
|
||||
params: 0
|
||||
}),
|
||||
new web3._extend.Method({
|
||||
name: 'getRootHash',
|
||||
call: 'bor_getRootHash',
|
||||
params: 2,
|
||||
}),
|
||||
]
|
||||
});
|
||||
`
|
||||
|
|
|
|||
|
|
@ -223,3 +223,7 @@ func (b *LesApiBackend) ServiceFilter(ctx context.Context, session *bloombits.Ma
|
|||
go session.Multiplex(bloomRetrievalBatch, bloomRetrievalWait, b.eth.bloomRequests)
|
||||
}
|
||||
}
|
||||
|
||||
func (b *LesApiBackend) GetRootHash(ctx context.Context, starBlockNr uint64, endBlockNr uint64) (string, error) {
|
||||
return "", errors.New("Not implemented")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@ import (
|
|||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
lru "github.com/hashicorp/golang-lru"
|
||||
"github.com/maticnetwork/bor/common"
|
||||
"github.com/maticnetwork/bor/consensus"
|
||||
"github.com/maticnetwork/bor/core"
|
||||
|
|
@ -37,7 +38,6 @@ import (
|
|||
"github.com/maticnetwork/bor/log"
|
||||
"github.com/maticnetwork/bor/params"
|
||||
"github.com/maticnetwork/bor/rlp"
|
||||
lru "github.com/hashicorp/golang-lru"
|
||||
)
|
||||
|
||||
var (
|
||||
|
|
|
|||
|
|
@ -5,9 +5,9 @@ import (
|
|||
uurl "net/url"
|
||||
"time"
|
||||
|
||||
"github.com/influxdata/influxdb/client"
|
||||
"github.com/maticnetwork/bor/log"
|
||||
"github.com/maticnetwork/bor/metrics"
|
||||
"github.com/influxdata/influxdb/client"
|
||||
)
|
||||
|
||||
type reporter struct {
|
||||
|
|
|
|||
|
|
@ -12,19 +12,13 @@ type IHeimdallClient struct {
|
|||
mock.Mock
|
||||
}
|
||||
|
||||
// Fetch provides a mock function with given fields: paths
|
||||
func (_m *IHeimdallClient) Fetch(paths ...string) (*bor.ResponseWithHeight, error) {
|
||||
_va := make([]interface{}, len(paths))
|
||||
for _i := range paths {
|
||||
_va[_i] = paths[_i]
|
||||
}
|
||||
var _ca []interface{}
|
||||
_ca = append(_ca, _va...)
|
||||
ret := _m.Called(_ca...)
|
||||
// Fetch provides a mock function with given fields: path, query
|
||||
func (_m *IHeimdallClient) Fetch(path string, query string) (*bor.ResponseWithHeight, error) {
|
||||
ret := _m.Called(path, query)
|
||||
|
||||
var r0 *bor.ResponseWithHeight
|
||||
if rf, ok := ret.Get(0).(func(...string) *bor.ResponseWithHeight); ok {
|
||||
r0 = rf(paths...)
|
||||
if rf, ok := ret.Get(0).(func(string, string) *bor.ResponseWithHeight); ok {
|
||||
r0 = rf(path, query)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).(*bor.ResponseWithHeight)
|
||||
|
|
@ -32,8 +26,8 @@ func (_m *IHeimdallClient) Fetch(paths ...string) (*bor.ResponseWithHeight, erro
|
|||
}
|
||||
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(1).(func(...string) error); ok {
|
||||
r1 = rf(paths...)
|
||||
if rf, ok := ret.Get(1).(func(string, string) error); ok {
|
||||
r1 = rf(path, query)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
|
@ -41,19 +35,36 @@ func (_m *IHeimdallClient) Fetch(paths ...string) (*bor.ResponseWithHeight, erro
|
|||
return r0, r1
|
||||
}
|
||||
|
||||
// FetchWithRetry provides a mock function with given fields: paths
|
||||
func (_m *IHeimdallClient) FetchWithRetry(paths ...string) (*bor.ResponseWithHeight, error) {
|
||||
_va := make([]interface{}, len(paths))
|
||||
for _i := range paths {
|
||||
_va[_i] = paths[_i]
|
||||
// FetchStateSyncEvents provides a mock function with given fields: fromID, to
|
||||
func (_m *IHeimdallClient) FetchStateSyncEvents(fromID uint64, to int64) ([]*bor.EventRecordWithTime, error) {
|
||||
ret := _m.Called(fromID, to)
|
||||
|
||||
var r0 []*bor.EventRecordWithTime
|
||||
if rf, ok := ret.Get(0).(func(uint64, int64) []*bor.EventRecordWithTime); ok {
|
||||
r0 = rf(fromID, to)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).([]*bor.EventRecordWithTime)
|
||||
}
|
||||
}
|
||||
var _ca []interface{}
|
||||
_ca = append(_ca, _va...)
|
||||
ret := _m.Called(_ca...)
|
||||
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(1).(func(uint64, int64) error); ok {
|
||||
r1 = rf(fromID, to)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// FetchWithRetry provides a mock function with given fields: path, query
|
||||
func (_m *IHeimdallClient) FetchWithRetry(path string, query string) (*bor.ResponseWithHeight, error) {
|
||||
ret := _m.Called(path, query)
|
||||
|
||||
var r0 *bor.ResponseWithHeight
|
||||
if rf, ok := ret.Get(0).(func(...string) *bor.ResponseWithHeight); ok {
|
||||
r0 = rf(paths...)
|
||||
if rf, ok := ret.Get(0).(func(string, string) *bor.ResponseWithHeight); ok {
|
||||
r0 = rf(path, query)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).(*bor.ResponseWithHeight)
|
||||
|
|
@ -61,8 +72,8 @@ func (_m *IHeimdallClient) FetchWithRetry(paths ...string) (*bor.ResponseWithHei
|
|||
}
|
||||
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(1).(func(...string) error); ok {
|
||||
r1 = rf(paths...)
|
||||
if rf, ok := ret.Get(1).(func(string, string) error); ok {
|
||||
r1 = rf(path, query)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,8 +25,8 @@ import (
|
|||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/maticnetwork/bor/log"
|
||||
"github.com/jackpal/go-nat-pmp"
|
||||
"github.com/maticnetwork/bor/log"
|
||||
)
|
||||
|
||||
// An implementation of nat.Interface can map local ports to ports
|
||||
|
|
|
|||
|
|
@ -35,11 +35,11 @@ import (
|
|||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/golang/snappy"
|
||||
"github.com/maticnetwork/bor/common/bitutil"
|
||||
"github.com/maticnetwork/bor/crypto"
|
||||
"github.com/maticnetwork/bor/crypto/ecies"
|
||||
"github.com/maticnetwork/bor/rlp"
|
||||
"github.com/golang/snappy"
|
||||
"golang.org/x/crypto/sha3"
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -29,12 +29,12 @@ import (
|
|||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/julienschmidt/httprouter"
|
||||
"github.com/maticnetwork/bor/event"
|
||||
"github.com/maticnetwork/bor/p2p"
|
||||
"github.com/maticnetwork/bor/p2p/enode"
|
||||
"github.com/maticnetwork/bor/p2p/simulations/adapters"
|
||||
"github.com/maticnetwork/bor/rpc"
|
||||
"github.com/julienschmidt/httprouter"
|
||||
"golang.org/x/net/websocket"
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -319,6 +319,7 @@ 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
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue