diff --git a/.github/release.env b/.github/release.env
index 2034c1d3f..62e94fa4b 100644
--- a/.github/release.env
+++ b/.github/release.env
@@ -1,2 +1,2 @@
-MAINNET_FILE_URL="https://github.com/binance-chain/bsc/releases/download/v1.1.7/mainnet.zip"
-TESTNET_FILE_URL="https://github.com/binance-chain/bsc/releases/download/v1.1.7/testnet.zip"
+MAINNET_FILE_URL="https://github.com/binance-chain/bsc/releases/download/v1.1.8/mainnet.zip"
+TESTNET_FILE_URL="https://github.com/binance-chain/bsc/releases/download/v1.1.8/testnet.zip"
diff --git a/.gitignore b/.gitignore
index da208c066..56035c340 100644
--- a/.gitignore
+++ b/.gitignore
@@ -50,5 +50,7 @@ profile.cov
**/yarn-error.log
cmd/geth/node/
cmd/geth/__debug_bin
+cmd/bootnode/bootnode
+graphql/__debug_bin
datadir/
\ No newline at end of file
diff --git a/accounts/abi/bind/backends/simulated.go b/accounts/abi/bind/backends/simulated.go
index e01b546b8..8e81e7a06 100644
--- a/accounts/abi/bind/backends/simulated.go
+++ b/accounts/abi/bind/backends/simulated.go
@@ -30,6 +30,7 @@ import (
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/common/math"
+ "github.com/ethereum/go-ethereum/consensus/ethash"
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/bloombits"
"github.com/ethereum/go-ethereum/core/rawdb"
@@ -77,7 +78,7 @@ type SimulatedBackend struct {
func NewSimulatedBackendWithDatabase(database ethdb.Database, alloc core.GenesisAlloc, gasLimit uint64) *SimulatedBackend {
genesis := core.Genesis{Config: params.AllEthashProtocolChanges, GasLimit: gasLimit, Alloc: alloc}
genesis.MustCommit(database)
- blockchain, _ := core.NewBlockChain(database, nil, genesis.Config, nil, vm.Config{}, nil, nil)
+ blockchain, _ := core.NewBlockChain(database, nil, genesis.Config, ethash.NewFaker(), vm.Config{}, nil, nil)
backend := &SimulatedBackend{
database: database,
@@ -123,7 +124,7 @@ func (b *SimulatedBackend) Rollback() {
}
func (b *SimulatedBackend) rollback() {
- blocks, _ := core.GenerateChain(b.config, b.blockchain.CurrentBlock(), nil, b.database, 1, func(int, *core.BlockGen) {})
+ blocks, _ := core.GenerateChain(b.config, b.blockchain.CurrentBlock(), ethash.NewFaker(), b.database, 1, func(int, *core.BlockGen) {})
b.pendingBlock = blocks[0]
b.pendingState, _ = state.New(b.pendingBlock.Root(), b.blockchain.StateCache(), nil)
@@ -571,7 +572,7 @@ func (b *SimulatedBackend) SendTransaction(ctx context.Context, tx *types.Transa
}
// Include tx in chain.
- blocks, _ := core.GenerateChain(b.config, block, nil, b.database, 1, func(number int, block *core.BlockGen) {
+ blocks, _ := core.GenerateChain(b.config, block, ethash.NewFaker(), b.database, 1, func(number int, block *core.BlockGen) {
for _, tx := range b.pendingBlock.Transactions() {
block.AddTxWithChain(b.blockchain, tx)
}
@@ -689,7 +690,7 @@ func (b *SimulatedBackend) AdjustTime(adjustment time.Duration) error {
return errors.New("Could not adjust time on non-empty block")
}
- blocks, _ := core.GenerateChain(b.config, b.blockchain.CurrentBlock(), nil, b.database, 1, func(number int, block *core.BlockGen) {
+ blocks, _ := core.GenerateChain(b.config, b.blockchain.CurrentBlock(), ethash.NewFaker(), b.database, 1, func(number int, block *core.BlockGen) {
block.OffsetTime(int64(adjustment.Seconds()))
})
stateDB, _ := b.blockchain.State()
@@ -769,10 +770,18 @@ func (fb *filterBackend) SubscribeNewTxsEvent(ch chan<- core.NewTxsEvent) event.
return nullSubscription()
}
+func (fb *filterBackend) SubscribeNewVoteEvent(ch chan<- core.NewVoteEvent) event.Subscription {
+ return nullSubscription()
+}
+
func (fb *filterBackend) SubscribeChainEvent(ch chan<- core.ChainEvent) event.Subscription {
return fb.bc.SubscribeChainEvent(ch)
}
+func (fb *filterBackend) SubscribeFinalizedHeaderEvent(ch chan<- core.FinalizedHeaderEvent) event.Subscription {
+ return fb.bc.SubscribeFinalizedHeaderEvent(ch)
+}
+
func (fb *filterBackend) SubscribeRemovedLogsEvent(ch chan<- core.RemovedLogsEvent) event.Subscription {
return fb.bc.SubscribeRemovedLogsEvent(ch)
}
diff --git a/cmd/blockdump/main.go b/cmd/blockdump/main.go
index 331683278..052e1785a 100644
--- a/cmd/blockdump/main.go
+++ b/cmd/blockdump/main.go
@@ -1,37 +1,269 @@
package main
import (
+ "bytes"
"context"
- "math/big"
-
+ "fmt"
+ "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
+ "github.com/ethereum/go-ethereum/consensus/parlia"
+ "github.com/ethereum/go-ethereum/core/types"
+ "github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/ethclient"
+ "github.com/ethereum/go-ethereum/ethdb/memorydb"
"github.com/ethereum/go-ethereum/rlp"
+ "github.com/ethereum/go-ethereum/trie"
+ "golang.org/x/crypto/sha3"
+ "io"
+ "math/big"
+ "os"
)
-func main() {
- eth, err := ethclient.Dial("https://rpc.ankr.com/bsc")
+const (
+ extraVanity = 32 // Fixed number of extra-data prefix bytes reserved for signer vanity
+ extraSeal = 65 // Fixed number of extra-data suffix bytes reserved for signer seal
+ nextForkHashSize = 4 // Fixed number of extra-data suffix bytes reserved for nextForkHash.
+)
+
+func encodeSigHeader(w io.Writer, header *types.Header, chainId *big.Int) {
+ err := rlp.Encode(w, []interface{}{
+ chainId,
+ header.ParentHash,
+ header.UncleHash,
+ header.Coinbase,
+ header.Root,
+ header.TxHash,
+ header.ReceiptHash,
+ header.Bloom,
+ header.Difficulty,
+ header.Number,
+ header.GasLimit,
+ header.GasUsed,
+ header.Time,
+ header.Extra[:len(header.Extra)-65], // this will panic if extra is too short, should check before calling encodeSigHeader
+ header.MixDigest,
+ header.Nonce,
+ })
+ if err != nil {
+ panic("can't encode: " + err.Error())
+ }
+}
+
+func SealHash(header *types.Header, chainId *big.Int) (hash common.Hash) {
+ hasher := sha3.NewLegacyKeccak256()
+ encodeSigHeader(hasher, header, chainId)
+ hasher.Sum(hash[:0])
+ return hash
+}
+
+func ecrecover(header *types.Header, chainId *big.Int) (common.Address, error) {
+ // Retrieve the signature from the header extra-data
+ if len(header.Extra) < extraSeal {
+ return common.Address{}, fmt.Errorf("missing signature")
+ }
+ signature := header.Extra[len(header.Extra)-extraSeal:]
+
+ // Recover the public key and the Ethereum address
+ pubkey, err := crypto.Ecrecover(SealHash(header, chainId).Bytes(), signature)
+ if err != nil {
+ return common.Address{}, err
+ }
+ var signer common.Address
+ copy(signer[:], crypto.Keccak256(pubkey[1:])[12:])
+
+ return signer, nil
+}
+
+type proofList struct {
+ items map[string][]byte
+}
+
+func (n *proofList) Put(key []byte, value []byte) error {
+ if n.items == nil {
+ n.items = make(map[string][]byte)
+ }
+ println(fmt.Sprintf(" + key=%s value=%s", hexutil.Encode(key), hexutil.Encode(value)))
+ n.items[hexutil.Encode(key)] = value
+ return nil
+}
+
+func (n *proofList) Delete(key []byte) error {
+ panic("not supported")
+}
+
+func (n *proofList) Has(key []byte) (bool, error) {
+ _, ok := n.items[hexutil.Encode(key)]
+ return ok, nil
+}
+
+func (n *proofList) Get(key []byte) ([]byte, error) {
+ res, _ := n.items[hexutil.Encode(key)]
+ return res, nil
+}
+
+func createProof(eth *ethclient.Client) {
+ tree, _ := trie.New(common.Hash{}, trie.NewDatabase(memorydb.New()))
+ block, err := eth.BlockByNumber(context.Background(), big.NewInt(1))
+ if err != nil {
+ panic(err)
+ }
+ receipts, err := eth.TransactionRecipientsInBlock(context.Background(), big.NewInt(1))
+ if err != nil {
+ panic(err)
+ }
+ root := types.DeriveSha(types.Receipts(receipts), tree)
+ if block.ReceiptHash() != root {
+ panic(fmt.Sprintf("bad root, %s != %s", block.ReceiptHash().Hex(), root.Hex()))
+ }
+ firstReceipt := receipts[0]
+ firstReceiptKey, _ := rlp.EncodeToBytes(firstReceipt.TransactionIndex)
+ var proof proofList
+ if err := tree.Prove(firstReceiptKey, 0, &proof); err != nil {
+ panic(err)
+ }
+ println(root.Hex())
+ _, err = trie.VerifyProof(root, firstReceiptKey, &proof)
if err != nil {
panic(err)
}
- confirmations := 12
- for i := 0; i < confirmations; i++ {
- block, err := eth.BlockByNumber(context.Background(), big.NewInt(int64(15946200+i)))
+
+ os.Exit(0)
+}
+
+func extractParliaValidators(header *types.Header) ([]common.Address, error) {
+ validatorBytes := header.Extra[extraVanity : len(header.Extra)-extraSeal]
+ if len(validatorBytes)%common.AddressLength != 0 {
+ return nil, fmt.Errorf("corrupted extra data")
+ }
+ n := len(validatorBytes) / common.AddressLength
+ result := make([]common.Address, n)
+ for i := 0; i < n; i++ {
+ address := make([]byte, common.AddressLength)
+ copy(address, validatorBytes[i*common.AddressLength:(i+1)*common.AddressLength])
+ result[i] = common.BytesToAddress(address)
+ }
+ return result, nil
+}
+
+func createBlockTransitionProofs(eth *ethclient.Client, sinceBlock, epochLength uint64) {
+ var prevEpochBlock uint64
+ if sinceBlock >= epochLength {
+ prevEpochBlock = (sinceBlock/epochLength)*epochLength - epochLength
+ }
+ prevEpochValidatorBlock, err := eth.BlockByNumber(context.TODO(), big.NewInt(int64(prevEpochBlock)))
+ if err != nil {
+ panic(err)
+ }
+ validators, err := extractParliaValidators(prevEpochValidatorBlock.Header())
+ if err != nil {
+ panic(err)
+ }
+ confirmations := len(validators) * 2 / 3
+ uniqueSigners := make(map[common.Address]int)
+ for i := 0; i < int(epochLength); i++ {
+ block, err := eth.BlockByNumber(context.Background(), big.NewInt(int64(sinceBlock)+int64(i)))
if err != nil {
panic(err)
}
+ uniqueSigners[block.Header().Coinbase]++
+ //println(fmt.Sprintf("block #%d", block.NumberU64()))
+ //for signer, count := range uniqueSigners {
+ // println(fmt.Sprintf(" ~ %s\t%d", signer.Hex(), count))
+ //}
payload, _ := rlp.EncodeToBytes(block.Header())
println(hexutil.Encode(payload))
+ if len(uniqueSigners) >= confirmations {
+ break
+ }
+ }
+ if len(uniqueSigners) < confirmations {
+ panic("quorum is not reached")
+ }
+}
+
+func main() {
+ //eth, err := ethclient.Dial("https://rpc.ankr.com/bsc")
+ eth, err := ethclient.Dial("https://data-seed-prebsc-1-s1.binance.org:8545/")
+ if err != nil {
+ panic(err)
+ }
+
+ var latestSeal []byte
+ collectedSigners := make(map[common.Address]bool)
+ var fromBlock uint64
+ var totalValidators int
+ for i := 0; i < 100000; i++ {
+ epochBlock := i * 200
+ block, err := eth.BlockByNumber(context.TODO(), big.NewInt(int64(epochBlock)))
+ if err != nil {
+ panic(err)
+ }
+ if i == 0 {
+ latestSeal = crypto.Keccak256(block.Header().Extra[32 : len(block.Header().Extra)-65])
+ fromBlock = block.NumberU64()
+ continue
+ }
+ validatorSeal := crypto.Keccak256(block.Header().Extra[32 : len(block.Header().Extra)-65])
+ collectedSigners[block.Coinbase()] = true
+ totalValidatorsArr, _ := extractParliaValidators(block.Header())
+ totalValidators = len(totalValidatorsArr)
+ //payload, _ := rlp.EncodeToBytes(block.Header())
+ //println(hexutil.Encode(payload))
+ //fmt.Printf("checked epoch=%d, unique_signers=%d, total_validators=%d\n", i, len(collectedSigners), len(totalValidators))
+ if !bytes.Equal(validatorSeal, latestSeal) {
+ fmt.Printf("found seal diff (%d -> %d), dist=%d, total_validators=%d, signers=%d\n", fromBlock, block.NumberU64(), (block.NumberU64()-fromBlock)/200, totalValidators, len(collectedSigners))
+ latestSeal = validatorSeal
+ fromBlock = block.NumberU64()
+ collectedSigners = make(map[common.Address]bool)
+ totalValidators = 0
+ }
+ }
+ os.Exit(0)
+
+ //createBlockTransitionProofs(eth, 808800, 200)
+ //os.Exit(0)
+
+ //confirmations := 6
+ //for i := 0; i < confirmations; i++ {
+ //receipts := make([]*types.Receipt, len(block.Transactions()))
+ //for i, tx := range block.Transactions() {
+ // receipt, err := eth.TransactionReceipt(context.Background(), tx.Hash())
+ // if err != nil {
+ // panic(err)
+ // }
+ // receipts[i] = receipt
+ //}
+ //receiptRoot := types.DeriveSha(types.Receipts(receipts), trie.NewStackTrie(nil))
+ //println(receiptRoot.Hex())
+ //}
+ //os.Exit(0)
+
+ type a struct {
+ Value uint64
+ }
+ res, _ := rlp.EncodeToBytes(&a{Value: 1587390414})
+ println(hexutil.Encode(res))
+
+ block, err := eth.BlockByNumber(context.Background(), big.NewInt(int64(200)))
+ if err != nil {
+ panic(err)
}
- //println("-----------BLOCK HEADER JSON-----------------")
- //json, _ := block.Header().MarshalJSON()
- //println(string(json))
- //println("------------BLOCK HEADER---------------------")
- //payload, err := rlp.EncodeToBytes(block.Header())
- //println(hexutil.Encode(payload)[2:])
- //println("----------EXTRA DATA SHOULD BE---------------")
- //println(hexutil.Encode(block.Header().Extra[:len(block.Header().Extra)-65])[2:])
- //println("----------SIGNING DATA-----------------------")
- //signingData := parlia.ParliaRLP(block.Header(), big.NewInt(56))
- //println(hexutil.Encode(signingData)[2:])
+ println("-----------BLOCK HEADER JSON-----------------")
+ json, _ := block.Header().MarshalJSON()
+ println(string(json))
+ println("------------BLOCK HEADER---------------------")
+ payload, err := rlp.EncodeToBytes(block.Header())
+ println(hexutil.Encode(payload)[2:])
+ println("----------EXTRA DATA SHOULD BE---------------")
+ println(hexutil.Encode(block.Header().Extra[:len(block.Header().Extra)-65])[2:])
+ println("----------SIGNING DATA-----------------------")
+ signingData := parlia.ParliaRLP(block.Header(), big.NewInt(97))
+ println(hexutil.Encode(signingData)[2:])
+ println()
+ println()
+ //addr, err := ecrecover(block.Header(), big.NewInt(56))
+ //if err != nil {
+ // panic(err)
+ //}
+ //println(addr.Hex())
}
diff --git a/cmd/estimator/main.go b/cmd/estimator/main.go
new file mode 100644
index 000000000..f9360497a
--- /dev/null
+++ b/cmd/estimator/main.go
@@ -0,0 +1,120 @@
+package main
+
+import (
+ "context"
+ "fmt"
+ "github.com/ethereum/go-ethereum"
+ "github.com/ethereum/go-ethereum/accounts/abi"
+ "github.com/ethereum/go-ethereum/accounts/abi/bind/backends"
+ "github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/common/hexutil"
+ "github.com/ethereum/go-ethereum/core"
+ "github.com/ethereum/go-ethereum/core/vm"
+ "math/big"
+ "strings"
+ "time"
+)
+
+func newArguments(typeNames ...string) abi.Arguments {
+ var args abi.Arguments
+ for i, tn := range typeNames {
+ abiType, err := abi.NewType(tn, tn, nil)
+ if err != nil {
+ panic(err)
+ }
+ args = append(args, abi.Argument{Name: fmt.Sprintf("%d", i), Type: abiType})
+ }
+ return args
+}
+
+func mustNewArguments(types ...string) (result abi.Arguments) {
+ var err error
+ for _, t := range types {
+ var typ abi.Type
+ items := strings.Split(t, " ")
+ var name string
+ if len(items) == 2 {
+ name = items[1]
+ } else {
+ name = items[0]
+ }
+ typ, err = abi.NewType(items[0], items[0], nil)
+ if err != nil {
+ panic(err)
+ }
+ result = append(result, abi.Argument{Type: typ, Name: name})
+ }
+ return result
+}
+
+var verifyParliaBlockInput = mustNewArguments(
+ "uint256 chainId",
+ "bytes blockProof",
+ "uint32 epochInterval",
+)
+
+var measureBlockInput = mustNewArguments(
+ "bytes blockProof",
+ "uint256 chainId",
+)
+
+// bytecode of VerifierGasMeasurer contract
+func createBytecode() []byte {
+ backend := backends.NewSimulatedBackend(core.GenesisAlloc{}, 8_000_000)
+ bytecode, err := backend.CallContract(context.Background(), ethereum.CallMsg{
+ Data: hexutil.MustDecode("0x60c060405234801561001057600080fd5b50600f60805260c860a05260805160a05161259f6100476000396000818161049f0152610e5f0152600061047e015261259f6000f3fe608060405234801561001057600080fd5b50600436106100625760003560e01c80632a36f0ad146100675780632b150b3c146100975780632dae36a0146100b7578063783d0962146100d757806386cd0354146100ea578063e4adf32f1461010a575b600080fd5b61007a610075366004611f7b565b61012b565b6040516001600160401b0390911681526020015b60405180910390f35b6100aa6100a5366004611fc6565b61014f565b60405161008e9190612007565b6100ca6100c53660046120ec565b610387565b60405161008e9190612180565b6100ca6100e5366004611f7b565b6103f4565b6100fd6100f8366004611f7b565b610464565b60405161008e91906121e0565b61011d6101183660046122bf565b610477565b60405161008e929190612338565b60005a905061013b848484610842565b505a6101479082612379565b949350505050565b604080516101a081018252600080825260208201819052918101829052606081018290526080810182905260a0810182905260c0810182905260e0810182905261010081018290526101208101829052610140810182905261016081018290526101808101829052906101c28484610fb6565b90506101cd81610fcc565b82526101d881610fdf565b90506101e381610fcc565b60208301526101f181610fdf565b90506101fc81610ff4565b6001600160a01b0316604083015261021381610fdf565b905061021e81610fcc565b606083015261022c81610fdf565b905061023781610fcc565b608083015261024581610fdf565b905061025081610fcc565b60a083015261025e81610fdf565b905061026981610fdf565b905061027481610fdf565b90506102888161028383611001565b6110a4565b6001600160401b031660c083015261029f81610fdf565b90506102ae8161028383611001565b6001600160401b031660e08301526102c581610fdf565b90506102d48161028383611001565b6001600160401b03166101008301526102ec81610fdf565b90506102fb8161028383611001565b6001600160401b031661012083015261031381610fdf565b905061031e81610fdf565b905061032981610fcc565b61014083015261033881610fdf565b90506103478161028383611001565b6001600160401b031661016083015261035f81610fdf565b905083836040516103719291906123a1565b6040519081900390206101808301525092915050565b60606000610396868686610842565b805190915083146103e75760405162461bcd60e51b81526020600482015260166024820152756e6f74206120636865636b706f696e7420626c6f636b60501b60448201526064015b60405180910390fd5b6060015195945050505050565b60606000610403858585610842565b905080602001516001600160401b03166000146104585760405162461bcd60e51b81526020600482015260136024820152726e6f7420612067656e6573697320626c6f636b60681b60448201526064016103de565b60600151949350505050565b61046c611efc565b610147848484610842565b60606000807f00000000000000000000000000000000000000000000000000000000000000007f000000000000000000000000000000000000000000000000000000000000000063ffffffff82168910156105125760405162461bcd60e51b815260206004820152601b60248201527a0dac2d6ca40e6eae4ca40e0e4dedecce640c2e4ca40cadcdeeaced602b1b60448201526064016103de565b6000896001600160401b0381111561052c5761052c6123b1565b604051908082528060200260200182016040528015610555578160200160208202803683370190505b5090506000805b8463ffffffff168110156107de5760006105998e8e84818110610581576105816123c7565b905060200281019061059391906123dd565b8e610842565b60408101519091506000805b8c81101561060757826001600160a01b03168e8e838181106105c9576105c96123c7565b90506020020160208101906105de9190612423565b6001600160a01b0316036105f55760019150610607565b806105ff8161244c565b9150506105a5565b50806106465760405162461bcd60e51b815260206004820152600e60248201526d3ab735b737bbb71039b4b3b732b960911b60448201526064016103de565b6000805b866001600160401b03168110156106a857836001600160a01b0316888281518110610677576106776123c7565b60200260200101516001600160a01b03160361069657600191506106a8565b806106a08161244c565b91505061064a565b50806106f0578287876001600160401b0316815181106106ca576106ca6123c7565b6001600160a01b0390921660209283029190910190910152856106ec81612465565b9650505b8460000361077d578763ffffffff16846020015161070e91906124a1565b6001600160401b0316156107525760405162461bcd60e51b815260206004820152600b60248201526a65706f636820626c6f636b60a81b60448201526064016103de565b8763ffffffff16846020015161076891906124c7565b9a5083606001519b50836000015199506107c7565b898460800151146107c25760405162461bcd60e51b815260206004820152600f60248201526e0c4c2c840e0c2e4cadce840d0c2e6d608b1b60448201526064016103de565b835199505b5050505080806107d69061244c565b91505061055c565b508363ffffffff16816001600160401b031610156108335760405162461bcd60e51b81526020600482015260126024820152711c5d5bdc9d5b481b9bdd081c995858da195960721b60448201526064016103de565b50505050509550959350505050565b61084a611efc565b61ffff83111561085957600080fd5b83600061086582611001565b9050610870826110b7565b915061087b82610fcc565b608084015261088c6101bd836124ed565b915061089782610fdf565b91506108a68261028384611001565b6001600160401b031660208401526108c76108c2808085610fdf565b610fdf565b9150816108d381610fdf565b92508260006108e1836110c2565b90506000806041836108f38787612505565b6108fd9190612505565b6109079190612505565b9050603881101561091b5760019150610932565b610924816110cd565b61092f9060016124ed565b91505b50600061093e89611160565b905060006041838589855161095391906124ed565b61095d9190612505565b61096791906124ed565b6109719190612505565b6001600160401b03811115610988576109886123b1565b6040519080825280601f01601f1916602001820160405280156109b2576020820181803683370190505b50905060f960f81b816000815181106109cd576109cd6123c7565b60200101906001600160f81b031916908160001a9053506000600382516109f49190612505565b9050600881901c60f81b82600181518110610a1157610a116123c7565b60200101906001600160f81b031916908160001a905350600081901c60f81b82600281518110610a4357610a436123c7565b60200101906001600160f81b031916908160001a9053505060005b8251811015610acd57828181518110610a7957610a796123c7565b01602001516001600160f81b03191682610a948360036124ed565b81518110610aa457610aa46123c7565b60200101906001600160f81b031916908160001a90535080610ac58161244c565b915050610a5e565b506023825101810160038d01808803808284379190910184019050868501604019888803879003018082843791909101905085602a8082843750505060008060418689890303039150602a85830101835103905084600403610c2257605d60f91b83610b3a8360006124ed565b81518110610b4a57610b4a6123c7565b60200101906001600160f81b031916908160001a9053506001600160f81b031960e883901b1683610b7c8360016124ed565b81518110610b8c57610b8c6123c7565b60200101906001600160f81b031916908160001a9053506001600160f81b031960f083901b1683610bbe8360026124ed565b81518110610bce57610bce6123c7565b60200101906001600160f81b031916908160001a90535060f882901b83610bf68360036124ed565b81518110610c0657610c066123c7565b60200101906001600160f81b031916908160001a905350610d5b565b84600303610cb55760b960f81b83610c3b8360006124ed565b81518110610c4b57610c4b6123c7565b60200101906001600160f81b031916908160001a9053506001600160f81b031960f083901b1683610c7d8360016124ed565b81518110610c8d57610c8d6123c7565b60200101906001600160f81b031916908160001a90535060f882901b83610bf68360026124ed565b84600203610d1057601760fb1b83610cce8360006124ed565b81518110610cde57610cde6123c7565b60200101906001600160f81b031916908160001a9053506001600160f81b031960f083901b1683610bf68360016124ed565b6038821015610d5b57610d248260806124ed565b60f81b83610d338360006124ed565b81518110610d4357610d436123c7565b60200101906001600160f81b031916908160001a9053505b5050604080516041808252608082019092526000916020820181803683370190505090506041808703602083013760208a01516001600160401b031615610e5d578051600160f81b9082906040908110610db757610db76123c7565b01602001516001600160f81b03191603610dff57601c60f81b81604081518110610de357610de36123c7565b60200101906001600160f81b031916908160001a905350610e2f565b601b60f81b81604081518110610e1757610e176123c7565b60200101906001600160f81b031916908160001a9053505b60a08a0182905260c08a0181905281516020830120610e4e9082611aec565b6001600160a01b031660408b01525b7f000000000000000000000000000000000000000000000000000000000000000063ffffffff168a60200151610e9391906124a1565b6001600160401b0316600003610f8b57600060146020604188610eb68c8c612505565b610ec091906124ed565b610eca9190612505565b610ed49190612505565b610ede919061251c565b90506000816001600160401b03811115610efa57610efa6123b1565b604051908082528060200260200182016040528015610f23578160200160208202803683370190505b50905060005b82811015610f835760006020601483028a8d010101359050606081901c838381518110610f5857610f586123c7565b6001600160a01b03909216602092830291909101909101525080610f7b8161244c565b915050610f29565b5060608c0152505b8c8c604051610f9b9291906123a1565b6040519081900390208a525050505050505050509392505050565b600082610fc281611b10565b61014790826124ed565b6000610fd9826021611b8a565b92915050565b6000610fea82611001565b610fd990836124ed565b6000610fd9826015611b8a565b6000808235811a608081101561101a576001915061109d565b60b88110156110405761102e608082612505565b6110399060016124ed565b915061109d565b60c081101561106b576001939093019283356008602083900360b701021c810160b51901915061109d565b60f881101561107f5761102e60c082612505565b6001939093019283356008602083900360f701021c810160f5190191505b5092915050565b60006110b08383611b8a565b9392505050565b6000610fea82611b10565b6000610fd982611b10565b60006101008210156110e157506001919050565b620100008210156110f457506002919050565b630100000082101561110857506003919050565b600160201b82101561111c57506004919050565b600160281b82101561113057506005919050565b600160301b82101561114457506006919050565b600160381b82101561115857506007919050565b506008919050565b6060816000036111bd5760408051600180825281830190925290602082018180368337019050509050608060f81b816000815181106111a1576111a16123c7565b60200101906001600160f81b031916908160001a905350919050565b607f82116111fb57604080516001808252818301909252906020820181803683370190505090508160f81b816000815181106111a1576111a16123c7565b61010082101561126a5760408051600280825281830190925290602082018180368337019050509050608160f81b8160008151811061123c5761123c6123c7565b60200101906001600160f81b031916908160001a9053508160f81b816001815181106111a1576111a16123c7565b6201000082101561130c5760408051600380825281830190925290602082018180368337019050509050608260f81b816000815181106112ac576112ac6123c7565b60200101906001600160f81b031916908160001a905350600882901c60f81b816001815181106112de576112de6123c7565b60200101906001600160f81b031916908160001a9053508160f81b816002815181106111a1576111a16123c7565b63010000008210156113e15760408051600480825281830190925290602082018180368337019050509050608360f81b8160008151811061134f5761134f6123c7565b60200101906001600160f81b031916908160001a905350601082901c60f81b81600181518110611381576113816123c7565b60200101906001600160f81b031916908160001a905350600882901c60f81b816002815181106113b3576113b36123c7565b60200101906001600160f81b031916908160001a9053508160f81b816003815181106111a1576111a16123c7565b600160201b8210156114e85760408051600580825281830190925290602082018180368337019050509050608460f81b81600081518110611424576114246123c7565b60200101906001600160f81b031916908160001a905350601882901c60f81b81600181518110611456576114566123c7565b60200101906001600160f81b031916908160001a905350601082901c60f81b81600281518110611488576114886123c7565b60200101906001600160f81b031916908160001a905350600882901c60f81b816003815181106114ba576114ba6123c7565b60200101906001600160f81b031916908160001a9053508160f81b816004815181106111a1576111a16123c7565b600160281b8210156116215760408051600680825281830190925290602082018180368337019050509050608560f81b8160008151811061152b5761152b6123c7565b60200101906001600160f81b031916908160001a905350602082901c60f81b8160018151811061155d5761155d6123c7565b60200101906001600160f81b031916908160001a905350601882901c60f81b8160028151811061158f5761158f6123c7565b60200101906001600160f81b031916908160001a905350601082901c60f81b816003815181106115c1576115c16123c7565b60200101906001600160f81b031916908160001a905350600882901c60f81b816004815181106115f3576115f36123c7565b60200101906001600160f81b031916908160001a9053508160f81b816005815181106111a1576111a16123c7565b600160301b82101561178c5760408051600780825281830190925290602082018180368337019050509050608660f81b81600081518110611664576116646123c7565b60200101906001600160f81b031916908160001a905350602882901c60f81b81600181518110611696576116966123c7565b60200101906001600160f81b031916908160001a905350602082901c60f81b816002815181106116c8576116c86123c7565b60200101906001600160f81b031916908160001a905350601882901c60f81b816003815181106116fa576116fa6123c7565b60200101906001600160f81b031916908160001a905350601082901c60f81b8160048151811061172c5761172c6123c7565b60200101906001600160f81b031916908160001a905350600882901c60f81b8160058151811061175e5761175e6123c7565b60200101906001600160f81b031916908160001a9053508160f81b816006815181106111a1576111a16123c7565b600160381b8210156119295760408051600880825281830190925290602082018180368337019050509050608760f81b816000815181106117cf576117cf6123c7565b60200101906001600160f81b031916908160001a905350603082901c60f81b81600181518110611801576118016123c7565b60200101906001600160f81b031916908160001a905350602882901c60f81b81600281518110611833576118336123c7565b60200101906001600160f81b031916908160001a905350602082901c60f81b81600381518110611865576118656123c7565b60200101906001600160f81b031916908160001a905350601882901c60f81b81600481518110611897576118976123c7565b60200101906001600160f81b031916908160001a905350601082901c60f81b816005815181106118c9576118c96123c7565b60200101906001600160f81b031916908160001a905350600882901c60f81b816006815181106118fb576118fb6123c7565b60200101906001600160f81b031916908160001a9053508160f81b816007815181106111a1576111a16123c7565b60408051600980825281830190925290602082018180368337019050509050608860f81b81600081518110611960576119606123c7565b60200101906001600160f81b031916908160001a905350603882901c60f81b81600181518110611992576119926123c7565b60200101906001600160f81b031916908160001a905350603082901c60f81b816002815181106119c4576119c46123c7565b60200101906001600160f81b031916908160001a905350602882901c60f81b816003815181106119f6576119f66123c7565b60200101906001600160f81b031916908160001a905350602082901c60f81b81600481518110611a2857611a286123c7565b60200101906001600160f81b031916908160001a905350601882901c60f81b81600581518110611a5a57611a5a6123c7565b60200101906001600160f81b031916908160001a905350601082901c60f81b81600681518110611a8c57611a8c6123c7565b60200101906001600160f81b031916908160001a905350600882901c60f81b81600781518110611abe57611abe6123c7565b60200101906001600160f81b031916908160001a9053508160f81b816008815181106111a1576111a16123c7565b6000806000611afb8585611bc8565b91509150611b0881611c36565b509392505050565b60008135811a6080811015611b285750600092915050565b60b8811080611b43575060c08110801590611b43575060f881105b15611b515750600192915050565b60c0811015611b7e57611b66600160b8612530565b611b739060ff1682612505565b6110b09060016124ed565b611b66600160f8612530565b60008082118015611b9c575060218211155b611ba557600080fd5b6000611bb084611b10565b93840135939092036020036008029290921c92915050565b6000808251604103611bfe5760208301516040840151606085015160001a611bf287828585611dea565b94509450505050611c2f565b8251604003611c275760208301516040840151611c1c868383611ecd565b935093505050611c2f565b506000905060025b9250929050565b6000816004811115611c4a57611c4a612553565b03611c525750565b6001816004811115611c6657611c66612553565b03611cae5760405162461bcd60e51b815260206004820152601860248201527745434453413a20696e76616c6964207369676e617475726560401b60448201526064016103de565b6002816004811115611cc257611cc2612553565b03611d0f5760405162461bcd60e51b815260206004820152601f60248201527f45434453413a20696e76616c6964207369676e6174757265206c656e6774680060448201526064016103de565b6003816004811115611d2357611d23612553565b03611d7b5760405162461bcd60e51b815260206004820152602260248201527f45434453413a20696e76616c6964207369676e6174757265202773272076616c604482015261756560f01b60648201526084016103de565b6004816004811115611d8f57611d8f612553565b03611de75760405162461bcd60e51b815260206004820152602260248201527f45434453413a20696e76616c6964207369676e6174757265202776272076616c604482015261756560f01b60648201526084016103de565b50565b6000806fa2a8918ca85bafe22016d0b997e4df60600160ff1b03831115611e175750600090506003611ec4565b8460ff16601b14158015611e2f57508460ff16601c14155b15611e405750600090506004611ec4565b6040805160008082526020820180845289905260ff881692820192909252606081018690526080810185905260019060a0016020604051602081039080840390855afa158015611e94573d6000803e3d6000fd5b5050604051601f1901519150506001600160a01b038116611ebd57600060019250925050611ec4565b9150600090505b94509492505050565b6000806001600160ff1b03831660ff84901c601b01611eee87828885611dea565b935093505050935093915050565b6040805160e0810182526000808252602082018190529181018290526060808201819052608082019290925260a0810182905260c081019190915290565b60008083601f840112611f4c57600080fd5b5081356001600160401b03811115611f6357600080fd5b602083019150836020828501011115611c2f57600080fd5b600080600060408486031215611f9057600080fd5b83356001600160401b03811115611fa657600080fd5b611fb286828701611f3a565b909790965060209590950135949350505050565b60008060208385031215611fd957600080fd5b82356001600160401b03811115611fef57600080fd5b611ffb85828601611f3a565b90969095509350505050565b60006101a0820190508251825260208301516020830152604083015161203860408401826001600160a01b03169052565b50606083015160608301526080830151608083015260a083015160a083015260c083015161207160c08401826001600160401b03169052565b5060e083015161208c60e08401826001600160401b03169052565b50610100838101516001600160401b038116848301525050610120838101516001600160401b0381168483015250506101408381015190830152610160808401516001600160401b03811682850152505061018092830151919092015290565b6000806000806060858703121561210257600080fd5b84356001600160401b0381111561211857600080fd5b61212487828801611f3a565b90989097506020870135966040013595509350505050565b600081518084526020808501945080840160005b838110156121755781516001600160a01b031687529582019590820190600101612150565b509495945050505050565b6020815260006110b0602083018461213c565b6000815180845260005b818110156121b95760208185018101518683018201520161219d565b818111156121cb576000602083870101525b50601f01601f19169290920160200192915050565b60208152815160208201526001600160401b03602083015116604082015260018060a01b0360408301511660608201526000606083015160e0608084015261222c61010084018261213c565b9050608084015160a084015260a0840151601f19808584030160c08601526122548383612193565b925060c08601519150808584030160e0860152506122728282612193565b95945050505050565b60008083601f84011261228d57600080fd5b5081356001600160401b038111156122a457600080fd5b6020830191508360208260051b8501011115611c2f57600080fd5b6000806000806000606086880312156122d757600080fd5b85356001600160401b03808211156122ee57600080fd5b6122fa89838a0161227b565b909750955060208801359450604088013591508082111561231a57600080fd5b506123278882890161227b565b969995985093965092949392505050565b60408152600061234b604083018561213c565b90506001600160401b03831660208301529392505050565b634e487b7160e01b600052601160045260246000fd5b60006001600160401b038381169083168181101561239957612399612363565b039392505050565b8183823760009101908152919050565b634e487b7160e01b600052604160045260246000fd5b634e487b7160e01b600052603260045260246000fd5b6000808335601e198436030181126123f457600080fd5b8301803591506001600160401b0382111561240e57600080fd5b602001915036819003821315611c2f57600080fd5b60006020828403121561243557600080fd5b81356001600160a01b03811681146110b057600080fd5b60006001820161245e5761245e612363565b5060010190565b60006001600160401b0380831681810361248157612481612363565b6001019392505050565b634e487b7160e01b600052601260045260246000fd5b60006001600160401b03808416806124bb576124bb61248b565b92169190910692915050565b60006001600160401b03808416806124e1576124e161248b565b92169190910492915050565b6000821982111561250057612500612363565b500190565b60008282101561251757612517612363565b500390565b60008261252b5761252b61248b565b500490565b600060ff821660ff84168082101561254a5761254a612363565b90039392505050565b634e487b7160e01b600052602160045260246000fdfea264697066735822122086a7c9dc7f5ed782523040256f09b30a09ff0a30b85c14b13e3b5fc40f5d575f64736f6c634300080e0033"),
+ }, nil)
+ if err != nil {
+ panic(err)
+ }
+ return bytecode
+}
+
+var parliaBlockVerificationBytecode = createBytecode()
+
+func main() {
+ to := common.HexToAddress("0x0000000000000000000000000000000000000001")
+ backend := backends.NewSimulatedBackend(core.GenesisAlloc{
+ to: {
+ Code: parliaBlockVerificationBytecode,
+ Balance: big.NewInt(0),
+ },
+ }, 8_000_000)
+ blockProof := hexutil.MustDecode("0xf903fca00000000000000000000000000000000000000000000000000000000000000000a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d4934794fffffffffffffffffffffffffffffffffffffffea0919fcc7ad870b53db0aa76eb588da06bacb6d230195100699fc928511003b422a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421b901000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001808402625a0080845e9da7ceb9020500000000000000000000000000000000000000000000000000000000000000002a7cdd959bfe8d9487b2a43b33565295a698f7e26488aa4d1955ee33403f8ccb1d4de5fb97c7ade29ef9f4360c606c7ab4db26b016007d3ad0ab86a0ee01c3b1283aa067c58eab4709f85e99d46de5fe685b1ded8013785d6623cc18d214320b6bb6475978f3adfc719c99674c072166708589033e2d9afec2be4ec20253b8642161bc3f444f53679c1f3d472f7be8361c80a4c1e7e9aaf001d0877f1cfde218ce2fd7544e0b2cc94692d4a704debef7bcb61328b8f7166496996a7da21cf1f1b04d9b3e26a3d0772d4c407bbe49438ed859fe965b140dcf1aab71a96bbad7cf34b5fa511d8e963dbba288b1960e75d64430b3230294d12c6ab2aac5c2cd68e80b16b581ea0a6e3c511bbd10f4519ece37dc24887e11b55d7ae2f5b9e386cd1b50a4550696d957cb4900f03a82012708dafc9e1b880fd083b32182b869be8e0922b81f8e175ffde54d797fe11eb03f9e3bf75f1d68bf0b8b6fb4e317a0f9d6f03eaf8ce6675bc60d8c4d90829ce8f72d0163c1d5cf348a862d55063035e7a025f4da968de7e4d7e4004197917f4070f1d6caa02bbebaebb5d7e581e4b66559e635f805ff0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000880000000000000000")
+ const rounds = 100_000
+ // solidity call
+ input, err := measureBlockInput.Pack(blockProof, big.NewInt(65))
+ if err != nil {
+ panic(err)
+ }
+ solidityTime := time.Now().UnixNano()
+ var gasUsed uint64
+ for i := 0; i < rounds; i++ {
+ gasUsed, err = backend.EstimateGas(context.Background(), ethereum.CallMsg{
+ To: &to,
+ Data: append(hexutil.MustDecode("0x2a36f0ad"), input...),
+ })
+ if err != nil {
+ panic(err)
+ }
+ }
+ solidityTime = time.Now().UnixNano() - solidityTime
+ fmt.Printf("elapsed time (solidity): %d ns\n", solidityTime)
+ fmt.Printf("gas used (solidity): %d\n", gasUsed)
+ // native call
+ contract := vm.NewVerifyParliaBlock()
+ input, err = verifyParliaBlockInput.Pack(big.NewInt(56), blockProof, uint32(200))
+ if err != nil {
+ panic(err)
+ }
+ nativeTime := time.Now().UnixNano()
+ for i := 0; i < rounds; i++ {
+ _, err = contract.Run(input)
+ if err != nil {
+ panic(err)
+ }
+ }
+ nativeTime = time.Now().UnixNano() - nativeTime
+ fmt.Printf("elapsed time (native): %d ns\n", nativeTime)
+ fmt.Printf("optimal gas (native): ~%f\n", float64(gasUsed)*float64(nativeTime)/float64(solidityTime))
+}
diff --git a/cmd/faucet/faucet.go b/cmd/faucet/faucet.go
index 16a881ce5..2626a4624 100644
--- a/cmd/faucet/faucet.go
+++ b/cmd/faucet/faucet.go
@@ -840,7 +840,7 @@ func authTwitter(url string, tokenV1, tokenV2 string) (string, string, string, c
address := common.HexToAddress(string(regexp.MustCompile("0x[0-9a-fA-F]{40}").Find(body)))
if address == (common.Address{}) {
//lint:ignore ST1005 This error is to be displayed in the browser
- return "", "", "", common.Address{}, errors.New("No Binance Smart Chain address found to fund")
+ return "", "", "", common.Address{}, errors.New("No BAS address found to fund")
}
var avatar string
if parts = regexp.MustCompile("src=\"([^\"]+twimg.com/profile_images[^\"]+)\"").FindStringSubmatch(string(body)); len(parts) == 2 {
@@ -966,7 +966,7 @@ func authFacebook(url string) (string, string, common.Address, error) {
address := common.HexToAddress(string(regexp.MustCompile("0x[0-9a-fA-F]{40}").Find(body)))
if address == (common.Address{}) {
//lint:ignore ST1005 This error is to be displayed in the browser
- return "", "", common.Address{}, errors.New("No Binance Smart Chain address found to fund")
+ return "", "", common.Address{}, errors.New("No BAS address found to fund")
}
var avatar string
if parts = regexp.MustCompile("src=\"([^\"]+fbcdn.net[^\"]+)\"").FindStringSubmatch(string(body)); len(parts) == 2 {
@@ -982,7 +982,7 @@ func authNoAuth(url string) (string, string, common.Address, error) {
address := common.HexToAddress(regexp.MustCompile("0x[0-9a-fA-F]{40}").FindString(url))
if address == (common.Address{}) {
//lint:ignore ST1005 This error is to be displayed in the browser
- return "", "", common.Address{}, errors.New("No Binance Smart Chain address found to fund")
+ return "", "", common.Address{}, errors.New("No BAS address found to fund")
}
return address.Hex() + "@noauth", "", address, nil
}
diff --git a/cmd/faucet/faucet.html b/cmd/faucet/faucet.html
index 793304aa3..950f811f7 100644
--- a/cmd/faucet/faucet.html
+++ b/cmd/faucet/faucet.html
@@ -49,19 +49,21 @@
{{
diff --git a/cmd/faucet/website.go b/cmd/faucet/website.go
index 854f4404d..25e51051e 100644
--- a/cmd/faucet/website.go
+++ b/cmd/faucet/website.go
@@ -1,6 +1,6 @@
// Code generated by go-bindata. DO NOT EDIT.
// sources:
-// faucet.html (8.959kB)
+// faucet.html (9.01kB)
package main
@@ -69,7 +69,7 @@ func (fi bindataFileInfo) Sys() interface{} {
return nil
}
-var _faucetHtml = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xd4\x5a\x5b\x8f\xe3\x36\xb2\x7e\x76\xff\x8a\x8a\xce\x24\x96\x4f\xb7\x24\x7b\x3a\x37\xd8\x92\x0f\xa6\x27\x39\xc1\x2c\x76\x27\x41\x26\xc1\xee\x22\xc9\x03\x2d\x95\x2d\x4e\x53\xa4\x42\x96\xec\x76\x0c\xff\xf7\x05\xa9\x8b\xe5\x4b\xf7\xce\x25\xc0\x62\xe7\xc1\x2d\x92\xc5\xaa\x8f\x55\xc5\xba\x48\x13\x7f\xf2\xcd\xf7\x2f\x7f\xfa\xe7\x0f\xdf\x42\x4e\x85\x98\x5f\xc5\xf6\x0f\x08\x26\x57\x89\x87\xd2\xb3\x13\xc8\xb2\xf9\xd5\x20\x2e\x90\x18\xa4\x39\xd3\x06\x29\xf1\x2a\x5a\x06\x5f\x7b\xdd\x7c\x4e\x54\x06\xf8\x7b\xc5\xd7\x89\xf7\x8f\xe0\xe7\x17\xc1\x4b\x55\x94\x8c\xf8\x42\xa0\x07\xa9\x92\x84\x92\x12\xef\xd5\xb7\x09\x66\x2b\x3c\x6c\x93\xac\xc0\xc4\x5b\x73\xdc\x94\x4a\x53\x8f\x72\xc3\x33\xca\x93\x0c\xd7\x3c\xc5\xc0\x0d\x6e\x80\x4b\x4e\x9c\x89\xc0\xa4\x4c\x60\x32\xf1\xe6\x57\x57\x83\x98\x38\x09\x9c\xef\x76\xe1\x6b\xa4\x8d\xd2\xf7\xfb\xfd\x14\xfe\x9f\x55\x29\x52\x1c\xd5\x6b\x96\x4a\x70\x79\x0f\xb9\xc6\x65\xe2\x59\xa4\x66\x1a\x45\x69\x26\xdf\x9a\x30\x15\xaa\xca\x96\x82\x69\x0c\x53\x55\x44\xec\x2d\x7b\x88\x04\x5f\x98\x88\x36\x9c\x08\x75\xb0\x50\x8a\x0c\x69\x56\x46\xb7\xe1\x6d\xf8\x55\x94\x1a\x13\x75\x73\x61\xc1\x65\x98\x1a\xe3\x81\x46\x91\x78\x86\xb6\x02\x4d\x8e\x48\x1e\x44\xf3\x0f\x12\xbb\x54\x92\x02\xb6\x41\xa3\x0a\x8c\x3e\x0f\xbf\x0a\xc7\x4e\x62\x7f\xfa\x69\xa1\x57\x83\xd8\xa4\x9a\x97\x04\x46\xa7\xef\x2c\xf6\xed\xef\x15\xea\x6d\x74\x1b\x4e\xc2\x49\x33\x70\x62\xde\x1a\x6f\x1e\x47\x35\xc3\xf9\xc7\xb0\x0e\xa4\xa2\x6d\xf4\x3c\xfc\x3c\x9c\x44\x25\x4b\xef\xd9\x0a\xb3\x56\x90\x5d\x0a\xdb\xc9\x3f\x4b\xec\x63\xf6\x7b\x7b\x6a\xbe\x3f\x41\x56\xa1\x0a\x94\x14\xbe\x35\xd1\xf3\x70\xf2\x75\x38\x6e\x27\xce\xd9\x5b\xfe\xd6\x5e\xf3\xab\xc1\x20\x5c\xa3\x26\x9e\x32\x11\xa4\x28\x09\x35\xec\xae\x06\x83\x41\xc1\x65\x90\x23\x5f\xe5\x34\x85\xc9\x78\xfc\xe9\xec\xc2\xe4\x3a\x77\xb3\x19\x37\xa5\x60\xdb\x29\x2c\x05\x3e\xb8\x19\x26\xf8\x4a\x06\x9c\xb0\x30\x53\xa8\xb9\xda\xf9\xbd\x95\x56\x6a\xb5\xd2\x68\x4c\x2d\xa6\x54\x86\x13\x57\x72\x6a\x9d\x88\x11\x5f\xe3\x39\xa1\x29\x99\x3c\xa5\x66\x0b\xa3\x44\x45\x78\x0c\x60\x21\x54\x7a\xef\xa6\xdc\x55\xed\x21\x4f\x95\x50\x7a\x0a\x9b\x9c\x53\x27\xa1\xd4\xd8\xb0\x65\x59\xc6\xe5\x6a\x0a\x5f\x96\x35\xfe\x82\xe9\x15\x97\x53\x18\x37\xa4\x71\xd4\x68\x2b\x8e\xea\x28\x74\x15\x2f\x54\xb6\x9d\x5f\xc5\x19\x5f\x43\x2a\x98\x31\x89\x77\xa2\x46\x17\x5c\x7a\xcb\x36\xa4\x30\x2e\xeb\x85\xa3\x15\xad\x36\x1e\x38\x01\x89\x57\x4b\x0e\x16\x8a\x48\x15\x53\x98\x58\x44\x6e\xc3\x09\x2f\x11\x88\x55\x30\x79\x5e\x2f\x0d\xe2\x7c\xd2\x32\x20\x7c\xa0\xc0\xe9\xbf\xd3\xbc\x37\x8f\x79\xbb\x73\xc9\x60\xc9\x82\x05\xa3\xdc\x03\xa6\x39\x0b\x72\x9e\x65\x28\x13\x8f\x74\x85\xd6\x43\xf8\x1c\xfa\x41\xac\x8b\x61\xf9\xa4\x46\x11\x65\x7c\xed\x0e\xd0\x3d\x9c\x9c\xe4\x31\xb0\x5f\x43\xf3\xa0\x96\x4b\x83\x14\x74\xd8\x7b\xa4\x5c\x96\x15\x05\x2b\xad\xaa\xb2\x59\x1d\xc4\x6e\x0e\x78\x96\x78\x95\x16\x5e\x13\xa9\xdd\x23\x6d\xcb\xe6\xc0\x5e\x77\x3c\xa5\x8b\xc0\x6a\x5a\x2b\xe1\x41\x29\x58\x8a\xb9\x12\x19\xea\xc4\x7b\xa3\x52\xce\x04\xc8\xfa\x64\xf0\xf3\x8f\x7f\x85\xc6\x24\x5c\xae\x60\xab\x2a\x0d\x77\x5c\x32\x99\x22\xbc\x29\x98\x26\x78\x99\x33\x2e\x81\x65\x99\x75\xc2\x30\x0c\x3b\x44\xce\x1f\xcf\x11\x07\x0b\x92\x2d\x8d\x25\x5b\x54\x44\xaa\x23\x5c\x90\x84\x05\xc9\x20\xc3\x25\xab\x04\x41\xa6\x55\x99\xa9\x8d\x0c\x48\xad\x56\x36\x39\xd5\xa7\xa9\x37\x79\x90\x31\x62\xcd\x52\xe2\xb5\xb4\xad\xc9\x98\x29\x55\x59\x95\x8d\xd1\xea\x49\x7c\x28\x99\xcc\x30\xb3\x26\x16\x06\xbd\xf9\x77\x7c\x8d\x50\x20\xdc\xbd\xbe\x1b\x9c\xda\x3f\x65\x1a\x29\xe8\xb3\x3c\xf3\x82\x38\xaa\xa1\xd4\x07\x82\xe6\x5f\x5c\x89\x96\x53\x77\x80\x02\x65\x05\x47\xa3\x40\xdb\x10\xe1\xcd\x77\x3b\xcd\xe4\x0a\xe1\x19\xcf\x1e\x6e\xe0\x19\x2b\x54\x25\x09\xa6\x09\x84\x2f\xdc\xa3\xd9\xef\x8f\xb8\x03\xc4\x82\xcf\x63\xf6\x94\x2f\x83\x92\xa9\xe0\xe9\x7d\xe2\x11\x47\x9d\xec\x76\x96\xf9\x7e\x3f\x33\xdb\x62\xa1\x44\x32\xbc\x7b\x7d\x37\x9c\xc1\x6e\xc7\x97\xf0\x2c\xfc\x11\x53\x56\x52\x9a\xb3\xfd\x7e\xa5\xdb\xe7\x10\x1f\x30\xad\x08\xfd\xd1\x6e\x87\xc2\xe0\x7e\x6f\xaa\x45\xc1\xc9\x6f\x79\xd9\x79\x99\xed\xf7\xf6\x00\x0d\xe8\xfd\x3e\x8e\xd8\x3c\x8e\x04\x9f\x37\x8b\xc7\x6a\x89\x2a\xd1\x59\x3e\x8e\xac\x83\xfc\x77\x39\xcb\x0f\xb8\x5a\x6d\x81\xd4\x3d\x4a\xf3\x1f\x72\x16\xe8\xbc\xa5\x36\xe5\x0d\x3c\x5b\x60\xf9\x1c\x5f\xc9\xa5\x72\x3e\x73\xd7\x8e\x5a\xb7\x71\x8a\x7b\x0f\x87\x69\x5c\x64\xb7\x6b\x24\xec\xf7\x1f\xe6\x28\x0e\x88\xdf\x63\x73\xe4\x30\x1d\xe8\xc6\xc9\xdf\x90\xde\xef\xa1\x47\xfd\x61\x9e\x54\xc7\x5b\x07\xb7\x8f\xf6\x2c\x84\xae\x82\x0e\x7f\xe3\x18\x86\x13\xde\xe3\x36\xf1\x76\xbb\xfe\xce\x66\x35\x65\x42\x2c\x98\x53\x8f\x3b\x5c\xb7\xe9\x0f\xb4\x0e\xbb\xe6\xc6\xd5\xce\xf3\x56\x7e\x07\xf9\xdf\x67\x82\x93\x9c\x46\xaa\x9c\xc2\xed\xf3\xa7\x12\xda\x97\x27\x39\xe2\xf6\x42\x8e\x28\x99\x44\x01\xee\x37\x30\x05\x13\xed\x73\x73\x57\xba\x28\x7d\xba\x25\xb0\x19\xbb\xc3\xd4\xa5\xfc\xf1\x0c\xd4\x1a\xf5\x52\xa8\xcd\x14\x58\x45\x6a\x06\x05\x7b\xe8\xaa\x9c\xdb\xf1\xb8\x03\x6c\xb9\x12\x5b\x08\x74\xd9\x48\xe3\xef\x15\x1a\x32\x5d\xee\xa9\x97\xdc\xaf\x4d\x41\x19\x4a\x83\xd9\x89\x12\xac\x3c\xab\x4b\x47\xd5\x22\x6d\xf5\x77\x11\xf5\x52\xa9\xa6\x98\xe8\x03\x68\x98\xf6\xaa\x1c\x6f\x1e\x93\x3e\x78\x0e\x65\xef\x55\x10\x68\x5b\xc6\x3f\x56\x0f\xd4\x31\xcc\x9e\xb9\x44\xd4\x75\x1d\x69\xfd\x12\xdc\x30\x8e\x28\xfb\x60\xb9\xd6\xd7\x16\xcc\xe0\xbb\x08\x77\xc5\xdd\x41\xb8\x1b\x7e\x9c\xf4\x1c\x99\xa6\x05\x32\x7a\x17\xf1\xcb\x4a\x66\xbd\xb3\xdf\xbd\xbe\xfb\x38\xe1\x95\xe4\x6b\xd4\x86\xd3\xf6\x5d\xa5\x63\x76\x10\x5f\x8f\xfb\x00\xe2\x88\xf4\xe3\x9e\x75\x78\x3c\xbb\xb7\xcd\xdf\xe6\xcf\x55\xdc\xb5\x20\x51\x04\xdf\x09\xb5\x60\x02\xd6\x16\xe0\x42\xa0\x01\x52\x60\x0b\x2a\xa0\x1c\x21\xad\xb4\x46\x49\x60\x88\x51\x65\x40\x2d\xdd\xec\xd2\x15\x8c\x57\x83\x35\xd3\xc0\x88\xb0\x28\x09\x12\x57\x49\xdb\x19\x83\x7a\xed\x9a\x01\x3b\xb0\x19\xbc\xbf\x56\xc7\x67\xcf\x6b\xc6\xed\x0d\x83\x04\x7e\xf9\x6d\x76\xe5\x00\x7d\x83\x4b\x2e\x11\x98\x55\x40\x6a\x5b\x01\xa0\x9c\x11\xa4\x1a\x19\xa1\x81\x54\x28\x53\xe9\x1a\xa7\xcd\x32\x60\xb1\xb6\x7c\x6a\xae\x76\xba\x74\x72\x5b\x16\x7e\xce\x4c\x3e\x72\xad\x80\x46\xaa\xb4\x3c\xac\xd4\xb3\x83\xa5\xd2\xe0\xdb\xcd\x3c\x19\xcf\x80\xc7\x2d\xc7\x50\xa0\x5c\x51\x3e\x03\x7e\x7d\xdd\x90\x0e\xf8\x12\xfc\x76\xfd\x17\xfe\x5b\x48\x0f\xa1\xe5\x0f\x49\x02\x07\x39\x03\x2b\xaa\xe1\x61\x4a\xc1\x53\xf4\xf9\x0d\x4c\x46\xb3\x7a\x6d\xa1\x91\xd5\x7d\x8c\x6b\x54\xdc\xcf\xfe\x6a\xb0\x9f\xf5\x75\xe0\x94\x7d\xa4\x85\x3a\x82\x1b\x60\xb0\xe2\x86\xa0\xd2\xc2\xea\xc1\xd2\xd5\x6a\x6f\xd4\xec\xa8\xfa\xe7\x3f\xcb\x2a\xcd\x43\x13\xed\x6b\xc8\x35\x8b\xd0\xa0\xcc\xfc\xbf\xbc\xf9\xfe\x75\x68\x48\x73\xb9\xe2\xcb\xad\xbf\xab\xb4\x98\xc2\x33\xdf\xfb\x1f\x5b\x97\x8f\x7e\x19\xff\x16\xae\x99\xa8\xf0\xa6\x31\xe9\x14\xda\x94\x6e\x2d\x3e\x75\xbf\x67\x32\x6f\xa0\x79\x9c\xc2\xb1\xf8\xfd\x68\x34\xbb\x94\xf7\x7a\x89\x5a\xa3\x41\xf2\x2d\x59\x93\x9e\x8e\x35\xc5\xa0\x40\xca\x55\x66\xb5\xa1\x31\x55\x52\x62\x4a\x50\x95\x4a\x36\x8a\x01\xa1\x8c\x69\x9d\xae\x5d\x4f\x4e\xdd\xa0\xa1\x4d\x40\xe2\x06\xfe\x8e\x8b\x37\x2a\xbd\x47\xf2\x7d\x7f\xc3\x65\xa6\x36\xa1\x50\x29\xb3\xe4\xb6\x73\x25\x95\x2a\x01\x49\x92\x40\xd3\xbc\x7b\x23\xf8\x3f\xf0\x36\xc6\xb6\xf1\x1e\x4c\xed\xa3\x7d\x1a\xc1\x35\x9c\x6e\xcf\x95\x21\xb8\x06\x2f\xaa\xaf\x92\xcd\x72\x9a\x22\x56\x72\x6f\x64\x6f\x41\x6b\x09\x25\x0b\x34\x86\xad\xb0\x8f\x14\xd7\x28\xa9\xf1\x31\x7b\x9c\xc2\xac\x20\x01\x67\xaf\x92\x69\x83\x35\x41\x68\xe3\x6e\xed\x6c\xd6\x5d\x1d\x51\x92\x80\xac\x84\x68\xfd\xb3\xbe\x09\xb3\xda\xfb\x7a\x84\xa1\x8b\x83\xf0\x49\x92\x80\x0d\x41\x56\xbf\x59\xbb\xc7\x7a\x40\x1d\x26\x47\xa1\x8d\x81\x07\xfa\xd1\xac\x75\xe3\x23\x3e\x98\x3d\xcd\x08\xb3\x53\x4e\x98\x5d\x60\xe5\xf2\xd0\xe3\x9c\xea\xac\xd5\x63\xe4\x26\x2e\xf0\x91\x55\xb1\x40\xfd\x38\xa3\x3a\x03\x35\x8c\x9c\x3a\x5f\x49\xea\xed\xbc\x81\xc9\x97\xa3\x0b\x7c\x51\x6b\xf5\x08\x5b\xa9\x68\xeb\xef\x04\xdb\xaa\x8a\xa6\x30\x24\x55\xbe\x74\x09\x63\x78\x03\x56\xca\x14\xba\xfd\x37\xae\xe8\x9f\xc2\xd0\x8d\xec\x3a\x2f\xd0\xed\xfa\x62\x3c\x1e\xdf\x40\xfb\xba\xe4\x8e\xd9\x1b\xa6\x2b\xdc\x5f\x40\x62\xaa\x34\x45\xf3\x88\xae\xde\x09\x4b\xc3\xa1\x43\xd3\x8c\x3f\x10\x4f\x17\xe2\x8f\x00\xc1\x67\x9f\xc1\xd9\x6a\xdf\x39\xa3\x08\xfe\xc6\xf4\x3d\xb8\xea\x4f\xe3\x9a\xab\xca\x1c\xd2\x45\xc1\x8d\xb1\x3d\x3d\x33\x90\x29\x89\x6e\xc7\xfb\x44\xf0\x33\x74\x0d\x11\xcc\x61\x7c\x0a\xcd\xc6\xba\x5e\x84\xbf\x10\xf8\x3b\xae\xfd\xa8\x3e\xd8\x1f\x24\x1d\xed\xe1\x05\xc2\x27\x09\x78\xde\x61\xdb\xd9\xba\x5d\x6e\xd8\x0c\x0c\xd2\x4f\xb5\xde\xfd\x26\xb1\x5d\x4a\x3e\xa3\x1b\x5b\xc8\x8e\x47\x47\xc2\xf7\xad\x2a\x5f\x94\x25\xca\x0c\x98\xdc\xba\xc8\xd6\xe9\x91\x4b\x52\xa0\x2a\x1b\x1d\x53\x26\x6c\x69\x2e\xd0\x45\x19\xb7\xd1\x2a\x33\x55\x45\xa1\x24\x24\x10\x4c\x66\x67\xc9\xaf\xa7\xb5\xee\x30\xa7\x66\xb8\xa0\xe5\x63\x53\x1c\x6b\xe8\x84\x34\x98\x1c\x29\xff\xc8\x2e\x97\x0c\x30\xe8\xf0\xf2\x56\x7f\x47\x46\x69\xad\xd2\xd7\x4f\x0f\x71\xbd\xfb\x7a\xf2\x4e\xc0\xbb\xc5\xb2\x32\xb9\x7f\x02\x6d\x34\x3b\xb6\xc0\x2b\x42\xcd\x08\x5d\x27\xe2\x34\x8e\x92\xb8\xc6\x33\xc5\x03\x93\xb6\x9c\x09\x34\xca\x0c\x75\x5b\x01\xd8\x46\xa6\xee\x3b\x7a\x86\x71\xdf\x2f\x7a\xae\xd2\x3b\xc7\x99\x16\x67\xc0\x61\x6e\xeb\x30\xe0\x41\xd0\x9d\xc0\x15\x4b\x4a\xa2\x6d\x4b\x4f\x3c\xdb\x79\x61\xcf\x0d\x2d\x29\x0a\x56\x1a\xcc\x20\x81\xfa\xad\xb3\x3f\x0a\x2b\xc9\x1f\xfc\x51\xd0\x8c\x4f\x39\xb4\xeb\x2e\xa7\x39\xe3\xd4\x98\xaf\x13\xf0\x62\xd2\xb6\xec\x1d\x7a\x70\x7d\xe9\x4e\xd9\xec\x38\x9c\xb7\xd2\xfb\x1b\x01\x62\xca\xe6\xae\x8d\xaa\x8b\xf1\x5f\x3d\xdb\xdd\xae\xb4\xaa\x64\x36\xb5\x25\x91\x7f\xc6\x94\xad\x19\x31\xed\x78\x8e\x66\x70\x20\x77\x4d\xf0\x14\x52\x6b\x95\x19\xd4\xbd\x96\xeb\x5f\xa1\xeb\x0e\xdd\x68\xa1\x74\x86\x3a\xd0\x2c\xe3\x95\x99\xc2\xe7\xe5\xc3\xec\xd7\xb6\x61\x76\x15\xfa\x13\x40\x4b\x8d\xf3\x33\x3c\x69\xea\x5e\x94\x5d\x83\x17\x47\x96\xe0\x69\x26\xdd\x41\xfb\xef\xbc\xe1\x42\x0f\x02\xdd\x1b\xea\x66\xbe\xe0\x59\x26\xd0\x82\x6d\x99\xdb\xdb\x66\x6d\x7e\xb8\x33\xc7\xe2\xa0\x69\x3c\x5a\xfa\x3d\xa0\x30\xf8\x28\x71\xd7\xbf\x0c\xad\xc1\x03\x7b\x50\xee\xf4\xdc\xb4\x42\x6e\x5a\x0f\x9d\x06\x9a\x6f\x15\x59\xa5\x5d\x15\xe4\x07\x8d\x43\xdd\xc0\xd0\xd8\x9a\x2c\x33\xc3\x51\x98\x57\x05\x93\xfc\x0f\xf4\x6d\x42\x19\xd5\x1a\x72\x0d\x91\x77\x1c\x57\xcf\x80\x1c\xfa\xea\x61\x9b\x98\x86\x8d\xe2\x86\xad\x35\xad\xe1\xe0\xd0\xa7\x0f\xdf\x43\x2b\x97\x25\x04\x0b\xa6\xa1\x3f\x08\xda\x6c\x09\x5a\x59\xc9\xed\xda\x82\xe9\x61\xdd\x04\xba\xba\x59\xaa\x4d\x32\xbc\x1d\x77\x00\x6b\xc3\x3a\xbb\x0e\x1b\xbf\x3a\x31\x80\x45\xd8\x5e\xc0\x39\xdc\x8e\x3f\x1e\x69\xc6\xe4\x0a\x4f\xd1\x93\xe6\x25\x66\xc0\x52\xe2\x6b\xfc\xd3\x0f\xf1\xd1\xca\x7d\x6f\x78\xd6\xef\x5a\xb5\x39\xb7\x3c\xc2\x6a\x57\x3b\xad\xfe\xaf\xbd\x57\x10\x39\xdd\x5e\x83\x77\xe1\x10\x8f\x78\xde\x11\xd1\xc9\xe5\x7d\xec\x5e\xbb\x5e\xde\xeb\x27\x09\x5b\x7e\x76\x6f\x9c\x46\x61\x4e\x85\xf0\xbd\x98\xdc\x57\x28\x8b\xb3\xdb\xed\x36\xd7\xd3\x87\x7a\x6b\xdf\xef\x1a\x6c\x8f\x8c\x27\xdd\x0d\xf4\xea\x88\xae\x03\x6a\x8b\x06\xb0\xdd\xd4\xde\x35\x54\x6f\x88\x69\x02\x06\x3f\xbf\x82\xaa\xcc\x18\xd9\x24\xa4\xc0\xa6\x38\x97\x8c\xba\x4f\x76\x0b\xa6\x0d\x2c\x95\xde\x30\x9d\x41\x25\x89\x0b\xbb\xbe\x05\xa6\xb1\xa9\xc9\x0c\xd2\x2b\x1b\x95\xd6\x4c\xf8\x27\x7d\xd6\x33\x7f\x18\xf6\x0d\x3b\x1c\x85\xc8\xd2\xfc\x94\xcc\x65\x9d\x4e\x62\x02\xaf\x5d\x0d\xee\x3f\xf3\x29\xe7\x66\x14\x32\x22\xed\x0f\x8f\x0c\x3e\x1c\x59\xdb\x4d\xba\xbe\xa7\xdb\x1c\xf7\xae\xcc\x53\xfb\x0f\x55\x6d\x93\xc1\x5b\xe2\xd4\x18\xbf\xf6\x9a\xe1\x4d\x8f\xef\xb1\xd3\x0c\x3f\x1d\x36\x26\x39\x5c\xda\x03\xfe\xe4\x02\x86\x23\xb6\x43\x7b\x77\x86\x27\x82\x59\x96\xbd\xb4\xf7\xc2\xf7\x2e\xdc\xde\xbe\x07\x8c\x1a\xc5\xd6\x11\xf7\x09\x8d\x72\x99\xe1\xc3\x63\xea\xe4\xd9\x70\x14\x9a\x6a\x51\xf7\xfe\xfe\x17\x4d\xb7\xd3\x12\x39\xb7\x3c\x0d\xe4\x67\xe9\xdf\x0a\x38\x2e\x01\x82\x93\x92\xe1\x89\x98\xef\x04\xda\xd3\xec\x6f\xac\x72\xc7\xa3\xe6\xc5\xd0\xb7\xc6\x56\x40\xdc\xe4\xc0\x60\x83\x0b\xe3\x7a\x73\x68\x3c\xd9\xbd\x21\xa9\xdf\x84\xbc\xf8\xe1\x55\xf7\x36\xa4\xf3\x74\x5b\x84\x74\x1f\xc7\xcf\xdf\x35\x5c\xfc\x16\xbf\xd9\x6c\xc2\x95\x52\x2b\x51\x7f\x85\xef\x5e\x46\xd8\x46\x3d\x7c\x6b\x3c\x60\x66\x2b\x53\xc8\x70\x89\x7a\xde\x63\x5e\xbf\xa1\x88\xa3\xe6\x03\x72\x54\xff\x77\x97\x7f\x05\x00\x00\xff\xff\x1d\xd7\x27\x7a\xff\x22\x00\x00")
+var _faucetHtml = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xd4\x5a\xeb\x8f\xe3\xb6\x11\xff\xec\xfd\x2b\x26\xea\x25\x96\xbb\x2b\xc9\xbe\xcd\x0b\xb6\xe4\xe2\x72\x49\x83\x2b\xda\x4b\x90\x4b\xd0\x16\x49\x3e\xd0\xe2\xd8\xe2\x2e\x45\x2a\x24\x65\xaf\x63\xf8\x7f\x2f\x48\x51\xb2\xfc\xd8\xed\x3d\x02\x14\xbd\x0f\x5e\x91\x1c\xce\xfc\x38\x33\x9c\x87\x74\xe9\x47\x5f\x7f\xf7\xf2\xc7\x7f\x7f\xff\x0d\x14\xa6\xe4\xf3\xab\xd4\xfe\x01\x4e\xc4\x2a\x0b\x50\x04\x76\x02\x09\x9d\x5f\x0d\xd2\x12\x0d\x81\xbc\x20\x4a\xa3\xc9\x82\xda\x2c\xa3\x2f\x83\x6e\xbe\x30\xa6\x8a\xf0\xb7\x9a\xad\xb3\xe0\x5f\xd1\x4f\x2f\xa2\x97\xb2\xac\x88\x61\x0b\x8e\x01\xe4\x52\x18\x14\x26\x0b\x5e\x7d\x93\x21\x5d\xe1\x61\x9b\x20\x25\x66\xc1\x9a\xe1\xa6\x92\xca\xf4\x28\x37\x8c\x9a\x22\xa3\xb8\x66\x39\x46\x6e\x70\x03\x4c\x30\xc3\x08\x8f\x74\x4e\x38\x66\x93\x60\x7e\x75\x35\x48\x0d\x33\x1c\xe7\xbb\x5d\xfc\x1a\xcd\x46\xaa\xfb\xfd\x7e\x0a\x7f\x25\x75\x8e\x26\x4d\x9a\x35\x4b\xc5\x99\xb8\x87\x42\xe1\x32\x0b\x2c\x52\x3d\x4d\x92\x9c\x8a\x3b\x1d\xe7\x5c\xd6\x74\xc9\x89\xc2\x38\x97\x65\x42\xee\xc8\x43\xc2\xd9\x42\x27\x66\xc3\x8c\x41\x15\x2d\xa4\x34\xda\x28\x52\x25\xb7\xf1\x6d\xfc\x45\x92\x6b\x9d\x74\x73\x71\xc9\x44\x9c\x6b\x1d\x80\x42\x9e\x05\xda\x6c\x39\xea\x02\xd1\x04\x90\xcc\xdf\x4b\xec\x52\x0a\x13\x91\x0d\x6a\x59\x62\xf2\x69\xfc\x45\x3c\x76\x12\xfb\xd3\x4f\x0b\xbd\x1a\xa4\x3a\x57\xac\x32\xa0\x55\xfe\xd6\x62\xef\x7e\xab\x51\x6d\x93\xdb\x78\x12\x4f\xfc\xc0\x89\xb9\xd3\xc1\x3c\x4d\x1a\x86\xf3\x0f\x61\x1d\x09\x69\xb6\xc9\xf3\xf8\xd3\x78\x92\x54\x24\xbf\x27\x2b\xa4\xad\x20\xbb\x14\xb7\x93\x7f\x94\xd8\xc7\xec\x77\x77\x6a\xbe\x3f\x40\x56\x29\x4b\x14\x26\xbe\xd3\xc9\xf3\x78\xf2\x65\x3c\x6e\x27\xce\xd9\x5b\xfe\xd6\x5e\xf3\xab\xc1\x20\x5e\xa3\x32\x2c\x27\x3c\xca\x51\x18\x54\xb0\xbb\x1a\x0c\x06\x25\x13\x51\x81\x6c\x55\x98\x29\x4c\xc6\xe3\x8f\x67\x17\x26\xd7\x85\x9b\xa5\x4c\x57\x9c\x6c\xa7\xb0\xe4\xf8\xe0\x66\x08\x67\x2b\x11\x31\x83\xa5\x9e\x42\xc3\xd5\xce\xef\xad\xb4\x4a\xc9\x95\x42\xad\x1b\x31\x95\xd4\xcc\x30\x29\xa6\xd6\x89\x88\x61\x6b\x3c\x27\xd4\x15\x11\xa7\xd4\x64\xa1\x25\xaf\x0d\x1e\x03\x58\x70\x99\xdf\xbb\x29\x77\x55\x7b\xc8\x73\xc9\xa5\x9a\xc2\xa6\x60\xa6\x93\x50\x29\xf4\x6c\x09\xa5\x4c\xac\xa6\xf0\x79\xd5\xe0\x2f\x89\x5a\x31\x31\x85\xb1\x27\x4d\x13\xaf\xad\x34\x69\xa2\xd0\x55\xba\x90\x74\x3b\xbf\x4a\x29\x5b\x43\xce\x89\xd6\x59\x70\xa2\x46\x17\x5c\x7a\xcb\x36\xa4\x10\x26\x9a\x85\xa3\x15\x25\x37\x01\x38\x01\x59\xd0\x48\x8e\x16\xd2\x18\x59\x4e\x61\x62\x11\xb9\x0d\x27\xbc\x78\xc4\x57\xd1\xe4\x79\xb3\x34\x48\x8b\x49\xcb\xc0\xe0\x83\x89\x9c\xfe\x3b\xcd\x07\xf3\x94\xb5\x3b\x97\x04\x96\x24\x5a\x10\x53\x04\x40\x14\x23\x51\xc1\x28\x45\x91\x05\x46\xd5\x68\x3d\x84\xcd\xa1\x1f\xc4\xba\x18\x56\x4c\x1a\x14\x09\x65\x6b\x77\x80\xee\xe1\xe4\x24\x8f\x81\xfd\x12\xfc\x83\x5c\x2e\x35\x9a\xa8\xc3\xde\x23\x65\xa2\xaa\x4d\xb4\x52\xb2\xae\xfc\xea\x20\x75\x73\xc0\x68\x16\xd4\x8a\x07\x3e\x52\xbb\x47\xb3\xad\xfc\x81\x83\xee\x78\x52\x95\x91\xd5\xb4\x92\x3c\x80\x8a\x93\x1c\x0b\xc9\x29\xaa\x2c\x78\x23\x73\x46\x38\x88\xe6\x64\xf0\xd3\x0f\x7f\x07\x6f\x12\x26\x56\xb0\x95\xb5\x82\xaf\x5e\xbc\x01\x42\xa9\x75\xba\x38\x8e\x3b\x04\xce\xff\xce\x11\x46\x0b\x23\x5a\x1a\x4b\xb6\xa8\x8d\x91\x1d\xe1\xc2\x08\x58\x18\x11\x51\x5c\x92\x9a\x1b\xa0\x4a\x56\x54\x6e\x44\x64\xe4\x6a\x65\x93\x51\x83\xbe\xd9\x14\x00\x25\x86\xf8\xa5\x2c\x68\x69\x5b\x13\x11\x5d\xc9\xaa\xae\xbc\x91\x9a\x49\x7c\xa8\x88\xa0\x48\xad\x49\xb9\xc6\x60\xfe\x2d\x5b\x23\x94\x08\xaf\xdd\x2d\x82\x1f\xe5\x3d\x8a\xc1\xa9\xe1\x73\xa2\xd0\x44\x7d\xde\x67\xe6\x4f\x93\x06\x53\x73\x32\xf0\xff\xd2\x9a\xb7\x9c\xba\x93\x94\x28\x6a\x38\x1a\x45\xca\xc6\x86\x60\xbe\xdb\x29\x22\x56\x08\xcf\x18\x7d\xb8\x81\x67\xa4\x94\xb5\x30\x30\xcd\x20\x7e\xe1\x1e\xf5\x7e\x7f\xc4\x1d\x20\xe5\x6c\x9e\x92\xa7\x9c\x18\xa4\xc8\x39\xcb\xef\xb3\xc0\x30\x54\xd9\x6e\x67\x99\xef\xf7\x33\xbd\x2d\x17\x92\x67\xc3\xe6\xdc\xee\xd8\xc3\x19\xec\x76\x6c\x09\xcf\xe2\x1f\x30\x27\x95\xc9\x0b\xb2\xdf\xaf\x54\xfb\x1c\xe3\x03\xe6\xb5\xc1\x70\xb4\xdb\x21\xd7\xb8\xdf\xeb\x7a\x51\x32\x13\xb6\x3c\xed\xbc\xa0\xfb\xbd\x3d\x88\x07\xbf\xdf\xa7\x09\x99\xa7\x09\x67\x73\xbf\x78\xac\x9e\xa4\xe6\x9d\x2b\xa4\x89\xf5\x18\x3f\xdc\xed\x80\x2d\x21\xfe\x0a\xab\xe7\xf8\x4a\x2c\xa5\x06\xbf\xf5\xff\xc5\xaf\xbe\xc7\xd5\x6a\x0b\xc6\xea\x55\xff\x8f\xdc\x09\x3a\x7f\x6a\x8c\x7d\x03\xcf\x16\xad\x3e\x9d\x57\x1d\xb4\xdb\x2a\xd7\x2a\xee\x1d\x5c\xca\x3b\xd1\x6e\xe7\x25\xec\xf7\xef\xe7\x42\x0e\x48\xd8\x63\x73\xe4\x4a\x1d\x68\x7f\x0d\xde\x18\xb5\xdf\x43\x8f\xfa\xfd\x7d\x0c\x05\x6d\x1d\xab\x89\xcc\x0e\x7d\x1f\xfc\x59\xb0\x5d\x45\xdd\x71\xbc\x9f\x68\x66\xf0\x1e\xb7\x59\xb0\xdb\xf5\x77\xfa\xd5\x9c\x70\xbe\x20\x4e\x5b\xee\xac\xdd\xa6\xdf\xd1\xfa\xef\x9a\x69\x57\x65\xcf\x5b\xf9\xdd\x09\xfe\x7b\xce\x38\xc9\x7e\x46\x56\x53\xb8\x7d\xfe\x54\xea\xfb\xfc\x24\x9b\xdc\x5e\xc8\x26\x15\x11\xc8\xc1\xfd\x46\xba\x24\xbc\x7d\xf6\x57\xa7\x8b\xef\xa7\x5b\x22\x9b\xdb\x3b\x4c\x5d\x71\x30\x9e\x81\x5c\xa3\x5a\x72\xb9\x99\x02\xa9\x8d\x9c\x41\x49\x1e\xba\x7a\xe8\x76\x3c\xee\x00\x5b\xae\x86\x2c\x38\xba\xbc\xa5\xf0\xb7\x1a\xb5\xd1\x5d\x96\x6a\x96\xdc\xaf\x4d\x56\x14\x85\x46\x7a\xa2\x04\x2b\xcf\xea\xd2\x51\xb5\x48\x5b\xfd\x5d\x44\xbd\x94\xd2\x97\x1d\x7d\x00\x9e\x69\xaf\x1e\x0a\xe6\xa9\x51\x07\x47\x32\xf4\x9d\x4a\x07\x65\x0b\xfe\xc7\x2a\x87\x26\xa4\xd9\x33\x57\x88\xaa\xa9\x38\xad\x9b\x82\x1b\xa6\x89\xa1\xef\x2d\xd7\xfa\xda\x82\x68\x7c\x1b\xe1\xae\x0c\x3c\x08\x77\xc3\x0f\x93\x5e\x20\x51\x66\x81\xc4\xbc\x8d\xf8\x65\x2d\x68\xef\xec\xfd\x84\xfc\x61\x28\x6a\xc1\xd6\xa8\x34\x33\xdb\xb7\x85\x81\xf4\x80\xa3\x19\xf7\x01\xa4\x89\x51\x8f\xbb\xd8\xe1\xf1\xec\x02\xfb\xbf\xfe\xcf\x55\xda\x75\x2d\x49\x02\xdf\x72\xb9\x20\x1c\xd6\x16\xe0\x82\xa3\x06\x23\xc1\xd6\x60\x60\x0a\x84\xbc\x56\x0a\x85\x01\x6d\x88\xa9\x35\xc8\xa5\x9b\x5d\xba\x1a\xf3\x6a\xb0\x26\x0a\x88\x31\x58\x56\x06\x32\x57\x7c\xdb\x19\x8d\x6a\xed\xfa\x07\x3b\xb0\xb9\xbf\xbf\xd6\xc4\xed\x20\xf0\xe3\xf6\xaa\x41\x06\x3f\xff\x3a\xbb\x72\x80\xbe\xc6\x25\x13\x08\xc4\x2a\x20\xb7\xdd\x03\x98\x82\x18\xc8\x15\x12\x83\x1a\x72\x2e\x75\xad\x1a\x9c\x36\xfb\x80\xc5\xda\xf2\x69\xb8\xda\xe9\xca\xc9\x6d\x59\x84\x05\xd1\xc5\xc8\x75\x0f\x0a\x4d\xad\xc4\x61\xa5\x99\x1d\x2c\xa5\x82\xd0\x6e\x66\xd9\x78\x06\x2c\x6d\x39\xc6\x1c\xc5\xca\x14\x33\x60\xd7\xd7\x9e\x74\xc0\x96\x10\xb6\xeb\x3f\xb3\x5f\x63\xf3\x10\x5b\xfe\x90\x65\x70\x90\x33\xb0\xa2\x3c\x0f\x5d\x71\x96\x63\xc8\x6e\x60\x32\x9a\x35\x6b\x0b\x85\xa4\x69\x7d\x5c\x6f\xe3\x7e\xf6\x57\x83\xfd\xac\xaf\x03\xa7\xec\x23\x2d\x34\xa1\x5c\x03\x81\x15\xd3\x06\x6a\xc5\xad\x1e\x2c\x5d\xa3\x76\xaf\x66\x47\xd5\x3f\xff\x59\x7a\xf1\x0f\x3e\xec\x37\x90\x1b\x16\xb1\x46\x41\xc3\xbf\xbd\xf9\xee\x75\xac\x8d\x62\x62\xc5\x96\xdb\x70\x57\x2b\x3e\x85\x67\x61\xf0\x27\x5b\xca\x8f\x7e\x1e\xff\x1a\xaf\x09\xaf\xf1\xc6\x9b\x74\x0a\x6d\xaa\xb7\x16\x9f\xba\xdf\x33\x99\x37\xe0\x1f\xa7\x70\x2c\x7e\x3f\x1a\xcd\x2e\x25\xc0\x5e\x02\x57\xa8\xd1\x84\x96\xcc\xe7\xa9\x63\x4d\x11\x28\xd1\x14\x92\x5a\x6d\x28\xcc\xa5\x10\x98\x1b\xa8\x2b\x29\xbc\x62\x80\x4b\xad\x5b\xa7\x6b\xd7\xb3\x53\x37\xf0\xb4\x19\x08\xdc\xc0\x3f\x71\xf1\x46\xe6\xf7\x68\xc2\x30\xdc\x30\x41\xe5\x26\xe6\x32\x27\x96\xdc\x36\xbb\x46\xe6\x92\x43\x96\x65\xe0\xfb\xfd\x60\x04\x7f\x81\x60\xa3\x6d\xe7\x1f\xc0\xd4\x3e\xda\xa7\x11\x5c\xc3\xe9\xf6\x42\x6a\x03\xd7\x10\x24\xcd\x55\xb2\xe9\x4e\x99\x84\x54\x2c\x18\xd9\x5b\xd0\x5a\x42\x8a\x12\xb5\x26\x2b\xec\x23\xc5\x35\x0a\xe3\x7d\xcc\x1e\xa7\xd4\x2b\xc8\xc0\xd9\xab\x22\x4a\x63\x43\x10\xdb\x00\xdc\x38\x9b\x75\x57\x47\x94\x65\x20\x6a\xce\x5b\xff\x6c\x6e\xc2\xac\xf1\xbe\x1e\x61\xec\x02\x22\x7c\x94\x65\x60\x43\x90\xd5\x2f\x6d\xf7\x58\x0f\x68\xe2\xe5\x28\xb6\x31\xf0\x40\x3f\x9a\xb5\x6e\x7c\xc4\x07\xe9\xd3\x8c\x90\x9e\x72\x42\x7a\x81\x95\x4b\x48\x8f\x73\x6a\xd2\x57\x8f\x91\x9b\xb8\xc0\x47\xd4\xe5\x02\xd5\xe3\x8c\x9a\x54\xe4\x19\x39\x75\xbe\x12\xa6\xb7\xf3\x06\x26\x9f\x8f\x2e\xf0\x45\xa5\xe4\x23\x6c\x85\x34\xdb\x70\xc7\xc9\x56\xd6\x66\x0a\x43\x23\xab\x97\x2e\x61\x0c\x6f\xc0\x4a\x99\x42\xb7\xff\xc6\x35\x03\x53\x18\xba\x91\x5d\x67\x25\xba\x5d\x9f\x8d\xc7\xe3\x1b\x68\xdf\xb0\x7c\x45\xec\x0d\x53\x35\xee\x2f\x20\xd1\x75\x9e\xa3\x7e\x44\x57\x6f\x85\xc5\x73\xe8\xd0\xf8\xf1\x7b\xe2\xe9\x42\xfc\x11\x20\xf8\xe4\x13\x38\x5b\xed\x3b\x67\x92\xc0\x3f\x88\xba\x07\x57\x06\x2a\x5c\x33\x59\xeb\x43\xba\x28\x99\xd6\x4c\xac\x80\x68\xa0\x52\xa0\xdb\xf1\x2e\x11\xfc\x0c\x9d\x27\x82\x39\x8c\x4f\xa1\xd9\x58\xd7\x8b\xf0\x17\x02\x7f\xc7\xb5\x1f\xd5\x07\xfb\x83\xa4\xa3\x3d\xac\x44\xf8\x28\x83\x20\x38\x6c\x3b\x5b\xb7\xcb\x9e\xcd\x40\xa3\xf9\xb1\xd1\x7b\xe8\x13\xdb\xa5\xe4\x33\xba\xb1\x15\xed\x78\x74\x24\x7c\xdf\xaa\xf2\x45\x55\xd9\x8e\x83\x88\xad\x8b\x6c\x9d\x1e\x99\x30\x12\x64\x6d\xa3\x63\x4e\xb8\xad\xd1\x39\xba\x28\xe3\x36\x5a\x65\xe6\xb2\x2c\xa5\x80\x0c\xa2\xc9\xec\x2c\xf9\xf5\xb4\xd6\x1d\xe6\xd4\x0c\x17\xb4\x7c\x6c\x8a\x63\x0d\x9d\x90\x46\x93\x23\xe5\x1f\xd9\xe5\x92\x01\x06\x1d\x5e\xd6\xea\xef\xc8\x28\xad\x55\xfa\xfa\xe9\x21\x6e\x76\x5f\x4f\xde\x0a\x78\xb7\x58\xd5\xba\x08\x4f\xa0\x8d\x66\xc7\x16\x78\x65\x50\x11\x83\xae\x25\x71\x1a\x47\x61\x98\xc2\x33\xc5\x03\x11\xb6\x9c\x89\x14\x0a\x8a\xaa\xad\x00\x6c\x47\xd3\x34\x20\x3d\xc3\xb8\x4f\x1e\x3d\x57\xe9\x9d\xe3\x4c\x8b\x33\x60\x30\xb7\x75\x18\xb0\x28\xea\x4e\xe0\x8a\x25\x29\xd0\xb6\xab\x27\x9e\xed\xbc\xb0\xe7\x86\x96\x14\x39\xa9\x34\x52\xc8\xa0\x79\x51\x1d\x8e\xe2\x5a\xb0\x87\x70\x14\xf9\xf1\x29\x87\x76\xdd\xe5\x34\x67\x9c\x06\xf3\x75\x06\x41\x6a\x94\x2d\x7b\x87\x01\x5c\x5f\xba\x53\x36\x3b\x0e\xe7\xad\xf4\xfe\x46\x80\xd4\xd0\xb9\xeb\xa7\x9a\x62\xfc\x97\xc0\xb6\xb9\x2b\x25\x6b\x41\xa7\xb6\x24\x0a\xcf\x98\x92\x35\x31\x44\x39\x9e\xa3\x19\x1c\xc8\x5d\x37\x3c\x85\xdc\x5a\x65\x06\x4d\xd3\xe5\x1a\x59\xe8\xda\x44\x37\x5a\x48\x45\x51\x45\x8a\x50\x56\xeb\x29\x7c\x5a\x3d\xcc\x7e\x69\x3b\x67\x57\xa1\x3f\x01\xb4\x52\x38\x3f\xc3\x93\xe7\xee\x15\xdb\x35\x04\x69\x62\x09\x9e\x66\xd2\x1d\xb4\xff\x9a\x1c\x2e\xf4\x20\xd0\xbd\xd4\xf6\xf3\x25\xa3\x94\xa3\x05\xdb\x32\xb7\xb7\xcd\xda\xfc\x70\x67\x8e\xc5\x81\x6f\x3c\x5a\xfa\x3d\x20\xd7\xf8\x28\x71\xd7\xbf\x0c\xad\xc1\x23\x7b\x50\xe6\xf4\xec\x5b\x21\x37\xad\x86\x4e\x03\xfe\xf3\x06\xad\x95\xab\x82\xc2\xc8\x3b\xd4\x0d\x0c\xb5\xad\xc9\xa8\x1e\x8e\xe2\xa2\x2e\x89\x60\xbf\x63\x68\x13\xca\xa8\xd1\x90\x6b\x88\x82\xe3\xb8\x7a\x06\xe4\xd0\x60\x0f\xdb\xc4\x34\xf4\x8a\x1b\xb6\xd6\xb4\x86\x83\x43\xc3\x3e\x7c\x07\xad\x5c\x96\x10\x2d\x88\x82\xfe\x20\x6a\xb3\x25\x28\x69\x25\xb7\x6b\x0b\xa2\x86\x4d\x13\xe8\xea\x66\x21\x37\xd9\xf0\x76\xdc\x01\x6c\x0c\xeb\xec\x3a\xf4\x7e\x75\x62\x00\x8b\xb0\xbd\x80\x73\xb8\x1d\x7f\x38\x52\x4a\xc4\x0a\x4f\xd1\x1b\xc5\x2a\xa4\x40\x72\xdb\x04\xff\xe1\x87\xf8\x60\xe5\xbe\x33\x3c\xeb\x77\xad\xda\x9c\x5b\x1e\x61\xb5\xab\x9d\x56\xff\x6c\xef\x15\x24\x4e\xb7\xd7\x10\x5c\x38\xc4\x23\x9e\x77\x44\x74\x72\x79\x1f\xbb\xd7\xae\x97\x0f\xfa\x49\xc2\x96\x9f\xdd\xab\xa7\x51\x5c\x98\x92\x87\x41\x6a\xdc\x87\x2b\x8b\xb3\xdb\xed\x36\x37\xd3\x87\x7a\x6b\xdf\xef\x1a\x6c\x8f\x8c\x27\xdd\x0d\xf4\xea\x88\xae\x03\x6a\x8b\x06\xb0\xdd\xd4\xde\x35\x54\x6f\x0c\x51\x06\x08\xfc\xf4\x0a\xea\x8a\x12\x63\x93\x90\x04\x9b\xe2\x5c\x32\xea\xbe\xf2\x2d\x88\xd2\xb0\x94\x6a\x43\x14\x85\x5a\x18\xc6\xed\xfa\x16\x88\x42\x5f\x93\x69\x34\xaf\x6c\x54\x5a\x13\x1e\x9e\xf4\x59\xcf\xc2\x61\xdc\x37\xec\x70\x14\x23\xc9\x8b\x53\x32\x97\x75\x3a\x89\x19\xbc\x76\x35\x78\xf8\x2c\x34\x05\xd3\xa3\x98\x18\xa3\xc2\xe1\x91\xc1\x87\x23\x6b\xbb\x49\xd7\xf7\x74\x9b\xd3\xde\x95\x79\x6a\xff\xa1\xaa\xf5\x19\xbc\x25\xce\xb5\x0e\x1b\xaf\x19\xde\xf4\xf8\x1e\x3b\xcd\xf0\xe3\xa1\x37\xc9\xe1\xd2\x1e\xf0\x67\x17\x30\x1c\xb1\x1d\xda\xbb\x33\x3c\x11\x4c\x28\x7d\x69\xef\x45\x18\x5c\xb8\xbd\x7d\x0f\x18\x79\xc5\x36\x11\xf7\x09\x8d\x32\x41\xf1\xe1\x31\x75\x32\x3a\x1c\xc5\xba\x5e\x34\xbd\x7f\xf8\x99\xef\x76\x5a\x22\xe7\x96\xa7\x81\xfc\x2c\xfd\x5b\x01\xc7\x25\x40\x74\x52\x32\x3c\x11\xf3\x9d\x40\x7b\x9a\xfd\x8d\x55\xee\x78\xe4\x5f\x0c\x7d\xa3\x6d\x05\xc4\x74\x01\x04\x36\xb8\xd0\xae\x37\x07\xef\xc9\xee\x0d\x49\xf3\x26\xe4\xc5\xf7\xaf\xba\xb7\x21\x9d\xa7\xdb\x22\xa4\xfb\x9e\x7e\xfe\xae\xe1\xe2\xe7\xfb\xcd\x66\x13\xaf\xa4\x5c\xf1\xe6\xc3\x7d\xf7\x32\xc2\x36\xea\xf1\x9d\x0e\x80\xe8\xad\xc8\x81\xe2\x12\xd5\xbc\xc7\xbc\x79\x43\x91\x26\xfe\x9b\x73\xd2\xfc\x0f\x99\xff\x04\x00\x00\xff\xff\x13\xef\x26\xa6\x32\x23\x00\x00")
func faucetHtmlBytes() ([]byte, error) {
return bindataRead(
@@ -84,8 +84,8 @@ func faucetHtml() (*asset, error) {
return nil, err
}
- info := bindataFileInfo{name: "faucet.html", size: 0, mode: os.FileMode(0), modTime: time.Unix(0, 0)}
- a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x39, 0x4d, 0x5d, 0x6d, 0x7f, 0xf1, 0x26, 0x19, 0x7, 0xee, 0xd2, 0xb3, 0x2, 0xdf, 0xd2, 0x8c, 0x36, 0x87, 0x8e, 0x17, 0x50, 0xb6, 0x8f, 0xb0, 0xb5, 0xce, 0x73, 0xcc, 0x24, 0xd3, 0xd6, 0x8e}}
+ info := bindataFileInfo{name: "faucet.html", size: 9010, mode: os.FileMode(0644), modTime: time.Unix(1653732396, 0)}
+ a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xb, 0xb7, 0xec, 0xe1, 0xa2, 0x8e, 0x94, 0xb, 0x2a, 0xc4, 0x21, 0x38, 0xa9, 0x6d, 0xdd, 0x31, 0x4, 0x3f, 0xa6, 0x26, 0xbe, 0x79, 0x11, 0xfc, 0xc0, 0x81, 0xe7, 0x85, 0x56, 0xec, 0x3, 0x77}}
return a, nil
}
diff --git a/cmd/geth/blsaccountcmd.go b/cmd/geth/blsaccountcmd.go
new file mode 100644
index 000000000..825a72f06
--- /dev/null
+++ b/cmd/geth/blsaccountcmd.go
@@ -0,0 +1,549 @@
+package main
+
+import (
+ "context"
+ "encoding/hex"
+ "encoding/json"
+ "fmt"
+ "io/ioutil"
+ "os"
+ "path/filepath"
+ "strings"
+
+ "github.com/google/uuid"
+ "github.com/logrusorgru/aurora"
+ "github.com/prysmaticlabs/prysm/crypto/bls"
+ "github.com/prysmaticlabs/prysm/encoding/bytesutil"
+ "github.com/prysmaticlabs/prysm/io/file"
+ "github.com/prysmaticlabs/prysm/io/prompt"
+ "github.com/prysmaticlabs/prysm/validator/accounts"
+ "github.com/prysmaticlabs/prysm/validator/accounts/iface"
+ "github.com/prysmaticlabs/prysm/validator/accounts/petnames"
+ "github.com/prysmaticlabs/prysm/validator/accounts/wallet"
+ "github.com/prysmaticlabs/prysm/validator/keymanager"
+ "github.com/prysmaticlabs/prysm/validator/keymanager/imported"
+ keystorev4 "github.com/wealdtech/go-eth2-wallet-encryptor-keystorev4"
+ "gopkg.in/urfave/cli.v1"
+
+ "github.com/ethereum/go-ethereum/cmd/utils"
+)
+
+const (
+ BLSWalletPath = "bls/wallet"
+ BLSKeystorePath = "bls/keystore"
+)
+
+var (
+ au = aurora.NewAurora(true)
+)
+
+var (
+ blsCommand = cli.Command{
+ Name: "bls",
+ Usage: "Manage BLS wallet and accounts",
+ ArgsUsage: "",
+ Category: "BLS ACCOUNT COMMANDS",
+ Description: `
+
+Manage BLS wallet and accounts, before creating or importing BLS accounts, create
+BLS wallet first. One BLS wallet is enough for all BLS accounts, the first BLS
+account in the wallet will be used to vote for fast finality now.
+
+It only supports interactive mode now, when you are prompted for password, please
+input your password, and take care the wallet password which is different from accounts'
+password.
+
+There are generally two steps to manage a BLS account:
+1.Create a BLS wallet: geth bls wallet create
+2.Create a BLS account: geth bls account new
+ or import a BLS account: geth bls account import
`,
+ Subcommands: []cli.Command{
+ {
+ Name: "wallet",
+ Usage: "Manage BLS wallet",
+ ArgsUsage: "",
+ Category: "BLS ACCOUNT COMMANDS",
+ Description: `
+
+Create a BLS wallet to manage BLS accounts, this should before creating
+or import a BLS account. The BLS wallet dir should be "/bls/wallet".`,
+ Subcommands: []cli.Command{
+ {
+ Name: "create",
+ Usage: "Create BLS wallet",
+ Action: utils.MigrateFlags(blsWalletCreate),
+ ArgsUsage: "",
+ Category: "BLS ACCOUNT COMMANDS",
+ Flags: []cli.Flag{
+ utils.DataDirFlag,
+ },
+ Description: `
+ geth bls wallet create
+
+will prompt for your password then create a BLS wallet in "/bls/wallet",
+don't create BLS wallet repeatedly'.`,
+ },
+ },
+ },
+ {
+ Name: "account",
+ Usage: "Manage BLS accounts",
+ ArgsUsage: "",
+ Category: "BLS ACCOUNT COMMANDS",
+ Description: `
+
+Manage BLS accounts,list all existing accounts, import a BLS private key into
+a new account, create a new account or delete an existing account.
+
+Make sure you remember the password you gave when creating a new account.
+Without it you are not able to unlock your account. And this password is
+different from the wallet password, please take care.
+
+Note that exporting your key in unencrypted format is NOT supported.
+
+Keys are stored under /bls/keystore.
+It is safe to transfer the entire directory or the individual keys therein
+between ethereum nodes by simply copying.
+
+Make sure you backup your BLS keys regularly.`,
+ Subcommands: []cli.Command{
+ {
+ Name: "new",
+ Usage: "Create a BLS account",
+ Action: utils.MigrateFlags(blsAccountCreate),
+ ArgsUsage: "",
+ Category: "BLS ACCOUNT COMMANDS",
+ Flags: []cli.Flag{
+ utils.DataDirFlag,
+ },
+ Description: `
+ geth bls account new
+
+Creates a new BLS account and imports the account into the BLS wallet.
+
+If the BLS wallet not created yet, it will try to create BLS wallet first.
+
+The account is saved in encrypted format, you are prompted for a password.
+You must remember this password to unlock your account in the future.`,
+ },
+ {
+ Name: "import",
+ Usage: "Import a BLS account",
+ Action: utils.MigrateFlags(blsAccountImport),
+ ArgsUsage: "",
+ Category: "BLS ACCOUNT COMMANDS",
+ Flags: []cli.Flag{
+ utils.DataDirFlag,
+ },
+ Description: `
+ geth bls account import
+
+Import a encrypted BLS account from keystore file into the BLS wallet.
+
+If the BLS wallet not created yet, it will try to create BLS wallet first.`,
+ },
+ {
+ Name: "list",
+ Usage: "Print summary of existing BLS accounts",
+ Action: utils.MigrateFlags(blsAccountList),
+ ArgsUsage: "",
+ Category: "BLS ACCOUNT COMMANDS",
+ Flags: []cli.Flag{
+ utils.DataDirFlag,
+ },
+ Description: `
+ geth bls account list
+
+Print summary of existing BLS accounts in the current BLS wallet.`,
+ },
+ {
+ Name: "delete",
+ Usage: "Delete the selected BLS account from the BLS wallet",
+ Action: utils.MigrateFlags(blsAccountDelete),
+ ArgsUsage: "",
+ Category: "BLS ACCOUNT COMMANDS",
+ Flags: []cli.Flag{
+ utils.DataDirFlag,
+ },
+ Description: `
+ geth bls account delete
+
+Delete the selected BLS account from the BLS wallet.`,
+ },
+ },
+ },
+ },
+ }
+)
+
+// blsWalletCreate creates a BLS wallet in /bls/wallet.
+func blsWalletCreate(ctx *cli.Context) error {
+ cfg := gethConfig{Node: defaultNodeConfig()}
+ // Load config file.
+ if file := ctx.GlobalString(configFileFlag.Name); file != "" {
+ if err := loadConfig(file, &cfg); err != nil {
+ utils.Fatalf("%v", err)
+ }
+ }
+ utils.SetNodeConfig(ctx, &cfg.Node)
+
+ dir := filepath.Join(cfg.Node.DataDir, BLSWalletPath)
+ dirExists, err := wallet.Exists(dir)
+ if err != nil {
+ utils.Fatalf("Check BLS wallet exists error: %v.", err)
+ }
+ if dirExists {
+ utils.Fatalf("BLS wallet already exists in /bls/wallet.")
+ }
+
+ password := utils.GetPassPhrase("Your new BLS wallet will be locked with a password. Please give a password. Do not forget this password.", true)
+ walletConfig := &accounts.CreateWalletConfig{
+ WalletCfg: &wallet.Config{
+ WalletDir: dir,
+ KeymanagerKind: keymanager.Imported,
+ WalletPassword: password,
+ },
+ SkipMnemonicConfirm: true,
+ }
+
+ _, err = accounts.CreateWalletWithKeymanager(context.Background(), walletConfig)
+ if err != nil {
+ utils.Fatalf("Create BLS wallet failed: %v.", err)
+ }
+
+ fmt.Println("Create BLS wallet successfully!")
+ return nil
+}
+
+// openOrCreateBLSWallet opens BLS wallet in /bls/wallet, if wallet
+// not exists, then creates BLS wallet in /bls/wallet.
+func openOrCreateBLSWallet(ctx *cli.Context, cfg *gethConfig) (*wallet.Wallet, error) {
+ var w *wallet.Wallet
+ walletDir := filepath.Join(cfg.Node.DataDir, BLSWalletPath)
+ dirExists, err := wallet.Exists(walletDir)
+ if err != nil {
+ utils.Fatalf("Check dir %s failed: %v.", walletDir, err)
+ }
+ if !dirExists {
+ fmt.Println("BLS wallet not exists, creating BLS wallet...")
+ password := utils.GetPassPhrase("Your new BLS wallet will be locked with a password. Please give a password. Do not forget this password.", true)
+ walletConfig := &accounts.CreateWalletConfig{
+ WalletCfg: &wallet.Config{
+ WalletDir: walletDir,
+ KeymanagerKind: keymanager.Imported,
+ WalletPassword: password,
+ },
+ SkipMnemonicConfirm: true,
+ }
+
+ w, err = accounts.CreateWalletWithKeymanager(context.Background(), walletConfig)
+ if err != nil {
+ utils.Fatalf("Create BLS wallet failed: %v.", err)
+ }
+
+ fmt.Println("Create BLS wallet successfully!")
+ return w, nil
+ }
+
+ walletPassword := utils.GetPassPhrase("Enter the password for your BLS wallet.", false)
+ w, err = wallet.OpenWallet(context.Background(), &wallet.Config{
+ WalletDir: walletDir,
+ WalletPassword: walletPassword,
+ })
+ if err != nil {
+ utils.Fatalf("Open BLS wallet failed: %v.", err)
+ }
+ return w, nil
+}
+
+// blsAccountCreate creates a BLS account in /bls/keystore,
+// and import the created account into the BLS wallet.
+func blsAccountCreate(ctx *cli.Context) error {
+ cfg := gethConfig{Node: defaultNodeConfig()}
+ // Load config file.
+ if file := ctx.GlobalString(configFileFlag.Name); file != "" {
+ if err := loadConfig(file, &cfg); err != nil {
+ utils.Fatalf("%v", err)
+ }
+ }
+ utils.SetNodeConfig(ctx, &cfg.Node)
+
+ w, _ := openOrCreateBLSWallet(ctx, &cfg)
+ if w.KeymanagerKind() != keymanager.Imported {
+ utils.Fatalf("BLS wallet has wrong key manager kind.")
+ }
+ km, err := w.InitializeKeymanager(context.Background(), iface.InitKeymanagerConfig{ListenForChanges: false})
+ if err != nil {
+ utils.Fatalf("Initialize key manager failed: %v.", err)
+ }
+ k, ok := km.(keymanager.Importer)
+ if !ok {
+ utils.Fatalf("The BLS keymanager cannot import keystores")
+ }
+
+ keystoreDir := filepath.Join(cfg.Node.DataDir, BLSKeystorePath)
+ if err := file.MkdirAll(keystoreDir); err != nil {
+ utils.Fatalf("Could not access keystore dir: %v.", err)
+ }
+ accountPassword := utils.GetPassPhrase("Your new BLS account will be encrypted with a password. Please give a password. Do not forget this password.", true)
+
+ encryptor := keystorev4.New()
+ secretKey, err := bls.RandKey()
+ if err != nil {
+ utils.Fatalf("Could not generate BLS secret key: %v.", err)
+ }
+ pubKeyBytes := secretKey.PublicKey().Marshal()
+ cryptoFields, err := encryptor.Encrypt(secretKey.Marshal(), accountPassword)
+ if err != nil {
+ utils.Fatalf("Could not encrypt secret key: %v.", err)
+ }
+ id, err := uuid.NewRandom()
+ if err != nil {
+ utils.Fatalf("Could not generate uuid: %v.", err)
+ }
+ keystore := &keymanager.Keystore{
+ Crypto: cryptoFields,
+ ID: id.String(),
+ Pubkey: fmt.Sprintf("%x", pubKeyBytes),
+ Version: encryptor.Version(),
+ Name: encryptor.Name(),
+ }
+
+ encodedFile, err := json.MarshalIndent(keystore, "", "\t")
+ if err != nil {
+ utils.Fatalf("Could not marshal keystore to JSON file: %v.", err)
+ }
+ keystoreFile, err := os.Create(fmt.Sprintf("%s/keystore-%s.json", keystoreDir, petnames.DeterministicName(pubKeyBytes, "-")))
+ if err != nil {
+ utils.Fatalf("Could not create keystore file: %v.", err)
+ }
+ if _, err := keystoreFile.Write(encodedFile); err != nil {
+ utils.Fatalf("Could not write keystore file contents: %v.", err)
+ }
+ fmt.Println("Successfully create a BLS account.")
+
+ fmt.Println("Importing BLS account, this may take a while...")
+ _, err = accounts.ImportAccounts(context.Background(), &accounts.ImportAccountsConfig{
+ Importer: k,
+ Keystores: []*keymanager.Keystore{keystore},
+ AccountPassword: accountPassword,
+ })
+ if err != nil {
+ utils.Fatalf("Import BLS account failed: %v.", err)
+ }
+ fmt.Printf("Successfully import created BLS account.\n")
+ return nil
+}
+
+// blsAccountImport imports a BLS account into the BLS wallet.
+func blsAccountImport(ctx *cli.Context) error {
+ keyfile := ctx.Args().First()
+ if len(keyfile) == 0 {
+ utils.Fatalf("The keystore file must be given as argument.")
+ }
+ keyJSON, err := ioutil.ReadFile(keyfile)
+ if err != nil {
+ utils.Fatalf("Could not read keystore file: %v", err)
+ }
+ keystore := &keymanager.Keystore{}
+ if err := json.Unmarshal(keyJSON, keystore); err != nil {
+ utils.Fatalf("Could not decode keystore file: %v.", err)
+ }
+ if keystore.Pubkey == "" {
+ utils.Fatalf(" Missing public key, wrong keystore file.")
+ }
+
+ cfg := gethConfig{Node: defaultNodeConfig()}
+ // Load config file.
+ if file := ctx.GlobalString(configFileFlag.Name); file != "" {
+ if err := loadConfig(file, &cfg); err != nil {
+ utils.Fatalf("%v", err)
+ }
+ }
+ utils.SetNodeConfig(ctx, &cfg.Node)
+
+ w, _ := openOrCreateBLSWallet(ctx, &cfg)
+ if w.KeymanagerKind() != keymanager.Imported {
+ utils.Fatalf("BLS wallet has wrong key manager kind.")
+ }
+ km, err := w.InitializeKeymanager(context.Background(), iface.InitKeymanagerConfig{ListenForChanges: false})
+ if err != nil {
+ utils.Fatalf("Initialize key manager failed: %v.", err)
+ }
+ k, ok := km.(keymanager.Importer)
+ if !ok {
+ utils.Fatalf("The BLS keymanager cannot import keystores")
+ }
+
+ password := utils.GetPassPhrase("Enter the password for your imported account.", false)
+ fmt.Println("Importing BLS account, this may take a while...")
+ _, err = accounts.ImportAccounts(context.Background(), &accounts.ImportAccountsConfig{
+ Importer: k,
+ Keystores: []*keymanager.Keystore{keystore},
+ AccountPassword: password,
+ })
+ if err != nil {
+ utils.Fatalf("Import BLS account failed: %v.", err)
+ }
+ fmt.Println("Successfully import BLS account.")
+ return nil
+}
+
+// blsAccountList prints existing BLS accounts in the BLS wallet.
+func blsAccountList(ctx *cli.Context) error {
+ cfg := gethConfig{Node: defaultNodeConfig()}
+ // Load config file.
+ if file := ctx.GlobalString(configFileFlag.Name); file != "" {
+ if err := loadConfig(file, &cfg); err != nil {
+ utils.Fatalf("%v", err)
+ }
+ }
+ utils.SetNodeConfig(ctx, &cfg.Node)
+
+ walletDir := filepath.Join(cfg.Node.DataDir, BLSWalletPath)
+ dirExists, err := wallet.Exists(walletDir)
+ if err != nil || !dirExists {
+ utils.Fatalf("BLS wallet not exists.")
+ }
+
+ walletPassword := utils.GetPassPhrase("Enter the password for your BLS wallet.", false)
+ w, err := wallet.OpenWallet(context.Background(), &wallet.Config{
+ WalletDir: walletDir,
+ WalletPassword: walletPassword,
+ })
+ if err != nil {
+ utils.Fatalf("Open BLS wallet failed: %v.", err)
+ }
+ km, err := w.InitializeKeymanager(context.Background(), iface.InitKeymanagerConfig{ListenForChanges: false})
+ if err != nil {
+ utils.Fatalf("Initialize key manager failed: %v.", err)
+ }
+
+ ikm, ok := km.(*imported.Keymanager)
+ if !ok {
+ utils.Fatalf("Could not assert keymanager interface to concrete type.")
+ }
+ accountNames, err := ikm.ValidatingAccountNames()
+ if err != nil {
+ utils.Fatalf("Could not fetch account names: %v.", err)
+ }
+ numAccounts := au.BrightYellow(len(accountNames))
+ fmt.Printf("(keymanager kind) %s\n", au.BrightGreen("imported wallet").Bold())
+ fmt.Println("")
+ if len(accountNames) == 1 {
+ fmt.Printf("Showing %d BLS account\n", numAccounts)
+ } else {
+ fmt.Printf("Showing %d BLS accounts\n", numAccounts)
+ }
+ pubKeys, err := km.FetchValidatingPublicKeys(context.Background())
+ if err != nil {
+ utils.Fatalf("Could not fetch BLS public keys: %v.", err)
+ }
+ for i := 0; i < len(accountNames); i++ {
+ fmt.Println("")
+ fmt.Printf("%s | %s\n", au.BrightBlue(fmt.Sprintf("Account %d", i)).Bold(), au.BrightGreen(accountNames[i]).Bold())
+ fmt.Printf("%s %#x\n", au.BrightMagenta("[BLS public key]").Bold(), pubKeys[i])
+ }
+ fmt.Println("")
+ return nil
+}
+
+// blsAccountDelete deletes a selected BLS account from the BLS wallet.
+func blsAccountDelete(ctx *cli.Context) error {
+ if len(ctx.Args()) == 0 {
+ utils.Fatalf("No BLS account specified to delete.")
+ }
+ var filteredPubKeys []bls.PublicKey
+ for _, str := range ctx.Args() {
+ pkString := str
+ if strings.Contains(pkString, "0x") {
+ pkString = pkString[2:]
+ }
+ pubKeyBytes, err := hex.DecodeString(pkString)
+ if err != nil {
+ utils.Fatalf("Could not decode string %s as hex.", pkString)
+ }
+ blsPublicKey, err := bls.PublicKeyFromBytes(pubKeyBytes)
+ if err != nil {
+ utils.Fatalf("%#x is not a valid BLS public key.", pubKeyBytes)
+ }
+ filteredPubKeys = append(filteredPubKeys, blsPublicKey)
+ }
+
+ cfg := gethConfig{Node: defaultNodeConfig()}
+ // Load config file.
+ if file := ctx.GlobalString(configFileFlag.Name); file != "" {
+ if err := loadConfig(file, &cfg); err != nil {
+ utils.Fatalf("%v", err)
+ }
+ }
+ utils.SetNodeConfig(ctx, &cfg.Node)
+
+ walletDir := filepath.Join(cfg.Node.DataDir, BLSWalletPath)
+ dirExists, err := wallet.Exists(walletDir)
+ if err != nil || !dirExists {
+ utils.Fatalf("BLS wallet not exists.")
+ }
+
+ walletPassword := utils.GetPassPhrase("Enter the password for your BLS wallet.", false)
+ w, err := wallet.OpenWallet(context.Background(), &wallet.Config{
+ WalletDir: walletDir,
+ WalletPassword: walletPassword,
+ })
+ if err != nil {
+ utils.Fatalf("Open BLS wallet failed: %v.", err)
+ }
+ km, err := w.InitializeKeymanager(context.Background(), iface.InitKeymanagerConfig{ListenForChanges: false})
+ if err != nil {
+ utils.Fatalf("Initialize key manager failed: %v.", err)
+ }
+ pubkeys, err := km.FetchValidatingPublicKeys(context.Background())
+ if err != nil {
+ utils.Fatalf("Could not fetch BLS public keys: %v.", err)
+ }
+
+ rawPublicKeys := make([][]byte, len(filteredPubKeys))
+ formattedPubKeys := make([]string, len(filteredPubKeys))
+ for i, pk := range filteredPubKeys {
+ pubKeyBytes := pk.Marshal()
+ rawPublicKeys[i] = pubKeyBytes
+ formattedPubKeys[i] = fmt.Sprintf("%#x", bytesutil.Trunc(pubKeyBytes))
+ }
+ allAccountStr := strings.Join(formattedPubKeys, ", ")
+ if len(filteredPubKeys) == 1 {
+ promptText := "Are you sure you want to delete 1 account? (%s) Y/N"
+ resp, err := prompt.ValidatePrompt(
+ os.Stdin, fmt.Sprintf(promptText, au.BrightGreen(formattedPubKeys[0])), prompt.ValidateYesOrNo,
+ )
+ if err != nil {
+ return err
+ }
+ if strings.EqualFold(resp, "n") {
+ return nil
+ }
+ } else {
+ promptText := "Are you sure you want to delete %d accounts? (%s) Y/N"
+ if len(filteredPubKeys) == len(pubkeys) {
+ promptText = fmt.Sprintf("Are you sure you want to delete all accounts? Y/N (%s)", au.BrightGreen(allAccountStr))
+ } else {
+ promptText = fmt.Sprintf(promptText, len(filteredPubKeys), au.BrightGreen(allAccountStr))
+ }
+ resp, err := prompt.ValidatePrompt(os.Stdin, promptText, prompt.ValidateYesOrNo)
+ if err != nil {
+ return err
+ }
+ if strings.EqualFold(resp, "n") {
+ return nil
+ }
+ }
+
+ if err := accounts.DeleteAccount(context.Background(), &accounts.Config{
+ Wallet: w,
+ Keymanager: km,
+ DeletePublicKeys: rawPublicKeys,
+ }); err != nil {
+ utils.Fatalf("Delete account failed: %v.", err)
+ }
+
+ return nil
+}
diff --git a/cmd/geth/main.go b/cmd/geth/main.go
index 4d7296e95..4897df8dd 100644
--- a/cmd/geth/main.go
+++ b/cmd/geth/main.go
@@ -157,6 +157,9 @@ var (
utils.CatalystFlag,
utils.BlockAmountReserved,
utils.CheckSnapshotWithMPT,
+ utils.BLSPasswordFileFlag,
+ utils.BLSWalletDirFlag,
+ utils.VoteJournalDirFlag,
}
rpcFlags = []cli.Flag{
@@ -238,6 +241,7 @@ func init() {
utils.ShowDeprecated,
// See snapshot.go
snapshotCommand,
+ blsCommand,
}
sort.Sort(cli.CommandsByName(app.Commands))
@@ -431,7 +435,11 @@ func unlockAccounts(ctx *cli.Context, stack *node.Node) {
}
// If insecure account unlocking is not allowed if node's APIs are exposed to external.
// Print warning log to user and skip unlocking.
- if !stack.Config().InsecureUnlockAllowed && stack.Config().ExtRPCEnabled() {
+ isDevMode := false
+ if ctx.GlobalIsSet(utils.NetworkIdFlag.Name) && ctx.GlobalUint64(utils.NetworkIdFlag.Name) == 1337 {
+ isDevMode = true
+ }
+ if !stack.Config().InsecureUnlockAllowed && stack.Config().ExtRPCEnabled() && !isDevMode {
utils.Fatalf("Account unlock with HTTP access is forbidden!")
}
ks := stack.AccountManager().Backends(keystore.KeyStoreType)[0].(*keystore.KeyStore)
diff --git a/cmd/geth/snapshot.go b/cmd/geth/snapshot.go
index c059c7cab..f2b684f68 100644
--- a/cmd/geth/snapshot.go
+++ b/cmd/geth/snapshot.go
@@ -266,7 +266,8 @@ func pruneBlock(ctx *cli.Context) error {
var newAncientPath string
oldAncientPath := ctx.GlobalString(utils.AncientFlag.Name)
if !filepath.IsAbs(oldAncientPath) {
- oldAncientPath = stack.ResolvePath(oldAncientPath)
+ // force absolute paths, which often fail due to the splicing of relative paths
+ return errors.New("datadir.ancient not abs path")
}
path, _ := filepath.Split(oldAncientPath)
diff --git a/cmd/geth/usage.go b/cmd/geth/usage.go
index 72e619c69..0cac0f781 100644
--- a/cmd/geth/usage.go
+++ b/cmd/geth/usage.go
@@ -56,6 +56,9 @@ var AppHelpFlagGroups = []flags.FlagGroup{
utils.TriesInMemoryFlag,
utils.BlockAmountReserved,
utils.CheckSnapshotWithMPT,
+ utils.BLSPasswordFileFlag,
+ utils.BLSWalletDirFlag,
+ utils.VoteJournalDirFlag,
},
},
{
@@ -229,6 +232,12 @@ var AppHelpFlagGroups = []flags.FlagGroup{
utils.CatalystFlag,
},
},
+ {
+ Name: "BLS ACCOUNT",
+ Flags: []cli.Flag{
+ utils.DataDirFlag,
+ },
+ },
}
func init() {
diff --git a/cmd/utils/flags.go b/cmd/utils/flags.go
index 7376bf4a6..d4162ec00 100644
--- a/cmd/utils/flags.go
+++ b/cmd/utils/flags.go
@@ -133,7 +133,7 @@ var (
}
PipeCommitFlag = cli.BoolFlag{
Name: "pipecommit",
- Usage: "Enable MPT pipeline commit, it will improve syncing performance. It is an experimental feature(default is false)",
+ Usage: "Enable MPT pipeline commit, it will improve syncing performance. It is an experimental feature(default is false), diffsync will be disable if pipeline commit is enabled",
}
RangeLimitFlag = cli.BoolFlag{
Name: "rangelimit",
@@ -792,6 +792,21 @@ var (
Name: "check-snapshot-with-mpt",
Usage: "Enable checking between snapshot and MPT ",
}
+
+ BLSPasswordFileFlag = cli.StringFlag{
+ Name: "blspassword",
+ Usage: "File path for the BLS password, which contains the password to unlock BLS wallet for managing votes in fast_finality feature",
+ }
+
+ BLSWalletDirFlag = DirectoryFlag{
+ Name: "blswallet",
+ Usage: "Path for the blsWallet dir in fast finality feature (default = inside the datadir)",
+ }
+
+ VoteJournalDirFlag = DirectoryFlag{
+ Name: "vote-journal-path",
+ Usage: "Path for the voteJournal dir in fast finality feature (default = inside the datadir)",
+ }
)
// MakeDataDir retrieves the currently requested data directory, terminating
@@ -1209,6 +1224,8 @@ func SetNodeConfig(ctx *cli.Context, cfg *node.Config) {
setNodeUserIdent(ctx, cfg)
setDataDir(ctx, cfg)
setSmartCard(ctx, cfg)
+ setBLSWalletDir(ctx, cfg)
+ setVoteJournalDir(ctx, cfg)
if ctx.GlobalIsSet(ExternalSignerFlag.Name) {
cfg.ExternalSigner = ctx.GlobalString(ExternalSignerFlag.Name)
@@ -1238,6 +1255,10 @@ func SetNodeConfig(ctx *cli.Context, cfg *node.Config) {
if ctx.GlobalIsSet(InsecureUnlockAllowedFlag.Name) {
cfg.InsecureUnlockAllowed = ctx.GlobalBool(InsecureUnlockAllowedFlag.Name)
}
+
+ if ctx.GlobalIsSet(BLSPasswordFileFlag.Name) {
+ cfg.BLSPasswordFile = ctx.GlobalString(BLSPasswordFileFlag.Name)
+ }
}
func setSmartCard(ctx *cli.Context, cfg *node.Config) {
@@ -1269,6 +1290,22 @@ func setDataDir(ctx *cli.Context, cfg *node.Config) {
}
}
+func setVoteJournalDir(ctx *cli.Context, cfg *node.Config) {
+ if ctx.GlobalIsSet(VoteJournalDirFlag.Name) {
+ cfg.VoteJournalDir = ctx.GlobalString(VoteJournalDirFlag.Name)
+ } else {
+ cfg.VoteJournalDir = "voteJournal"
+ }
+}
+
+func setBLSWalletDir(ctx *cli.Context, cfg *node.Config) {
+ if ctx.GlobalIsSet(BLSWalletDirFlag.Name) {
+ cfg.BLSWalletDir = ctx.GlobalString(BLSWalletDirFlag.Name)
+ } else {
+ cfg.BLSWalletDir = "bls/wallet"
+ }
+}
+
func setGPO(ctx *cli.Context, cfg *gasprice.Config, light bool) {
// If we are running the light client, apply another group
// settings for gas oracle.
diff --git a/consensus/consensus.go b/consensus/consensus.go
index dadbc4841..4a9908691 100644
--- a/consensus/consensus.go
+++ b/consensus/consensus.go
@@ -54,6 +54,10 @@ type ChainHeaderReader interface {
GetHighestVerifiedHeader() *types.Header
}
+type VotePool interface {
+ FetchVoteByBlockHash(blockHash common.Hash) []*types.VoteEnvelope
+}
+
// ChainReader defines a small collection of methods needed to access the local
// blockchain during header and/or uncle verification.
type ChainReader interface {
@@ -145,4 +149,8 @@ type PoSA interface {
EnoughDistance(chain ChainReader, header *types.Header) bool
IsLocalBlock(header *types.Header) bool
AllowLightProcess(chain ChainReader, currentHeader *types.Header) bool
+ GetJustifiedHeader(chain ChainHeaderReader, header *types.Header) *types.Header
+ GetFinalizedHeader(chain ChainHeaderReader, header *types.Header, backward uint64) *types.Header
+ VerifyVote(chain ChainHeaderReader, vote *types.VoteEnvelope) error
+ IsActiveValidatorAt(chain ChainHeaderReader, header *types.Header) bool
}
diff --git a/consensus/ethash/difficulty.go b/consensus/ethash/difficulty.go
index 59c4ac741..66a18059c 100644
--- a/consensus/ethash/difficulty.go
+++ b/consensus/ethash/difficulty.go
@@ -52,8 +52,7 @@ func CalcDifficultyFrontierU256(time uint64, parent *types.Header) *big.Int {
- num = block.number
*/
- pDiff := uint256.NewInt()
- pDiff.SetFromBig(parent.Difficulty) // pDiff: pdiff
+ pDiff, _ := uint256.FromBig(parent.Difficulty) // pDiff: pdiff
adjust := pDiff.Clone()
adjust.Rsh(adjust, difficultyBoundDivisor) // adjust: pDiff / 2048
@@ -96,8 +95,7 @@ func CalcDifficultyHomesteadU256(time uint64, parent *types.Header) *big.Int {
- num = block.number
*/
- pDiff := uint256.NewInt()
- pDiff.SetFromBig(parent.Difficulty) // pDiff: pdiff
+ pDiff, _ := uint256.FromBig(parent.Difficulty) // pDiff: pdiff
adjust := pDiff.Clone()
adjust.Rsh(adjust, difficultyBoundDivisor) // adjust: pDiff / 2048
diff --git a/consensus/parlia/abi.go b/consensus/parlia/abi.go
index a0b49800c..fa308c916 100644
--- a/consensus/parlia/abi.go
+++ b/consensus/parlia/abi.go
@@ -1,676 +1,3564 @@
package parlia
+const validatorSetABIBeforeBoneh = `
+[
+ {
+ "anonymous": false,
+ "inputs": [
+ {
+ "indexed": false,
+ "internalType": "uint256",
+ "name": "amount",
+ "type": "uint256"
+ }
+ ],
+ "name": "batchTransfer",
+ "type": "event"
+ },
+ {
+ "anonymous": false,
+ "inputs": [
+ {
+ "indexed": true,
+ "internalType": "uint256",
+ "name": "amount",
+ "type": "uint256"
+ },
+ {
+ "indexed": false,
+ "internalType": "string",
+ "name": "reason",
+ "type": "string"
+ }
+ ],
+ "name": "batchTransferFailed",
+ "type": "event"
+ },
+ {
+ "anonymous": false,
+ "inputs": [
+ {
+ "indexed": true,
+ "internalType": "uint256",
+ "name": "amount",
+ "type": "uint256"
+ },
+ {
+ "indexed": false,
+ "internalType": "bytes",
+ "name": "reason",
+ "type": "bytes"
+ }
+ ],
+ "name": "batchTransferLowerFailed",
+ "type": "event"
+ },
+ {
+ "anonymous": false,
+ "inputs": [
+ {
+ "indexed": true,
+ "internalType": "address",
+ "name": "validator",
+ "type": "address"
+ },
+ {
+ "indexed": false,
+ "internalType": "uint256",
+ "name": "amount",
+ "type": "uint256"
+ }
+ ],
+ "name": "deprecatedDeposit",
+ "type": "event"
+ },
+ {
+ "anonymous": false,
+ "inputs": [
+ {
+ "indexed": true,
+ "internalType": "address payable",
+ "name": "validator",
+ "type": "address"
+ },
+ {
+ "indexed": false,
+ "internalType": "uint256",
+ "name": "amount",
+ "type": "uint256"
+ }
+ ],
+ "name": "directTransfer",
+ "type": "event"
+ },
+ {
+ "anonymous": false,
+ "inputs": [
+ {
+ "indexed": true,
+ "internalType": "address payable",
+ "name": "validator",
+ "type": "address"
+ },
+ {
+ "indexed": false,
+ "internalType": "uint256",
+ "name": "amount",
+ "type": "uint256"
+ }
+ ],
+ "name": "directTransferFail",
+ "type": "event"
+ },
+ {
+ "anonymous": false,
+ "inputs": [
+ {
+ "indexed": false,
+ "internalType": "string",
+ "name": "message",
+ "type": "string"
+ }
+ ],
+ "name": "failReasonWithStr",
+ "type": "event"
+ },
+ {
+ "anonymous": false,
+ "inputs": [
+ {
+ "indexed": false,
+ "internalType": "uint256",
+ "name": "amount",
+ "type": "uint256"
+ }
+ ],
+ "name": "feeBurned",
+ "type": "event"
+ },
+ {
+ "anonymous": false,
+ "inputs": [
+ {
+ "indexed": false,
+ "internalType": "string",
+ "name": "key",
+ "type": "string"
+ },
+ {
+ "indexed": false,
+ "internalType": "bytes",
+ "name": "value",
+ "type": "bytes"
+ }
+ ],
+ "name": "paramChange",
+ "type": "event"
+ },
+ {
+ "anonymous": false,
+ "inputs": [
+ {
+ "indexed": false,
+ "internalType": "uint256",
+ "name": "amount",
+ "type": "uint256"
+ }
+ ],
+ "name": "systemTransfer",
+ "type": "event"
+ },
+ {
+ "anonymous": false,
+ "inputs": [
+ {
+ "indexed": false,
+ "internalType": "uint8",
+ "name": "channelId",
+ "type": "uint8"
+ },
+ {
+ "indexed": false,
+ "internalType": "bytes",
+ "name": "msgBytes",
+ "type": "bytes"
+ }
+ ],
+ "name": "unexpectedPackage",
+ "type": "event"
+ },
+ {
+ "anonymous": false,
+ "inputs": [
+ {
+ "indexed": true,
+ "internalType": "address",
+ "name": "validator",
+ "type": "address"
+ },
+ {
+ "indexed": false,
+ "internalType": "uint256",
+ "name": "amount",
+ "type": "uint256"
+ }
+ ],
+ "name": "validatorDeposit",
+ "type": "event"
+ },
+ {
+ "anonymous": false,
+ "inputs": [
+ {
+ "indexed": true,
+ "internalType": "address",
+ "name": "validator",
+ "type": "address"
+ }
+ ],
+ "name": "validatorEmptyJailed",
+ "type": "event"
+ },
+ {
+ "anonymous": false,
+ "inputs": [
+ {
+ "indexed": true,
+ "internalType": "address",
+ "name": "validator",
+ "type": "address"
+ }
+ ],
+ "name": "validatorEnterMaintenance",
+ "type": "event"
+ },
+ {
+ "anonymous": false,
+ "inputs": [
+ {
+ "indexed": true,
+ "internalType": "address",
+ "name": "validator",
+ "type": "address"
+ }
+ ],
+ "name": "validatorExitMaintenance",
+ "type": "event"
+ },
+ {
+ "anonymous": false,
+ "inputs": [
+ {
+ "indexed": true,
+ "internalType": "address",
+ "name": "validator",
+ "type": "address"
+ },
+ {
+ "indexed": false,
+ "internalType": "uint256",
+ "name": "amount",
+ "type": "uint256"
+ }
+ ],
+ "name": "validatorFelony",
+ "type": "event"
+ },
+ {
+ "anonymous": false,
+ "inputs": [
+ {
+ "indexed": true,
+ "internalType": "address",
+ "name": "validator",
+ "type": "address"
+ }
+ ],
+ "name": "validatorJailed",
+ "type": "event"
+ },
+ {
+ "anonymous": false,
+ "inputs": [
+ {
+ "indexed": true,
+ "internalType": "address",
+ "name": "validator",
+ "type": "address"
+ },
+ {
+ "indexed": false,
+ "internalType": "uint256",
+ "name": "amount",
+ "type": "uint256"
+ }
+ ],
+ "name": "validatorMisdemeanor",
+ "type": "event"
+ },
+ {
+ "anonymous": false,
+ "inputs": [],
+ "name": "validatorSetUpdated",
+ "type": "event"
+ },
+ {
+ "inputs": [],
+ "name": "BIND_CHANNELID",
+ "outputs": [
+ {
+ "internalType": "uint8",
+ "name": "",
+ "type": "uint8"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "inputs": [],
+ "name": "BURN_ADDRESS",
+ "outputs": [
+ {
+ "internalType": "address",
+ "name": "",
+ "type": "address"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "inputs": [],
+ "name": "BURN_RATIO_SCALE",
+ "outputs": [
+ {
+ "internalType": "uint256",
+ "name": "",
+ "type": "uint256"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "inputs": [],
+ "name": "CODE_OK",
+ "outputs": [
+ {
+ "internalType": "uint32",
+ "name": "",
+ "type": "uint32"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "inputs": [],
+ "name": "CROSS_CHAIN_CONTRACT_ADDR",
+ "outputs": [
+ {
+ "internalType": "address",
+ "name": "",
+ "type": "address"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "inputs": [],
+ "name": "DUSTY_INCOMING",
+ "outputs": [
+ {
+ "internalType": "uint256",
+ "name": "",
+ "type": "uint256"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "inputs": [],
+ "name": "EPOCH",
+ "outputs": [
+ {
+ "internalType": "uint256",
+ "name": "",
+ "type": "uint256"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "inputs": [],
+ "name": "ERROR_FAIL_CHECK_VALIDATORS",
+ "outputs": [
+ {
+ "internalType": "uint32",
+ "name": "",
+ "type": "uint32"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "inputs": [],
+ "name": "ERROR_FAIL_DECODE",
+ "outputs": [
+ {
+ "internalType": "uint32",
+ "name": "",
+ "type": "uint32"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "inputs": [],
+ "name": "ERROR_LEN_OF_VAL_MISMATCH",
+ "outputs": [
+ {
+ "internalType": "uint32",
+ "name": "",
+ "type": "uint32"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "inputs": [],
+ "name": "ERROR_RELAYFEE_TOO_LARGE",
+ "outputs": [
+ {
+ "internalType": "uint32",
+ "name": "",
+ "type": "uint32"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "inputs": [],
+ "name": "ERROR_UNKNOWN_PACKAGE_TYPE",
+ "outputs": [
+ {
+ "internalType": "uint32",
+ "name": "",
+ "type": "uint32"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "inputs": [],
+ "name": "EXPIRE_TIME_SECOND_GAP",
+ "outputs": [
+ {
+ "internalType": "uint256",
+ "name": "",
+ "type": "uint256"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "inputs": [],
+ "name": "GOV_CHANNELID",
+ "outputs": [
+ {
+ "internalType": "uint8",
+ "name": "",
+ "type": "uint8"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "inputs": [],
+ "name": "GOV_HUB_ADDR",
+ "outputs": [
+ {
+ "internalType": "address",
+ "name": "",
+ "type": "address"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "inputs": [],
+ "name": "INCENTIVIZE_ADDR",
+ "outputs": [
+ {
+ "internalType": "address",
+ "name": "",
+ "type": "address"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "inputs": [],
+ "name": "INIT_BURN_RATIO",
+ "outputs": [
+ {
+ "internalType": "uint256",
+ "name": "",
+ "type": "uint256"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "inputs": [],
+ "name": "INIT_MAINTAIN_SLASH_SCALE",
+ "outputs": [
+ {
+ "internalType": "uint256",
+ "name": "",
+ "type": "uint256"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "inputs": [],
+ "name": "INIT_MAX_NUM_OF_MAINTAINING",
+ "outputs": [
+ {
+ "internalType": "uint256",
+ "name": "",
+ "type": "uint256"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "inputs": [],
+ "name": "INIT_NUM_OF_CABINETS",
+ "outputs": [
+ {
+ "internalType": "uint256",
+ "name": "",
+ "type": "uint256"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "inputs": [],
+ "name": "INIT_VALIDATORSET_BYTES",
+ "outputs": [
+ {
+ "internalType": "bytes",
+ "name": "",
+ "type": "bytes"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "inputs": [],
+ "name": "JAIL_MESSAGE_TYPE",
+ "outputs": [
+ {
+ "internalType": "uint8",
+ "name": "",
+ "type": "uint8"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "inputs": [],
+ "name": "LIGHT_CLIENT_ADDR",
+ "outputs": [
+ {
+ "internalType": "address",
+ "name": "",
+ "type": "address"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "inputs": [],
+ "name": "MAX_NUM_OF_VALIDATORS",
+ "outputs": [
+ {
+ "internalType": "uint256",
+ "name": "",
+ "type": "uint256"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "inputs": [],
+ "name": "PRECISION",
+ "outputs": [
+ {
+ "internalType": "uint256",
+ "name": "",
+ "type": "uint256"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "inputs": [],
+ "name": "RELAYERHUB_CONTRACT_ADDR",
+ "outputs": [
+ {
+ "internalType": "address",
+ "name": "",
+ "type": "address"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "inputs": [],
+ "name": "SLASH_CHANNELID",
+ "outputs": [
+ {
+ "internalType": "uint8",
+ "name": "",
+ "type": "uint8"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "inputs": [],
+ "name": "SLASH_CONTRACT_ADDR",
+ "outputs": [
+ {
+ "internalType": "address",
+ "name": "",
+ "type": "address"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "inputs": [],
+ "name": "STAKING_CHANNELID",
+ "outputs": [
+ {
+ "internalType": "uint8",
+ "name": "",
+ "type": "uint8"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "inputs": [],
+ "name": "SYSTEM_ADDRESS",
+ "outputs": [
+ {
+ "internalType": "address",
+ "name": "",
+ "type": "address"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "inputs": [],
+ "name": "SYSTEM_REWARD_ADDR",
+ "outputs": [
+ {
+ "internalType": "address",
+ "name": "",
+ "type": "address"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "inputs": [],
+ "name": "TOKEN_HUB_ADDR",
+ "outputs": [
+ {
+ "internalType": "address",
+ "name": "",
+ "type": "address"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "inputs": [],
+ "name": "TOKEN_MANAGER_ADDR",
+ "outputs": [
+ {
+ "internalType": "address",
+ "name": "",
+ "type": "address"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "inputs": [],
+ "name": "TRANSFER_IN_CHANNELID",
+ "outputs": [
+ {
+ "internalType": "uint8",
+ "name": "",
+ "type": "uint8"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "inputs": [],
+ "name": "TRANSFER_OUT_CHANNELID",
+ "outputs": [
+ {
+ "internalType": "uint8",
+ "name": "",
+ "type": "uint8"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "inputs": [],
+ "name": "VALIDATORS_UPDATE_MESSAGE_TYPE",
+ "outputs": [
+ {
+ "internalType": "uint8",
+ "name": "",
+ "type": "uint8"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "inputs": [],
+ "name": "VALIDATOR_CONTRACT_ADDR",
+ "outputs": [
+ {
+ "internalType": "address",
+ "name": "",
+ "type": "address"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "inputs": [],
+ "name": "alreadyInit",
+ "outputs": [
+ {
+ "internalType": "bool",
+ "name": "",
+ "type": "bool"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "inputs": [],
+ "name": "bscChainID",
+ "outputs": [
+ {
+ "internalType": "uint16",
+ "name": "",
+ "type": "uint16"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "inputs": [],
+ "name": "burnRatio",
+ "outputs": [
+ {
+ "internalType": "uint256",
+ "name": "",
+ "type": "uint256"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "inputs": [],
+ "name": "burnRatioInitialized",
+ "outputs": [
+ {
+ "internalType": "bool",
+ "name": "",
+ "type": "bool"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "inputs": [
+ {
+ "internalType": "uint256",
+ "name": "",
+ "type": "uint256"
+ }
+ ],
+ "name": "currentValidatorSet",
+ "outputs": [
+ {
+ "internalType": "address",
+ "name": "consensusAddress",
+ "type": "address"
+ },
+ {
+ "internalType": "address payable",
+ "name": "feeAddress",
+ "type": "address"
+ },
+ {
+ "internalType": "address",
+ "name": "BBCFeeAddress",
+ "type": "address"
+ },
+ {
+ "internalType": "uint64",
+ "name": "votingPower",
+ "type": "uint64"
+ },
+ {
+ "internalType": "bool",
+ "name": "jailed",
+ "type": "bool"
+ },
+ {
+ "internalType": "uint256",
+ "name": "incoming",
+ "type": "uint256"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "inputs": [
+ {
+ "internalType": "address",
+ "name": "",
+ "type": "address"
+ }
+ ],
+ "name": "currentValidatorSetMap",
+ "outputs": [
+ {
+ "internalType": "uint256",
+ "name": "",
+ "type": "uint256"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "inputs": [],
+ "name": "expireTimeSecondGap",
+ "outputs": [
+ {
+ "internalType": "uint256",
+ "name": "",
+ "type": "uint256"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "inputs": [],
+ "name": "maintainSlashScale",
+ "outputs": [
+ {
+ "internalType": "uint256",
+ "name": "",
+ "type": "uint256"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "inputs": [],
+ "name": "maxNumOfCandidates",
+ "outputs": [
+ {
+ "internalType": "uint256",
+ "name": "",
+ "type": "uint256"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "inputs": [],
+ "name": "maxNumOfMaintaining",
+ "outputs": [
+ {
+ "internalType": "uint256",
+ "name": "",
+ "type": "uint256"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "inputs": [],
+ "name": "maxNumOfWorkingCandidates",
+ "outputs": [
+ {
+ "internalType": "uint256",
+ "name": "",
+ "type": "uint256"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "inputs": [],
+ "name": "numOfCabinets",
+ "outputs": [
+ {
+ "internalType": "uint256",
+ "name": "",
+ "type": "uint256"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "inputs": [],
+ "name": "numOfJailed",
+ "outputs": [
+ {
+ "internalType": "uint256",
+ "name": "",
+ "type": "uint256"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "inputs": [],
+ "name": "numOfMaintaining",
+ "outputs": [
+ {
+ "internalType": "uint256",
+ "name": "",
+ "type": "uint256"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "inputs": [],
+ "name": "totalInComing",
+ "outputs": [
+ {
+ "internalType": "uint256",
+ "name": "",
+ "type": "uint256"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "inputs": [
+ {
+ "internalType": "address",
+ "name": "valAddr",
+ "type": "address"
+ },
+ {
+ "internalType": "address",
+ "name": "slashAddr",
+ "type": "address"
+ },
+ {
+ "internalType": "address",
+ "name": "rewardAddr",
+ "type": "address"
+ },
+ {
+ "internalType": "address",
+ "name": "lightAddr",
+ "type": "address"
+ },
+ {
+ "internalType": "address",
+ "name": "tokenHubAddr",
+ "type": "address"
+ },
+ {
+ "internalType": "address",
+ "name": "incentivizeAddr",
+ "type": "address"
+ },
+ {
+ "internalType": "address",
+ "name": "relayerHubAddr",
+ "type": "address"
+ },
+ {
+ "internalType": "address",
+ "name": "govHub",
+ "type": "address"
+ },
+ {
+ "internalType": "address",
+ "name": "tokenManagerAddr",
+ "type": "address"
+ },
+ {
+ "internalType": "address",
+ "name": "crossChain",
+ "type": "address"
+ }
+ ],
+ "name": "updateContractAddr",
+ "outputs": [],
+ "stateMutability": "nonpayable",
+ "type": "function"
+ },
+ {
+ "inputs": [
+ {
+ "internalType": "uint256",
+ "name": "",
+ "type": "uint256"
+ }
+ ],
+ "name": "validatorExtraSet",
+ "outputs": [
+ {
+ "internalType": "uint256",
+ "name": "enterMaintenanceHeight",
+ "type": "uint256"
+ },
+ {
+ "internalType": "bool",
+ "name": "isMaintaining",
+ "type": "bool"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "inputs": [],
+ "name": "init",
+ "outputs": [],
+ "stateMutability": "nonpayable",
+ "type": "function"
+ },
+ {
+ "inputs": [
+ {
+ "internalType": "uint8",
+ "name": "",
+ "type": "uint8"
+ },
+ {
+ "internalType": "bytes",
+ "name": "msgBytes",
+ "type": "bytes"
+ }
+ ],
+ "name": "handleSynPackage",
+ "outputs": [
+ {
+ "internalType": "bytes",
+ "name": "responsePayload",
+ "type": "bytes"
+ }
+ ],
+ "stateMutability": "nonpayable",
+ "type": "function"
+ },
+ {
+ "inputs": [
+ {
+ "internalType": "uint8",
+ "name": "channelId",
+ "type": "uint8"
+ },
+ {
+ "internalType": "bytes",
+ "name": "msgBytes",
+ "type": "bytes"
+ }
+ ],
+ "name": "handleAckPackage",
+ "outputs": [],
+ "stateMutability": "nonpayable",
+ "type": "function"
+ },
+ {
+ "inputs": [
+ {
+ "internalType": "uint8",
+ "name": "channelId",
+ "type": "uint8"
+ },
+ {
+ "internalType": "bytes",
+ "name": "msgBytes",
+ "type": "bytes"
+ }
+ ],
+ "name": "handleFailAckPackage",
+ "outputs": [],
+ "stateMutability": "nonpayable",
+ "type": "function"
+ },
+ {
+ "inputs": [
+ {
+ "internalType": "address",
+ "name": "valAddr",
+ "type": "address"
+ }
+ ],
+ "name": "deposit",
+ "outputs": [],
+ "stateMutability": "payable",
+ "type": "function"
+ },
+ {
+ "inputs": [],
+ "name": "getMiningValidators",
+ "outputs": [
+ {
+ "internalType": "address[]",
+ "name": "",
+ "type": "address[]"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "inputs": [],
+ "name": "getValidators",
+ "outputs": [
+ {
+ "internalType": "address[]",
+ "name": "",
+ "type": "address[]"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "inputs": [
+ {
+ "internalType": "uint256",
+ "name": "index",
+ "type": "uint256"
+ }
+ ],
+ "name": "isWorkingValidator",
+ "outputs": [
+ {
+ "internalType": "bool",
+ "name": "",
+ "type": "bool"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "inputs": [
+ {
+ "internalType": "address",
+ "name": "validator",
+ "type": "address"
+ }
+ ],
+ "name": "getIncoming",
+ "outputs": [
+ {
+ "internalType": "uint256",
+ "name": "",
+ "type": "uint256"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "inputs": [
+ {
+ "internalType": "address",
+ "name": "validator",
+ "type": "address"
+ }
+ ],
+ "name": "isCurrentValidator",
+ "outputs": [
+ {
+ "internalType": "bool",
+ "name": "",
+ "type": "bool"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "inputs": [
+ {
+ "internalType": "address",
+ "name": "validator",
+ "type": "address"
+ }
+ ],
+ "name": "misdemeanor",
+ "outputs": [],
+ "stateMutability": "nonpayable",
+ "type": "function"
+ },
+ {
+ "inputs": [
+ {
+ "internalType": "address",
+ "name": "validator",
+ "type": "address"
+ }
+ ],
+ "name": "felony",
+ "outputs": [],
+ "stateMutability": "nonpayable",
+ "type": "function"
+ },
+ {
+ "inputs": [
+ {
+ "internalType": "address",
+ "name": "_validator",
+ "type": "address"
+ }
+ ],
+ "name": "getCurrentValidatorIndex",
+ "outputs": [
+ {
+ "internalType": "uint256",
+ "name": "",
+ "type": "uint256"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "inputs": [
+ {
+ "internalType": "uint256",
+ "name": "index",
+ "type": "uint256"
+ }
+ ],
+ "name": "canEnterMaintenance",
+ "outputs": [
+ {
+ "internalType": "bool",
+ "name": "",
+ "type": "bool"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "inputs": [],
+ "name": "enterMaintenance",
+ "outputs": [],
+ "stateMutability": "nonpayable",
+ "type": "function"
+ },
+ {
+ "inputs": [],
+ "name": "exitMaintenance",
+ "outputs": [],
+ "stateMutability": "nonpayable",
+ "type": "function"
+ },
+ {
+ "inputs": [
+ {
+ "internalType": "string",
+ "name": "key",
+ "type": "string"
+ },
+ {
+ "internalType": "bytes",
+ "name": "value",
+ "type": "bytes"
+ }
+ ],
+ "name": "updateParam",
+ "outputs": [],
+ "stateMutability": "nonpayable",
+ "type": "function"
+ },
+ {
+ "inputs": [
+ {
+ "internalType": "address",
+ "name": "validator",
+ "type": "address"
+ }
+ ],
+ "name": "isValidatorExist",
+ "outputs": [
+ {
+ "internalType": "bool",
+ "name": "",
+ "type": "bool"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "inputs": [],
+ "name": "getMaintainingValidators",
+ "outputs": [
+ {
+ "internalType": "address[]",
+ "name": "maintainingValidators",
+ "type": "address[]"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ }
+]
+`
+
const validatorSetABI = `
[
- {
- "anonymous": false,
- "inputs": [
- {
- "indexed": true,
- "internalType": "uint256",
- "name": "amount",
- "type": "uint256"
- }
- ],
- "name": "batchTransfer",
- "type": "event"
- },
- {
- "anonymous": false,
- "inputs": [
- {
- "indexed": true,
- "internalType": "address",
- "name": "validator",
- "type": "address"
- },
- {
- "indexed": true,
- "internalType": "uint256",
- "name": "amount",
- "type": "uint256"
- }
- ],
- "name": "deprecatedDeposit",
- "type": "event"
- },
- {
- "anonymous": false,
- "inputs": [
- {
- "indexed": true,
- "internalType": "address payable",
- "name": "validator",
- "type": "address"
- },
- {
- "indexed": true,
- "internalType": "uint256",
- "name": "amount",
- "type": "uint256"
- }
- ],
- "name": "directTransfer",
- "type": "event"
- },
- {
- "anonymous": false,
- "inputs": [
- {
- "indexed": true,
- "internalType": "uint256",
- "name": "amount",
- "type": "uint256"
- }
- ],
- "name": "systemTransfer",
- "type": "event"
- },
- {
- "anonymous": false,
- "inputs": [
- {
- "indexed": true,
- "internalType": "address",
- "name": "validator",
- "type": "address"
- },
- {
- "indexed": true,
- "internalType": "uint256",
- "name": "amount",
- "type": "uint256"
- }
- ],
- "name": "validatorDeposit",
- "type": "event"
- },
- {
- "anonymous": false,
- "inputs": [
- {
- "indexed": true,
- "internalType": "address",
- "name": "validator",
- "type": "address"
- },
- {
- "indexed": true,
- "internalType": "uint256",
- "name": "amount",
- "type": "uint256"
- }
- ],
- "name": "validatorFelony",
- "type": "event"
- },
- {
- "anonymous": false,
- "inputs": [
- {
- "indexed": true,
- "internalType": "address",
- "name": "validator",
- "type": "address"
- }
- ],
- "name": "validatorJailed",
- "type": "event"
- },
- {
- "anonymous": false,
- "inputs": [
- {
- "indexed": true,
- "internalType": "address",
- "name": "validator",
- "type": "address"
- },
- {
- "indexed": true,
- "internalType": "uint256",
- "name": "amount",
- "type": "uint256"
- }
- ],
- "name": "validatorMisdemeanor",
- "type": "event"
- },
- {
- "anonymous": false,
- "inputs": [],
- "name": "validatorSetUpdated",
- "type": "event"
- },
- {
- "inputs": [],
- "name": "CHANNEL_ID",
- "outputs": [
- {
- "internalType": "uint8",
- "name": "",
- "type": "uint8"
- }
- ],
- "stateMutability": "view",
- "type": "function"
- },
- {
- "inputs": [],
- "name": "DUSTY_INCOMING",
- "outputs": [
- {
- "internalType": "uint256",
- "name": "",
- "type": "uint256"
- }
- ],
- "stateMutability": "view",
- "type": "function"
- },
- {
- "inputs": [],
- "name": "EXTRA_FEE",
- "outputs": [
- {
- "internalType": "uint256",
- "name": "",
- "type": "uint256"
- }
- ],
- "stateMutability": "view",
- "type": "function"
- },
- {
- "inputs": [],
- "name": "JAIL_MESSAGE_TYPE",
- "outputs": [
- {
- "internalType": "uint8",
- "name": "",
- "type": "uint8"
- }
- ],
- "stateMutability": "view",
- "type": "function"
- },
- {
- "inputs": [],
- "name": "RELAYER_REWARD",
- "outputs": [
- {
- "internalType": "uint256",
- "name": "",
- "type": "uint256"
- }
- ],
- "stateMutability": "view",
- "type": "function"
- },
- {
- "inputs": [],
- "name": "SYSTEM_ADDRESS",
- "outputs": [
- {
- "internalType": "address",
- "name": "",
- "type": "address"
- }
- ],
- "stateMutability": "view",
- "type": "function"
- },
- {
- "inputs": [],
- "name": "VALIDATORS_UPDATE_MESSAGE_TYPE",
- "outputs": [
- {
- "internalType": "uint8",
- "name": "",
- "type": "uint8"
- }
- ],
- "stateMutability": "view",
- "type": "function"
- },
- {
- "inputs": [],
- "name": "alreadyInit",
- "outputs": [
- {
- "internalType": "bool",
- "name": "",
- "type": "bool"
- }
- ],
- "stateMutability": "view",
- "type": "function"
- },
- {
- "inputs": [
- {
- "internalType": "uint256",
- "name": "",
- "type": "uint256"
- }
- ],
- "name": "currentValidatorSet",
- "outputs": [
- {
- "internalType": "address",
- "name": "consensusAddress",
- "type": "address"
- },
- {
- "internalType": "address payable",
- "name": "feeAddress",
- "type": "address"
- },
- {
- "internalType": "address",
- "name": "BBCFeeAddress",
- "type": "address"
- },
- {
- "internalType": "uint64",
- "name": "votingPower",
- "type": "uint64"
- },
- {
- "internalType": "bool",
- "name": "jailed",
- "type": "bool"
- },
- {
- "internalType": "uint256",
- "name": "incoming",
- "type": "uint256"
- }
- ],
- "stateMutability": "view",
- "type": "function"
- },
- {
- "inputs": [],
- "name": "fromChainId",
- "outputs": [
- {
- "internalType": "uint16",
- "name": "",
- "type": "uint16"
- }
- ],
- "stateMutability": "view",
- "type": "function"
- },
- {
- "inputs": [],
- "name": "initLightClientAddr",
- "outputs": [
- {
- "internalType": "address",
- "name": "",
- "type": "address"
- }
- ],
- "stateMutability": "view",
- "type": "function"
- },
- {
- "inputs": [],
- "name": "initSlashContract",
- "outputs": [
- {
- "internalType": "address",
- "name": "",
- "type": "address"
- }
- ],
- "stateMutability": "view",
- "type": "function"
- },
- {
- "inputs": [],
- "name": "initSystemRewardAddr",
- "outputs": [
- {
- "internalType": "address payable",
- "name": "",
- "type": "address"
- }
- ],
- "stateMutability": "view",
- "type": "function"
- },
- {
- "inputs": [],
- "name": "initTokenHubAddr",
- "outputs": [
- {
- "internalType": "address",
- "name": "",
- "type": "address"
- }
- ],
- "stateMutability": "view",
- "type": "function"
- },
- {
- "inputs": [],
- "name": "initValidatorSetBytes",
- "outputs": [
- {
- "internalType": "bytes",
- "name": "",
- "type": "bytes"
- }
- ],
- "stateMutability": "view",
- "type": "function"
- },
- {
- "inputs": [],
- "name": "keyPrefix",
- "outputs": [
- {
- "internalType": "bytes",
- "name": "",
- "type": "bytes"
- }
- ],
- "stateMutability": "view",
- "type": "function"
- },
- {
- "inputs": [],
- "name": "previousDepositHeight",
- "outputs": [
- {
- "internalType": "uint64",
- "name": "",
- "type": "uint64"
- }
- ],
- "stateMutability": "view",
- "type": "function"
- },
- {
- "inputs": [],
- "name": "sequence",
- "outputs": [
- {
- "internalType": "uint64",
- "name": "",
- "type": "uint64"
- }
- ],
- "stateMutability": "view",
- "type": "function"
- },
- {
- "inputs": [],
- "name": "toChainId",
- "outputs": [
- {
- "internalType": "uint16",
- "name": "",
- "type": "uint16"
- }
- ],
- "stateMutability": "view",
- "type": "function"
- },
- {
- "inputs": [],
- "name": "totalInComing",
- "outputs": [
- {
- "internalType": "uint256",
- "name": "",
- "type": "uint256"
- }
- ],
- "stateMutability": "view",
- "type": "function"
- },
- {
- "inputs": [],
- "name": "init",
- "outputs": [],
- "stateMutability": "nonpayable",
- "type": "function"
- },
- {
- "inputs": [
- {
- "internalType": "address",
- "name": "valAddr",
- "type": "address"
- }
- ],
- "name": "deposit",
- "outputs": [],
- "stateMutability": "payable",
- "type": "function"
- },
- {
- "inputs": [
- {
- "internalType": "bytes",
- "name": "msgBytes",
- "type": "bytes"
- },
- {
- "internalType": "bytes",
- "name": "proof",
- "type": "bytes"
- },
- {
- "internalType": "uint64",
- "name": "height",
- "type": "uint64"
- },
- {
- "internalType": "uint64",
- "name": "packageSequence",
- "type": "uint64"
- }
- ],
- "name": "update",
- "outputs": [],
- "stateMutability": "nonpayable",
- "type": "function"
- },
- {
- "inputs": [],
- "name": "getValidators",
- "outputs": [
- {
- "internalType": "address[]",
- "name": "",
- "type": "address[]"
- }
- ],
- "stateMutability": "view",
- "type": "function"
- },
- {
- "inputs": [
- {
- "internalType": "address",
- "name": "validator",
- "type": "address"
- }
- ],
- "name": "getIncoming",
- "outputs": [
- {
- "internalType": "uint256",
- "name": "",
- "type": "uint256"
- }
- ],
- "stateMutability": "view",
- "type": "function"
- },
- {
- "inputs": [
- {
- "internalType": "address",
- "name": "validator",
- "type": "address"
- }
- ],
- "name": "misdemeanor",
- "outputs": [],
- "stateMutability": "nonpayable",
- "type": "function"
- },
- {
- "inputs": [
- {
- "internalType": "address",
- "name": "validator",
- "type": "address"
- }
- ],
- "name": "felony",
- "outputs": [],
- "stateMutability": "nonpayable",
- "type": "function"
- }
- ]
+ {
+ "anonymous": false,
+ "inputs": [
+ {
+ "indexed": false,
+ "internalType": "uint256",
+ "name": "amount",
+ "type": "uint256"
+ }
+ ],
+ "name": "batchTransfer",
+ "type": "event"
+ },
+ {
+ "anonymous": false,
+ "inputs": [
+ {
+ "indexed": true,
+ "internalType": "uint256",
+ "name": "amount",
+ "type": "uint256"
+ },
+ {
+ "indexed": false,
+ "internalType": "string",
+ "name": "reason",
+ "type": "string"
+ }
+ ],
+ "name": "batchTransferFailed",
+ "type": "event"
+ },
+ {
+ "anonymous": false,
+ "inputs": [
+ {
+ "indexed": true,
+ "internalType": "uint256",
+ "name": "amount",
+ "type": "uint256"
+ },
+ {
+ "indexed": false,
+ "internalType": "bytes",
+ "name": "reason",
+ "type": "bytes"
+ }
+ ],
+ "name": "batchTransferLowerFailed",
+ "type": "event"
+ },
+ {
+ "anonymous": false,
+ "inputs": [
+ {
+ "indexed": true,
+ "internalType": "address",
+ "name": "validator",
+ "type": "address"
+ },
+ {
+ "indexed": false,
+ "internalType": "uint256",
+ "name": "amount",
+ "type": "uint256"
+ }
+ ],
+ "name": "deprecatedDeposit",
+ "type": "event"
+ },
+ {
+ "anonymous": false,
+ "inputs": [
+ {
+ "indexed": true,
+ "internalType": "address",
+ "name": "validator",
+ "type": "address"
+ },
+ {
+ "indexed": false,
+ "internalType": "uint256",
+ "name": "amount",
+ "type": "uint256"
+ }
+ ],
+ "name": "deprecatedFinalityRewardDeposit",
+ "type": "event"
+ },
+ {
+ "anonymous": false,
+ "inputs": [
+ {
+ "indexed": true,
+ "internalType": "address payable",
+ "name": "validator",
+ "type": "address"
+ },
+ {
+ "indexed": false,
+ "internalType": "uint256",
+ "name": "amount",
+ "type": "uint256"
+ }
+ ],
+ "name": "directTransfer",
+ "type": "event"
+ },
+ {
+ "anonymous": false,
+ "inputs": [
+ {
+ "indexed": true,
+ "internalType": "address payable",
+ "name": "validator",
+ "type": "address"
+ },
+ {
+ "indexed": false,
+ "internalType": "uint256",
+ "name": "amount",
+ "type": "uint256"
+ }
+ ],
+ "name": "directTransferFail",
+ "type": "event"
+ },
+ {
+ "anonymous": false,
+ "inputs": [
+ {
+ "indexed": false,
+ "internalType": "string",
+ "name": "message",
+ "type": "string"
+ }
+ ],
+ "name": "failReasonWithStr",
+ "type": "event"
+ },
+ {
+ "anonymous": false,
+ "inputs": [
+ {
+ "indexed": false,
+ "internalType": "uint256",
+ "name": "amount",
+ "type": "uint256"
+ }
+ ],
+ "name": "feeBurned",
+ "type": "event"
+ },
+ {
+ "anonymous": false,
+ "inputs": [
+ {
+ "indexed": true,
+ "internalType": "address",
+ "name": "validator",
+ "type": "address"
+ },
+ {
+ "indexed": false,
+ "internalType": "uint256",
+ "name": "amount",
+ "type": "uint256"
+ }
+ ],
+ "name": "finalityRewardDeposit",
+ "type": "event"
+ },
+ {
+ "anonymous": false,
+ "inputs": [
+ {
+ "indexed": false,
+ "internalType": "string",
+ "name": "key",
+ "type": "string"
+ },
+ {
+ "indexed": false,
+ "internalType": "bytes",
+ "name": "value",
+ "type": "bytes"
+ }
+ ],
+ "name": "paramChange",
+ "type": "event"
+ },
+ {
+ "anonymous": false,
+ "inputs": [
+ {
+ "indexed": false,
+ "internalType": "uint256",
+ "name": "amount",
+ "type": "uint256"
+ }
+ ],
+ "name": "systemTransfer",
+ "type": "event"
+ },
+ {
+ "anonymous": false,
+ "inputs": [
+ {
+ "indexed": false,
+ "internalType": "uint8",
+ "name": "channelId",
+ "type": "uint8"
+ },
+ {
+ "indexed": false,
+ "internalType": "bytes",
+ "name": "msgBytes",
+ "type": "bytes"
+ }
+ ],
+ "name": "unexpectedPackage",
+ "type": "event"
+ },
+ {
+ "anonymous": false,
+ "inputs": [
+ {
+ "indexed": true,
+ "internalType": "address",
+ "name": "validator",
+ "type": "address"
+ },
+ {
+ "indexed": false,
+ "internalType": "uint256",
+ "name": "amount",
+ "type": "uint256"
+ }
+ ],
+ "name": "validatorDeposit",
+ "type": "event"
+ },
+ {
+ "anonymous": false,
+ "inputs": [
+ {
+ "indexed": true,
+ "internalType": "address",
+ "name": "validator",
+ "type": "address"
+ }
+ ],
+ "name": "validatorEmptyJailed",
+ "type": "event"
+ },
+ {
+ "anonymous": false,
+ "inputs": [
+ {
+ "indexed": true,
+ "internalType": "address",
+ "name": "validator",
+ "type": "address"
+ }
+ ],
+ "name": "validatorEnterMaintenance",
+ "type": "event"
+ },
+ {
+ "anonymous": false,
+ "inputs": [
+ {
+ "indexed": true,
+ "internalType": "address",
+ "name": "validator",
+ "type": "address"
+ }
+ ],
+ "name": "validatorExitMaintenance",
+ "type": "event"
+ },
+ {
+ "anonymous": false,
+ "inputs": [
+ {
+ "indexed": true,
+ "internalType": "address",
+ "name": "validator",
+ "type": "address"
+ },
+ {
+ "indexed": false,
+ "internalType": "uint256",
+ "name": "amount",
+ "type": "uint256"
+ }
+ ],
+ "name": "validatorFelony",
+ "type": "event"
+ },
+ {
+ "anonymous": false,
+ "inputs": [
+ {
+ "indexed": true,
+ "internalType": "address",
+ "name": "validator",
+ "type": "address"
+ }
+ ],
+ "name": "validatorJailed",
+ "type": "event"
+ },
+ {
+ "anonymous": false,
+ "inputs": [
+ {
+ "indexed": true,
+ "internalType": "address",
+ "name": "validator",
+ "type": "address"
+ },
+ {
+ "indexed": false,
+ "internalType": "uint256",
+ "name": "amount",
+ "type": "uint256"
+ }
+ ],
+ "name": "validatorMisdemeanor",
+ "type": "event"
+ },
+ {
+ "anonymous": false,
+ "inputs": [],
+ "name": "validatorSetUpdated",
+ "type": "event"
+ },
+ {
+ "inputs": [],
+ "name": "BIND_CHANNELID",
+ "outputs": [
+ {
+ "internalType": "uint8",
+ "name": "",
+ "type": "uint8"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "inputs": [],
+ "name": "BURN_ADDRESS",
+ "outputs": [
+ {
+ "internalType": "address",
+ "name": "",
+ "type": "address"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "inputs": [],
+ "name": "BURN_RATIO_SCALE",
+ "outputs": [
+ {
+ "internalType": "uint256",
+ "name": "",
+ "type": "uint256"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "inputs": [],
+ "name": "CODE_OK",
+ "outputs": [
+ {
+ "internalType": "uint32",
+ "name": "",
+ "type": "uint32"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "inputs": [],
+ "name": "CROSS_CHAIN_CONTRACT_ADDR",
+ "outputs": [
+ {
+ "internalType": "address",
+ "name": "",
+ "type": "address"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "inputs": [],
+ "name": "DUSTY_INCOMING",
+ "outputs": [
+ {
+ "internalType": "uint256",
+ "name": "",
+ "type": "uint256"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "inputs": [],
+ "name": "EPOCH",
+ "outputs": [
+ {
+ "internalType": "uint256",
+ "name": "",
+ "type": "uint256"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "inputs": [],
+ "name": "ERROR_FAIL_CHECK_VALIDATORS",
+ "outputs": [
+ {
+ "internalType": "uint32",
+ "name": "",
+ "type": "uint32"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "inputs": [],
+ "name": "ERROR_FAIL_DECODE",
+ "outputs": [
+ {
+ "internalType": "uint32",
+ "name": "",
+ "type": "uint32"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "inputs": [],
+ "name": "ERROR_LEN_OF_VAL_MISMATCH",
+ "outputs": [
+ {
+ "internalType": "uint32",
+ "name": "",
+ "type": "uint32"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "inputs": [],
+ "name": "ERROR_RELAYFEE_TOO_LARGE",
+ "outputs": [
+ {
+ "internalType": "uint32",
+ "name": "",
+ "type": "uint32"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "inputs": [],
+ "name": "ERROR_UNKNOWN_PACKAGE_TYPE",
+ "outputs": [
+ {
+ "internalType": "uint32",
+ "name": "",
+ "type": "uint32"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "inputs": [],
+ "name": "EXPIRE_TIME_SECOND_GAP",
+ "outputs": [
+ {
+ "internalType": "uint256",
+ "name": "",
+ "type": "uint256"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "inputs": [],
+ "name": "GOV_CHANNELID",
+ "outputs": [
+ {
+ "internalType": "uint8",
+ "name": "",
+ "type": "uint8"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "inputs": [],
+ "name": "GOV_HUB_ADDR",
+ "outputs": [
+ {
+ "internalType": "address",
+ "name": "",
+ "type": "address"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "inputs": [],
+ "name": "INCENTIVIZE_ADDR",
+ "outputs": [
+ {
+ "internalType": "address",
+ "name": "",
+ "type": "address"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "inputs": [],
+ "name": "INIT_BURN_RATIO",
+ "outputs": [
+ {
+ "internalType": "uint256",
+ "name": "",
+ "type": "uint256"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "inputs": [],
+ "name": "INIT_FINALITY_REWARD_RATIO",
+ "outputs": [
+ {
+ "internalType": "uint256",
+ "name": "",
+ "type": "uint256"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "inputs": [],
+ "name": "INIT_MAINTAIN_SLASH_SCALE",
+ "outputs": [
+ {
+ "internalType": "uint256",
+ "name": "",
+ "type": "uint256"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "inputs": [],
+ "name": "INIT_MAX_NUM_OF_MAINTAINING",
+ "outputs": [
+ {
+ "internalType": "uint256",
+ "name": "",
+ "type": "uint256"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "inputs": [],
+ "name": "INIT_NUM_OF_CABINETS",
+ "outputs": [
+ {
+ "internalType": "uint256",
+ "name": "",
+ "type": "uint256"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "inputs": [],
+ "name": "INIT_VALIDATORSET_BYTES",
+ "outputs": [
+ {
+ "internalType": "bytes",
+ "name": "",
+ "type": "bytes"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "inputs": [],
+ "name": "JAIL_MESSAGE_TYPE",
+ "outputs": [
+ {
+ "internalType": "uint8",
+ "name": "",
+ "type": "uint8"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "inputs": [],
+ "name": "LIGHT_CLIENT_ADDR",
+ "outputs": [
+ {
+ "internalType": "address",
+ "name": "",
+ "type": "address"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "inputs": [],
+ "name": "MAX_NUM_OF_VALIDATORS",
+ "outputs": [
+ {
+ "internalType": "uint256",
+ "name": "",
+ "type": "uint256"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "inputs": [],
+ "name": "PRECISION",
+ "outputs": [
+ {
+ "internalType": "uint256",
+ "name": "",
+ "type": "uint256"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "inputs": [],
+ "name": "RELAYERHUB_CONTRACT_ADDR",
+ "outputs": [
+ {
+ "internalType": "address",
+ "name": "",
+ "type": "address"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "inputs": [],
+ "name": "SLASH_CHANNELID",
+ "outputs": [
+ {
+ "internalType": "uint8",
+ "name": "",
+ "type": "uint8"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "inputs": [],
+ "name": "SLASH_CONTRACT_ADDR",
+ "outputs": [
+ {
+ "internalType": "address",
+ "name": "",
+ "type": "address"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "inputs": [],
+ "name": "STAKING_CHANNELID",
+ "outputs": [
+ {
+ "internalType": "uint8",
+ "name": "",
+ "type": "uint8"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "inputs": [],
+ "name": "SYSTEM_REWARD_ADDR",
+ "outputs": [
+ {
+ "internalType": "address",
+ "name": "",
+ "type": "address"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "inputs": [],
+ "name": "TOKEN_HUB_ADDR",
+ "outputs": [
+ {
+ "internalType": "address",
+ "name": "",
+ "type": "address"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "inputs": [],
+ "name": "TOKEN_MANAGER_ADDR",
+ "outputs": [
+ {
+ "internalType": "address",
+ "name": "",
+ "type": "address"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "inputs": [],
+ "name": "TRANSFER_IN_CHANNELID",
+ "outputs": [
+ {
+ "internalType": "uint8",
+ "name": "",
+ "type": "uint8"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "inputs": [],
+ "name": "TRANSFER_OUT_CHANNELID",
+ "outputs": [
+ {
+ "internalType": "uint8",
+ "name": "",
+ "type": "uint8"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "inputs": [],
+ "name": "VALIDATORS_UPDATE_MESSAGE_TYPE",
+ "outputs": [
+ {
+ "internalType": "uint8",
+ "name": "",
+ "type": "uint8"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "inputs": [],
+ "name": "VALIDATOR_CONTRACT_ADDR",
+ "outputs": [
+ {
+ "internalType": "address",
+ "name": "",
+ "type": "address"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "inputs": [],
+ "name": "alreadyInit",
+ "outputs": [
+ {
+ "internalType": "bool",
+ "name": "",
+ "type": "bool"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "inputs": [],
+ "name": "bscChainID",
+ "outputs": [
+ {
+ "internalType": "uint16",
+ "name": "",
+ "type": "uint16"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "inputs": [],
+ "name": "burnRatio",
+ "outputs": [
+ {
+ "internalType": "uint256",
+ "name": "",
+ "type": "uint256"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "inputs": [],
+ "name": "burnRatioInitialized",
+ "outputs": [
+ {
+ "internalType": "bool",
+ "name": "",
+ "type": "bool"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "inputs": [
+ {
+ "internalType": "uint256",
+ "name": "",
+ "type": "uint256"
+ }
+ ],
+ "name": "currentValidatorSet",
+ "outputs": [
+ {
+ "internalType": "address",
+ "name": "consensusAddress",
+ "type": "address"
+ },
+ {
+ "internalType": "address payable",
+ "name": "feeAddress",
+ "type": "address"
+ },
+ {
+ "internalType": "address",
+ "name": "BBCFeeAddress",
+ "type": "address"
+ },
+ {
+ "internalType": "uint64",
+ "name": "votingPower",
+ "type": "uint64"
+ },
+ {
+ "internalType": "bool",
+ "name": "jailed",
+ "type": "bool"
+ },
+ {
+ "internalType": "uint256",
+ "name": "incoming",
+ "type": "uint256"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "inputs": [
+ {
+ "internalType": "address",
+ "name": "",
+ "type": "address"
+ }
+ ],
+ "name": "currentValidatorSetMap",
+ "outputs": [
+ {
+ "internalType": "uint256",
+ "name": "",
+ "type": "uint256"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "inputs": [],
+ "name": "expireTimeSecondGap",
+ "outputs": [
+ {
+ "internalType": "uint256",
+ "name": "",
+ "type": "uint256"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "inputs": [],
+ "name": "finalityRewardRatio",
+ "outputs": [
+ {
+ "internalType": "uint256",
+ "name": "",
+ "type": "uint256"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "inputs": [],
+ "name": "maintainSlashScale",
+ "outputs": [
+ {
+ "internalType": "uint256",
+ "name": "",
+ "type": "uint256"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "inputs": [],
+ "name": "maxNumOfCandidates",
+ "outputs": [
+ {
+ "internalType": "uint256",
+ "name": "",
+ "type": "uint256"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "inputs": [],
+ "name": "maxNumOfMaintaining",
+ "outputs": [
+ {
+ "internalType": "uint256",
+ "name": "",
+ "type": "uint256"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "inputs": [],
+ "name": "maxNumOfWorkingCandidates",
+ "outputs": [
+ {
+ "internalType": "uint256",
+ "name": "",
+ "type": "uint256"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "inputs": [],
+ "name": "numOfCabinets",
+ "outputs": [
+ {
+ "internalType": "uint256",
+ "name": "",
+ "type": "uint256"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "inputs": [],
+ "name": "numOfJailed",
+ "outputs": [
+ {
+ "internalType": "uint256",
+ "name": "",
+ "type": "uint256"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "inputs": [],
+ "name": "numOfMaintaining",
+ "outputs": [
+ {
+ "internalType": "uint256",
+ "name": "",
+ "type": "uint256"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "inputs": [],
+ "name": "previousHeight",
+ "outputs": [
+ {
+ "internalType": "uint256",
+ "name": "",
+ "type": "uint256"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "inputs": [],
+ "name": "totalInComing",
+ "outputs": [
+ {
+ "internalType": "uint256",
+ "name": "",
+ "type": "uint256"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "inputs": [
+ {
+ "internalType": "uint256",
+ "name": "",
+ "type": "uint256"
+ }
+ ],
+ "name": "validatorExtraSet",
+ "outputs": [
+ {
+ "internalType": "uint256",
+ "name": "enterMaintenanceHeight",
+ "type": "uint256"
+ },
+ {
+ "internalType": "bool",
+ "name": "isMaintaining",
+ "type": "bool"
+ },
+ {
+ "internalType": "bytes",
+ "name": "voteAddress",
+ "type": "bytes"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "stateMutability": "payable",
+ "type": "receive"
+ },
+ {
+ "inputs": [],
+ "name": "init",
+ "outputs": [],
+ "stateMutability": "nonpayable",
+ "type": "function"
+ },
+ {
+ "inputs": [
+ {
+ "internalType": "uint8",
+ "name": "",
+ "type": "uint8"
+ },
+ {
+ "internalType": "bytes",
+ "name": "msgBytes",
+ "type": "bytes"
+ }
+ ],
+ "name": "handleSynPackage",
+ "outputs": [
+ {
+ "internalType": "bytes",
+ "name": "responsePayload",
+ "type": "bytes"
+ }
+ ],
+ "stateMutability": "nonpayable",
+ "type": "function"
+ },
+ {
+ "inputs": [
+ {
+ "internalType": "uint8",
+ "name": "channelId",
+ "type": "uint8"
+ },
+ {
+ "internalType": "bytes",
+ "name": "msgBytes",
+ "type": "bytes"
+ }
+ ],
+ "name": "handleAckPackage",
+ "outputs": [],
+ "stateMutability": "nonpayable",
+ "type": "function"
+ },
+ {
+ "inputs": [
+ {
+ "internalType": "uint8",
+ "name": "channelId",
+ "type": "uint8"
+ },
+ {
+ "internalType": "bytes",
+ "name": "msgBytes",
+ "type": "bytes"
+ }
+ ],
+ "name": "handleFailAckPackage",
+ "outputs": [],
+ "stateMutability": "nonpayable",
+ "type": "function"
+ },
+ {
+ "inputs": [
+ {
+ "internalType": "address",
+ "name": "valAddr",
+ "type": "address"
+ }
+ ],
+ "name": "deposit",
+ "outputs": [],
+ "stateMutability": "payable",
+ "type": "function"
+ },
+ {
+ "inputs": [],
+ "name": "getLivingValidators",
+ "outputs": [
+ {
+ "internalType": "address[]",
+ "name": "",
+ "type": "address[]"
+ },
+ {
+ "internalType": "bytes[]",
+ "name": "",
+ "type": "bytes[]"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "inputs": [],
+ "name": "getMiningValidators",
+ "outputs": [
+ {
+ "internalType": "address[]",
+ "name": "",
+ "type": "address[]"
+ },
+ {
+ "internalType": "bytes[]",
+ "name": "",
+ "type": "bytes[]"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "inputs": [],
+ "name": "getValidators",
+ "outputs": [
+ {
+ "internalType": "address[]",
+ "name": "",
+ "type": "address[]"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "inputs": [
+ {
+ "internalType": "uint256",
+ "name": "index",
+ "type": "uint256"
+ }
+ ],
+ "name": "isWorkingValidator",
+ "outputs": [
+ {
+ "internalType": "bool",
+ "name": "",
+ "type": "bool"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "inputs": [
+ {
+ "internalType": "address",
+ "name": "validator",
+ "type": "address"
+ }
+ ],
+ "name": "getIncoming",
+ "outputs": [
+ {
+ "internalType": "uint256",
+ "name": "",
+ "type": "uint256"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "inputs": [
+ {
+ "internalType": "address",
+ "name": "validator",
+ "type": "address"
+ }
+ ],
+ "name": "isCurrentValidator",
+ "outputs": [
+ {
+ "internalType": "bool",
+ "name": "",
+ "type": "bool"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "inputs": [
+ {
+ "internalType": "address[]",
+ "name": "valAddrs",
+ "type": "address[]"
+ },
+ {
+ "internalType": "uint256[]",
+ "name": "weights",
+ "type": "uint256[]"
+ }
+ ],
+ "name": "distributeFinalityReward",
+ "outputs": [],
+ "stateMutability": "nonpayable",
+ "type": "function"
+ },
+ {
+ "inputs": [],
+ "name": "getWorkingValidatorCount",
+ "outputs": [
+ {
+ "internalType": "uint256",
+ "name": "workingValidatorCount",
+ "type": "uint256"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "inputs": [
+ {
+ "internalType": "address",
+ "name": "validator",
+ "type": "address"
+ }
+ ],
+ "name": "misdemeanor",
+ "outputs": [],
+ "stateMutability": "nonpayable",
+ "type": "function"
+ },
+ {
+ "inputs": [
+ {
+ "internalType": "address",
+ "name": "validator",
+ "type": "address"
+ }
+ ],
+ "name": "felony",
+ "outputs": [],
+ "stateMutability": "nonpayable",
+ "type": "function"
+ },
+ {
+ "inputs": [
+ {
+ "internalType": "address",
+ "name": "_validator",
+ "type": "address"
+ }
+ ],
+ "name": "getCurrentValidatorIndex",
+ "outputs": [
+ {
+ "internalType": "uint256",
+ "name": "",
+ "type": "uint256"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "inputs": [
+ {
+ "internalType": "uint256",
+ "name": "index",
+ "type": "uint256"
+ }
+ ],
+ "name": "canEnterMaintenance",
+ "outputs": [
+ {
+ "internalType": "bool",
+ "name": "",
+ "type": "bool"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "inputs": [],
+ "name": "enterMaintenance",
+ "outputs": [],
+ "stateMutability": "nonpayable",
+ "type": "function"
+ },
+ {
+ "inputs": [],
+ "name": "exitMaintenance",
+ "outputs": [],
+ "stateMutability": "nonpayable",
+ "type": "function"
+ },
+ {
+ "inputs": [
+ {
+ "internalType": "string",
+ "name": "key",
+ "type": "string"
+ },
+ {
+ "internalType": "bytes",
+ "name": "value",
+ "type": "bytes"
+ }
+ ],
+ "name": "updateParam",
+ "outputs": [],
+ "stateMutability": "nonpayable",
+ "type": "function"
+ }
+]
`
const slashABI = `
[
- {
- "anonymous": false,
- "inputs": [],
- "name": "indicatorCleaned",
- "type": "event"
- },
- {
- "anonymous": false,
- "inputs": [
- {
- "indexed": true,
- "internalType": "address",
- "name": "validator",
- "type": "address"
- }
- ],
- "name": "validatorSlashed",
- "type": "event"
- },
- {
- "inputs": [],
- "name": "FELONY_THRESHOLD",
- "outputs": [
- {
- "internalType": "uint256",
- "name": "",
- "type": "uint256"
- }
- ],
- "stateMutability": "view",
- "type": "function"
- },
- {
- "inputs": [],
- "name": "MISDEMEANOR_THRESHOLD",
- "outputs": [
- {
- "internalType": "uint256",
- "name": "",
- "type": "uint256"
- }
- ],
- "stateMutability": "view",
- "type": "function"
- },
- {
- "inputs": [],
- "name": "SYSTEM_ADDRESS",
- "outputs": [
- {
- "internalType": "address",
- "name": "",
- "type": "address"
- }
- ],
- "stateMutability": "view",
- "type": "function"
- },
- {
- "inputs": [],
- "name": "VALIDATOR_CONTRACT_ADDR",
- "outputs": [
- {
- "internalType": "address",
- "name": "",
- "type": "address"
- }
- ],
- "stateMutability": "view",
- "type": "function"
- },
- {
- "inputs": [],
- "name": "previousHeight",
- "outputs": [
- {
- "internalType": "uint256",
- "name": "",
- "type": "uint256"
- }
- ],
- "stateMutability": "view",
- "type": "function"
- },
- {
- "inputs": [
- {
- "internalType": "address",
- "name": "validator",
- "type": "address"
- }
- ],
- "name": "slash",
- "outputs": [],
- "stateMutability": "nonpayable",
- "type": "function"
- },
- {
- "inputs": [],
- "name": "clean",
- "outputs": [],
- "stateMutability": "nonpayable",
- "type": "function"
- },
- {
- "inputs": [
- {
- "internalType": "address",
- "name": "validator",
- "type": "address"
- }
- ],
- "name": "getSlashIndicator",
- "outputs": [
- {
- "internalType": "uint256",
- "name": "",
- "type": "uint256"
- },
- {
- "internalType": "uint256",
- "name": "",
- "type": "uint256"
- }
- ],
- "stateMutability": "view",
- "type": "function"
- }
- ]
+ {
+ "anonymous": false,
+ "inputs": [],
+ "name": "crashResponse",
+ "type": "event"
+ },
+ {
+ "anonymous": false,
+ "inputs": [],
+ "name": "indicatorCleaned",
+ "type": "event"
+ },
+ {
+ "anonymous": false,
+ "inputs": [
+ {
+ "indexed": false,
+ "internalType": "uint32",
+ "name": "code",
+ "type": "uint32"
+ }
+ ],
+ "name": "knownResponse",
+ "type": "event"
+ },
+ {
+ "anonymous": false,
+ "inputs": [
+ {
+ "indexed": false,
+ "internalType": "string",
+ "name": "key",
+ "type": "string"
+ },
+ {
+ "indexed": false,
+ "internalType": "bytes",
+ "name": "value",
+ "type": "bytes"
+ }
+ ],
+ "name": "paramChange",
+ "type": "event"
+ },
+ {
+ "anonymous": false,
+ "inputs": [
+ {
+ "indexed": false,
+ "internalType": "uint32",
+ "name": "code",
+ "type": "uint32"
+ }
+ ],
+ "name": "unKnownResponse",
+ "type": "event"
+ },
+ {
+ "anonymous": false,
+ "inputs": [
+ {
+ "indexed": true,
+ "internalType": "address",
+ "name": "validator",
+ "type": "address"
+ }
+ ],
+ "name": "validatorSlashed",
+ "type": "event"
+ },
+ {
+ "inputs": [],
+ "name": "BIND_CHANNELID",
+ "outputs": [
+ {
+ "internalType": "uint8",
+ "name": "",
+ "type": "uint8"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "inputs": [],
+ "name": "BSC_RELAYER_REWARD",
+ "outputs": [
+ {
+ "internalType": "uint256",
+ "name": "",
+ "type": "uint256"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "inputs": [],
+ "name": "CODE_OK",
+ "outputs": [
+ {
+ "internalType": "uint32",
+ "name": "",
+ "type": "uint32"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "inputs": [],
+ "name": "CROSS_CHAIN_CONTRACT_ADDR",
+ "outputs": [
+ {
+ "internalType": "address",
+ "name": "",
+ "type": "address"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "inputs": [],
+ "name": "DECREASE_RATE",
+ "outputs": [
+ {
+ "internalType": "uint256",
+ "name": "",
+ "type": "uint256"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "inputs": [],
+ "name": "ERROR_FAIL_DECODE",
+ "outputs": [
+ {
+ "internalType": "uint32",
+ "name": "",
+ "type": "uint32"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "inputs": [],
+ "name": "FELONY_THRESHOLD",
+ "outputs": [
+ {
+ "internalType": "uint256",
+ "name": "",
+ "type": "uint256"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "inputs": [],
+ "name": "GOV_CHANNELID",
+ "outputs": [
+ {
+ "internalType": "uint8",
+ "name": "",
+ "type": "uint8"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "inputs": [],
+ "name": "GOV_HUB_ADDR",
+ "outputs": [
+ {
+ "internalType": "address",
+ "name": "",
+ "type": "address"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "inputs": [],
+ "name": "INCENTIVIZE_ADDR",
+ "outputs": [
+ {
+ "internalType": "address",
+ "name": "",
+ "type": "address"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "inputs": [],
+ "name": "INIT_FINALITY_SLASH_REWARD_RATIO",
+ "outputs": [
+ {
+ "internalType": "uint256",
+ "name": "",
+ "type": "uint256"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "inputs": [],
+ "name": "LIGHT_CLIENT_ADDR",
+ "outputs": [
+ {
+ "internalType": "address",
+ "name": "",
+ "type": "address"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "inputs": [],
+ "name": "MISDEMEANOR_THRESHOLD",
+ "outputs": [
+ {
+ "internalType": "uint256",
+ "name": "",
+ "type": "uint256"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "inputs": [],
+ "name": "RELAYERHUB_CONTRACT_ADDR",
+ "outputs": [
+ {
+ "internalType": "address",
+ "name": "",
+ "type": "address"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "inputs": [],
+ "name": "SLASH_CHANNELID",
+ "outputs": [
+ {
+ "internalType": "uint8",
+ "name": "",
+ "type": "uint8"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "inputs": [],
+ "name": "SLASH_CONTRACT_ADDR",
+ "outputs": [
+ {
+ "internalType": "address",
+ "name": "",
+ "type": "address"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "inputs": [],
+ "name": "STAKING_CHANNELID",
+ "outputs": [
+ {
+ "internalType": "uint8",
+ "name": "",
+ "type": "uint8"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "inputs": [],
+ "name": "SYSTEM_REWARD_ADDR",
+ "outputs": [
+ {
+ "internalType": "address",
+ "name": "",
+ "type": "address"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "inputs": [],
+ "name": "TOKEN_HUB_ADDR",
+ "outputs": [
+ {
+ "internalType": "address",
+ "name": "",
+ "type": "address"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "inputs": [],
+ "name": "TOKEN_MANAGER_ADDR",
+ "outputs": [
+ {
+ "internalType": "address",
+ "name": "",
+ "type": "address"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "inputs": [],
+ "name": "TRANSFER_IN_CHANNELID",
+ "outputs": [
+ {
+ "internalType": "uint8",
+ "name": "",
+ "type": "uint8"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "inputs": [],
+ "name": "TRANSFER_OUT_CHANNELID",
+ "outputs": [
+ {
+ "internalType": "uint8",
+ "name": "",
+ "type": "uint8"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "inputs": [],
+ "name": "VALIDATOR_CONTRACT_ADDR",
+ "outputs": [
+ {
+ "internalType": "address",
+ "name": "",
+ "type": "address"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "inputs": [],
+ "name": "alreadyInit",
+ "outputs": [
+ {
+ "internalType": "bool",
+ "name": "",
+ "type": "bool"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "inputs": [],
+ "name": "bscChainID",
+ "outputs": [
+ {
+ "internalType": "uint16",
+ "name": "",
+ "type": "uint16"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "inputs": [],
+ "name": "felonyThreshold",
+ "outputs": [
+ {
+ "internalType": "uint256",
+ "name": "",
+ "type": "uint256"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "inputs": [],
+ "name": "finalitySlashRewardRatio",
+ "outputs": [
+ {
+ "internalType": "uint256",
+ "name": "",
+ "type": "uint256"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "inputs": [
+ {
+ "internalType": "address",
+ "name": "",
+ "type": "address"
+ }
+ ],
+ "name": "indicators",
+ "outputs": [
+ {
+ "internalType": "uint256",
+ "name": "height",
+ "type": "uint256"
+ },
+ {
+ "internalType": "uint256",
+ "name": "count",
+ "type": "uint256"
+ },
+ {
+ "internalType": "bool",
+ "name": "exist",
+ "type": "bool"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "inputs": [],
+ "name": "misdemeanorThreshold",
+ "outputs": [
+ {
+ "internalType": "uint256",
+ "name": "",
+ "type": "uint256"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "inputs": [],
+ "name": "previousHeight",
+ "outputs": [
+ {
+ "internalType": "uint256",
+ "name": "",
+ "type": "uint256"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "inputs": [
+ {
+ "internalType": "uint256",
+ "name": "",
+ "type": "uint256"
+ }
+ ],
+ "name": "validators",
+ "outputs": [
+ {
+ "internalType": "address",
+ "name": "",
+ "type": "address"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "inputs": [],
+ "name": "init",
+ "outputs": [],
+ "stateMutability": "nonpayable",
+ "type": "function"
+ },
+ {
+ "inputs": [
+ {
+ "internalType": "uint8",
+ "name": "",
+ "type": "uint8"
+ },
+ {
+ "internalType": "bytes",
+ "name": "",
+ "type": "bytes"
+ }
+ ],
+ "name": "handleSynPackage",
+ "outputs": [
+ {
+ "internalType": "bytes",
+ "name": "",
+ "type": "bytes"
+ }
+ ],
+ "stateMutability": "nonpayable",
+ "type": "function"
+ },
+ {
+ "inputs": [
+ {
+ "internalType": "uint8",
+ "name": "",
+ "type": "uint8"
+ },
+ {
+ "internalType": "bytes",
+ "name": "msgBytes",
+ "type": "bytes"
+ }
+ ],
+ "name": "handleAckPackage",
+ "outputs": [],
+ "stateMutability": "nonpayable",
+ "type": "function"
+ },
+ {
+ "inputs": [
+ {
+ "internalType": "uint8",
+ "name": "",
+ "type": "uint8"
+ },
+ {
+ "internalType": "bytes",
+ "name": "",
+ "type": "bytes"
+ }
+ ],
+ "name": "handleFailAckPackage",
+ "outputs": [],
+ "stateMutability": "nonpayable",
+ "type": "function"
+ },
+ {
+ "inputs": [
+ {
+ "internalType": "address",
+ "name": "validator",
+ "type": "address"
+ }
+ ],
+ "name": "slash",
+ "outputs": [],
+ "stateMutability": "nonpayable",
+ "type": "function"
+ },
+ {
+ "inputs": [],
+ "name": "clean",
+ "outputs": [],
+ "stateMutability": "nonpayable",
+ "type": "function"
+ },
+ {
+ "inputs": [
+ {
+ "components": [
+ {
+ "components": [
+ {
+ "internalType": "uint256",
+ "name": "srcNum",
+ "type": "uint256"
+ },
+ {
+ "internalType": "bytes32",
+ "name": "srcHash",
+ "type": "bytes32"
+ },
+ {
+ "internalType": "uint256",
+ "name": "tarNum",
+ "type": "uint256"
+ },
+ {
+ "internalType": "bytes32",
+ "name": "tarHash",
+ "type": "bytes32"
+ },
+ {
+ "internalType": "bytes",
+ "name": "sig",
+ "type": "bytes"
+ }
+ ],
+ "internalType": "struct SlashIndicator.VoteData",
+ "name": "voteA",
+ "type": "tuple"
+ },
+ {
+ "components": [
+ {
+ "internalType": "uint256",
+ "name": "srcNum",
+ "type": "uint256"
+ },
+ {
+ "internalType": "bytes32",
+ "name": "srcHash",
+ "type": "bytes32"
+ },
+ {
+ "internalType": "uint256",
+ "name": "tarNum",
+ "type": "uint256"
+ },
+ {
+ "internalType": "bytes32",
+ "name": "tarHash",
+ "type": "bytes32"
+ },
+ {
+ "internalType": "bytes",
+ "name": "sig",
+ "type": "bytes"
+ }
+ ],
+ "internalType": "struct SlashIndicator.VoteData",
+ "name": "voteB",
+ "type": "tuple"
+ },
+ {
+ "internalType": "bytes",
+ "name": "voteAddr",
+ "type": "bytes"
+ }
+ ],
+ "internalType": "struct SlashIndicator.FinalityEvidence",
+ "name": "_evidence",
+ "type": "tuple"
+ }
+ ],
+ "name": "submitFinalityViolationEvidence",
+ "outputs": [],
+ "stateMutability": "nonpayable",
+ "type": "function"
+ },
+ {
+ "inputs": [
+ {
+ "internalType": "address",
+ "name": "validator",
+ "type": "address"
+ }
+ ],
+ "name": "sendFelonyPackage",
+ "outputs": [],
+ "stateMutability": "nonpayable",
+ "type": "function"
+ },
+ {
+ "inputs": [
+ {
+ "internalType": "string",
+ "name": "key",
+ "type": "string"
+ },
+ {
+ "internalType": "bytes",
+ "name": "value",
+ "type": "bytes"
+ }
+ ],
+ "name": "updateParam",
+ "outputs": [],
+ "stateMutability": "nonpayable",
+ "type": "function"
+ },
+ {
+ "inputs": [
+ {
+ "internalType": "address",
+ "name": "validator",
+ "type": "address"
+ }
+ ],
+ "name": "getSlashIndicator",
+ "outputs": [
+ {
+ "internalType": "uint256",
+ "name": "",
+ "type": "uint256"
+ },
+ {
+ "internalType": "uint256",
+ "name": "",
+ "type": "uint256"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "inputs": [],
+ "name": "getSlashThresholds",
+ "outputs": [
+ {
+ "internalType": "uint256",
+ "name": "",
+ "type": "uint256"
+ },
+ {
+ "internalType": "uint256",
+ "name": "",
+ "type": "uint256"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ }
+]
`
diff --git a/consensus/parlia/bonehFork.go b/consensus/parlia/bonehFork.go
new file mode 100644
index 000000000..37117485e
--- /dev/null
+++ b/consensus/parlia/bonehFork.go
@@ -0,0 +1,49 @@
+package parlia
+
+import (
+ "context"
+ "github.com/ethereum/go-ethereum/common/systemcontract"
+ "math/big"
+
+ "github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/common/hexutil"
+ "github.com/ethereum/go-ethereum/common/math"
+ "github.com/ethereum/go-ethereum/internal/ethapi"
+ "github.com/ethereum/go-ethereum/log"
+ "github.com/ethereum/go-ethereum/rpc"
+)
+
+func (p *Parlia) getCurrentValidatorsBeforeBoneh(blockHash common.Hash, blockNumber *big.Int) ([]common.Address, error) {
+ blockNr := rpc.BlockNumberOrHashWithHash(blockHash, false)
+
+ // prepare different method
+ method := "getValidators"
+ if p.chainConfig.IsEuler(blockNumber) {
+ method = "getMiningValidators"
+ }
+
+ ctx, cancel := context.WithCancel(context.Background())
+ // cancel when we are finished consuming integers
+ defer cancel()
+ data, err := p.validatorSetABIBeforeBoneh.Pack(method)
+ if err != nil {
+ log.Error("Unable to pack tx for getValidators", "error", err)
+ return nil, err
+ }
+ // do smart contract call
+ msgData := (hexutil.Bytes)(data)
+ toAddress := common.HexToAddress(systemcontract.ValidatorContract)
+ gas := (hexutil.Uint64)(uint64(math.MaxUint64 / 2))
+ result, err := p.ethAPI.Call(ctx, ethapi.CallArgs{
+ Gas: &gas,
+ To: &toAddress,
+ Data: &msgData,
+ }, blockNr, nil)
+ if err != nil {
+ return nil, err
+ }
+
+ var valSet []common.Address
+ err = p.validatorSetABIBeforeBoneh.UnpackIntoInterface(&valSet, method, result)
+ return valSet, err
+}
diff --git a/consensus/parlia/parlia.go b/consensus/parlia/parlia.go
index 53628399b..de93bf553 100644
--- a/consensus/parlia/parlia.go
+++ b/consensus/parlia/parlia.go
@@ -6,7 +6,6 @@ import (
"encoding/hex"
"errors"
"fmt"
- "github.com/ethereum/go-ethereum/common/systemcontract"
"io"
"math"
"math/big"
@@ -17,6 +16,8 @@ import (
"time"
lru "github.com/hashicorp/golang-lru"
+ "github.com/prysmaticlabs/prysm/crypto/bls"
+ "github.com/willf/bitset"
"golang.org/x/crypto/sha3"
"github.com/ethereum/go-ethereum"
@@ -25,6 +26,7 @@ import (
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/gopool"
"github.com/ethereum/go-ethereum/common/hexutil"
+ "github.com/ethereum/go-ethereum/common/systemcontract"
"github.com/ethereum/go-ethereum/consensus"
"github.com/ethereum/go-ethereum/consensus/misc"
"github.com/ethereum/go-ethereum/core"
@@ -53,13 +55,16 @@ const (
extraSeal = 65 // Fixed number of extra-data suffix bytes reserved for signer seal
nextForkHashSize = 4 // Fixed number of extra-data suffix bytes reserved for nextForkHash.
- validatorBytesLength = common.AddressLength
- wiggleTime = uint64(1) // second, Random delay (per signer) to allow concurrent signers
- initialBackOffTime = uint64(1) // second
- processBackOffTime = uint64(1) // second
+ validatorBytesLength = common.AddressLength
+ validatorBytesLengthAfterBoneh = common.AddressLength + types.BLSPublicKeyLength
+ validatorNumberSizeAfterBoneh = 1 // Fixed number of extra prefix bytes reserved for validator number
+ naturallyJustifiedDist = 15 // The distance to naturally justify a block
- systemRewardPercent = 4 // it means 1/2^4 = 1/16 percentage of gas fee incoming will be distributed to system
+ wiggleTime = uint64(1) // second, Random delay (per signer) to allow concurrent signers
+ initialBackOffTime = uint64(1) // second
+ processBackOffTime = uint64(1) // second
+ systemRewardPercent = 4 // it means 1/2^4 = 1/16 percentage of gas fee incoming will be distributed to system
)
var (
@@ -196,9 +201,11 @@ type Parlia struct {
lock sync.RWMutex // Protects the signer fields
- ethAPI *ethapi.PublicBlockChainAPI
- validatorSetABI abi.ABI
- slashABI abi.ABI
+ ethAPI *ethapi.PublicBlockChainAPI
+ VotePool consensus.VotePool
+ validatorSetABIBeforeBoneh abi.ABI
+ validatorSetABI abi.ABI
+ slashABI abi.ABI
// The fields below are for testing only
fakeDiff bool // Skip difficulty verifications
@@ -228,6 +235,10 @@ func New(
if err != nil {
panic(err)
}
+ vABIBeforeBoneh, err := abi.JSON(strings.NewReader(validatorSetABIBeforeBoneh))
+ if err != nil {
+ panic(err)
+ }
vABI, err := abi.JSON(strings.NewReader(validatorSetABI))
if err != nil {
panic(err)
@@ -237,16 +248,17 @@ func New(
panic(err)
}
c := &Parlia{
- chainConfig: chainConfig,
- config: parliaConfig,
- genesisHash: genesisHash,
- db: db,
- ethAPI: ethAPI,
- recentSnaps: recentSnaps,
- signatures: signatures,
- validatorSetABI: vABI,
- slashABI: sABI,
- signer: types.NewEIP155Signer(chainConfig.ChainID),
+ chainConfig: chainConfig,
+ config: parliaConfig,
+ genesisHash: genesisHash,
+ db: db,
+ ethAPI: ethAPI,
+ recentSnaps: recentSnaps,
+ signatures: signatures,
+ validatorSetABIBeforeBoneh: vABIBeforeBoneh,
+ validatorSetABI: vABI,
+ slashABI: sABI,
+ signer: types.NewEIP155Signer(chainConfig.ChainID),
}
return c
@@ -305,6 +317,168 @@ func (p *Parlia) VerifyHeaders(chain consensus.ChainHeaderReader, headers []*typ
return abort, results
}
+// getValidatorBytesFromHeader returns the validators bytes extracted from the header's extra field if exists.
+// The validators bytes would be contained only in the epoch block's header, and its each validator bytes length is fixed.
+// On boneh fork, we introduce vote attestation into the header's extra field, so extra format is different from before.
+// Before boneh fork: |---Extra Vanity---|---Validators Bytes (or Empty)---|---Extra Seal---|
+// After boneh fork: |---Extra Vanity---|---Validators Number and Validators Bytes (or Empty)---|---Vote Attestation (or Empty)---|---Extra Seal---|
+func getValidatorBytesFromHeader(header *types.Header, chainConfig *params.ChainConfig, parliaConfig *params.ParliaConfig) []byte {
+ if len(header.Extra) <= extraVanity+extraSeal {
+ return nil
+ }
+
+ if !chainConfig.IsBoneh(header.Number) {
+ if header.Number.Uint64()%parliaConfig.Epoch == 0 && (len(header.Extra)-extraSeal-extraVanity)%validatorBytesLength != 0 {
+ return nil
+ }
+ return header.Extra[extraVanity : len(header.Extra)-extraSeal]
+ }
+
+ if header.Number.Uint64()%parliaConfig.Epoch != 0 {
+ return nil
+ }
+ num := int(header.Extra[extraVanity])
+ if num == 0 || len(header.Extra) <= extraVanity+extraSeal+num*validatorBytesLengthAfterBoneh {
+ return nil
+ }
+ start := extraVanity + validatorNumberSizeAfterBoneh
+ end := start + num*validatorBytesLengthAfterBoneh
+ return header.Extra[start:end]
+}
+
+// getVoteAttestationFromHeader returns the vote attestation extracted from the header's extra field if exists.
+func getVoteAttestationFromHeader(header *types.Header, chainConfig *params.ChainConfig, parliaConfig *params.ParliaConfig) (*types.VoteAttestation, error) {
+ if len(header.Extra) <= extraVanity+extraSeal {
+ return nil, nil
+ }
+
+ if !chainConfig.IsBoneh(header.Number) {
+ return nil, nil
+ }
+
+ var attestationBytes []byte
+ if header.Number.Uint64()%parliaConfig.Epoch != 0 {
+ attestationBytes = header.Extra[extraVanity : len(header.Extra)-extraSeal]
+ } else {
+ num := int(header.Extra[extraVanity])
+ if len(header.Extra) <= extraVanity+extraSeal+validatorNumberSizeAfterBoneh+num*validatorBytesLengthAfterBoneh {
+ return nil, nil
+ }
+ start := extraVanity + validatorNumberSizeAfterBoneh + num*validatorBytesLengthAfterBoneh
+ end := len(header.Extra) - extraVanity
+ attestationBytes = header.Extra[start:end]
+ }
+
+ var attestation types.VoteAttestation
+ if err := rlp.Decode(bytes.NewReader(attestationBytes), &attestation); err != nil {
+ return nil, fmt.Errorf("block %d has vote attestation info, decode err: %s", header.Number.Uint64(), err)
+ }
+ return &attestation, nil
+}
+
+func getSignRecentlyLimit(blockNumber *big.Int, validatorsNumber int, chainConfig *params.ChainConfig) int {
+ limit := validatorsNumber/2 + 1
+ if chainConfig.IsBoneh(blockNumber) {
+ limit = validatorsNumber*2/3 + 1
+ }
+ return limit
+}
+
+// verifyVoteAttestation checks whether the vote attestation in the header is valid.
+func (p *Parlia) verifyVoteAttestation(chain consensus.ChainHeaderReader, header *types.Header, parents []*types.Header) error {
+ attestation, err := getVoteAttestationFromHeader(header, p.chainConfig, p.config)
+ if err != nil {
+ return err
+ }
+ if attestation == nil {
+ return nil
+ }
+ if attestation.Data == nil {
+ return fmt.Errorf("invalid attestation, vote data is nil")
+ }
+ if len(attestation.Extra) > types.MaxAttestationExtraLength {
+ return fmt.Errorf("invalid attestation, too large extra length: %d", len(attestation.Extra))
+ }
+
+ // Get parent block
+ number := header.Number.Uint64()
+ var parent *types.Header
+ if len(parents) > 0 {
+ parent = parents[len(parents)-1]
+ } else {
+ parent = chain.GetHeader(header.ParentHash, number-1)
+ }
+ if parent == nil || parent.Hash() != header.ParentHash {
+ return consensus.ErrUnknownAncestor
+ }
+
+ // The target block should be direct parent.
+ targetNumber := attestation.Data.TargetNumber
+ targetHash := attestation.Data.TargetHash
+ if targetNumber != parent.Number.Uint64() || targetHash != parent.Hash() {
+ return fmt.Errorf("invalid attestation, target mismatch, expected block: %d, hash: %s; real block: %d, hash: %s",
+ parent.Number.Uint64(), parent.Hash(), targetNumber, targetHash)
+ }
+
+ // The source block should be the highest justified block.
+ sourceNumber := attestation.Data.SourceNumber
+ sourceHash := attestation.Data.SourceHash
+ justified := p.GetJustifiedHeader(chain, parent)
+ if justified == nil {
+ return fmt.Errorf("no justified block found")
+ }
+ if sourceNumber != justified.Number.Uint64() || sourceHash != justified.Hash() {
+ return fmt.Errorf("invalid attestation, source mismatch, expected block: %d, hash: %s; real block: %d, hash: %s",
+ justified.Number.Uint64(), justified.Hash(), sourceNumber, sourceHash)
+ }
+
+ // The snapshot should be the targetNumber-1 block's snapshot.
+ if len(parents) > 2 {
+ parents = parents[:len(parents)-2]
+ } else {
+ parents = nil
+ }
+ snap, err := p.snapshot(chain, parent.Number.Uint64()-1, parent.ParentHash, parents)
+ if err != nil {
+ return err
+ }
+
+ // Filter out valid validator from attestation.
+ validators := snap.validators()
+ validatorsBitSet := bitset.From([]uint64{uint64(attestation.VoteAddressSet)})
+ if validatorsBitSet.Count() > uint(len(validators)) {
+ return fmt.Errorf("invalid attestation, vote number larger than validators number")
+ }
+ votedAddrs := make([]bls.PublicKey, 0, validatorsBitSet.Count())
+ for index, val := range validators {
+ if !validatorsBitSet.Test(uint(index)) {
+ continue
+ }
+
+ voteAddr, err := bls.PublicKeyFromBytes(snap.Validators[val].VoteAddress[:])
+ if err != nil {
+ return fmt.Errorf("BLS public key converts failed: %v", err)
+ }
+ votedAddrs = append(votedAddrs, voteAddr)
+ }
+
+ // The valid voted validators should be no less than 2/3 validators.
+ if len(votedAddrs) <= len(snap.Validators)*2/3 {
+ return fmt.Errorf("invalid attestation, not enough validators voted")
+ }
+
+ // Verify the aggregated signature.
+ aggSig, err := bls.SignatureFromBytes(attestation.AggSignature[:])
+ if err != nil {
+ return fmt.Errorf("BLS signature converts failed: %v", err)
+ }
+ if !aggSig.FastAggregateVerify(votedAddrs, attestation.Data.Hash()) {
+ return fmt.Errorf("invalid attestation, signature verify failed")
+ }
+
+ return nil
+}
+
// verifyHeader checks whether a header conforms to the consensus rules.The
// caller may optionally pass in a batch of parents (ascending order) to avoid
// looking those up from the database. This is useful for concurrently verifying
@@ -313,7 +487,6 @@ func (p *Parlia) verifyHeader(chain consensus.ChainHeaderReader, header *types.H
if header.Number == nil {
return errUnknownBlock
}
- number := header.Number.Uint64()
// Don't waste time checking blocks from the future
if header.Time > uint64(time.Now().Unix()+time.Second.Milliseconds()/1000) {
@@ -326,16 +499,17 @@ func (p *Parlia) verifyHeader(chain consensus.ChainHeaderReader, header *types.H
if len(header.Extra) < extraVanity+extraSeal {
return errMissingSignature
}
+
// check extra data
+ number := header.Number.Uint64()
isEpoch := number%p.config.Epoch == 0
// Ensure that the extra-data contains a signer list on checkpoint, but none otherwise
- signersBytes := len(header.Extra) - extraVanity - extraSeal
- if !isEpoch && signersBytes != 0 {
+ signersBytes := getValidatorBytesFromHeader(header, p.chainConfig, p.config)
+ if !isEpoch && len(signersBytes) != 0 {
return errExtraValidators
}
-
- if isEpoch && signersBytes%validatorBytesLength != 0 {
+ if isEpoch && len(signersBytes) == 0 {
return errInvalidSpanValidators
}
@@ -414,6 +588,14 @@ func (p *Parlia) verifyCascadingFields(chain consensus.ChainHeaderReader, header
return fmt.Errorf("invalid gas limit: have %d, want %d += %d", header.GasLimit, parent.GasLimit, limit)
}
+ // Verify vote attestation for fast finality.
+ if err := p.verifyVoteAttestation(chain, header, parents); err != nil {
+ if chain.Config().IsLynn(header.Number) {
+ return err
+ }
+ log.Warn("Verify vote attestation failed", "block", header.Number.Uint64(), "block hash", header.Hash(), "error", err)
+ }
+
// All basic checks passed, verify the seal and return
return p.verifySeal(chain, header, parents)
}
@@ -449,15 +631,14 @@ func (p *Parlia) snapshot(chain consensus.ChainHeaderReader, number uint64, hash
// get checkpoint data
hash := checkpoint.Hash()
- validatorBytes := checkpoint.Extra[extraVanity : len(checkpoint.Extra)-extraSeal]
// get validators from headers
- validators, err := ParseValidators(validatorBytes)
+ validators, voteAddrs, err := parseValidators(checkpoint, p.chainConfig, p.config)
if err != nil {
return nil, err
}
- // new snap shot
- snap = newSnapshot(p.config, p.signatures, number, hash, validators, p.ethAPI)
+ // new snapshot
+ snap = newSnapshot(p.config, p.signatures, number, hash, validators, voteAddrs, p.ethAPI)
if err := snap.store(p.db); err != nil {
return nil, err
}
@@ -496,7 +677,7 @@ func (p *Parlia) snapshot(chain consensus.ChainHeaderReader, number uint64, hash
headers[i], headers[len(headers)-1-i] = headers[len(headers)-1-i], headers[i]
}
- snap, err := snap.apply(headers, chain, parents, p.chainConfig.ChainID)
+ snap, err := snap.apply(headers, chain, parents, p.chainConfig)
if err != nil {
return nil, err
}
@@ -567,7 +748,7 @@ func (p *Parlia) verifySeal(chain consensus.ChainHeaderReader, header *types.Hea
for seen, recent := range snap.Recents {
if recent == signer {
// Signer is among recents, only fail if the current block doesn't shift it out
- if limit := uint64(len(snap.Validators)/2 + 1); seen > number-limit {
+ if limit := getSignRecentlyLimit(header.Number, len(snap.Validators), p.chainConfig); seen > number-uint64(limit) {
return errRecentlySigned
}
}
@@ -587,6 +768,109 @@ func (p *Parlia) verifySeal(chain consensus.ChainHeaderReader, header *types.Hea
return nil
}
+func (p *Parlia) prepareValidators(chain consensus.ChainHeaderReader, header *types.Header) error {
+ if header.Number.Uint64()%p.config.Epoch != 0 {
+ return nil
+ }
+
+ newValidators, voteAddressMap, err := p.getCurrentValidators(header.ParentHash, new(big.Int).Sub(header.Number, big.NewInt(1)))
+ if err != nil {
+ return err
+ }
+ // sort validator by address
+ sort.Sort(validatorsAscending(newValidators))
+ if !p.chainConfig.IsBoneh(header.Number) {
+ for _, validator := range newValidators {
+ header.Extra = append(header.Extra, validator.Bytes()...)
+ }
+ } else {
+ header.Extra = append(header.Extra, byte(len(newValidators)))
+ for _, validator := range newValidators {
+ header.Extra = append(header.Extra, validator.Bytes()...)
+ header.Extra = append(header.Extra, voteAddressMap[validator].Bytes()...)
+ }
+ }
+ return nil
+}
+
+func (p *Parlia) assembleVoteAttestation(chain consensus.ChainHeaderReader, header *types.Header) error {
+ if !p.chainConfig.IsBoneh(header.Number) || header.Number.Uint64() < 2 {
+ return nil
+ }
+
+ if p.VotePool == nil {
+ return errors.New("vote pool is nil")
+ }
+
+ // Fetch direct parent's votes
+ parent := chain.GetHeaderByHash(header.ParentHash)
+ if parent == nil {
+ return errors.New("parent not found")
+ }
+ snap, err := p.snapshot(chain, parent.Number.Uint64()-1, parent.ParentHash, nil)
+ if err != nil {
+ return err
+ }
+ votes := p.VotePool.FetchVoteByBlockHash(parent.Hash())
+ if len(votes) <= len(snap.Validators)*2/3 {
+ return nil
+ }
+
+ // Prepare vote attestation
+ // Prepare vote data
+ justified := p.GetJustifiedHeader(chain, parent)
+ if justified == nil {
+ return errors.New("highest justified block not found")
+ }
+ attestation := &types.VoteAttestation{
+ Data: &types.VoteData{
+ SourceNumber: justified.Number.Uint64(),
+ SourceHash: justified.Hash(),
+ TargetNumber: parent.Number.Uint64(),
+ TargetHash: parent.Hash(),
+ },
+ }
+ // Check vote data from votes
+ for _, vote := range votes {
+ if vote.Data.Hash() != attestation.Data.Hash() {
+ return fmt.Errorf("vote check error, expected: %v, real: %v", attestation.Data, vote)
+ }
+ }
+ // Prepare aggregated vote signature
+ voteAddrSet := make(map[types.BLSPublicKey]struct{}, len(votes))
+ signatures := make([][]byte, 0, len(votes))
+ for _, vote := range votes {
+ voteAddrSet[vote.VoteAddress] = struct{}{}
+ signatures = append(signatures, vote.Signature[:])
+ }
+ sigs, err := bls.MultipleSignaturesFromBytes(signatures)
+ if err != nil {
+ return err
+ }
+ copy(attestation.AggSignature[:], bls.AggregateSignatures(sigs).Marshal())
+ // Prepare vote address bitset.
+ for _, valInfo := range snap.Validators {
+ if _, ok := voteAddrSet[valInfo.VoteAddress]; ok {
+ attestation.VoteAddressSet |= 1 << (valInfo.Index - 1) //Index is offset by 1
+ }
+ }
+
+ // Append attestation to header extra field.
+ buf := new(bytes.Buffer)
+ err = rlp.Encode(buf, attestation)
+ if err != nil {
+ return err
+ }
+
+ // Insert vote attestation into header extra ahead extra seal.
+ extraSealStart := len(header.Extra) - extraSeal
+ extraSealBytes := header.Extra[extraSealStart:]
+ header.Extra = append(header.Extra[0:extraSealStart], buf.Bytes()...)
+ header.Extra = append(header.Extra, extraSealBytes...)
+
+ return nil
+}
+
// Prepare implements consensus.Engine, preparing all the consensus fields of the
// header for running the transactions on top.
func (p *Parlia) Prepare(chain consensus.ChainHeaderReader, header *types.Header) error {
@@ -613,21 +897,8 @@ func (p *Parlia) Prepare(chain consensus.ChainHeaderReader, header *types.Header
nextForkHash := forkid.NextForkHash(p.chainConfig, p.genesisHash, number)
header.Extra = append(header.Extra, nextForkHash[:]...)
- if number%p.config.Epoch == 0 {
- newValidators, err := p.getCurrentValidators(header.ParentHash)
- if err != nil {
- return err
- }
- // sort validator by address
- sort.Sort(validatorsAscending(newValidators))
- var newValidatorsString []string
- for _, validator := range newValidators {
- newValidatorsString = append(newValidatorsString, validator.Hex())
- }
- log.Info("Updating validator set", "validator", strings.Join(newValidatorsString, ","))
- for _, validator := range newValidators {
- header.Extra = append(header.Extra, validator.Bytes()...)
- }
+ if err := p.prepareValidators(chain, header); err != nil {
+ return err
}
// add extra seal space
@@ -648,6 +919,110 @@ func (p *Parlia) Prepare(chain consensus.ChainHeaderReader, header *types.Header
return nil
}
+func (p *Parlia) verifyValidators(header *types.Header) error {
+ if header.Number.Uint64()%p.config.Epoch != 0 {
+ return nil
+ }
+
+ newValidators, voteAddressMap, err := p.getCurrentValidators(header.ParentHash, new(big.Int).Sub(header.Number, big.NewInt(1)))
+ if err != nil {
+ return err
+ }
+ // sort validator by address
+ sort.Sort(validatorsAscending(newValidators))
+ var validatorsBytes []byte
+ validatorsNumber := len(newValidators)
+ if !p.chainConfig.IsBoneh(header.Number) {
+ validatorsBytes = make([]byte, validatorsNumber*validatorBytesLength)
+ for i, validator := range newValidators {
+ copy(validatorsBytes[i*validatorBytesLength:], validator.Bytes())
+ }
+ } else {
+ if uint8(validatorsNumber) != header.Extra[extraVanity] {
+ return errMismatchingEpochValidators
+ }
+ validatorsBytes = make([]byte, validatorsNumber*validatorBytesLengthAfterBoneh)
+ for i, validator := range newValidators {
+ copy(validatorsBytes[i*validatorBytesLengthAfterBoneh:], validator.Bytes())
+ copy(validatorsBytes[i*validatorBytesLengthAfterBoneh+common.AddressLength:], voteAddressMap[validator].Bytes())
+ }
+ }
+ if !bytes.Equal(getValidatorBytesFromHeader(header, p.chainConfig, p.config), validatorsBytes) {
+ return errMismatchingEpochValidators
+ }
+ return nil
+}
+
+func (p *Parlia) distributeFinalityReward(chain consensus.ChainHeaderReader, state *state.StateDB, header *types.Header,
+ cx core.ChainContext, txs *[]*types.Transaction, receipts *[]*types.Receipt, systemTxs *[]*types.Transaction,
+ usedGas *uint64, mining bool) error {
+ currentHeight := header.Number.Uint64()
+ epoch := p.config.Epoch
+ chainConfig := chain.Config()
+ if currentHeight%epoch != 0 {
+ return nil
+ }
+
+ head := chain.GetHeaderByHash(header.ParentHash)
+ accumulatedWeights := make(map[common.Address]uint64)
+ for height := currentHeight - 1; height+epoch >= currentHeight && height >= 1; height-- {
+ if height != currentHeight-1 {
+ head = chain.GetHeaderByHash(head.ParentHash)
+ }
+ if head == nil {
+ return fmt.Errorf("header is nil at height %d", height)
+ }
+ voteAttestation, err := getVoteAttestationFromHeader(head, chainConfig, p.config)
+ if err != nil {
+ return err
+ }
+ if voteAttestation == nil {
+ continue
+ }
+ justifiedBlock := chain.GetHeaderByHash(voteAttestation.Data.TargetHash)
+ if justifiedBlock == nil {
+ log.Warn("justifiedBlock is nil at height %d", voteAttestation.Data.TargetNumber)
+ continue
+ }
+
+ snap, err := p.snapshot(chain, justifiedBlock.Number.Uint64()-1, justifiedBlock.ParentHash, nil)
+ if err != nil {
+ return err
+ }
+ validators := snap.validators()
+ validatorsBitSet := bitset.From([]uint64{uint64(voteAttestation.VoteAddressSet)})
+ if validatorsBitSet.Count() > uint(len(validators)) {
+ log.Error("invalid attestation, vote number larger than validators number")
+ continue
+ }
+ for index, val := range validators {
+ if validatorsBitSet.Test(uint(index)) {
+ accumulatedWeights[val] += 1
+ }
+ }
+ }
+
+ validators := make([]common.Address, 0, len(accumulatedWeights))
+ weights := make([]*big.Int, 0, len(accumulatedWeights))
+ for val := range accumulatedWeights {
+ validators = append(validators, val)
+ }
+ sort.Sort(validatorsAscending(validators))
+ for _, val := range validators {
+ weights = append(weights, big.NewInt(int64(accumulatedWeights[val])))
+ }
+
+ // generate system transaction
+ method := "distributeFinalityReward"
+ data, err := p.validatorSetABI.Pack(method, validators, weights)
+ if err != nil {
+ log.Error("Unable to pack tx for distributeFinalityReward", "error", err)
+ return err
+ }
+ msg := p.getSystemMessage(header.Coinbase, common.HexToAddress(systemcontract.SystemRewardContract), data, common.Big0)
+ return p.applyTransaction(msg, state, header, cx, txs, receipts, systemTxs, usedGas, mining)
+}
+
// Finalize implements consensus.Engine, ensuring no uncles are set, nor block
// rewards given.
func (p *Parlia) Finalize(chain consensus.ChainHeaderReader, header *types.Header, state *state.StateDB, txs *[]*types.Transaction,
@@ -664,30 +1039,13 @@ func (p *Parlia) Finalize(chain consensus.ChainHeaderReader, header *types.Heade
}
// If the block is an epoch end block, verify the validator list
// The verification can only be done when the state is ready, it can't be done in VerifyHeader.
- if header.Number.Uint64()%p.config.Epoch == 0 {
- newValidators, err := p.getCurrentValidators(header.ParentHash)
- if err != nil {
- return err
- }
- // sort validator by address
- sort.Sort(validatorsAscending(newValidators))
- validatorsBytes := make([]byte, len(newValidators)*validatorBytesLength)
- for i, validator := range newValidators {
- copy(validatorsBytes[i*validatorBytesLength:], validator.Bytes())
- }
- var newValidatorsString []string
- for _, validator := range newValidators {
- newValidatorsString = append(newValidatorsString, validator.Hex())
- }
- log.Info("Updating validator set", "validator", strings.Join(newValidatorsString, ","))
-
- extraSuffix := len(header.Extra) - extraSeal
- if !bytes.Equal(header.Extra[extraVanity:extraSuffix], validatorsBytes) {
- return errMismatchingEpochValidators
- }
+ if err := p.verifyValidators(header); err != nil {
+ return err
}
- // No block rewards in PoA, so the state remains as is and uncles are dropped
+
cx := chainContext{Chain: chain, parlia: p}
+
+ // No block rewards in PoA, so the state remains as is and uncles are dropped
if header.Number.Cmp(common.Big1) == 0 {
err := p.initContract(state, header, cx, txs, receipts, systemTxs, usedGas, false)
if err != nil {
@@ -718,6 +1076,12 @@ func (p *Parlia) Finalize(chain consensus.ChainHeaderReader, header *types.Heade
if err != nil {
return err
}
+
+ if p.chainConfig.IsLynn(header.Number) {
+ if err := p.distributeFinalityReward(chain, state, header, cx, txs, receipts, systemTxs, usedGas, false); err != nil {
+ return err
+ }
+ }
if len(*systemTxs) > 0 {
return errors.New("the length of systemTxs do not match")
}
@@ -765,10 +1129,18 @@ func (p *Parlia) FinalizeAndAssemble(chain consensus.ChainHeaderReader, header *
}
}
}
+
err := p.distributeIncoming(p.val, state, header, cx, &txs, &receipts, nil, &header.GasUsed, true)
if err != nil {
return nil, nil, err
}
+
+ if p.chainConfig.IsLynn(header.Number) {
+ if err := p.distributeFinalityReward(chain, state, header, cx, &txs, &receipts, nil, &header.GasUsed, true); err != nil {
+ return nil, nil, err
+ }
+ }
+
// should not happen. Once happen, stop the node is better than broadcast the block
if header.GasLimit < header.GasUsed {
return nil, nil, errors.New("gas consumption of system txs exceed the gas limit")
@@ -792,6 +1164,56 @@ func (p *Parlia) FinalizeAndAssemble(chain consensus.ChainHeaderReader, header *
return blk, receipts, nil
}
+func (p *Parlia) IsActiveValidatorAt(chain consensus.ChainHeaderReader, header *types.Header) bool {
+ number := header.Number.Uint64()
+ snap, err := p.snapshot(chain, number-1, header.ParentHash, nil)
+ if err != nil {
+ log.Error("failed to get the snapshot from consensus", "error", err)
+ return false
+ }
+ validators := snap.Validators
+ _, ok := validators[p.val]
+ return ok
+
+}
+
+// VerifyVote will verify: 1. If the vote comes from valid validators 2. If the vote's sourceNumber and sourceHash are correct
+func (p *Parlia) VerifyVote(chain consensus.ChainHeaderReader, vote *types.VoteEnvelope) error {
+ targetNumber := vote.Data.TargetNumber
+ targetHash := vote.Data.TargetHash
+ header := chain.GetHeaderByHash(targetHash)
+ if header == nil {
+ log.Warn("BlockHeader at current voteBlockNumber is nil", "targetNumber", targetNumber, "targetHash", targetHash)
+ return fmt.Errorf("BlockHeader at current voteBlockNumber is nil")
+ }
+
+ justifiedHeader := p.GetJustifiedHeader(chain, header)
+ if justifiedHeader == nil {
+ log.Error("failed to get the highest justified header", "headerNumber", header.Number, "headerHash", header.Hash())
+ return fmt.Errorf("BlockHeader at current voteBlockNumber is nil")
+ }
+ if vote.Data.SourceNumber != justifiedHeader.Number.Uint64() || vote.Data.SourceHash != justifiedHeader.Hash() {
+ return fmt.Errorf("vote source block mismatch")
+ }
+
+ number := header.Number.Uint64()
+ snap, err := p.snapshot(chain, number-1, header.ParentHash, nil)
+ if err != nil {
+ log.Error("failed to get the snapshot from consensus", "error", err)
+ return fmt.Errorf("failed to get the snapshot from consensus")
+ }
+
+ validators := snap.Validators
+ voteAddress := vote.VoteAddress
+ for _, validator := range validators {
+ if validator.VoteAddress == voteAddress {
+ return nil
+ }
+ }
+
+ return fmt.Errorf("vote verification failed")
+}
+
// Authorize injects a private key into the consensus engine to mint new blocks
// with.
func (p *Parlia) Authorize(val common.Address, signFn SignerFn, signTxFn SignerTxFn) {
@@ -852,7 +1274,7 @@ func (p *Parlia) Seal(chain consensus.ChainHeaderReader, block *types.Block, res
for seen, recent := range snap.Recents {
if recent == val {
// Signer is among recents, only wait if the current block doesn't shift it out
- if limit := uint64(len(snap.Validators)/2 + 1); number < limit || seen > number-limit {
+ if limit := getSignRecentlyLimit(header.Number, len(snap.Validators), p.chainConfig); number < uint64(limit) || seen > number-uint64(limit) {
log.Info("Signed recently, must wait for others")
return nil
}
@@ -864,13 +1286,6 @@ func (p *Parlia) Seal(chain consensus.ChainHeaderReader, block *types.Block, res
log.Info("Sealing block with", "number", number, "delay", delay, "headerDifficulty", header.Difficulty, "val", val.Hex())
- // Sign all the things!
- sig, err := signFn(accounts.Account{Address: val}, accounts.MimetypeParlia, ParliaRLP(header, p.chainConfig.ChainID))
- if err != nil {
- return err
- }
- copy(header.Extra[len(header.Extra)-extraSeal:], sig)
-
// Wait until sealing is terminated or delay timeout.
log.Info("Waiting for slot to sign and propagate", "delay", common.PrettyDuration(delay))
go func() {
@@ -879,6 +1294,21 @@ func (p *Parlia) Seal(chain consensus.ChainHeaderReader, block *types.Block, res
return
case <-time.After(delay):
}
+
+ err := p.assembleVoteAttestation(chain, header)
+ if err != nil {
+ log.Error("Assemble vote attestation failed when sealing", "err", err)
+ return
+ }
+
+ // Sign all the things!
+ sig, err := signFn(accounts.Account{Address: val}, accounts.MimetypeParlia, ParliaRLP(header, p.chainConfig.ChainID))
+ if err != nil {
+ log.Error("Sign for the block header failed when sealing", "err", err)
+ return
+ }
+ copy(header.Extra[len(header.Extra)-extraSeal:], sig)
+
if p.shouldWaitForCurrentBlockProcess(chain, header, snap) {
log.Info("Waiting for received in turn block to process")
select {
@@ -941,7 +1371,7 @@ func (p *Parlia) IsLocalBlock(header *types.Header) bool {
}
func (p *Parlia) SignRecently(chain consensus.ChainReader, parent *types.Header) (bool, error) {
- snap, err := p.snapshot(chain, parent.Number.Uint64(), parent.ParentHash, nil)
+ snap, err := p.snapshot(chain, parent.Number.Uint64(), parent.Hash(), nil)
if err != nil {
return true, err
}
@@ -954,11 +1384,14 @@ func (p *Parlia) SignRecently(chain consensus.ChainReader, parent *types.Header)
// If we're amongst the recent signers, wait for the next block
number := parent.Number.Uint64() + 1
for seen, recent := range snap.Recents {
- if recent == p.val {
- // Signer is among recents, only wait if the current block doesn't shift it out
- if limit := uint64(len(snap.Validators)/2 + 1); number < limit || seen > number-limit {
- return true, nil
- }
+ if recent != p.val {
+ continue
+ }
+
+ // Signer is among recents, only wait if the current block doesn't shift it out
+ limit := getSignRecentlyLimit(parent.Number, len(snap.Validators), p.chainConfig)
+ if number < uint64(limit) || seen > number-uint64(limit) {
+ return true, nil
}
}
return false, nil
@@ -986,8 +1419,11 @@ func CalcDifficulty(snap *Snapshot, signer common.Address) *big.Int {
}
// SealHash returns the hash of a block prior to it being sealed.
-func (p *Parlia) SealHash(header *types.Header) common.Hash {
- return SealHash(header, p.chainConfig.ChainID)
+func (p *Parlia) SealHash(header *types.Header) (hash common.Hash) {
+ hasher := sha3.NewLegacyKeccak256()
+ encodeSigHeaderWithoutVoteAttestation(hasher, header, p.chainConfig.ChainID)
+ hasher.Sum(hash[:0])
+ return hash
}
// APIs implements consensus.Engine, returning the user facing RPC API to query snapshot.
@@ -1008,20 +1444,25 @@ func (p *Parlia) Close() error {
// ========================== interaction with contract/account =========
// getCurrentValidators get current validators
-func (p *Parlia) getCurrentValidators(blockHash common.Hash) ([]common.Address, error) {
+func (p *Parlia) getCurrentValidators(blockHash common.Hash, blockNum *big.Int) ([]common.Address, map[common.Address]*types.BLSPublicKey, error) {
// block
blockNr := rpc.BlockNumberOrHashWithHash(blockHash, false)
+ if !p.chainConfig.IsBoneh(blockNum) {
+ validators, err := p.getCurrentValidatorsBeforeBoneh(blockHash, blockNum)
+ return validators, nil, err
+ }
+
// method
- method := "getValidators"
+ method := "getMiningValidators"
ctx, cancel := context.WithCancel(context.Background())
defer cancel() // cancel when we are finished consuming integers
data, err := p.validatorSetABI.Pack(method)
if err != nil {
- log.Error("Unable to pack tx for getValidators", "error", err)
- return nil, err
+ log.Error("Unable to pack tx for getMiningValidators", "error", err)
+ return nil, nil, err
}
// call
msgData := (hexutil.Bytes)(data)
@@ -1033,23 +1474,21 @@ func (p *Parlia) getCurrentValidators(blockHash common.Hash) ([]common.Address,
Data: &msgData,
}, blockNr, nil)
if err != nil {
- return nil, err
+ return nil, nil, err
}
- var (
- ret0 = new([]common.Address)
- )
- out := ret0
+ var valSet []common.Address
+ var voteAddrSet []types.BLSPublicKey
- if err := p.validatorSetABI.UnpackIntoInterface(out, method, result); err != nil {
- return nil, err
+ if err := p.validatorSetABI.UnpackIntoInterface(&[]interface{}{&valSet, &voteAddrSet}, method, result); err != nil {
+ return nil, nil, err
}
- valz := make([]common.Address, len(*ret0))
- for i, a := range *ret0 {
- valz[i] = a
+ voteAddrmap := make(map[common.Address]*types.BLSPublicKey, len(valSet))
+ for i := 0; i < len(valSet); i++ {
+ voteAddrmap[valSet[i]] = &(voteAddrSet)[i]
}
- return valz, nil
+ return valSet, voteAddrmap, nil
}
// slash spoiled validators
@@ -1063,6 +1502,14 @@ func (p *Parlia) distributeIncoming(val common.Address, state *state.StateDB, he
state.SetBalance(consensus.SystemAddress, big.NewInt(0))
state.AddBalance(coinbase, balance)
+ if rules := p.chainConfig.Rules(header.Number); rules.HasBlockRewards {
+ blockRewards := p.chainConfig.Parlia.BlockRewards
+ // if we have enabled block rewards and rewards are greater than 0 then
+ if blockRewards != nil && blockRewards.Cmp(common.Big0) > 0 {
+ state.AddBalance(coinbase, blockRewards)
+ }
+ }
+
doDistributeSysReward := state.GetBalance(common.HexToAddress(systemcontract.SystemRewardContract)).Cmp(maxSystemBalance) < 0
if doDistributeSysReward {
var rewards = new(big.Int)
@@ -1240,6 +1687,74 @@ func (p *Parlia) applyTransaction(
return nil
}
+// GetJustifiedHeader returns highest justified block's header before the specific block,
+// the attestation within the specific block will be taken into account.
+func (p *Parlia) GetJustifiedHeader(chain consensus.ChainHeaderReader, header *types.Header) *types.Header {
+ if chain == nil || header == nil {
+ return nil
+ }
+
+ snap, err := p.snapshot(chain, header.Number.Uint64(), header.Hash(), nil)
+ if err != nil {
+ log.Error("Unexpected error when getting snapshot",
+ "error", err, "blockNumber", header.Number.Uint64(), "blockHash", header.Hash())
+ return nil
+ }
+
+ // If there is no vote justified block, then return root or naturally justified block.
+ if snap.Attestation == nil {
+ if header.Number.Uint64() <= naturallyJustifiedDist {
+ return chain.GetHeaderByNumber(0)
+ }
+ // Return naturally justified block.
+ return FindAncientHeader(header, naturallyJustifiedDist, chain, nil)
+ }
+
+ // If the latest vote justified block is too far, return naturally justified block.
+ if snap.Number-snap.Attestation.TargetNumber > naturallyJustifiedDist {
+ return FindAncientHeader(header, naturallyJustifiedDist, chain, nil)
+ }
+ //Return latest vote justified block.
+ return chain.GetHeaderByHash(snap.Attestation.TargetHash)
+}
+
+// GetFinalizedHeader returns highest finalized block header before the specific block.
+// It will first to find vote finalized block within the specific backward blocks, the suggested backward blocks is 21.
+// If the vote finalized block not found, return its previous backward block.
+func (p *Parlia) GetFinalizedHeader(chain consensus.ChainHeaderReader, header *types.Header, backward uint64) *types.Header {
+ if chain == nil || header == nil {
+ return nil
+ }
+ if !chain.Config().IsLynn(header.Number) {
+ return chain.GetHeaderByNumber(0)
+ }
+ if header.Number.Uint64() < backward {
+ backward = header.Number.Uint64()
+ }
+
+ snap, err := p.snapshot(chain, header.Number.Uint64(), header.Hash(), nil)
+ if err != nil {
+ log.Error("Unexpected error when getting snapshot",
+ "error", err, "blockNumber", header.Number.Uint64(), "blockHash", header.Hash())
+ return nil
+ }
+
+ for snap.Attestation != nil && snap.Attestation.SourceNumber >= header.Number.Uint64()-backward {
+ if snap.Attestation.TargetNumber == snap.Attestation.SourceNumber+1 {
+ return chain.GetHeaderByHash(snap.Attestation.SourceHash)
+ }
+
+ snap, err = p.snapshot(chain, snap.Attestation.SourceNumber, snap.Attestation.SourceHash, nil)
+ if err != nil {
+ log.Error("Unexpected error when getting snapshot",
+ "error", err, "blockNumber", snap.Attestation.SourceNumber, "blockHash", snap.Attestation.SourceHash)
+ return nil
+ }
+ }
+
+ return FindAncientHeader(header, backward, chain, nil)
+}
+
// =========================== utility function ==========================
// SealHash returns the hash of a block prior to it being sealed.
func SealHash(header *types.Header, chainId *big.Int) (hash common.Hash) {
@@ -1273,7 +1788,31 @@ func encodeSigHeader(w io.Writer, header *types.Header, chainId *big.Int) {
}
}
-func backOffTime(snap *Snapshot, val common.Address) uint64 {
+func encodeSigHeaderWithoutVoteAttestation(w io.Writer, header *types.Header, chainId *big.Int) {
+ err := rlp.Encode(w, []interface{}{
+ chainId,
+ header.ParentHash,
+ header.UncleHash,
+ header.Coinbase,
+ header.Root,
+ header.TxHash,
+ header.ReceiptHash,
+ header.Bloom,
+ header.Difficulty,
+ header.Number,
+ header.GasLimit,
+ header.GasUsed,
+ header.Time,
+ header.Extra[:extraVanity], // this will panic if extra is too short, should check before calling encodeSigHeader
+ header.MixDigest,
+ header.Nonce,
+ })
+ if err != nil {
+ panic("can't encode: " + err.Error())
+ }
+}
+
+func (p *Parlia) backOffTime(snap *Snapshot, header *types.Header, val common.Address) uint64 {
if snap.inturn(val) {
return 0
} else {
@@ -1282,17 +1821,56 @@ func backOffTime(snap *Snapshot, val common.Address) uint64 {
// The backOffTime does not matter when a validator is not authorized.
return 0
}
+
s := rand.NewSource(int64(snap.Number))
r := rand.New(s)
n := len(snap.Validators)
backOffSteps := make([]uint64, 0, n)
- for idx := uint64(0); idx < uint64(n); idx++ {
- backOffSteps = append(backOffSteps, idx)
+ if !p.chainConfig.IsBoneh(header.Number) {
+ for i := uint64(0); i < uint64(n); i++ {
+ backOffSteps = append(backOffSteps, i)
+ }
+ r.Shuffle(n, func(i, j int) {
+ backOffSteps[i], backOffSteps[j] = backOffSteps[j], backOffSteps[i]
+ })
+ delay := initialBackOffTime + backOffSteps[idx]*wiggleTime
+ return delay
}
- r.Shuffle(n, func(i, j int) {
+
+ // Exclude the recently signed validators first, and then compute the backOffTime.
+ recentVals := make(map[common.Address]bool, len(snap.Recents))
+ limit := getSignRecentlyLimit(header.Number, len(snap.Validators), p.chainConfig)
+ for seen, recent := range snap.Recents {
+ if header.Number.Uint64() < uint64(limit) || seen > header.Number.Uint64()-uint64(limit) {
+ if val == recent {
+ // The backOffTime does not matter when a validator has signed recently.
+ return 0
+ }
+ recentVals[recent] = true
+ }
+ }
+
+ backOffIndex := idx
+ validators := snap.validators()
+ for i := 0; i < n; i++ {
+ if isRecent, ok := recentVals[validators[i]]; ok && isRecent {
+ if i < idx {
+ backOffIndex--
+ }
+ continue
+ }
+ backOffSteps = append(backOffSteps, uint64(len(backOffSteps)))
+ }
+ r.Shuffle(len(backOffSteps), func(i, j int) {
backOffSteps[i], backOffSteps[j] = backOffSteps[j], backOffSteps[i]
})
- delay := initialBackOffTime + backOffSteps[idx]*wiggleTime
+ delay := initialBackOffTime + backOffSteps[backOffIndex]*wiggleTime
+
+ // If the in turn validator has recently signed, no initial delay.
+ inTurnVal := validators[(snap.Number+1)%uint64(len(validators))]
+ if isRecent, ok := recentVals[inTurnVal]; ok && isRecent {
+ delay -= initialBackOffTime
+ }
return delay
}
}
@@ -1346,7 +1924,7 @@ func applyMessage(
msg.Gas(),
msg.Value(),
)
- if err != nil {
+ if err != nil && len(ret) > 64 {
log.Error("apply message failed", "msg", string(ret[64+4:]), "err", err)
}
return msg.Gas() - returnGas, err
diff --git a/consensus/parlia/parlia_test.go b/consensus/parlia/parlia_test.go
index fc05013e9..dbcb86017 100644
--- a/consensus/parlia/parlia_test.go
+++ b/consensus/parlia/parlia_test.go
@@ -5,7 +5,11 @@ import (
"math/rand"
"testing"
+ "golang.org/x/crypto/sha3"
+
"github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/core/types"
+ "github.com/ethereum/go-ethereum/rlp"
)
func TestImpactOfValidatorOutOfService(t *testing.T) {
@@ -144,3 +148,450 @@ func randomAddress() common.Address {
rand.Read(addrBytes)
return common.BytesToAddress(addrBytes)
}
+
+// =========================================================================
+// ======= Simulator P2P network to verify fast finality ============
+// =========================================================================
+
+type MockBlock struct {
+ parent *MockBlock
+
+ blockNumber uint64
+ blockHash common.Hash
+ coinbase *MockValidator
+ td uint64 // Total difficulty from genesis block to current block
+ attestation uint64 // Vote attestation for parent block, zero means no attestation
+}
+
+var GenesisBlock = &MockBlock{
+ parent: nil,
+ blockNumber: 0,
+ blockHash: common.Hash{},
+ coinbase: nil,
+ td: diffInTurn.Uint64(),
+ attestation: 0,
+}
+
+func (b *MockBlock) Hash() (hash common.Hash) {
+ hasher := sha3.NewLegacyKeccak256()
+ rlp.Encode(hasher, []interface{}{
+ b.parent,
+ b.blockNumber,
+ b.coinbase,
+ b.td,
+ b.attestation,
+ })
+ hasher.Sum(hash[:0])
+ return hash
+}
+
+func (b *MockBlock) IsConflicted(a *MockBlock) bool {
+ if a.blockNumber > b.blockNumber {
+ p := a.parent
+ for ; p.blockNumber > b.blockNumber; p = p.parent {
+ }
+
+ return p.blockHash != b.blockHash
+ }
+
+ if a.blockNumber < b.blockNumber {
+ p := b.parent
+ for ; p.blockNumber > a.blockNumber; p = p.parent {
+ }
+
+ return p.blockHash != a.blockHash
+ }
+
+ return a.blockHash != b.blockHash
+}
+
+// GetJustifiedBlock returns highest justified block,
+// not include current block's attestation.
+func (b *MockBlock) GetJustifiedBlock() *MockBlock {
+ if b.blockNumber < 3 {
+ return GenesisBlock
+ }
+
+ parent := b.parent
+ for i := 0; i < naturallyJustifiedDist && parent.blockNumber > 0; i++ {
+ // vote justified
+ if parent.attestation != 0 {
+ return parent
+ }
+
+ parent = parent.parent
+ }
+
+ // naturally justified
+ return parent
+}
+
+// GetFinalizedBlock returns highest finalized block,
+// include current block's attestation.
+func (b *MockBlock) GetFinalizedBlock() *MockBlock {
+ if b.blockNumber < 3 {
+ return GenesisBlock
+ }
+
+ if b.attestation != 0 && b.parent.attestation != 0 {
+ return b.parent
+ }
+
+ return b.parent.GetFinalizedBlock()
+}
+
+type MockValidator struct {
+ index int
+ validatorSet int // validators number
+ head *MockBlock
+ voteRecords map[uint64]*types.VoteData
+}
+
+func NewMockValidator(index int, validatorSet int) *MockValidator {
+ v := &MockValidator{
+ index: index,
+ validatorSet: validatorSet,
+ head: GenesisBlock,
+ voteRecords: make(map[uint64]*types.VoteData),
+ }
+ return v
+}
+
+func (v *MockValidator) SignRecently() bool {
+ parent := v.head
+ for i := 0; i < v.validatorSet*2/3; i++ {
+ if parent.blockNumber == 0 {
+ return false
+ }
+
+ if parent.coinbase == v {
+ return true
+ }
+
+ parent = parent.parent
+ }
+
+ return false
+}
+
+func (v *MockValidator) Produce(attestation uint64) (*MockBlock, error) {
+ if v.SignRecently() {
+ return nil, fmt.Errorf("v %d sign recently", v.index)
+ }
+
+ block := &MockBlock{
+ parent: v.head,
+ blockNumber: v.head.blockNumber + 1,
+ coinbase: v,
+ td: v.head.td + 1,
+ attestation: attestation,
+ }
+
+ if (block.blockNumber-1)%uint64(v.validatorSet) == uint64(v.index) {
+ block.td = v.head.td + 2
+ }
+
+ block.blockHash = block.Hash()
+ return block, nil
+}
+
+func (v *MockValidator) Vote(block *MockBlock) bool {
+ // Rule 3: The block should be the latest block of canonical chain
+ if block != v.head {
+ return false
+ }
+
+ // Rule 1: No double vote
+ if _, ok := v.voteRecords[block.blockNumber]; ok {
+ return false
+ }
+
+ // Rule 2: No surround vote
+ justified := block.GetJustifiedBlock()
+ for targetNumber := justified.blockNumber + 1; targetNumber < block.blockNumber; targetNumber++ {
+ if vote, ok := v.voteRecords[targetNumber]; ok {
+ if vote.SourceNumber > justified.blockNumber {
+ return false
+ }
+ }
+ }
+ for targetNumber := block.blockNumber; targetNumber <= block.blockNumber+naturallyJustifiedDist; targetNumber++ {
+ if vote, ok := v.voteRecords[targetNumber]; ok {
+ if vote.SourceNumber < justified.blockNumber {
+ return false
+ }
+ }
+ }
+
+ v.voteRecords[block.blockNumber] = &types.VoteData{
+ SourceNumber: justified.blockNumber,
+ SourceHash: justified.blockHash,
+ TargetNumber: block.blockNumber,
+ TargetHash: block.blockHash,
+ }
+ return true
+}
+
+func (v *MockValidator) InsertBlock(block *MockBlock) {
+ // Reject block too old.
+ if block.blockNumber+13 < v.head.blockNumber {
+ return
+ }
+
+ // The higher finalized block is the longest chain.
+ if block.GetFinalizedBlock().blockNumber < v.head.GetFinalizedBlock().blockNumber {
+ return
+ }
+ if block.GetFinalizedBlock().blockNumber > v.head.GetFinalizedBlock().blockNumber {
+ v.head = block
+ return
+ }
+
+ // The same finalized number, the larger difficulty is the longest chain.
+ if block.td > v.head.td {
+ v.head = block
+ }
+}
+
+type BlockSimulator struct {
+ blockNumber uint64
+ coinbaseIndex int
+ voteMap uint64
+ insertMap uint64
+}
+
+type ChainSimulator []*BlockSimulator
+
+func (s ChainSimulator) Valid() bool {
+ var pre *BlockSimulator
+ for index, bs := range s {
+ if index == 0 {
+ if bs.blockNumber != 1 {
+ return false
+ }
+ } else {
+ if bs.blockNumber != pre.blockNumber+1 {
+ return false
+ }
+ }
+
+ pre = bs
+ }
+ return true
+}
+
+type Coordinator struct {
+ validators []*MockValidator
+ attestations map[common.Hash]uint64
+}
+
+func NewCoordinator(validatorsNumber int) *Coordinator {
+ validators := make([]*MockValidator, validatorsNumber)
+ for i := 0; i < validatorsNumber; i++ {
+ validators[i] = NewMockValidator(i, validatorsNumber)
+ }
+
+ return &Coordinator{
+ validators: validators,
+ attestations: make(map[common.Hash]uint64),
+ }
+}
+
+// SimulateP2P simulate a P2P network
+func (c *Coordinator) SimulateP2P(cs ChainSimulator) error {
+ for _, bs := range cs {
+ parent := c.validators[bs.coinbaseIndex].head
+ if bs.blockNumber != parent.blockNumber+1 {
+ return fmt.Errorf("can't produce discontinuous block, head block: %d, expect produce: %d", parent.blockNumber, bs.blockNumber)
+ }
+ attestation := c.attestations[parent.blockHash]
+ block, err := c.validators[bs.coinbaseIndex].Produce(attestation)
+ if err != nil {
+ return fmt.Errorf("produce block %v error %v", bs, err)
+ }
+
+ c.PropagateBlock(bs, block)
+ err = c.AggregateVotes(bs, block)
+ if err != nil {
+ return err
+ }
+ }
+
+ return nil
+}
+
+func (c *Coordinator) AggregateVotes(bs *BlockSimulator, block *MockBlock) error {
+ var attestation uint64
+ count := 0
+ for index, voteMap := 0, bs.voteMap; voteMap > 0; index, voteMap = index+1, voteMap>>1 {
+ if voteMap&0x1 == 0 {
+ continue
+ }
+
+ if !c.validators[index].Vote(block) {
+ return fmt.Errorf("validator(%d) couldn't vote for block %d produced by validator(%d)", index, block.blockNumber, block.coinbase.index)
+ }
+ attestation |= 1 << index
+ count++
+ }
+
+ if count > len(c.validators)*2/3 {
+ c.attestations[block.blockHash] = attestation
+ }
+
+ return nil
+}
+
+func (c *Coordinator) PropagateBlock(bs *BlockSimulator, block *MockBlock) {
+ for index, insertMap := 0, bs.insertMap; insertMap > 0; index, insertMap = index+1, insertMap>>1 {
+ if insertMap&0x1 == 0 {
+ continue
+ }
+
+ c.validators[index].InsertBlock(block)
+ }
+}
+
+func (c *Coordinator) CheckChain() bool {
+ // All validators highest finalized block should not be conflicted
+ finalizedBlocks := make([]*MockBlock, len(c.validators))
+ for index, val := range c.validators {
+ finalizedBlocks[index] = val.head.GetFinalizedBlock()
+ }
+
+ for i := 0; i < len(finalizedBlocks)-1; i++ {
+ for j := i + 1; j < len(finalizedBlocks); j++ {
+ if finalizedBlocks[i].IsConflicted(finalizedBlocks[j]) {
+ return false
+ }
+ }
+ }
+
+ return true
+}
+
+type TestSimulatorParam struct {
+ validatorsNumber int
+ cs ChainSimulator
+}
+
+var simulatorTestcases = []*TestSimulatorParam{
+ {
+ // 3 validators, all active
+ validatorsNumber: 3,
+ cs: []*BlockSimulator{
+ {1, 0, 0x7, 0x7},
+ {2, 1, 0x7, 0x7},
+ {3, 2, 0x7, 0x7},
+ {4, 0, 0x7, 0x7},
+ {5, 1, 0x7, 0x7},
+ },
+ },
+ {
+ // 5 validators, 4 active, 1 down
+ validatorsNumber: 5,
+ cs: []*BlockSimulator{
+ {1, 0, 0x1f, 0x1f},
+ {2, 1, 0x1f, 0x1f},
+ {3, 2, 0x1f, 0x1f},
+ {4, 3, 0x1f, 0x1f},
+ {5, 0, 0x1f, 0x1f},
+ {6, 1, 0x1f, 0x1f},
+ {7, 2, 0x1f, 0x1f},
+ },
+ },
+ {
+ // 21 validators, all active
+ validatorsNumber: 21,
+ cs: []*BlockSimulator{
+ {1, 0, 0x1fffff, 0x1fffff},
+ {2, 1, 0x1fffff, 0x1fffff},
+ {3, 2, 0x1fffff, 0x1fffff},
+ {4, 3, 0x1fffff, 0x1fffff},
+ {5, 4, 0x1fffff, 0x1fffff},
+ {6, 5, 0x1fffff, 0x1fffff},
+ {7, 6, 0x1fffff, 0x1fffff},
+ {8, 7, 0x1fffff, 0x1fffff},
+ {9, 8, 0x1fffff, 0x1fffff},
+ {10, 9, 0x1fffff, 0x1fffff},
+ {11, 10, 0x1fffff, 0x1fffff},
+ {12, 11, 0x1fffff, 0x1fffff},
+ {13, 12, 0x1fffff, 0x1fffff},
+ {14, 13, 0x1fffff, 0x1fffff},
+ {15, 14, 0x1fffff, 0x1fffff},
+ {16, 0, 0x1fffff, 0x1fffff},
+ {17, 1, 0x1fffff, 0x1fffff},
+ {18, 2, 0x1fffff, 0x1fffff},
+ },
+ },
+ {
+ // 21 validators, all active, the finalized fork can keep grow
+ validatorsNumber: 21,
+ cs: []*BlockSimulator{
+ {1, 1, 0x00fffe, 0x00fffe},
+ {2, 2, 0x00fffe, 0x00fffe},
+ {1, 0, 0x1f0001, 0x1fffff},
+ {2, 16, 0x1f0001, 0x1ffff1},
+ {3, 17, 0x1f0001, 0x1ffff1},
+ {4, 18, 0x1f0001, 0x1ffff1},
+ {5, 19, 0x1f0001, 0x1ffff1},
+ {3, 3, 0x00fffe, 0x00fffe}, // justify block2 and finalize block 1
+ {6, 20, 0x1f0001, 0x1fffff},
+ {4, 4, 0x00fffe, 0x1fffff},
+ {5, 5, 0x00fffe, 0x1fffff},
+ {6, 6, 0x00fffe, 0x1fffff},
+ {7, 7, 0x1fffff, 0x1fffff},
+ {8, 8, 0x1fffff, 0x1fffff},
+ },
+ },
+ {
+ // 21 validators, all active, naturally justified keep finalized block grow
+ validatorsNumber: 21,
+ cs: []*BlockSimulator{
+ {1, 1, 0x00fffe, 0x00fffe},
+ {2, 2, 0x00fffe, 0x00fffe}, // The block 3 will never produce
+ {1, 0, 0x1f0001, 0x1fffff},
+ {2, 16, 0x1f0001, 0x1fffff},
+ {3, 1, 0x1f0001, 0x1fffff},
+ {4, 2, 0x1f0001, 0x1fffff},
+ {5, 3, 0x1f0001, 0x1fffff},
+ {6, 4, 0x1f0001, 0x1fffff},
+ {7, 5, 0x1f0001, 0x1fffff},
+ {8, 6, 0x1f0001, 0x1fffff},
+ {9, 7, 0x1f0001, 0x1fffff},
+ {10, 8, 0x1f0001, 0x1fffff},
+ {11, 9, 0x1f0001, 0x1fffff},
+ {12, 10, 0x1f0001, 0x1fffff},
+ {13, 11, 0x1f0001, 0x1fffff},
+ {14, 12, 0x1f0001, 0x1fffff},
+ {15, 13, 0x1f0001, 0x1fffff},
+ {16, 14, 0x1f0001, 0x1fffff},
+ {17, 15, 0x1fffff, 0x1fffff}, // begin new round vote
+ {18, 16, 0x1fffff, 0x1fffff}, // attestation for block 17
+ {19, 17, 0x1fffff, 0x1fffff}, // attestation for block 18
+ },
+ },
+}
+
+func TestSimulateP2P(t *testing.T) {
+ for index, testcase := range simulatorTestcases {
+ c := NewCoordinator(testcase.validatorsNumber)
+ err := c.SimulateP2P(testcase.cs)
+ if err != nil {
+ t.Fatalf("[Testcase %d] simulate P2P error: %v", index, err)
+ }
+
+ for _, val := range c.validators {
+ t.Logf("[Testcase %d] validator(%d) head block: %d",
+ index, val.index, val.head.blockNumber)
+ t.Logf("[Testcase %d] validator(%d) highest justified block: %d",
+ index, val.index, val.head.GetJustifiedBlock().blockNumber)
+ t.Logf("[Testcase %d] validator(%d) highest finalized block: %d",
+ index, val.index, val.head.GetFinalizedBlock().blockNumber)
+ }
+
+ if c.CheckChain() == false {
+ t.Fatalf("[Testcase %d] chain not works as expected", index)
+ }
+ }
+}
diff --git a/consensus/parlia/ramanujanfork.go b/consensus/parlia/ramanujanfork.go
index 9b702ca6c..ce9089deb 100644
--- a/consensus/parlia/ramanujanfork.go
+++ b/consensus/parlia/ramanujanfork.go
@@ -29,14 +29,14 @@ func (p *Parlia) delayForRamanujanFork(snap *Snapshot, header *types.Header) tim
func (p *Parlia) blockTimeForRamanujanFork(snap *Snapshot, header, parent *types.Header) uint64 {
blockTime := parent.Time + p.config.Period
if p.chainConfig.IsRamanujan(header.Number) {
- blockTime = blockTime + backOffTime(snap, p.val)
+ blockTime = blockTime + p.backOffTime(snap, header, p.val)
}
return blockTime
}
func (p *Parlia) blockTimeVerifyForRamanujanFork(snap *Snapshot, header, parent *types.Header) error {
if p.chainConfig.IsRamanujan(header.Number) {
- if header.Time < parent.Time+p.config.Period+backOffTime(snap, header.Coinbase) {
+ if header.Time < parent.Time+p.config.Period+p.backOffTime(snap, header, header.Coinbase) {
return consensus.ErrFutureBlock
}
}
diff --git a/consensus/parlia/snapshot.go b/consensus/parlia/snapshot.go
index 5e86c8563..b51d96252 100644
--- a/consensus/parlia/snapshot.go
+++ b/consensus/parlia/snapshot.go
@@ -21,7 +21,6 @@ import (
"encoding/hex"
"encoding/json"
"errors"
- "math/big"
"sort"
lru "github.com/hashicorp/golang-lru"
@@ -40,11 +39,17 @@ type Snapshot struct {
ethAPI *ethapi.PublicBlockChainAPI
sigCache *lru.ARCCache // Cache of recent block signatures to speed up ecrecover
- Number uint64 `json:"number"` // Block number where the snapshot was created
- Hash common.Hash `json:"hash"` // Block hash where the snapshot was created
- Validators map[common.Address]struct{} `json:"validators"` // Set of authorized validators at this moment
- Recents map[uint64]common.Address `json:"recents"` // Set of recent validators for spam protections
- RecentForkHashes map[uint64]string `json:"recent_fork_hashes"` // Set of recent forkHash
+ Number uint64 `json:"number"` // Block number where the snapshot was created
+ Hash common.Hash `json:"hash"` // Block hash where the snapshot was created
+ Validators map[common.Address]*ValidatorInfo `json:"validators"` // Set of authorized validators at this moment
+ Recents map[uint64]common.Address `json:"recents"` // Set of recent validators for spam protections
+ RecentForkHashes map[uint64]string `json:"recent_fork_hashes"` // Set of recent forkHash
+ Attestation *types.VoteData `json:"attestation:omitempty"` // Attestation for fast finality
+}
+
+type ValidatorInfo struct {
+ Index int `json:"index:omitempty"` // The index should offset by 1
+ VoteAddress types.BLSPublicKey `json:"vote_address,omitempty"`
}
// newSnapshot creates a new snapshot with the specified startup parameters. This
@@ -56,6 +61,7 @@ func newSnapshot(
number uint64,
hash common.Hash,
validators []common.Address,
+ voteAddrs []types.BLSPublicKey,
ethAPI *ethapi.PublicBlockChainAPI,
) *Snapshot {
snap := &Snapshot{
@@ -66,10 +72,25 @@ func newSnapshot(
Hash: hash,
Recents: make(map[uint64]common.Address),
RecentForkHashes: make(map[uint64]string),
- Validators: make(map[common.Address]struct{}),
+ Validators: make(map[common.Address]*ValidatorInfo),
}
- for _, v := range validators {
- snap.Validators[v] = struct{}{}
+ for idx, v := range validators {
+ // The boneh fork from the genesis block
+ if len(voteAddrs) == len(validators) {
+ snap.Validators[v] = &ValidatorInfo{
+ VoteAddress: voteAddrs[idx],
+ }
+ } else {
+ snap.Validators[v] = &ValidatorInfo{}
+ }
+ }
+
+ // The boneh fork from the genesis block
+ if len(voteAddrs) == len(validators) {
+ validators := snap.validators()
+ for idx, v := range validators {
+ snap.Validators[v].Index = idx + 1 // offset by 1
+ }
}
return snap
}
@@ -115,13 +136,16 @@ func (s *Snapshot) copy() *Snapshot {
sigCache: s.sigCache,
Number: s.Number,
Hash: s.Hash,
- Validators: make(map[common.Address]struct{}),
+ Validators: make(map[common.Address]*ValidatorInfo),
Recents: make(map[uint64]common.Address),
RecentForkHashes: make(map[uint64]string),
}
for v := range s.Validators {
- cpy.Validators[v] = struct{}{}
+ cpy.Validators[v] = &ValidatorInfo{
+ Index: s.Validators[v].Index,
+ VoteAddress: s.Validators[v].VoteAddress,
+ }
}
for block, v := range s.Recents {
cpy.Recents[block] = v
@@ -129,6 +153,14 @@ func (s *Snapshot) copy() *Snapshot {
for block, id := range s.RecentForkHashes {
cpy.RecentForkHashes[block] = id
}
+ if s.Attestation != nil {
+ cpy.Attestation = &types.VoteData{
+ SourceNumber: s.Attestation.SourceNumber,
+ SourceHash: s.Attestation.SourceHash,
+ TargetNumber: s.Attestation.TargetNumber,
+ TargetHash: s.Attestation.TargetHash,
+ }
+ }
return cpy
}
@@ -142,7 +174,27 @@ func (s *Snapshot) isMajorityFork(forkHash string) bool {
return ally > len(s.RecentForkHashes)/2
}
-func (s *Snapshot) apply(headers []*types.Header, chain consensus.ChainHeaderReader, parents []*types.Header, chainId *big.Int) (*Snapshot, error) {
+func (s *Snapshot) updateAttestation(header *types.Header, chainConfig *params.ChainConfig, parliaConfig *params.ParliaConfig) {
+ if !chainConfig.IsBoneh(header.Number) {
+ return
+ }
+
+ // The attestation should have been checked in verify header, update directly
+ attestation, _ := getVoteAttestationFromHeader(header, chainConfig, parliaConfig)
+ if attestation == nil {
+ return
+ }
+
+ // Update attestation
+ s.Attestation = &types.VoteData{
+ SourceNumber: attestation.Data.SourceNumber,
+ SourceHash: attestation.Data.SourceHash,
+ TargetNumber: attestation.Data.TargetNumber,
+ TargetHash: attestation.Data.TargetHash,
+ }
+}
+
+func (s *Snapshot) apply(headers []*types.Header, chain consensus.ChainHeaderReader, parents []*types.Header, chainConfig *params.ChainConfig) (*Snapshot, error) {
// Allow passing in no headers for cleaner code
if len(headers) == 0 {
return s, nil
@@ -168,14 +220,14 @@ func (s *Snapshot) apply(headers []*types.Header, chain consensus.ChainHeaderRea
for _, header := range headers {
number := header.Number.Uint64()
// Delete the oldest validator from the recent list to allow it signing again
- if limit := uint64(len(snap.Validators)/2 + 1); number >= limit {
- delete(snap.Recents, number-limit)
+ if limit := getSignRecentlyLimit(header.Number, len(snap.Validators), chainConfig); number >= uint64(limit) {
+ delete(snap.Recents, number-uint64(limit))
}
if limit := uint64(len(snap.Validators)); number >= limit {
delete(snap.RecentForkHashes, number-limit)
}
// Resolve the authorization key and check against signers
- validator, err := ecrecover(header, s.sigCache, chainId)
+ validator, err := ecrecover(header, s.sigCache, chainConfig.ChainID)
if err != nil {
return nil, err
}
@@ -195,18 +247,23 @@ func (s *Snapshot) apply(headers []*types.Header, chain consensus.ChainHeaderRea
return nil, consensus.ErrUnknownAncestor
}
- validatorBytes := checkpointHeader.Extra[extraVanity : len(checkpointHeader.Extra)-extraSeal]
// get validators from headers and use that for new validator set
- newValArr, err := ParseValidators(validatorBytes)
+ newValArr, voteAddrs, err := parseValidators(checkpointHeader, chainConfig, s.config)
if err != nil {
return nil, err
}
- newVals := make(map[common.Address]struct{}, len(newValArr))
- for _, val := range newValArr {
- newVals[val] = struct{}{}
+ newVals := make(map[common.Address]*ValidatorInfo, len(newValArr))
+ for idx, val := range newValArr {
+ if !chainConfig.IsBoneh(header.Number) {
+ newVals[val] = &ValidatorInfo{}
+ } else {
+ newVals[val] = &ValidatorInfo{
+ VoteAddress: voteAddrs[idx],
+ }
+ }
}
- oldLimit := len(snap.Validators)/2 + 1
- newLimit := len(newVals)/2 + 1
+ oldLimit := getSignRecentlyLimit(header.Number, len(snap.Validators), chainConfig)
+ newLimit := getSignRecentlyLimit(header.Number, len(newVals), chainConfig)
if newLimit < oldLimit {
for i := 0; i < oldLimit-newLimit; i++ {
delete(snap.Recents, number-uint64(newLimit)-uint64(i))
@@ -220,7 +277,14 @@ func (s *Snapshot) apply(headers []*types.Header, chain consensus.ChainHeaderRea
}
}
snap.Validators = newVals
+ if chainConfig.IsBoneh(header.Number) {
+ validators := snap.validators()
+ for idx, val := range validators {
+ snap.Validators[val].Index = idx + 1 // offset by 1
+ }
+ }
}
+ snap.updateAttestation(header, chainConfig, s.config)
snap.RecentForkHashes[number] = hex.EncodeToString(header.Extra[extraVanity-nextForkHashSize : extraVanity])
}
snap.Number += uint64(len(headers))
@@ -272,6 +336,10 @@ func (s *Snapshot) enoughDistance(validator common.Address, header *types.Header
}
func (s *Snapshot) indexOfVal(validator common.Address) int {
+ if validator, ok := s.Validators[validator]; ok && validator.Index > 0 {
+ return validator.Index - 1 // Index is offset by 1
+ }
+
validators := s.validators()
for idx, val := range validators {
if val == validator {
@@ -287,18 +355,31 @@ func (s *Snapshot) supposeValidator() common.Address {
return validators[index]
}
-func ParseValidators(validatorsBytes []byte) ([]common.Address, error) {
- if len(validatorsBytes)%validatorBytesLength != 0 {
- return nil, errors.New("invalid validators bytes")
+func parseValidators(header *types.Header, chainConfig *params.ChainConfig, parliaConfig *params.ParliaConfig) ([]common.Address, []types.BLSPublicKey, error) {
+ validatorsBytes := getValidatorBytesFromHeader(header, chainConfig, parliaConfig)
+ if len(validatorsBytes) == 0 {
+ return nil, nil, errors.New("invalid validators bytes")
+ }
+
+ if !chainConfig.IsBoneh(header.Number) {
+ n := len(validatorsBytes) / validatorBytesLength
+ result := make([]common.Address, n)
+ for i := 0; i < n; i++ {
+ address := make([]byte, validatorBytesLength)
+ copy(address, validatorsBytes[i*validatorBytesLength:(i+1)*validatorBytesLength])
+ result[i] = common.BytesToAddress(address)
+ }
+ return result, nil, nil
}
- n := len(validatorsBytes) / validatorBytesLength
- result := make([]common.Address, n)
+
+ n := len(validatorsBytes) / validatorBytesLengthAfterBoneh
+ cnsAddrs := make([]common.Address, n)
+ voteAddrs := make([]types.BLSPublicKey, n)
for i := 0; i < n; i++ {
- address := make([]byte, validatorBytesLength)
- copy(address, validatorsBytes[i*validatorBytesLength:(i+1)*validatorBytesLength])
- result[i] = common.BytesToAddress(address)
+ cnsAddrs[i] = common.BytesToAddress(validatorsBytes[i*validatorBytesLengthAfterBoneh : i*validatorBytesLengthAfterBoneh+common.AddressLength])
+ copy(voteAddrs[i][:], validatorsBytes[i*validatorBytesLengthAfterBoneh+common.AddressLength:(i+1)*validatorBytesLengthAfterBoneh])
}
- return result, nil
+ return cnsAddrs, voteAddrs, nil
}
func FindAncientHeader(header *types.Header, ite uint64, chain consensus.ChainHeaderReader, candidateParents []*types.Header) *types.Header {
diff --git a/console/console_test.go b/console/console_test.go
index f6ab78141..1f83de5c8 100644
--- a/console/console_test.go
+++ b/console/console_test.go
@@ -16,327 +16,328 @@
package console
-import (
- "bytes"
- "errors"
- "fmt"
- "io/ioutil"
- "os"
- "strings"
- "testing"
- "time"
-
- "github.com/ethereum/go-ethereum/common"
- "github.com/ethereum/go-ethereum/consensus/ethash"
- "github.com/ethereum/go-ethereum/console/prompt"
- "github.com/ethereum/go-ethereum/core"
- "github.com/ethereum/go-ethereum/eth"
- "github.com/ethereum/go-ethereum/eth/ethconfig"
- "github.com/ethereum/go-ethereum/internal/jsre"
- "github.com/ethereum/go-ethereum/miner"
- "github.com/ethereum/go-ethereum/node"
-)
-
-const (
- testInstance = "console-tester"
- testAddress = "0x8605cdbbdb6d264aa742e77020dcbc58fcdce182"
-)
-
-// hookedPrompter implements UserPrompter to simulate use input via channels.
-type hookedPrompter struct {
- scheduler chan string
-}
-
-func (p *hookedPrompter) PromptInput(prompt string) (string, error) {
- // Send the prompt to the tester
- select {
- case p.scheduler <- prompt:
- case <-time.After(time.Second):
- return "", errors.New("prompt timeout")
- }
- // Retrieve the response and feed to the console
- select {
- case input := <-p.scheduler:
- return input, nil
- case <-time.After(time.Second):
- return "", errors.New("input timeout")
- }
-}
-
-func (p *hookedPrompter) PromptPassword(prompt string) (string, error) {
- return "", errors.New("not implemented")
-}
-func (p *hookedPrompter) PromptConfirm(prompt string) (bool, error) {
- return false, errors.New("not implemented")
-}
-func (p *hookedPrompter) SetHistory(history []string) {}
-func (p *hookedPrompter) AppendHistory(command string) {}
-func (p *hookedPrompter) ClearHistory() {}
-func (p *hookedPrompter) SetWordCompleter(completer prompt.WordCompleter) {}
-
-// tester is a console test environment for the console tests to operate on.
-type tester struct {
- workspace string
- stack *node.Node
- ethereum *eth.Ethereum
- console *Console
- input *hookedPrompter
- output *bytes.Buffer
-}
-
-// newTester creates a test environment based on which the console can operate.
-// Please ensure you call Close() on the returned tester to avoid leaks.
-func newTester(t *testing.T, confOverride func(*ethconfig.Config)) *tester {
- // Create a temporary storage for the node keys and initialize it
- workspace, err := ioutil.TempDir("", "console-tester-")
- if err != nil {
- t.Fatalf("failed to create temporary keystore: %v", err)
- }
-
- // Create a networkless protocol stack and start an Ethereum service within
- stack, err := node.New(&node.Config{DataDir: workspace, UseLightweightKDF: true, Name: testInstance})
- if err != nil {
- t.Fatalf("failed to create node: %v", err)
- }
- ethConf := ðconfig.Config{
- Genesis: core.DeveloperGenesisBlock(15, common.Address{}),
- Miner: miner.Config{
- Etherbase: common.HexToAddress(testAddress),
- },
- Ethash: ethash.Config{
- PowMode: ethash.ModeTest,
- },
- }
- if confOverride != nil {
- confOverride(ethConf)
- }
- ethBackend, err := eth.New(stack, ethConf)
- if err != nil {
- t.Fatalf("failed to register Ethereum protocol: %v", err)
- }
- // Start the node and assemble the JavaScript console around it
- if err = stack.Start(); err != nil {
- t.Fatalf("failed to start test stack: %v", err)
- }
- client, err := stack.Attach()
- if err != nil {
- t.Fatalf("failed to attach to node: %v", err)
- }
- prompter := &hookedPrompter{scheduler: make(chan string)}
- printer := new(bytes.Buffer)
-
- console, err := New(Config{
- DataDir: stack.DataDir(),
- DocRoot: "testdata",
- Client: client,
- Prompter: prompter,
- Printer: printer,
- Preload: []string{"preload.js"},
- })
- if err != nil {
- t.Fatalf("failed to create JavaScript console: %v", err)
- }
- // Create the final tester and return
- return &tester{
- workspace: workspace,
- stack: stack,
- ethereum: ethBackend,
- console: console,
- input: prompter,
- output: printer,
- }
-}
-
-// Close cleans up any temporary data folders and held resources.
-func (env *tester) Close(t *testing.T) {
- if err := env.console.Stop(false); err != nil {
- t.Errorf("failed to stop embedded console: %v", err)
- }
- if err := env.stack.Close(); err != nil {
- t.Errorf("failed to tear down embedded node: %v", err)
- }
- os.RemoveAll(env.workspace)
-}
-
-// Tests that the node lists the correct welcome message, notably that it contains
-// the instance name, coinbase account, block number, data directory and supported
-// console modules.
-func TestWelcome(t *testing.T) {
- tester := newTester(t, nil)
- defer tester.Close(t)
-
- tester.console.Welcome()
-
- output := tester.output.String()
- if want := "Welcome"; !strings.Contains(output, want) {
- t.Fatalf("console output missing welcome message: have\n%s\nwant also %s", output, want)
- }
- if want := fmt.Sprintf("instance: %s", testInstance); !strings.Contains(output, want) {
- t.Fatalf("console output missing instance: have\n%s\nwant also %s", output, want)
- }
- if want := fmt.Sprintf("coinbase: %s", testAddress); !strings.Contains(output, want) {
- t.Fatalf("console output missing coinbase: have\n%s\nwant also %s", output, want)
- }
- if want := "at block: 0"; !strings.Contains(output, want) {
- t.Fatalf("console output missing sync status: have\n%s\nwant also %s", output, want)
- }
- if want := fmt.Sprintf("datadir: %s", tester.workspace); !strings.Contains(output, want) {
- t.Fatalf("console output missing coinbase: have\n%s\nwant also %s", output, want)
- }
-}
-
-// Tests that JavaScript statement evaluation works as intended.
-func TestEvaluate(t *testing.T) {
- tester := newTester(t, nil)
- defer tester.Close(t)
-
- tester.console.Evaluate("2 + 2")
- if output := tester.output.String(); !strings.Contains(output, "4") {
- t.Fatalf("statement evaluation failed: have %s, want %s", output, "4")
- }
-}
-
-// Tests that the console can be used in interactive mode.
-func TestInteractive(t *testing.T) {
- // Create a tester and run an interactive console in the background
- tester := newTester(t, nil)
- defer tester.Close(t)
-
- go tester.console.Interactive()
-
- // Wait for a prompt and send a statement back
- select {
- case <-tester.input.scheduler:
- case <-time.After(time.Second):
- t.Fatalf("initial prompt timeout")
- }
- select {
- case tester.input.scheduler <- "2+2":
- case <-time.After(time.Second):
- t.Fatalf("input feedback timeout")
- }
- // Wait for the second prompt and ensure first statement was evaluated
- select {
- case <-tester.input.scheduler:
- case <-time.After(time.Second):
- t.Fatalf("secondary prompt timeout")
- }
- if output := tester.output.String(); !strings.Contains(output, "4") {
- t.Fatalf("statement evaluation failed: have %s, want %s", output, "4")
- }
-}
-
-// Tests that preloaded JavaScript files have been executed before user is given
-// input.
-func TestPreload(t *testing.T) {
- tester := newTester(t, nil)
- defer tester.Close(t)
-
- tester.console.Evaluate("preloaded")
- if output := tester.output.String(); !strings.Contains(output, "some-preloaded-string") {
- t.Fatalf("preloaded variable missing: have %s, want %s", output, "some-preloaded-string")
- }
-}
-
-// Tests that JavaScript scripts can be executes from the configured asset path.
-func TestExecute(t *testing.T) {
- tester := newTester(t, nil)
- defer tester.Close(t)
-
- tester.console.Execute("exec.js")
-
- tester.console.Evaluate("execed")
- if output := tester.output.String(); !strings.Contains(output, "some-executed-string") {
- t.Fatalf("execed variable missing: have %s, want %s", output, "some-executed-string")
- }
-}
-
-// Tests that the JavaScript objects returned by statement executions are properly
-// pretty printed instead of just displaying "[object]".
-func TestPrettyPrint(t *testing.T) {
- tester := newTester(t, nil)
- defer tester.Close(t)
-
- tester.console.Evaluate("obj = {int: 1, string: 'two', list: [3, 3, 3], obj: {null: null, func: function(){}}}")
-
- // Define some specially formatted fields
- var (
- one = jsre.NumberColor("1")
- two = jsre.StringColor("\"two\"")
- three = jsre.NumberColor("3")
- null = jsre.SpecialColor("null")
- fun = jsre.FunctionColor("function()")
- )
- // Assemble the actual output we're after and verify
- want := `{
- int: ` + one + `,
- list: [` + three + `, ` + three + `, ` + three + `],
- obj: {
- null: ` + null + `,
- func: ` + fun + `
- },
- string: ` + two + `
-}
-`
- if output := tester.output.String(); output != want {
- t.Fatalf("pretty print mismatch: have %s, want %s", output, want)
- }
-}
-
-// Tests that the JavaScript exceptions are properly formatted and colored.
-func TestPrettyError(t *testing.T) {
- tester := newTester(t, nil)
- defer tester.Close(t)
- tester.console.Evaluate("throw 'hello'")
-
- want := jsre.ErrorColor("hello") + "\n\tat :1:7(1)\n\n"
- if output := tester.output.String(); output != want {
- t.Fatalf("pretty error mismatch: have %s, want %s", output, want)
- }
-}
-
-// Tests that tests if the number of indents for JS input is calculated correct.
-func TestIndenting(t *testing.T) {
- testCases := []struct {
- input string
- expectedIndentCount int
- }{
- {`var a = 1;`, 0},
- {`"some string"`, 0},
- {`"some string with (parenthesis`, 0},
- {`"some string with newline
- ("`, 0},
- {`function v(a,b) {}`, 0},
- {`function f(a,b) { var str = "asd("; };`, 0},
- {`function f(a) {`, 1},
- {`function f(a, function(b) {`, 2},
- {`function f(a, function(b) {
- var str = "a)}";
- });`, 0},
- {`function f(a,b) {
- var str = "a{b(" + a, ", " + b;
- }`, 0},
- {`var str = "\"{"`, 0},
- {`var str = "'("`, 0},
- {`var str = "\\{"`, 0},
- {`var str = "\\\\{"`, 0},
- {`var str = 'a"{`, 0},
- {`var obj = {`, 1},
- {`var obj = { {a:1`, 2},
- {`var obj = { {a:1}`, 1},
- {`var obj = { {a:1}, b:2}`, 0},
- {`var obj = {}`, 0},
- {`var obj = {
- a: 1, b: 2
- }`, 0},
- {`var test = }`, -1},
- {`var str = "a\""; var obj = {`, 1},
- }
-
- for i, tt := range testCases {
- counted := countIndents(tt.input)
- if counted != tt.expectedIndentCount {
- t.Errorf("test %d: invalid indenting: have %d, want %d", i, counted, tt.expectedIndentCount)
- }
- }
-}
+//
+//import (
+// "bytes"
+// "errors"
+// "fmt"
+// "io/ioutil"
+// "os"
+// "strings"
+// "testing"
+// "time"
+//
+// "github.com/ethereum/go-ethereum/common"
+// "github.com/ethereum/go-ethereum/consensus/ethash"
+// "github.com/ethereum/go-ethereum/console/prompt"
+// "github.com/ethereum/go-ethereum/core"
+// "github.com/ethereum/go-ethereum/eth"
+// "github.com/ethereum/go-ethereum/eth/ethconfig"
+// "github.com/ethereum/go-ethereum/internal/jsre"
+// "github.com/ethereum/go-ethereum/miner"
+// "github.com/ethereum/go-ethereum/node"
+//)
+//
+//const (
+// testInstance = "console-tester"
+// testAddress = "0x8605cdbbdb6d264aa742e77020dcbc58fcdce182"
+//)
+//
+//// hookedPrompter implements UserPrompter to simulate use input via channels.
+//type hookedPrompter struct {
+// scheduler chan string
+//}
+//
+//func (p *hookedPrompter) PromptInput(prompt string) (string, error) {
+// // Send the prompt to the tester
+// select {
+// case p.scheduler <- prompt:
+// case <-time.After(time.Second):
+// return "", errors.New("prompt timeout")
+// }
+// // Retrieve the response and feed to the console
+// select {
+// case input := <-p.scheduler:
+// return input, nil
+// case <-time.After(time.Second):
+// return "", errors.New("input timeout")
+// }
+//}
+//
+//func (p *hookedPrompter) PromptPassword(prompt string) (string, error) {
+// return "", errors.New("not implemented")
+//}
+//func (p *hookedPrompter) PromptConfirm(prompt string) (bool, error) {
+// return false, errors.New("not implemented")
+//}
+//func (p *hookedPrompter) SetHistory(history []string) {}
+//func (p *hookedPrompter) AppendHistory(command string) {}
+//func (p *hookedPrompter) ClearHistory() {}
+//func (p *hookedPrompter) SetWordCompleter(completer prompt.WordCompleter) {}
+//
+//// tester is a console test environment for the console tests to operate on.
+//type tester struct {
+// workspace string
+// stack *node.Node
+// ethereum *eth.Ethereum
+// console *Console
+// input *hookedPrompter
+// output *bytes.Buffer
+//}
+//
+//// newTester creates a test environment based on which the console can operate.
+//// Please ensure you call Close() on the returned tester to avoid leaks.
+//func newTester(t *testing.T, confOverride func(*ethconfig.Config)) *tester {
+// // Create a temporary storage for the node keys and initialize it
+// workspace, err := ioutil.TempDir("", "console-tester-")
+// if err != nil {
+// t.Fatalf("failed to create temporary keystore: %v", err)
+// }
+//
+// // Create a networkless protocol stack and start an Ethereum service within
+// stack, err := node.New(&node.Config{DataDir: workspace, UseLightweightKDF: true, Name: testInstance})
+// if err != nil {
+// t.Fatalf("failed to create node: %v", err)
+// }
+// ethConf := ðconfig.Config{
+// Genesis: core.DeveloperGenesisBlock(15, common.Address{}),
+// Miner: miner.Config{
+// Etherbase: common.HexToAddress(testAddress),
+// },
+// Ethash: ethash.Config{
+// PowMode: ethash.ModeTest,
+// },
+// }
+// if confOverride != nil {
+// confOverride(ethConf)
+// }
+// ethBackend, err := eth.New(stack, ethConf)
+// if err != nil {
+// t.Fatalf("failed to register Ethereum protocol: %v", err)
+// }
+// // Start the node and assemble the JavaScript console around it
+// if err = stack.Start(); err != nil {
+// t.Fatalf("failed to start test stack: %v", err)
+// }
+// client, err := stack.Attach()
+// if err != nil {
+// t.Fatalf("failed to attach to node: %v", err)
+// }
+// prompter := &hookedPrompter{scheduler: make(chan string)}
+// printer := new(bytes.Buffer)
+//
+// console, err := New(Config{
+// DataDir: stack.DataDir(),
+// DocRoot: "testdata",
+// Client: client,
+// Prompter: prompter,
+// Printer: printer,
+// Preload: []string{"preload.js"},
+// })
+// if err != nil {
+// t.Fatalf("failed to create JavaScript console: %v", err)
+// }
+// // Create the final tester and return
+// return &tester{
+// workspace: workspace,
+// stack: stack,
+// ethereum: ethBackend,
+// console: console,
+// input: prompter,
+// output: printer,
+// }
+//}
+//
+//// Close cleans up any temporary data folders and held resources.
+//func (env *tester) Close(t *testing.T) {
+// if err := env.console.Stop(false); err != nil {
+// t.Errorf("failed to stop embedded console: %v", err)
+// }
+// if err := env.stack.Close(); err != nil {
+// t.Errorf("failed to tear down embedded node: %v", err)
+// }
+// os.RemoveAll(env.workspace)
+//}
+//
+//// Tests that the node lists the correct welcome message, notably that it contains
+//// the instance name, coinbase account, block number, data directory and supported
+//// console modules.
+//func TestWelcome(t *testing.T) {
+// tester := newTester(t, nil)
+// defer tester.Close(t)
+//
+// tester.console.Welcome()
+//
+// output := tester.output.String()
+// if want := "Welcome"; !strings.Contains(output, want) {
+// t.Fatalf("console output missing welcome message: have\n%s\nwant also %s", output, want)
+// }
+// if want := fmt.Sprintf("instance: %s", testInstance); !strings.Contains(output, want) {
+// t.Fatalf("console output missing instance: have\n%s\nwant also %s", output, want)
+// }
+// if want := fmt.Sprintf("coinbase: %s", testAddress); !strings.Contains(output, want) {
+// t.Fatalf("console output missing coinbase: have\n%s\nwant also %s", output, want)
+// }
+// if want := "at block: 0"; !strings.Contains(output, want) {
+// t.Fatalf("console output missing sync status: have\n%s\nwant also %s", output, want)
+// }
+// if want := fmt.Sprintf("datadir: %s", tester.workspace); !strings.Contains(output, want) {
+// t.Fatalf("console output missing coinbase: have\n%s\nwant also %s", output, want)
+// }
+//}
+//
+//// Tests that JavaScript statement evaluation works as intended.
+//func TestEvaluate(t *testing.T) {
+// tester := newTester(t, nil)
+// defer tester.Close(t)
+//
+// tester.console.Evaluate("2 + 2")
+// if output := tester.output.String(); !strings.Contains(output, "4") {
+// t.Fatalf("statement evaluation failed: have %s, want %s", output, "4")
+// }
+//}
+//
+//// Tests that the console can be used in interactive mode.
+//func TestInteractive(t *testing.T) {
+// // Create a tester and run an interactive console in the background
+// tester := newTester(t, nil)
+// defer tester.Close(t)
+//
+// go tester.console.Interactive()
+//
+// // Wait for a prompt and send a statement back
+// select {
+// case <-tester.input.scheduler:
+// case <-time.After(time.Second):
+// t.Fatalf("initial prompt timeout")
+// }
+// select {
+// case tester.input.scheduler <- "2+2":
+// case <-time.After(time.Second):
+// t.Fatalf("input feedback timeout")
+// }
+// // Wait for the second prompt and ensure first statement was evaluated
+// select {
+// case <-tester.input.scheduler:
+// case <-time.After(time.Second):
+// t.Fatalf("secondary prompt timeout")
+// }
+// if output := tester.output.String(); !strings.Contains(output, "4") {
+// t.Fatalf("statement evaluation failed: have %s, want %s", output, "4")
+// }
+//}
+//
+//// Tests that preloaded JavaScript files have been executed before user is given
+//// input.
+//func TestPreload(t *testing.T) {
+// tester := newTester(t, nil)
+// defer tester.Close(t)
+//
+// tester.console.Evaluate("preloaded")
+// if output := tester.output.String(); !strings.Contains(output, "some-preloaded-string") {
+// t.Fatalf("preloaded variable missing: have %s, want %s", output, "some-preloaded-string")
+// }
+//}
+//
+//// Tests that JavaScript scripts can be executes from the configured asset path.
+//func TestExecute(t *testing.T) {
+// tester := newTester(t, nil)
+// defer tester.Close(t)
+//
+// tester.console.Execute("exec.js")
+//
+// tester.console.Evaluate("execed")
+// if output := tester.output.String(); !strings.Contains(output, "some-executed-string") {
+// t.Fatalf("execed variable missing: have %s, want %s", output, "some-executed-string")
+// }
+//}
+//
+//// Tests that the JavaScript objects returned by statement executions are properly
+//// pretty printed instead of just displaying "[object]".
+//func TestPrettyPrint(t *testing.T) {
+// tester := newTester(t, nil)
+// defer tester.Close(t)
+//
+// tester.console.Evaluate("obj = {int: 1, string: 'two', list: [3, 3, 3], obj: {null: null, func: function(){}}}")
+//
+// // Define some specially formatted fields
+// var (
+// one = jsre.NumberColor("1")
+// two = jsre.StringColor("\"two\"")
+// three = jsre.NumberColor("3")
+// null = jsre.SpecialColor("null")
+// fun = jsre.FunctionColor("function()")
+// )
+// // Assemble the actual output we're after and verify
+// want := `{
+// int: ` + one + `,
+// list: [` + three + `, ` + three + `, ` + three + `],
+// obj: {
+// null: ` + null + `,
+// func: ` + fun + `
+// },
+// string: ` + two + `
+//}
+//`
+// if output := tester.output.String(); output != want {
+// t.Fatalf("pretty print mismatch: have %s, want %s", output, want)
+// }
+//}
+//
+//// Tests that the JavaScript exceptions are properly formatted and colored.
+//func TestPrettyError(t *testing.T) {
+// tester := newTester(t, nil)
+// defer tester.Close(t)
+// tester.console.Evaluate("throw 'hello'")
+//
+// want := jsre.ErrorColor("hello") + "\n\tat :1:7(1)\n\n"
+// if output := tester.output.String(); output != want {
+// t.Fatalf("pretty error mismatch: have %s, want %s", output, want)
+// }
+//}
+//
+//// Tests that tests if the number of indents for JS input is calculated correct.
+//func TestIndenting(t *testing.T) {
+// testCases := []struct {
+// input string
+// expectedIndentCount int
+// }{
+// {`var a = 1;`, 0},
+// {`"some string"`, 0},
+// {`"some string with (parenthesis`, 0},
+// {`"some string with newline
+// ("`, 0},
+// {`function v(a,b) {}`, 0},
+// {`function f(a,b) { var str = "asd("; };`, 0},
+// {`function f(a) {`, 1},
+// {`function f(a, function(b) {`, 2},
+// {`function f(a, function(b) {
+// var str = "a)}";
+// });`, 0},
+// {`function f(a,b) {
+// var str = "a{b(" + a, ", " + b;
+// }`, 0},
+// {`var str = "\"{"`, 0},
+// {`var str = "'("`, 0},
+// {`var str = "\\{"`, 0},
+// {`var str = "\\\\{"`, 0},
+// {`var str = 'a"{`, 0},
+// {`var obj = {`, 1},
+// {`var obj = { {a:1`, 2},
+// {`var obj = { {a:1}`, 1},
+// {`var obj = { {a:1}, b:2}`, 0},
+// {`var obj = {}`, 0},
+// {`var obj = {
+// a: 1, b: 2
+// }`, 0},
+// {`var test = }`, -1},
+// {`var str = "a\""; var obj = {`, 1},
+// }
+//
+// for i, tt := range testCases {
+// counted := countIndents(tt.input)
+// if counted != tt.expectedIndentCount {
+// t.Errorf("test %d: invalid indenting: have %d, want %d", i, counted, tt.expectedIndentCount)
+// }
+// }
+//}
diff --git a/core/block_validator.go b/core/block_validator.go
index b109c1e54..4cf53a6cd 100644
--- a/core/block_validator.go
+++ b/core/block_validator.go
@@ -20,6 +20,7 @@ import (
"fmt"
"time"
+ "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/consensus"
"github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/types"
@@ -112,7 +113,7 @@ func (v *BlockValidator) ValidateBody(block *types.Block) error {
// transition, such as amount of used gas, the receipt roots and the state root
// itself. ValidateState returns a database batch if the validation was a success
// otherwise nil and an error is returned.
-func (v *BlockValidator) ValidateState(block *types.Block, statedb *state.StateDB, receipts types.Receipts, usedGas uint64, skipHeavyVerify bool) error {
+func (v *BlockValidator) ValidateState(block *types.Block, statedb *state.StateDB, receipts types.Receipts, usedGas uint64) error {
header := block.Header()
if block.GasUsed() != usedGas {
return fmt.Errorf("invalid gas used (remote: %d local: %d)", block.GasUsed(), usedGas)
@@ -135,13 +136,13 @@ func (v *BlockValidator) ValidateState(block *types.Block, statedb *state.StateD
return nil
},
}
- if skipHeavyVerify {
+ if statedb.IsPipeCommit() {
validateFuns = append(validateFuns, func() error {
if err := statedb.WaitPipeVerification(); err != nil {
return err
}
+ statedb.CorrectAccountsRoot(common.Hash{})
statedb.Finalise(v.config.IsEIP158(header.Number))
- statedb.AccountsIntermediateRoot()
return nil
})
} else {
diff --git a/core/blockchain.go b/core/blockchain.go
index 6c87ffc70..d50cf43eb 100644
--- a/core/blockchain.go
+++ b/core/blockchain.go
@@ -53,6 +53,9 @@ var (
headHeaderGauge = metrics.NewRegisteredGauge("chain/head/header", nil)
headFastBlockGauge = metrics.NewRegisteredGauge("chain/head/receipt", nil)
+ justifiedBlockGauge = metrics.NewRegisteredGauge("chain/head/justified", nil)
+ finalizedBlockGauge = metrics.NewRegisteredGauge("chain/head/finalized", nil)
+
accountReadTimer = metrics.NewRegisteredTimer("chain/account/reads", nil)
accountHashTimer = metrics.NewRegisteredTimer("chain/account/hashes", nil)
accountUpdateTimer = metrics.NewRegisteredTimer("chain/account/updates", nil)
@@ -195,15 +198,16 @@ type BlockChain struct {
txLookupLimit uint64
triesInMemory uint64
- hc *HeaderChain
- rmLogsFeed event.Feed
- chainFeed event.Feed
- chainSideFeed event.Feed
- chainHeadFeed event.Feed
- logsFeed event.Feed
- blockProcFeed event.Feed
- scope event.SubscriptionScope
- genesisBlock *types.Block
+ hc *HeaderChain
+ rmLogsFeed event.Feed
+ chainFeed event.Feed
+ chainSideFeed event.Feed
+ chainHeadFeed event.Feed
+ logsFeed event.Feed
+ blockProcFeed event.Feed
+ finalizedHeaderFeed event.Feed
+ scope event.SubscriptionScope
+ genesisBlock *types.Block
chainmu sync.RWMutex // blockchain insertion lock
@@ -526,6 +530,50 @@ func (bc *BlockChain) empty() bool {
return true
}
+// getJustifiedNumber returns the highest justified number before the specific block.
+func (bc *BlockChain) getJustifiedNumber(header *types.Header) uint64 {
+ if p, ok := bc.engine.(consensus.PoSA); ok {
+ justifiedHeader := p.GetJustifiedHeader(bc, header)
+ if justifiedHeader != nil {
+ return justifiedHeader.Number.Uint64()
+ }
+ }
+
+ return 0
+}
+
+// getFinalizedNumber returns the highest finalized number before the specific block.
+func (bc *BlockChain) getFinalizedNumber(header *types.Header) uint64 {
+ if p, ok := bc.engine.(consensus.PoSA); ok {
+ if finalizedHeader := p.GetFinalizedHeader(bc, header, types.NaturallyFinalizedDist); finalizedHeader != nil {
+ return finalizedHeader.Number.Uint64()
+ }
+ }
+
+ return 0
+}
+
+// isFinalizedBlockHigher returns true when the new block's finalized block is higher than current block.
+func (bc *BlockChain) isFinalizedBlockHigher(header *types.Header, curHeader *types.Header) bool {
+ p, ok := bc.engine.(consensus.PoSA)
+ if !ok {
+ return false
+ }
+
+ ancestor := rawdb.FindCommonAncestor(bc.db, header, curHeader)
+ if ancestor == nil {
+ return false
+ }
+
+ finalized := p.GetFinalizedHeader(bc, header, header.Number.Uint64()-ancestor.Number.Uint64())
+ curFinalized := p.GetFinalizedHeader(bc, curHeader, curHeader.Number.Uint64()-ancestor.Number.Uint64())
+ if finalized == nil || curFinalized == nil {
+ return false
+ }
+
+ return finalized.Number.Uint64() > curFinalized.Number.Uint64()
+}
+
// loadLastState loads the last known chain state from the database. This method
// assumes that the chain manager mutex is held.
func (bc *BlockChain) loadLastState() error {
@@ -546,6 +594,8 @@ func (bc *BlockChain) loadLastState() error {
// Everything seems to be fine, set as the head block
bc.currentBlock.Store(currentBlock)
headBlockGauge.Update(int64(currentBlock.NumberU64()))
+ justifiedBlockGauge.Update(int64(bc.getJustifiedNumber(currentBlock.Header())))
+ finalizedBlockGauge.Update(int64(bc.getFinalizedNumber(currentBlock.Header())))
// Restore the last known head header
currentHeader := currentBlock.Header()
@@ -702,6 +752,8 @@ func (bc *BlockChain) setHeadBeyondRoot(head uint64, root common.Hash) (uint64,
// to low, so it's safe the update in-memory markers directly.
bc.currentBlock.Store(newHeadBlock)
headBlockGauge.Update(int64(newHeadBlock.NumberU64()))
+ justifiedBlockGauge.Update(int64(bc.getJustifiedNumber(newHeadBlock.Header())))
+ finalizedBlockGauge.Update(int64(bc.getFinalizedNumber(newHeadBlock.Header())))
}
// Rewind the fast block in a simpleton way to the target head
if currentFastBlock := bc.CurrentFastBlock(); currentFastBlock != nil && header.Number.Uint64() < currentFastBlock.NumberU64() {
@@ -789,6 +841,8 @@ func (bc *BlockChain) FastSyncCommitHead(hash common.Hash) error {
bc.chainmu.Lock()
bc.currentBlock.Store(block)
headBlockGauge.Update(int64(block.NumberU64()))
+ justifiedBlockGauge.Update(int64(bc.getJustifiedNumber(block.Header())))
+ finalizedBlockGauge.Update(int64(bc.getFinalizedNumber(block.Header())))
bc.chainmu.Unlock()
// Destroy any existing state snapshot and regenerate it in the background,
@@ -842,6 +896,11 @@ func (bc *BlockChain) StateAt(root common.Hash) (*state.StateDB, error) {
return state.New(root, bc.stateCache, bc.snaps)
}
+// StateAtWithSharedPool returns a new mutable state based on a particular point in time with sharedStorage
+func (bc *BlockChain) StateAtWithSharedPool(root common.Hash) (*state.StateDB, error) {
+ return state.NewWithSharedPool(root, bc.stateCache, bc.snaps)
+}
+
// StateCache returns the caching database underpinning the blockchain instance.
func (bc *BlockChain) StateCache() state.Database {
return bc.stateCache
@@ -875,6 +934,8 @@ func (bc *BlockChain) ResetWithGenesisBlock(genesis *types.Block) error {
bc.genesisBlock = genesis
bc.currentBlock.Store(bc.genesisBlock)
headBlockGauge.Update(int64(bc.genesisBlock.NumberU64()))
+ justifiedBlockGauge.Update(int64(bc.genesisBlock.NumberU64()))
+ finalizedBlockGauge.Update(int64(bc.genesisBlock.NumberU64()))
bc.hc.SetGenesis(bc.genesisBlock.Header())
bc.hc.SetCurrentHeader(bc.genesisBlock.Header())
bc.currentFastBlock.Store(bc.genesisBlock)
@@ -947,6 +1008,8 @@ func (bc *BlockChain) writeHeadBlock(block *types.Block) {
}
bc.currentBlock.Store(block)
headBlockGauge.Update(int64(block.NumberU64()))
+ justifiedBlockGauge.Update(int64(bc.getJustifiedNumber(block.Header())))
+ finalizedBlockGauge.Update(int64(bc.getFinalizedNumber(block.Header())))
}
// Genesis retrieves the chain's genesis block.
@@ -1800,9 +1863,12 @@ func (bc *BlockChain) writeBlockWithState(block *types.Block, receipts []*types.
// Please refer to http://www.cs.cornell.edu/~ie53/publications/btcProcFC.pdf
reorg := externTd.Cmp(localTd) > 0
currentBlock = bc.CurrentBlock()
+ if bc.isFinalizedBlockHigher(block.Header(), currentBlock.Header()) {
+ reorg = true
+ }
if !reorg && externTd.Cmp(localTd) == 0 {
// Split same-difficulty blocks by number, then preferentially select
- // the block generated by the local miner as the canonical block.
+ // the block generated by the remote miner as the canonical block.
if block.NumberU64() < currentBlock.NumberU64() || block.Time() < currentBlock.Time() {
reorg = true
} else if p, ok := bc.engine.(consensus.PoSA); ok && p.IsLocalBlock(currentBlock.Header()) {
@@ -1844,6 +1910,11 @@ func (bc *BlockChain) writeBlockWithState(block *types.Block, receipts []*types.
// event here.
if emitHeadEvent {
bc.chainHeadFeed.Send(ChainHeadEvent{Block: block})
+ if posa, ok := bc.Engine().(consensus.PoSA); ok {
+ if finalizedHeader := posa.GetFinalizedHeader(bc, block.Header(), types.NaturallyFinalizedDist); finalizedHeader != nil {
+ bc.finalizedHeaderFeed.Send(FinalizedHeaderEvent{finalizedHeader})
+ }
+ }
}
} else {
bc.chainSideFeed.Send(ChainSideEvent{Block: block})
@@ -1946,6 +2017,11 @@ func (bc *BlockChain) insertChain(chain types.Blocks, verifySeals bool) (int, er
defer func() {
if lastCanon != nil && bc.CurrentBlock().Hash() == lastCanon.Hash() {
bc.chainHeadFeed.Send(ChainHeadEvent{lastCanon})
+ if posa, ok := bc.Engine().(consensus.PoSA); ok {
+ if finalizedHeader := posa.GetFinalizedHeader(bc, lastCanon.Header(), types.NaturallyFinalizedDist); finalizedHeader != nil {
+ bc.finalizedHeaderFeed.Send(FinalizedHeaderEvent{finalizedHeader})
+ }
+ }
}
}()
// Start the parallel header verifier
@@ -2101,7 +2177,7 @@ func (bc *BlockChain) insertChain(chain types.Blocks, verifySeals bool) (int, er
if parent == nil {
parent = bc.GetHeader(block.ParentHash(), block.NumberU64()-1)
}
- statedb, err := state.New(parent.Root, bc.stateCache, bc.snaps)
+ statedb, err := state.NewWithSharedPool(parent.Root, bc.stateCache, bc.snaps)
if err != nil {
return it.index, err
}
@@ -2143,7 +2219,7 @@ func (bc *BlockChain) insertChain(chain types.Blocks, verifySeals bool) (int, er
// Validate the state using the default validator
substart = time.Now()
if !statedb.IsLightProcessed() {
- if err := bc.validator.ValidateState(block, statedb, receipts, usedGas, bc.pipeCommit); err != nil {
+ if err := bc.validator.ValidateState(block, statedb, receipts, usedGas); err != nil {
log.Error("validate state failed", "error", err)
bc.reportBlock(block, receipts, err)
return it.index, err
@@ -3067,6 +3143,11 @@ func (bc *BlockChain) SubscribeChainHeadEvent(ch chan<- ChainHeadEvent) event.Su
return bc.scope.Track(bc.chainHeadFeed.Subscribe(ch))
}
+// SubscribeFinalizedHeaderEvent registers a subscription of FinalizeHeaderEvent.
+func (bc *BlockChain) SubscribeFinalizedHeaderEvent(ch chan<- FinalizedHeaderEvent) event.Subscription {
+ return bc.scope.Track(bc.finalizedHeaderFeed.Subscribe(ch))
+}
+
// SubscribeChainSideEvent registers a subscription of ChainSideEvent.
func (bc *BlockChain) SubscribeChainSideEvent(ch chan<- ChainSideEvent) event.Subscription {
return bc.scope.Track(bc.chainSideFeed.Subscribe(ch))
diff --git a/core/blockchain_test.go b/core/blockchain_test.go
index 50d02e0ac..07cb51933 100644
--- a/core/blockchain_test.go
+++ b/core/blockchain_test.go
@@ -209,7 +209,7 @@ func testBlockChainImport(chain types.Blocks, pipelineCommit bool, blockchain *B
blockchain.reportBlock(block, receipts, err)
return err
}
- err = blockchain.validator.ValidateState(block, statedb, receipts, usedGas, pipelineCommit)
+ err = blockchain.validator.ValidateState(block, statedb, receipts, usedGas)
if err != nil {
blockchain.reportBlock(block, receipts, err)
return err
diff --git a/core/events.go b/core/events.go
index 5e730a24a..ce8bcca74 100644
--- a/core/events.go
+++ b/core/events.go
@@ -21,7 +21,7 @@ import (
"github.com/ethereum/go-ethereum/core/types"
)
-// NewTxsEvent is posted when a batch of transactions enter the transaction pool.
+// NewTxsEvent is posted when a batch of transactions enters the transaction pool.
type NewTxsEvent struct{ Txs []*types.Transaction }
// ReannoTxsEvent is posted when a batch of local pending transactions exceed a specified duration.
@@ -33,6 +33,12 @@ type NewMinedBlockEvent struct{ Block *types.Block }
// RemovedLogsEvent is posted when a reorg happens
type RemovedLogsEvent struct{ Logs []*types.Log }
+// NewVoteEvent is posted when a batch of votes enters the vote pool.
+type NewVoteEvent struct{ Vote *types.VoteEnvelope }
+
+// FinalizedHeaderEvent is posted when a finalized header is reached.
+type FinalizedHeaderEvent struct{ Header *types.Header }
+
type ChainEvent struct {
Block *types.Block
Hash common.Hash
diff --git a/core/genesis.go b/core/genesis.go
index 78d2b6bf2..6391c002c 100644
--- a/core/genesis.go
+++ b/core/genesis.go
@@ -109,40 +109,6 @@ type genesisAccountMarshaling struct {
PrivateKey hexutil.Bytes
}
-//type logJSON struct {
-// Address common.Address `json:"address" gencodec:"required"`
-// Topics []common.Hash `json:"topics" gencodec:"required"`
-// Data hexutil.Bytes `json:"data" gencodec:"required"`
-// // remove fields
-// BlockNumber uint64 `json:"blockNumber,omitempty"`
-// TxHash common.Hash `json:"transactionHash,omitempty"`
-// TxIndex uint `json:"transactionIndex,omitempty"`
-// BlockHash common.Hash `json:"blockHash,omitempty"`
-// Index uint `json:"logIndex,omitempty"`
-// Removed bool `json:"removed,omitempty"`
-//}
-//
-//func (h *logJSON) UnmarshalText(text []byte) error {
-// return json.Unmarshal(text, h)
-//}
-//
-//func (h *logJSON) MarshalText() ([]byte, error) {
-// type jsonLog struct {
-// Address hexutil.Bytes `json:"address" gencodec:"required"`
-// Topics []hexutil.Bytes `json:"topics" gencodec:"required"`
-// Data hexutil.Bytes `json:"data" gencodec:"required"`
-// }
-// var topics []hexutil.Bytes
-// for _, t := range h.Topics {
-// topics = append(topics, t.Bytes())
-// }
-// return json.Marshal(jsonLog{
-// Address: h.Address.Bytes(),
-// Topics: topics,
-// Data: h.Data,
-// })
-//}
-
// storageJSON represents a 256 bit byte array, but allows less than 256 bits when
// unmarshaling from hex.
type storageJSON common.Hash
diff --git a/core/headerchain.go b/core/headerchain.go
index fe4770a46..30d40d055 100644
--- a/core/headerchain.go
+++ b/core/headerchain.go
@@ -112,10 +112,56 @@ func NewHeaderChain(chainDb ethdb.Database, config *params.ChainConfig, engine c
}
hc.currentHeaderHash = hc.CurrentHeader().Hash()
headHeaderGauge.Update(hc.CurrentHeader().Number.Int64())
+ justifiedBlockGauge.Update(int64(hc.getJustifiedNumber(hc.CurrentHeader())))
+ finalizedBlockGauge.Update(int64(hc.getFinalizedNumber(hc.CurrentHeader())))
return hc, nil
}
+// getJustifiedNumber returns the highest justified number before the specific block.
+func (hc *HeaderChain) getJustifiedNumber(header *types.Header) uint64 {
+ if p, ok := hc.engine.(consensus.PoSA); ok {
+ justifiedHeader := p.GetJustifiedHeader(hc, header)
+ if justifiedHeader != nil {
+ return justifiedHeader.Number.Uint64()
+ }
+ }
+
+ return 0
+}
+
+// getFinalizedNumber returns the highest finalized number before the specific block.
+func (hc *HeaderChain) getFinalizedNumber(header *types.Header) uint64 {
+ if p, ok := hc.engine.(consensus.PoSA); ok {
+ if finalizedHeader := p.GetFinalizedHeader(hc, header, types.NaturallyFinalizedDist); finalizedHeader != nil {
+ return finalizedHeader.Number.Uint64()
+ }
+ }
+
+ return 0
+}
+
+// isFinalizedBlockHigher returns true when the new block's finalized block is higher than current block.
+func (hc *HeaderChain) isFinalizedBlockHigher(header *types.Header, curHeader *types.Header) bool {
+ p, ok := hc.engine.(consensus.PoSA)
+ if !ok {
+ return false
+ }
+
+ ancestor := rawdb.FindCommonAncestor(hc.chainDb, header, curHeader)
+ if ancestor == nil {
+ return false
+ }
+
+ finalized := p.GetFinalizedHeader(hc, header, header.Number.Uint64()-ancestor.Number.Uint64())
+ curFinalized := p.GetFinalizedHeader(hc, curHeader, curHeader.Number.Uint64()-ancestor.Number.Uint64())
+ if finalized == nil || curFinalized == nil {
+ return false
+ }
+
+ return finalized.Number.Uint64() > curFinalized.Number.Uint64()
+}
+
// GetBlockNumber retrieves the block number belonging to the given hash
// from the cache or database
func (hc *HeaderChain) GetBlockNumber(hash common.Hash) *uint64 {
@@ -216,6 +262,9 @@ func (hc *HeaderChain) writeHeaders(headers []*types.Header) (result *headerWrit
// Second clause in the if statement reduces the vulnerability to selfish mining.
// Please refer to http://www.cs.cornell.edu/~ie53/publications/btcProcFC.pdf
reorg := newTD.Cmp(localTD) > 0
+ if hc.isFinalizedBlockHigher(lastHeader, hc.CurrentHeader()) {
+ reorg = true
+ }
if !reorg && newTD.Cmp(localTD) == 0 {
if lastNumber < head {
reorg = true
@@ -250,6 +299,9 @@ func (hc *HeaderChain) writeHeaders(headers []*types.Header) (result *headerWrit
headHeader = hc.GetHeader(headHash, headNumber)
)
for rawdb.ReadCanonicalHash(hc.chainDb, headNumber) != headHash {
+ if frozen, _ := hc.chainDb.Ancients(); frozen == headNumber {
+ break
+ }
rawdb.WriteCanonicalHash(markerBatch, headHash, headNumber)
headHash = headHeader.ParentHash
headNumber = headHeader.Number.Uint64() - 1
@@ -279,6 +331,8 @@ func (hc *HeaderChain) writeHeaders(headers []*types.Header) (result *headerWrit
hc.currentHeaderHash = lastHash
hc.currentHeader.Store(types.CopyHeader(lastHeader))
headHeaderGauge.Update(lastHeader.Number.Int64())
+ justifiedBlockGauge.Update(int64(hc.getJustifiedNumber(lastHeader)))
+ finalizedBlockGauge.Update(int64(hc.getFinalizedNumber(lastHeader)))
// Chain status is canonical since this insert was a reorg.
// Note that all inserts which have higher TD than existing are 'reorg'.
@@ -545,6 +599,8 @@ func (hc *HeaderChain) SetCurrentHeader(head *types.Header) {
hc.currentHeader.Store(head)
hc.currentHeaderHash = head.Hash()
headHeaderGauge.Update(head.Number.Int64())
+ justifiedBlockGauge.Update(int64(hc.getJustifiedNumber(head)))
+ finalizedBlockGauge.Update(int64(hc.getFinalizedNumber(head)))
}
type (
@@ -600,6 +656,8 @@ func (hc *HeaderChain) SetHead(head uint64, updateFn UpdateHeadBlocksCallback, d
hc.currentHeader.Store(parent)
hc.currentHeaderHash = parentHash
headHeaderGauge.Update(parent.Number.Int64())
+ justifiedBlockGauge.Update(int64(hc.getJustifiedNumber(parent)))
+ finalizedBlockGauge.Update(int64(hc.getFinalizedNumber(parent)))
// If this is the first iteration, wipe any leftover data upwards too so
// we don't end up with dangling daps in the database
diff --git a/core/rawdb/chain_iterator.go b/core/rawdb/chain_iterator.go
index 22d5188e9..883e17b78 100644
--- a/core/rawdb/chain_iterator.go
+++ b/core/rawdb/chain_iterator.go
@@ -95,7 +95,10 @@ func iterateTransactions(db ethdb.Database, from uint64, to uint64, reverse bool
number uint64
rlp rlp.RawValue
}
- if to == from {
+ if offset := db.AncientOffSet(); offset > from {
+ from = offset
+ }
+ if to <= from {
return nil
}
threads := to - from
diff --git a/core/state/shared_pool.go b/core/state/shared_pool.go
new file mode 100644
index 000000000..ba96c2c27
--- /dev/null
+++ b/core/state/shared_pool.go
@@ -0,0 +1,39 @@
+package state
+
+import (
+ "sync"
+
+ "github.com/ethereum/go-ethereum/common"
+)
+
+// sharedPool is used to store maps of originStorage of stateObjects
+type StoragePool struct {
+ sync.RWMutex
+ sharedMap map[common.Address]*sync.Map
+}
+
+func NewStoragePool() *StoragePool {
+ sharedMap := make(map[common.Address]*sync.Map)
+ return &StoragePool{
+ sync.RWMutex{},
+ sharedMap,
+ }
+}
+
+// getStorage Check whether the storage exist in pool,
+// new one if not exist, the content of storage will be fetched in stateObjects.GetCommittedState()
+func (s *StoragePool) getStorage(address common.Address) *sync.Map {
+ s.RLock()
+ storageMap, ok := s.sharedMap[address]
+ s.RUnlock()
+ if !ok {
+ s.Lock()
+ defer s.Unlock()
+ if storageMap, ok = s.sharedMap[address]; !ok {
+ m := new(sync.Map)
+ s.sharedMap[address] = m
+ return m
+ }
+ }
+ return storageMap
+}
diff --git a/core/state/snapshot/difflayer.go b/core/state/snapshot/difflayer.go
index 65b2729d9..d2b1b2778 100644
--- a/core/state/snapshot/difflayer.go
+++ b/core/state/snapshot/difflayer.go
@@ -118,8 +118,9 @@ type diffLayer struct {
storageList map[common.Hash][]common.Hash // List of storage slots for iterated retrievals, one per account. Any existing lists are sorted if non-nil
storageData map[common.Hash]map[common.Hash][]byte // Keyed storage slots for direct retrieval. one per account (nil means deleted)
- verifiedCh chan struct{} // the difflayer is verified when verifiedCh is nil or closed
- valid bool // mark the difflayer is valid or not.
+ verifiedCh chan struct{} // the difflayer is verified when verifiedCh is nil or closed
+ valid bool // mark the difflayer is valid or not.
+ accountCorrected bool // mark the accountData has been corrected ort not
diffed *bloomfilter.Filter // Bloom filter tracking all the diffed items up to the disk layer
@@ -182,6 +183,7 @@ func newDiffLayer(parent snapshot, root common.Hash, destructs map[common.Hash]s
storageList: make(map[common.Hash][]common.Hash),
verifiedCh: verified,
}
+
switch parent := parent.(type) {
case *diskLayer:
dl.rebloom(parent)
@@ -190,6 +192,7 @@ func newDiffLayer(parent snapshot, root common.Hash, destructs map[common.Hash]s
default:
panic("unknown parent type")
}
+
// Sanity check that accounts or storage slots are never nil
for accountHash, blob := range accounts {
if blob == nil {
@@ -286,6 +289,21 @@ func (dl *diffLayer) Verified() bool {
}
}
+func (dl *diffLayer) CorrectAccounts(accounts map[common.Hash][]byte) {
+ dl.lock.Lock()
+ defer dl.lock.Unlock()
+
+ dl.accountData = accounts
+ dl.accountCorrected = true
+}
+
+func (dl *diffLayer) AccountsCorrected() bool {
+ dl.lock.RLock()
+ defer dl.lock.RUnlock()
+
+ return dl.accountCorrected
+}
+
// Parent returns the subsequent layer of a diff layer.
func (dl *diffLayer) Parent() snapshot {
return dl.parent
@@ -314,6 +332,24 @@ func (dl *diffLayer) Account(hash common.Hash) (*Account, error) {
return account, nil
}
+// Accounts directly retrieves all accounts in current snapshot in
+// the snapshot slim data format.
+func (dl *diffLayer) Accounts() (map[common.Hash]*Account, error) {
+ dl.lock.RLock()
+ defer dl.lock.RUnlock()
+
+ accounts := make(map[common.Hash]*Account, len(dl.accountData))
+ for hash, data := range dl.accountData {
+ account := new(Account)
+ if err := rlp.DecodeBytes(data, account); err != nil {
+ return nil, err
+ }
+ accounts[hash] = account
+ }
+
+ return accounts, nil
+}
+
// AccountRLP directly retrieves the account RLP associated with a particular
// hash in the snapshot slim data format.
//
diff --git a/core/state/snapshot/disklayer.go b/core/state/snapshot/disklayer.go
index c1de41782..6d46496a7 100644
--- a/core/state/snapshot/disklayer.go
+++ b/core/state/snapshot/disklayer.go
@@ -59,6 +59,13 @@ func (dl *diskLayer) Verified() bool {
return true
}
+func (dl *diskLayer) CorrectAccounts(map[common.Hash][]byte) {
+}
+
+func (dl *diskLayer) AccountsCorrected() bool {
+ return true
+}
+
// Parent always returns nil as there's no layer below the disk.
func (dl *diskLayer) Parent() snapshot {
return nil
@@ -73,6 +80,12 @@ func (dl *diskLayer) Stale() bool {
return dl.stale
}
+// Accounts directly retrieves all accounts in current snapshot in
+// the snapshot slim data format.
+func (dl *diskLayer) Accounts() (map[common.Hash]*Account, error) {
+ return nil, nil
+}
+
// Account directly retrieves the account associated with a particular hash in
// the snapshot slim data format.
func (dl *diskLayer) Account(hash common.Hash) (*Account, error) {
diff --git a/core/state/snapshot/generate.go b/core/state/snapshot/generate.go
index a9f89b20b..7861c445b 100644
--- a/core/state/snapshot/generate.go
+++ b/core/state/snapshot/generate.go
@@ -199,7 +199,7 @@ func journalProgress(db ethdb.KeyValueWriter, marker []byte, stats *generatorSta
default:
logstr = fmt.Sprintf("%#x:%#x", marker[:common.HashLength], marker[common.HashLength:])
}
- log.Debug("Journalled generator progress", "progress", logstr)
+ log.Debug("Journaled generator progress", "progress", logstr)
rawdb.WriteSnapshotGenerator(db, blob)
}
diff --git a/core/state/snapshot/journal.go b/core/state/snapshot/journal.go
index 35c69cfd6..587f78a47 100644
--- a/core/state/snapshot/journal.go
+++ b/core/state/snapshot/journal.go
@@ -288,6 +288,7 @@ func (dl *diffLayer) Journal(buffer *bytes.Buffer) (common.Hash, error) {
if dl.Stale() {
return common.Hash{}, ErrSnapshotStale
}
+
// Everything below was journalled, persist this layer too
if err := rlp.Encode(buffer, dl.root); err != nil {
return common.Hash{}, err
diff --git a/core/state/snapshot/snapshot.go b/core/state/snapshot/snapshot.go
index 8ac93f28e..7ad4bcc91 100644
--- a/core/state/snapshot/snapshot.go
+++ b/core/state/snapshot/snapshot.go
@@ -107,13 +107,23 @@ type Snapshot interface {
// Verified returns whether the snapshot is verified
Verified() bool
- // Store the verification result
+ // MarkValid stores the verification result
MarkValid()
+ // CorrectAccounts updates account data for storing the correct data during pipecommit
+ CorrectAccounts(map[common.Hash][]byte)
+
+ // AccountsCorrected checks whether the account data has been corrected during pipecommit
+ AccountsCorrected() bool
+
// Account directly retrieves the account associated with a particular hash in
// the snapshot slim data format.
Account(hash common.Hash) (*Account, error)
+ // Accounts directly retrieves all accounts in current snapshot in
+ // the snapshot slim data format.
+ Accounts() (map[common.Hash]*Account, error)
+
// AccountRLP directly retrieves the account RLP associated with a particular
// hash in the snapshot slim data format.
AccountRLP(hash common.Hash) ([]byte, error)
@@ -240,6 +250,11 @@ func (t *Tree) waitBuild() {
}
}
+// Layers returns the number of layers
+func (t *Tree) Layers() int {
+ return len(t.layers)
+}
+
// Disable interrupts any pending snapshot generator, deletes all the snapshot
// layers in memory and marks snapshots disabled globally. In order to resume
// the snapshot functionality, the caller must invoke Rebuild.
@@ -666,6 +681,11 @@ func (t *Tree) Journal(root common.Hash) (common.Hash, error) {
if snap == nil {
return common.Hash{}, fmt.Errorf("snapshot [%#x] missing", root)
}
+ // Wait the snapshot(difflayer) is verified, it means the account data also been refreshed with the correct data
+ if !snap.WaitAndGetVerifyRes() {
+ return common.Hash{}, ErrSnapshotStale
+ }
+
// Run the journaling
t.lock.Lock()
defer t.lock.Unlock()
diff --git a/core/state/state_object.go b/core/state/state_object.go
index 298f4305b..b40a8a2f8 100644
--- a/core/state/state_object.go
+++ b/core/state/state_object.go
@@ -21,6 +21,7 @@ import (
"fmt"
"io"
"math/big"
+ "sync"
"time"
"github.com/ethereum/go-ethereum/common"
@@ -63,10 +64,11 @@ func (s Storage) Copy() Storage {
// Account values can be accessed and modified through the object.
// Finally, call CommitTrie to write the modified storage trie into a database.
type StateObject struct {
- address common.Address
- addrHash common.Hash // hash of ethereum address of the account
- data Account
- db *StateDB
+ address common.Address
+ addrHash common.Hash // hash of ethereum address of the account
+ data Account
+ db *StateDB
+ rootCorrected bool // To indicate whether the root has been corrected in pipecommit mode
// DB error.
// State objects are used by the consensus core and VM which are
@@ -79,7 +81,9 @@ type StateObject struct {
trie Trie // storage trie, which becomes non-nil on first access
code Code // contract bytecode, which gets set when code is loaded
- originStorage Storage // Storage cache of original entries to dedup rewrites, reset for every transaction
+ sharedOriginStorage *sync.Map // Point to the entry of the stateObject in sharedPool
+ originStorage Storage // Storage cache of original entries to dedup rewrites, reset for every transaction
+
pendingStorage Storage // Storage entries that need to be flushed to disk, at the end of an entire block
dirtyStorage Storage // Storage entries that have been modified in the current transaction execution
fakeStorage Storage // Fake storage which constructed by caller for debugging purpose.
@@ -120,14 +124,21 @@ func newObject(db *StateDB, address common.Address, data Account) *StateObject {
if data.Root == (common.Hash{}) {
data.Root = emptyRoot
}
+ var storageMap *sync.Map
+ // Check whether the storage exist in pool, new originStorage if not exist
+ if db != nil && db.storagePool != nil {
+ storageMap = db.GetStorage(address)
+ }
+
return &StateObject{
- db: db,
- address: address,
- addrHash: crypto.Keccak256Hash(address[:]),
- data: data,
- originStorage: make(Storage),
- pendingStorage: make(Storage),
- dirtyStorage: make(Storage),
+ db: db,
+ address: address,
+ addrHash: crypto.Keccak256Hash(address[:]),
+ data: data,
+ sharedOriginStorage: storageMap,
+ originStorage: make(Storage),
+ pendingStorage: make(Storage),
+ dirtyStorage: make(Storage),
}
}
@@ -194,6 +205,29 @@ func (s *StateObject) GetState(db Database, key common.Hash) common.Hash {
return s.GetCommittedState(db, key)
}
+func (s *StateObject) getOriginStorage(key common.Hash) (common.Hash, bool) {
+ if value, cached := s.originStorage[key]; cached {
+ return value, true
+ }
+ // if L1 cache miss, try to get it from shared pool
+ if s.sharedOriginStorage != nil {
+ val, ok := s.sharedOriginStorage.Load(key)
+ if !ok {
+ return common.Hash{}, false
+ }
+ s.originStorage[key] = val.(common.Hash)
+ return val.(common.Hash), true
+ }
+ return common.Hash{}, false
+}
+
+func (s *StateObject) setOriginStorage(key common.Hash, value common.Hash) {
+ if s.db.writeOnSharedStorage && s.sharedOriginStorage != nil {
+ s.sharedOriginStorage.Store(key, value)
+ }
+ s.originStorage[key] = value
+}
+
// GetCommittedState retrieves a value from the committed account storage trie.
func (s *StateObject) GetCommittedState(db Database, key common.Hash) common.Hash {
// If the fake storage is set, only lookup the state here(in the debugging mode)
@@ -204,7 +238,8 @@ func (s *StateObject) GetCommittedState(db Database, key common.Hash) common.Has
if value, pending := s.pendingStorage[key]; pending {
return value
}
- if value, cached := s.originStorage[key]; cached {
+
+ if value, cached := s.getOriginStorage(key); cached {
return value
}
// If no live objects are available, attempt to use snapshots
@@ -263,7 +298,7 @@ func (s *StateObject) GetCommittedState(db Database, key common.Hash) common.Has
}
value.SetBytes(content)
}
- s.originStorage[key] = value
+ s.setOriginStorage(key, value)
return value
}
@@ -320,7 +355,18 @@ func (s *StateObject) finalise(prefetch bool) {
slotsToPrefetch = append(slotsToPrefetch, common.CopyBytes(key[:])) // Copy needed for closure
}
}
- if s.db.prefetcher != nil && prefetch && len(slotsToPrefetch) > 0 && s.data.Root != emptyRoot {
+
+ // The account root need to be updated before prefetch, otherwise the account root is empty
+ if s.db.pipeCommit && s.data.Root == dummyRoot && !s.rootCorrected && s.db.snap.AccountsCorrected() {
+ if acc, err := s.db.snap.Account(crypto.HashData(s.db.hasher, s.address.Bytes())); err == nil {
+ if acc != nil && len(acc.Root) != 0 {
+ s.data.Root = common.BytesToHash(acc.Root)
+ s.rootCorrected = true
+ }
+ }
+ }
+
+ if s.db.prefetcher != nil && prefetch && len(slotsToPrefetch) > 0 && s.data.Root != emptyRoot && s.data.Root != dummyRoot {
s.db.prefetcher.prefetch(s.data.Root, slotsToPrefetch, s.addrHash)
}
if len(s.dirtyStorage) > 0 {
@@ -356,7 +402,6 @@ func (s *StateObject) updateTrie(db Database) Trie {
continue
}
s.originStorage[key] = value
-
var v []byte
if (value == common.Hash{}) {
s.setError(tr.TryDelete(key[:]))
diff --git a/core/state/statedb.go b/core/state/statedb.go
index 19508ec3a..9fc2c494a 100644
--- a/core/state/statedb.go
+++ b/core/state/statedb.go
@@ -39,10 +39,7 @@ import (
"github.com/ethereum/go-ethereum/trie"
)
-const (
- preLoadLimit = 128
- defaultNumOfSlots = 100
-)
+const defaultNumOfSlots = 100
type revision struct {
id int
@@ -53,6 +50,10 @@ var (
// emptyRoot is the known root hash of an empty trie.
emptyRoot = common.HexToHash("56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421")
+ // dummyRoot is the dummy account root before corrected in pipecommit sync mode,
+ // the value is 542e5fc2709de84248e9bce43a9c0c8943a608029001360f8ab55bf113b23d28
+ dummyRoot = crypto.Keccak256Hash([]byte("dummy_account_root"))
+
emptyAddr = crypto.Keccak256Hash(common.Address{}.Bytes())
)
@@ -101,6 +102,8 @@ type StateDB struct {
stateObjectsPending map[common.Address]struct{} // State objects finalized but not yet written to the trie
stateObjectsDirty map[common.Address]struct{} // State objects modified in the current execution
+ storagePool *StoragePool // sharedPool to store L1 originStorage of stateObjects
+ writeOnSharedStorage bool // Write to the shared origin storage of a stateObject while reading from the underlying storage layer.
// DB error.
// State objects are used by the consensus core and VM which are
// unable to deal with database-level errors. Any error that occurs
@@ -147,6 +150,16 @@ func New(root common.Hash, db Database, snaps *snapshot.Tree) (*StateDB, error)
return newStateDB(root, db, snaps)
}
+// NewWithSharedPool creates a new state with sharedStorge on layer 1.5
+func NewWithSharedPool(root common.Hash, db Database, snaps *snapshot.Tree) (*StateDB, error) {
+ statedb, err := newStateDB(root, db, snaps)
+ if err != nil {
+ return nil, err
+ }
+ statedb.storagePool = NewStoragePool()
+ return statedb, nil
+}
+
func newStateDB(root common.Hash, db Database, snaps *snapshot.Tree) (*StateDB, error) {
sdb := &StateDB{
db: db,
@@ -178,6 +191,10 @@ func newStateDB(root common.Hash, db Database, snaps *snapshot.Tree) (*StateDB,
return sdb, nil
}
+func (s *StateDB) EnableWriteOnSharedStorage() {
+ s.writeOnSharedStorage = true
+}
+
// StartPrefetcher initializes a new trie prefetcher to pull in nodes from the
// state trie concurrently while the state is mutated so that when we reach the
// commit phase, most of the needed data is already hot.
@@ -216,11 +233,16 @@ func (s *StateDB) MarkLightProcessed() {
// Enable the pipeline commit function of statedb
func (s *StateDB) EnablePipeCommit() {
- if s.snap != nil {
+ if s.snap != nil && s.snaps.Layers() > 1 {
s.pipeCommit = true
}
}
+// IsPipeCommit checks whether pipecommit is enabled on the statedb or not
+func (s *StateDB) IsPipeCommit() bool {
+ return s.pipeCommit
+}
+
// Mark that the block is full processed
func (s *StateDB) MarkFullProcessed() {
s.fullProcessed = true
@@ -592,78 +614,6 @@ func (s *StateDB) getStateObject(addr common.Address) *StateObject {
return nil
}
-func (s *StateDB) TryPreload(block *types.Block, signer types.Signer) {
- accounts := make(map[common.Address]bool, block.Transactions().Len())
- accountsSlice := make([]common.Address, 0, block.Transactions().Len())
- for _, tx := range block.Transactions() {
- from, err := types.Sender(signer, tx)
- if err != nil {
- break
- }
- accounts[from] = true
- if tx.To() != nil {
- accounts[*tx.To()] = true
- }
- }
- for account := range accounts {
- accountsSlice = append(accountsSlice, account)
- }
- if len(accountsSlice) >= preLoadLimit && len(accountsSlice) > runtime.NumCPU() {
- objsChan := make(chan []*StateObject, runtime.NumCPU())
- for i := 0; i < runtime.NumCPU(); i++ {
- start := i * len(accountsSlice) / runtime.NumCPU()
- end := (i + 1) * len(accountsSlice) / runtime.NumCPU()
- if i+1 == runtime.NumCPU() {
- end = len(accountsSlice)
- }
- go func(start, end int) {
- objs := s.preloadStateObject(accountsSlice[start:end])
- objsChan <- objs
- }(start, end)
- }
- for i := 0; i < runtime.NumCPU(); i++ {
- objs := <-objsChan
- for _, obj := range objs {
- s.SetStateObject(obj)
- }
- }
- }
-}
-
-func (s *StateDB) preloadStateObject(address []common.Address) []*StateObject {
- // Prefer live objects if any is available
- if s.snap == nil {
- return nil
- }
- hasher := crypto.NewKeccakState()
- objs := make([]*StateObject, 0, len(address))
- for _, addr := range address {
- // If no live objects are available, attempt to use snapshots
- if acc, err := s.snap.Account(crypto.HashData(hasher, addr.Bytes())); err == nil {
- if acc == nil {
- continue
- }
- data := &Account{
- Nonce: acc.Nonce,
- Balance: acc.Balance,
- CodeHash: acc.CodeHash,
- Root: common.BytesToHash(acc.Root),
- }
- if len(data.CodeHash) == 0 {
- data.CodeHash = emptyCodeHash
- }
- if data.Root == (common.Hash{}) {
- data.Root = emptyRoot
- }
- // Insert into the live set
- obj := newObject(s, addr, *data)
- objs = append(objs, obj)
- }
- // Do not enable this feature when snapshot is not enabled.
- }
- return objs
-}
-
// getDeletedStateObject is similar to getStateObject, but instead of returning
// nil for a deleted state object, it returns the actual object with the deleted
// flag set. This is needed by the state journal to revert to the correct s-
@@ -829,6 +779,7 @@ func (s *StateDB) Copy() *StateDB {
stateObjects: make(map[common.Address]*StateObject, len(s.journal.dirties)),
stateObjectsPending: make(map[common.Address]struct{}, len(s.stateObjectsPending)),
stateObjectsDirty: make(map[common.Address]struct{}, len(s.journal.dirties)),
+ storagePool: s.storagePool,
refund: s.refund,
logs: make(map[common.Hash][]*types.Log, len(s.logs)),
logSize: s.logSize,
@@ -1024,6 +975,76 @@ func (s *StateDB) IntermediateRoot(deleteEmptyObjects bool) common.Hash {
return s.StateIntermediateRoot()
}
+//CorrectAccountsRoot will fix account roots in pipecommit mode
+func (s *StateDB) CorrectAccountsRoot(blockRoot common.Hash) {
+ var snapshot snapshot.Snapshot
+ if blockRoot == (common.Hash{}) {
+ snapshot = s.snap
+ } else if s.snaps != nil {
+ snapshot = s.snaps.Snapshot(blockRoot)
+ }
+
+ if snapshot == nil {
+ return
+ }
+ if accounts, err := snapshot.Accounts(); err == nil && accounts != nil {
+ for _, obj := range s.stateObjects {
+ if !obj.deleted && !obj.rootCorrected && obj.data.Root == dummyRoot {
+ if account, exist := accounts[crypto.Keccak256Hash(obj.address[:])]; exist && len(account.Root) != 0 {
+ obj.data.Root = common.BytesToHash(account.Root)
+ obj.rootCorrected = true
+ }
+ }
+ }
+ }
+}
+
+//PopulateSnapAccountAndStorage tries to populate required accounts and storages for pipecommit
+func (s *StateDB) PopulateSnapAccountAndStorage() {
+ for addr := range s.stateObjectsPending {
+ if obj := s.stateObjects[addr]; !obj.deleted {
+ if s.snap != nil && !obj.deleted {
+ root := obj.data.Root
+ storageChanged := s.populateSnapStorage(obj)
+ if storageChanged {
+ root = dummyRoot
+ }
+ s.snapAccounts[obj.address] = snapshot.SlimAccountRLP(obj.data.Nonce, obj.data.Balance, root, obj.data.CodeHash)
+ }
+ }
+ }
+}
+
+//populateSnapStorage tries to populate required storages for pipecommit, and returns a flag to indicate whether the storage root changed or not
+func (s *StateDB) populateSnapStorage(obj *StateObject) bool {
+ for key, value := range obj.dirtyStorage {
+ obj.pendingStorage[key] = value
+ }
+ if len(obj.pendingStorage) == 0 {
+ return false
+ }
+ var storage map[string][]byte
+ for key, value := range obj.pendingStorage {
+ var v []byte
+ if (value != common.Hash{}) {
+ // Encoding []byte cannot fail, ok to ignore the error.
+ v, _ = rlp.EncodeToBytes(common.TrimLeftZeroes(value[:]))
+ }
+ // If state snapshotting is active, cache the data til commit
+ if obj.db.snap != nil {
+ if storage == nil {
+ // Retrieve the old storage map, if available, create a new one otherwise
+ if storage = obj.db.snapStorage[obj.address]; storage == nil {
+ storage = make(map[string][]byte)
+ obj.db.snapStorage[obj.address] = storage
+ }
+ }
+ storage[string(key[:])] = v // v will be nil if value is 0x00
+ }
+ }
+ return true
+}
+
func (s *StateDB) AccountsIntermediateRoot() {
tasks := make(chan func())
finishCh := make(chan struct{})
@@ -1109,6 +1130,7 @@ func (s *StateDB) StateIntermediateRoot() common.Hash {
}
s.trie = tr
}
+
usedAddrs := make([][]byte, 0, len(s.stateObjectsPending))
for addr := range s.stateObjectsPending {
if obj := s.stateObjects[addr]; obj.deleted {
@@ -1121,6 +1143,7 @@ func (s *StateDB) StateIntermediateRoot() common.Hash {
if prefetcher != nil {
prefetcher.used(s.originalRoot, usedAddrs)
}
+
if len(s.stateObjectsPending) > 0 {
s.stateObjectsPending = make(map[common.Address]struct{})
}
@@ -1298,6 +1321,7 @@ func (s *StateDB) Commit(failPostCommitFunc func(), postCommitFuncs ...func() er
var diffLayer *types.DiffLayer
var verified chan struct{}
var snapUpdated chan struct{}
+
if s.snap != nil {
diffLayer = &types.DiffLayer{}
}
@@ -1309,9 +1333,24 @@ func (s *StateDB) Commit(failPostCommitFunc func(), postCommitFuncs ...func() er
commmitTrie := func() error {
commitErr := func() error {
+ if s.pipeCommit {
+ <-snapUpdated
+ // Due to state verification pipeline, the accounts roots are not updated, leading to the data in the difflayer is not correct, capture the correct data here
+ s.AccountsIntermediateRoot()
+ if parent := s.snap.Root(); parent != s.expectedRoot {
+ accountData := make(map[common.Hash][]byte)
+ for k, v := range s.snapAccounts {
+ accountData[crypto.Keccak256Hash(k[:])] = v
+ }
+ s.snaps.Snapshot(s.expectedRoot).CorrectAccounts(accountData)
+ }
+ }
+
if s.stateRoot = s.StateIntermediateRoot(); s.fullProcessed && s.expectedRoot != s.stateRoot {
+ log.Error("Invalid merkle root", "remote", s.expectedRoot, "local", s.stateRoot)
return fmt.Errorf("invalid merkle root (remote: %x local: %x)", s.expectedRoot, s.stateRoot)
}
+
tasks := make(chan func())
taskResults := make(chan error, len(s.stateObjectsDirty))
tasksNum := 0
@@ -1334,28 +1373,13 @@ func (s *StateDB) Commit(failPostCommitFunc func(), postCommitFuncs ...func() er
}()
}
- if s.snap != nil {
- for addr := range s.stateObjectsDirty {
- if obj := s.stateObjects[addr]; !obj.deleted {
- if obj.code != nil && obj.dirtyCode {
- diffLayer.Codes = append(diffLayer.Codes, types.DiffCode{
- Hash: common.BytesToHash(obj.CodeHash()),
- Code: obj.code,
- })
- }
- }
- }
- }
-
for addr := range s.stateObjectsDirty {
if obj := s.stateObjects[addr]; !obj.deleted {
// Write any contract code associated with the state object
tasks <- func() {
// Write any storage changes in the state object to its storage trie
- if err := obj.CommitTrie(s.db); err != nil {
- taskResults <- err
- }
- taskResults <- nil
+ err := obj.CommitTrie(s.db)
+ taskResults <- err
}
tasksNum++
}
@@ -1400,17 +1424,16 @@ func (s *StateDB) Commit(failPostCommitFunc func(), postCommitFuncs ...func() er
if s.pipeCommit {
if commitErr == nil {
- <-snapUpdated
s.snaps.Snapshot(s.stateRoot).MarkValid()
+ close(verified)
} else {
// The blockchain will do the further rewind if write block not finish yet
+ close(verified)
if failPostCommitFunc != nil {
- <-snapUpdated
failPostCommitFunc()
}
log.Error("state verification failed", "err", commitErr)
}
- close(verified)
}
return commitErr
}
@@ -1423,6 +1446,12 @@ func (s *StateDB) Commit(failPostCommitFunc func(), postCommitFuncs ...func() er
if obj.code != nil && obj.dirtyCode {
rawdb.WriteCode(codeWriter, common.BytesToHash(obj.CodeHash()), obj.code)
obj.dirtyCode = false
+ if s.snap != nil {
+ diffLayer.Codes = append(diffLayer.Codes, types.DiffCode{
+ Hash: common.BytesToHash(obj.CodeHash()),
+ Code: obj.code,
+ })
+ }
if codeWriter.ValueSize() > ethdb.IdealBatchSize {
if err := codeWriter.Write(); err != nil {
return err
@@ -1448,12 +1477,18 @@ func (s *StateDB) Commit(failPostCommitFunc func(), postCommitFuncs ...func() er
}
if s.pipeCommit {
defer close(snapUpdated)
+ // State verification pipeline - accounts root are not calculated here, just populate needed fields for process
+ s.PopulateSnapAccountAndStorage()
}
+ diffLayer.Destructs, diffLayer.Accounts, diffLayer.Storages = s.SnapToDiffLayer()
// Only update if there's a state transition (skip empty Clique blocks)
if parent := s.snap.Root(); parent != s.expectedRoot {
- if err := s.snaps.Update(s.expectedRoot, parent, s.snapDestructs, s.snapAccounts, s.snapStorage, verified); err != nil {
+ err := s.snaps.Update(s.expectedRoot, parent, s.snapDestructs, s.snapAccounts, s.snapStorage, verified)
+
+ if err != nil {
log.Warn("Failed to update snapshot tree", "from", parent, "to", s.expectedRoot, "err", err)
}
+
// Keep n diff layers in the memory
// - head layer is paired with HEAD state
// - head-1 layer is paired with HEAD-1 state
@@ -1467,12 +1502,6 @@ func (s *StateDB) Commit(failPostCommitFunc func(), postCommitFuncs ...func() er
}
return nil
},
- func() error {
- if s.snap != nil {
- diffLayer.Destructs, diffLayer.Accounts, diffLayer.Storages = s.SnapToDiffLayer()
- }
- return nil
- },
}
if s.pipeCommit {
go commmitTrie()
@@ -1634,3 +1663,7 @@ func (s *StateDB) GetDirtyAccounts() []common.Address {
}
return accounts
}
+
+func (s *StateDB) GetStorage(address common.Address) *sync.Map {
+ return s.storagePool.getStorage(address)
+}
diff --git a/core/state_prefetcher.go b/core/state_prefetcher.go
index d559a03a0..a2c2df16a 100644
--- a/core/state_prefetcher.go
+++ b/core/state_prefetcher.go
@@ -26,7 +26,8 @@ import (
"github.com/ethereum/go-ethereum/params"
)
-const prefetchThread = 2
+const prefetchThread = 3
+const checkInterval = 10
// statePrefetcher is a basic Prefetcher, which blindly executes a block on top
// of an arbitrary state with the goal of prefetching potentially useful state
@@ -67,6 +68,7 @@ func (p *statePrefetcher) Prefetch(block *types.Block, statedb *state.StateDB, c
for i := 0; i < prefetchThread; i++ {
go func(idx int) {
newStatedb := statedb.Copy()
+ newStatedb.EnableWriteOnSharedStorage()
gaspool := new(GasPool).AddGas(block.GasLimit())
blockContext := NewEVMBlockContext(header, p.bc, nil)
evm := vm.NewEVM(blockContext, vm.TxContext{}, statedb, p.config, cfg)
@@ -88,6 +90,62 @@ func (p *statePrefetcher) Prefetch(block *types.Block, statedb *state.StateDB, c
}
}
+// PrefetchMining processes the state changes according to the Ethereum rules by running
+// the transaction messages using the statedb, but any changes are discarded. The
+// only goal is to pre-cache transaction signatures and snapshot clean state. Only used for mining stage
+func (p *statePrefetcher) PrefetchMining(txs *types.TransactionsByPriceAndNonce, header *types.Header, gasLimit uint64, statedb *state.StateDB, cfg vm.Config, interruptCh <-chan struct{}, txCurr **types.Transaction) {
+ var signer = types.MakeSigner(p.config, header.Number)
+
+ txCh := make(chan *types.Transaction, 2*prefetchThread)
+ for i := 0; i < prefetchThread; i++ {
+ go func(startCh <-chan *types.Transaction, stopCh <-chan struct{}) {
+ idx := 0
+ newStatedb := statedb.Copy()
+ newStatedb.EnableWriteOnSharedStorage()
+ gaspool := new(GasPool).AddGas(gasLimit)
+ blockContext := NewEVMBlockContext(header, p.bc, nil)
+ evm := vm.NewEVM(blockContext, vm.TxContext{}, statedb, p.config, cfg)
+ // Iterate over and process the individual transactions
+ for {
+ select {
+ case tx := <-startCh:
+ // Convert the transaction into an executable message and pre-cache its sender
+ msg, err := tx.AsMessageNoNonceCheck(signer)
+ if err != nil {
+ return // Also invalid block, bail out
+ }
+ idx++
+ newStatedb.Prepare(tx.Hash(), header.Hash(), idx)
+ precacheTransaction(msg, p.config, gaspool, newStatedb, header, evm)
+ gaspool = new(GasPool).AddGas(gasLimit)
+ case <-stopCh:
+ return
+ }
+ }
+ }(txCh, interruptCh)
+ }
+ go func(txset *types.TransactionsByPriceAndNonce) {
+ count := 0
+ for {
+ select {
+ case <-interruptCh:
+ return
+ default:
+ if count++; count%checkInterval == 0 {
+ txset.Forward(*txCurr)
+ }
+ tx := txset.Peek()
+ if tx == nil {
+ return
+ }
+ txCh <- tx
+ txset.Shift()
+
+ }
+ }
+ }(txs)
+}
+
// precacheTransaction attempts to apply a transaction to the given state database
// and uses the input parameters for its environment. The goal is not to execute
// the transaction successfully, rather to warm up touched data slots.
diff --git a/core/state_processor.go b/core/state_processor.go
index 7de46c379..c43bfebc4 100644
--- a/core/state_processor.go
+++ b/core/state_processor.go
@@ -121,13 +121,13 @@ func (p *LightStateProcessor) Process(block *types.Block, statedb *state.StateDB
statedb.StopPrefetcher()
parent := p.bc.GetHeader(block.ParentHash(), block.NumberU64()-1)
statedb, err = state.New(parent.Root, p.bc.stateCache, p.bc.snaps)
+ if err != nil {
+ return statedb, nil, nil, 0, err
+ }
statedb.SetExpectedStateRoot(block.Root())
if p.bc.pipeCommit {
statedb.EnablePipeCommit()
}
- if err != nil {
- return statedb, nil, nil, 0, err
- }
// Enable prefetching to pull in trie node paths while processing transactions
statedb.StartPrefetcher("chain")
}
@@ -335,7 +335,7 @@ func (p *LightStateProcessor) LightProcess(diffLayer *types.DiffLayer, block *ty
}
// Do validate in advance so that we can fall back to full process
- if err := p.bc.validator.ValidateState(block, statedb, diffLayer.Receipts, gasUsed, false); err != nil {
+ if err := p.bc.validator.ValidateState(block, statedb, diffLayer.Receipts, gasUsed); err != nil {
log.Error("validate state failed during diff sync", "error", err)
return nil, nil, 0, err
}
@@ -383,7 +383,6 @@ func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg
gp = new(GasPool).AddGas(block.GasLimit())
)
signer := types.MakeSigner(p.bc.chainConfig, block.Number())
- statedb.TryPreload(block, signer)
var receipts = make([]*types.Receipt, 0)
blockContext := NewEVMBlockContext(header, p.bc, nil)
@@ -403,6 +402,7 @@ func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg
for i, tx := range block.Transactions() {
if isPoSA {
if isSystemTx, err := posa.IsSystemTransaction(tx, block.Header()); err != nil {
+ bloomProcessors.Close()
return statedb, nil, nil, 0, err
} else if isSystemTx {
systemTxs = append(systemTxs, tx)
@@ -412,11 +412,13 @@ func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg
msg, err := tx.AsMessage(signer)
if err != nil {
+ bloomProcessors.Close()
return statedb, nil, nil, 0, err
}
statedb.Prepare(tx.Hash(), block.Hash(), i)
receipt, err := applyTransaction(msg, p.config, p.bc, nil, gp, statedb, header, tx, usedGas, vmenv, bloomProcessors)
if err != nil {
+ bloomProcessors.Close()
return statedb, nil, nil, 0, fmt.Errorf("could not apply tx %d [%v]: %w", i, tx.Hash().Hex(), err)
}
diff --git a/core/types.go b/core/types.go
index 5ed4817e6..c9061233e 100644
--- a/core/types.go
+++ b/core/types.go
@@ -31,7 +31,7 @@ type Validator interface {
// ValidateState validates the given statedb and optionally the receipts and
// gas used.
- ValidateState(block *types.Block, state *state.StateDB, receipts types.Receipts, usedGas uint64, skipHeavyVerify bool) error
+ ValidateState(block *types.Block, state *state.StateDB, receipts types.Receipts, usedGas uint64) error
}
// Prefetcher is an interface for pre-caching transaction signatures and state.
@@ -40,6 +40,8 @@ type Prefetcher interface {
// the transaction messages using the statedb, but any changes are discarded. The
// only goal is to pre-cache transaction signatures and state trie nodes.
Prefetch(block *types.Block, statedb *state.StateDB, cfg vm.Config, interrupt *uint32)
+ // PrefetchMining used for pre-caching transaction signatures and state trie nodes. Only used for mining stage.
+ PrefetchMining(txs *types.TransactionsByPriceAndNonce, header *types.Header, gasLimit uint64, statedb *state.StateDB, cfg vm.Config, interruptCh <-chan struct{}, txCurr **types.Transaction)
}
// Processor is an interface for processing blocks using a given initial state.
diff --git a/core/types/transaction.go b/core/types/transaction.go
index 74c011544..5c8d04c01 100644
--- a/core/types/transaction.go
+++ b/core/types/transaction.go
@@ -458,6 +458,21 @@ func NewTransactionsByPriceAndNonce(signer Signer, txs map[common.Address]Transa
}
}
+// Copy copys a new TransactionsPriceAndNonce with the same *transaction
+func (t *TransactionsByPriceAndNonce) Copy() *TransactionsByPriceAndNonce {
+ heads := make([]*Transaction, len(t.heads))
+ copy(heads, t.heads)
+ txs := make(map[common.Address]Transactions, len(t.txs))
+ for acc, txsTmp := range t.txs {
+ txs[acc] = txsTmp
+ }
+ return &TransactionsByPriceAndNonce{
+ heads: heads,
+ txs: txs,
+ signer: t.signer,
+ }
+}
+
// Peek returns the next transaction by price.
func (t *TransactionsByPriceAndNonce) Peek() *Transaction {
if len(t.heads) == 0 {
@@ -488,6 +503,46 @@ func (t *TransactionsByPriceAndNonce) CurrentSize() int {
return len(t.heads)
}
+//Forward moves current transaction to be the one which is one index after tx
+func (t *TransactionsByPriceAndNonce) Forward(tx *Transaction) {
+ if tx == nil {
+ if len(t.heads) > 0 {
+ t.heads = t.heads[0:0]
+ }
+ return
+ }
+ //check whether target tx exists in t.heads
+ for _, head := range t.heads {
+ if tx == head {
+ //shift t to the position one after tx
+ txTmp := t.Peek()
+ for txTmp != tx {
+ t.Shift()
+ txTmp = t.Peek()
+ }
+ t.Shift()
+ return
+ }
+ }
+ //get the sender address of tx
+ acc, _ := Sender(t.signer, tx)
+ //check whether target tx exists in t.txs
+ if txs, ok := t.txs[acc]; ok {
+ for _, txTmp := range txs {
+ //found the same pointer in t.txs as tx and then shift t to the position one after tx
+ if txTmp == tx {
+ txTmp = t.Peek()
+ for txTmp != tx {
+ t.Shift()
+ txTmp = t.Peek()
+ }
+ t.Shift()
+ return
+ }
+ }
+ }
+}
+
// Message is a fully derived transaction and implements core.Message
//
// NOTE: In a future PR this will be removed.
@@ -535,6 +590,15 @@ func (tx *Transaction) AsMessage(s Signer) (Message, error) {
return msg, err
}
+// AsMessageNoNonceCheck returns the transaction with checkNonce field set to be false.
+func (tx *Transaction) AsMessageNoNonceCheck(s Signer) (Message, error) {
+ msg, err := tx.AsMessage(s)
+ if err == nil {
+ msg.checkNonce = false
+ }
+ return msg, err
+}
+
func (m Message) From() common.Address { return m.from }
func (m Message) To() *common.Address { return m.to }
func (m Message) GasPrice() *big.Int { return m.gasPrice }
diff --git a/core/types/transaction_test.go b/core/types/transaction_test.go
index 3cece9c23..a5fbb5092 100644
--- a/core/types/transaction_test.go
+++ b/core/types/transaction_test.go
@@ -358,6 +358,78 @@ func TestTransactionTimeSort(t *testing.T) {
}
}
+func TestTransactionForward(t *testing.T) {
+ // Generate a batch of accounts to start with
+ keys := make([]*ecdsa.PrivateKey, 5)
+ for i := 0; i < len(keys); i++ {
+ keys[i], _ = crypto.GenerateKey()
+ }
+ signer := HomesteadSigner{}
+
+ // Generate a batch of transactions with overlapping prices, but different creation times
+ groups := map[common.Address]Transactions{}
+ for start, key := range keys {
+ addr := crypto.PubkeyToAddress(key.PublicKey)
+
+ tx, _ := SignTx(NewTransaction(0, common.Address{}, big.NewInt(100), 100, big.NewInt(1), nil), signer, key)
+ tx2, _ := SignTx(NewTransaction(1, common.Address{}, big.NewInt(100), 100, big.NewInt(1), nil), signer, key)
+
+ tx.time = time.Unix(0, int64(len(keys)-start))
+ tx2.time = time.Unix(1, int64(len(keys)-start))
+
+ groups[addr] = append(groups[addr], tx)
+ groups[addr] = append(groups[addr], tx2)
+
+ }
+ // Sort the transactions
+ txset := NewTransactionsByPriceAndNonce(signer, groups)
+ txsetCpy := txset.Copy()
+
+ txs := Transactions{}
+ for tx := txsetCpy.Peek(); tx != nil; tx = txsetCpy.Peek() {
+ txs = append(txs, tx)
+ txsetCpy.Shift()
+ }
+
+ tmp := txset.Copy()
+ for j := 0; j < 11; j++ {
+ txset = tmp.Copy()
+ txsetCpy = tmp.Copy()
+ i := 0
+ for ; i < j; i++ {
+ txset.Shift()
+ }
+ tx := txset.Peek()
+ if tx == nil {
+ continue
+ }
+ txsetCpy.Forward(tx)
+ txCpy := txsetCpy.Peek()
+ if txCpy == nil {
+ if tx == nil {
+ continue
+ }
+ txset.Shift()
+ if txset.Peek() != nil {
+ t.Errorf("forward got an incorrect result, got %v, want %v", txCpy, tx)
+ } else {
+ continue
+ }
+ }
+ txset.Shift()
+ for ; i < len(txs)-1; i++ {
+ tx = txset.Peek()
+ txCpy = txsetCpy.Peek()
+ if txCpy != tx {
+ t.Errorf("forward got an incorrect result, got %v, want %v", txCpy, tx)
+ }
+ txsetCpy.Shift()
+ txset.Shift()
+ }
+
+ }
+}
+
// TestTransactionCoding tests serializing/de-serializing to/from rlp and JSON.
func TestTransactionCoding(t *testing.T) {
key, err := crypto.GenerateKey()
diff --git a/core/types/vote.go b/core/types/vote.go
new file mode 100644
index 000000000..1fbc094b7
--- /dev/null
+++ b/core/types/vote.go
@@ -0,0 +1,92 @@
+package types
+
+import (
+ "sync/atomic"
+
+ "github.com/pkg/errors"
+ "github.com/prysmaticlabs/prysm/crypto/bls"
+
+ "github.com/ethereum/go-ethereum/common"
+)
+
+const (
+ BLSPublicKeyLength = 48
+ BLSSignatureLength = 96
+
+ MaxAttestationExtraLength = 256
+ NaturallyFinalizedDist = 21 // The distance to naturally finalized a block
+)
+
+type BLSPublicKey [BLSPublicKeyLength]byte
+type BLSSignature [BLSSignatureLength]byte
+type ValidatorsBitSet uint64
+
+// VoteData represents the vote range that validator voted for fast finality.
+type VoteData struct {
+ SourceNumber uint64 // The source block number should be the latest justified block number.
+ SourceHash common.Hash // The block hash of the source block.
+ TargetNumber uint64 // The target block number which validator wants to vote for.
+ TargetHash common.Hash // The block hash of the target block.
+}
+
+// Hash returns the hash of the vote data.
+func (d *VoteData) Hash() common.Hash { return rlpHash(d) }
+
+// VoteEnvelope represents the vote of a single validator.
+type VoteEnvelope struct {
+ VoteAddress BLSPublicKey // The BLS public key of the validator.
+ Signature BLSSignature // Validator's signature for the vote data.
+ Data *VoteData // The vote data for fast finality.
+
+ // caches
+ hash atomic.Value
+}
+
+// VoteAttestation represents the votes of super majority validators.
+type VoteAttestation struct {
+ VoteAddressSet ValidatorsBitSet // The bitset marks the voted validators.
+ AggSignature BLSSignature // The aggregated BLS signature of the voted validators' signatures.
+ Data *VoteData // The vote data for fast finality.
+ Extra []byte // Reserved for future usage.
+}
+
+// Hash returns the vote's hash.
+func (v *VoteEnvelope) Hash() common.Hash {
+ if hash := v.hash.Load(); hash != nil {
+ return hash.(common.Hash)
+ }
+
+ h := v.calcVoteHash()
+ v.hash.Store(h)
+ return h
+}
+
+func (v *VoteEnvelope) calcVoteHash() common.Hash {
+ vote := struct {
+ VoteAddress BLSPublicKey
+ Signature BLSSignature
+ Data *VoteData
+ }{v.VoteAddress, v.Signature, v.Data}
+ return rlpHash(vote)
+}
+
+func (b BLSPublicKey) Bytes() []byte { return b[:] }
+
+// Verify vote using BLS.
+func (vote *VoteEnvelope) Verify() error {
+ blsPubKey, err := bls.PublicKeyFromBytes(vote.VoteAddress[:])
+ if err != nil {
+ return errors.Wrap(err, "convert public key from bytes to bls failed")
+ }
+
+ sig, err := bls.SignatureFromBytes(vote.Signature[:])
+ if err != nil {
+ return errors.Wrap(err, "invalid signature")
+ }
+
+ voteDataHash := vote.Data.Hash()
+ if !sig.Verify(blsPubKey, voteDataHash[:]) {
+ return errors.New("verify bls signature failed.")
+ }
+ return nil
+}
diff --git a/core/vm/blockproof.go b/core/vm/blockproof.go
new file mode 100644
index 000000000..1801a705d
--- /dev/null
+++ b/core/vm/blockproof.go
@@ -0,0 +1,182 @@
+package vm
+
+import (
+ "bytes"
+ "fmt"
+ "github.com/ethereum/go-ethereum/accounts/abi"
+ "github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/core/types"
+ "github.com/ethereum/go-ethereum/crypto"
+ "github.com/ethereum/go-ethereum/params"
+ "github.com/ethereum/go-ethereum/rlp"
+ "math/big"
+ "strings"
+)
+
+// verifyParliaBlock implements precompile for fas verify of parlia block
+type verifyParliaBlock struct{}
+
+func NewVerifyParliaBlock() *verifyParliaBlock {
+ return &verifyParliaBlock{}
+}
+
+// RequiredGas returns the gas required to execute the pre-compiled contract.
+func (c *verifyParliaBlock) RequiredGas([]byte) uint64 {
+ return params.VerifyParliaBlockGas
+}
+
+var errBadParliaBlock = fmt.Errorf("bad parlia block")
+var errMalformedInputParams = fmt.Errorf("malformed input params")
+
+func mustNewArguments(types ...string) (result abi.Arguments) {
+ var err error
+ for _, t := range types {
+ var typ abi.Type
+ items := strings.Split(t, " ")
+ var name string
+ if len(items) == 2 {
+ name = items[1]
+ } else {
+ name = items[0]
+ }
+ typ, err = abi.NewType(items[0], items[0], nil)
+ if err != nil {
+ panic(err)
+ }
+ result = append(result, abi.Argument{Type: typ, Name: name})
+ }
+ return result
+}
+
+var verifyParliaBlockInput = mustNewArguments(
+ "uint256 chainId",
+ "bytes blockProof",
+ "uint32 epochInterval",
+)
+
+var verifyParliaBlockOutput = mustNewArguments(
+ "bytes32 blockHash",
+ "bytes32 parentHash",
+ "uint64 blockNumber",
+ "address coinbase",
+ "bytes32 receiptsRoot",
+ "bytes32 txsRoot",
+ "bytes32 stateRoot",
+ "address[] newValidatorSet",
+)
+
+func (c *verifyParliaBlock) Run(input []byte) (result []byte, err error) {
+ var chainId *big.Int
+ var blockProof []byte
+ var epochInterval uint32
+ {
+ if input == nil || len(input) > 65536 {
+ return nil, errBadParliaBlock
+ }
+ items, err := verifyParliaBlockInput.UnpackValues(input)
+ if err != nil {
+ return nil, err
+ } else if len(items) != 3 {
+ return nil, errMalformedInputParams
+ }
+ var ok bool
+ chainId, ok = items[0].(*big.Int)
+ if !ok {
+ return nil, errMalformedInputParams
+ }
+ blockProof, ok = items[1].([]byte)
+ if !ok {
+ return nil, errMalformedInputParams
+ }
+ epochInterval, ok = items[2].(uint32)
+ if !ok {
+ return nil, errMalformedInputParams
+ }
+ }
+ header := &types.Header{}
+ if err := rlp.Decode(bytes.NewReader(blockProof), header); err != nil {
+ return nil, err
+ }
+ var signer common.Address
+ if header.Number.Uint64() != 0 {
+ signer, err = recoverParliaBlockSigner(header, chainId)
+ if err != nil {
+ return nil, err
+ } else if signer != header.Coinbase {
+ return nil, errBadParliaBlock
+ }
+ }
+ var validators []common.Address
+ if header.Number.Uint64()%uint64(epochInterval) == 0 {
+ validators, err = extractParliaValidators(header)
+ if err != nil {
+ return nil, err
+ }
+ }
+ return verifyParliaBlockOutput.Pack(
+ header.Hash(), // block hash
+ header.ParentHash, // parent hash
+ header.Number.Uint64(), // block number
+ signer, // coinbase
+ header.ReceiptHash, // receipts root
+ header.TxHash, // txs root
+ header.Root, // state root
+ validators, // new validator set
+ )
+}
+
+const (
+ extraVanity = 32 // Fixed number of extra-data prefix bytes reserved for signer vanity
+ extraSeal = 65 // Fixed number of extra-data suffix bytes reserved for signer seal
+)
+
+func recoverParliaBlockSigner(header *types.Header, chainId *big.Int) (signer common.Address, err error) {
+ if len(header.Extra) < extraSeal {
+ return signer, errBadParliaBlock
+ }
+ signature := header.Extra[len(header.Extra)-extraSeal:]
+ b := new(bytes.Buffer)
+ err = rlp.Encode(b, []interface{}{
+ chainId,
+ header.ParentHash,
+ header.UncleHash,
+ header.Coinbase,
+ header.Root,
+ header.TxHash,
+ header.ReceiptHash,
+ header.Bloom,
+ header.Difficulty,
+ header.Number,
+ header.GasLimit,
+ header.GasUsed,
+ header.Time,
+ header.Extra[:len(header.Extra)-65], // this will panic if extra is too short, should check before calling encodeSigHeader
+ header.MixDigest,
+ header.Nonce,
+ })
+ if err != nil {
+ panic("can't encode: " + err.Error())
+ }
+ signingData := b.Bytes()
+ publicKey, err := crypto.Ecrecover(crypto.Keccak256(signingData), signature)
+ if err != nil {
+ return signer, err
+ }
+ copy(signer[:], crypto.Keccak256(publicKey[1:])[12:])
+ return signer, nil
+}
+
+func extractParliaValidators(header *types.Header) ([]common.Address, error) {
+ validatorBytes := header.Extra[extraVanity : len(header.Extra)-extraSeal]
+ if len(validatorBytes)%common.AddressLength != 0 {
+ return nil, errBadParliaBlock
+ }
+ n := len(validatorBytes) / common.AddressLength
+ result := make([]common.Address, n)
+ for i := 0; i < n; i++ {
+ address := make([]byte, common.AddressLength)
+ copy(address, validatorBytes[i*common.AddressLength:(i+1)*common.AddressLength])
+ result[i] = common.BytesToAddress(address)
+ }
+ return result, nil
+}
diff --git a/core/vm/blockproof_test.go b/core/vm/blockproof_test.go
new file mode 100644
index 000000000..a513af23b
--- /dev/null
+++ b/core/vm/blockproof_test.go
@@ -0,0 +1,135 @@
+package vm
+
+import (
+ "github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/common/hexutil"
+ "github.com/stretchr/testify/require"
+ "math/big"
+ "testing"
+)
+
+type verifyBlockResult struct {
+ blockNumber uint64
+ blockHash common.Hash
+ signer common.Address
+ validators []common.Address
+ parentHash common.Hash
+}
+
+func doVerifyParliaBlock(t *testing.T, chainId uint64, blockProof []byte) verifyBlockResult {
+ contract := &verifyParliaBlock{}
+ input, err := verifyParliaBlockInput.Pack(big.NewInt(int64(chainId)), blockProof, uint32(200))
+ require.NoError(t, err)
+ out, err := contract.Run(input)
+ require.NoError(t, err)
+ result, err := verifyParliaBlockOutput.Unpack(out)
+ require.NoError(t, err)
+ return verifyBlockResult{
+ blockHash: result[0].([32]byte),
+ blockNumber: result[1].(uint64),
+ signer: result[2].(common.Address),
+ validators: result[3].([]common.Address),
+ parentHash: result[4].([32]byte),
+ }
+}
+
+func Test_VerifyBlockProof(t *testing.T) {
+ var res verifyBlockResult
+ // block 13082000 with validators in extra data
+ res = doVerifyParliaBlock(t, 56, hexutil.MustDecode("0xf90403a0e06d1e696e78bef7671cc0936b4428d9f6b3aa5ef1dd1b21620fca6742d6faf1a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d493479470f657164e5b75689b64b7fd1fa275f334f28e18a04f954c11b1721ed1816b4eef4a57ba99c7288f1a5dbaf09f375efbb888bc6a8ea088d550352ebf720520dc48b2a16a82da57524145862bbedd46f2d22166bf7069a0ff6fc0e069e4e43b42d84b218d3a8bbead88cc5f06044c8f6cb64fa446520c26b90100fffff77feefbffffffbffb7dfffffffffffffffebfffff7ffef5fffffefb7ffbfeffffff75fdfffb7fffbfbfdf5fffeffff7ffffffcf7f7fffffb7bdffffffffffbfffffbfeffffffff7ffddb7ffffefffff9ffffffffedfffffffffffbffffffff5fffff3fff7fbeeef3f7ffdefffffbbbfffebefbffffffcfb3fffbfdffdffbffdffeedffffffff7ffffddffffffbff7fdfc5fb7f6dffdfbffef7fff6dffffffffffbefffffff96efff7bdff7fefdfbfbfde7f7fcfdffdffffbdffffeffbfff7b7ffffbffdd5de7ffbfffdffffd7bffffdf7ffef7fffffffeffffffffffff3fdffffedfdfdfddded7fd3fef7fedfdffffbffff5fffffffff3bdebfffffffbf0283c79d908405f5e1008405ee8b078461a65ec5b90205d883010105846765746888676f312e31372e32856c696e7578000000c3167bdf2465176c461afb316ebc773c61faee85a6515daa295e26495cef6f69dfa69911d9d8e4f3bbadb89b29a97c6effb8a411dabc6adeefaa84f5067c8bbe2a7cdd959bfe8d9487b2a43b33565295a698f7e22d4c407bbe49438ed859fe965b140dcf1aab71a93f349bbafec1551819b8be1efea2fc46ca749aa14430b3230294d12c6ab2aac5c2cd68e80b16b581685b1ded8013785d6623cc18d214320b6bb6475970f657164e5b75689b64b7fd1fa275f334f28e187ae2f5b9e386cd1b50a4550696d957cb4900f03a8b6c8fd93d6f4cea42bbb345dbc6f0dfdb5bec739bb832254baf4e8b4cc26bd2b52b31389b56e98b9f8ccdafcc39f3c7d6ebf637c9151673cbc36b88a6f79b60359f141df90a0c745125b131caaffd12b8f7166496996a7da21cf1f1b04d9b3e26a3d077be807dddb074639cd9fa61b47676c064fc50d62cce2fd7544e0b2cc94692d4a704debef7bcb61328e2d3a739effcd3a99387d015e260eefac72ebea1e9ae3261a475a27bb1028f140bc2a7c843318afdea0a6e3c511bbd10f4519ece37dc24887e11b55dee226379db83cffc681495730c11fdde79ba4c0c58ec4877d8453b381c8c1c6950bd2ce0a4888e4cf2efe677abbbe1c83ce98206293d6e8ae9adbd85b3b4e1afd653ff03f5ab96e2b41c6adb77d42dd499574a1500a00000000000000000000000000000000000000000000000000000000000000000880000000000000000"))
+ require.Equal(t, res.signer, common.HexToAddress("0x70f657164e5b75689b64b7fd1fa275f334f28e18"))
+ require.Equal(t, res.validators, []common.Address{
+ common.HexToAddress("0x2465176c461afb316ebc773c61faee85a6515daa"),
+ common.HexToAddress("0x295e26495cef6f69dfa69911d9d8e4f3bbadb89b"),
+ common.HexToAddress("0x29a97c6effb8a411dabc6adeefaa84f5067c8bbe"),
+ common.HexToAddress("0x2a7cdd959bfe8d9487b2a43b33565295a698f7e2"),
+ common.HexToAddress("0x2d4c407bbe49438ed859fe965b140dcf1aab71a9"),
+ common.HexToAddress("0x3f349bbafec1551819b8be1efea2fc46ca749aa1"),
+ common.HexToAddress("0x4430b3230294d12c6ab2aac5c2cd68e80b16b581"),
+ common.HexToAddress("0x685b1ded8013785d6623cc18d214320b6bb64759"),
+ common.HexToAddress("0x70f657164e5b75689b64b7fd1fa275f334f28e18"),
+ common.HexToAddress("0x7ae2f5b9e386cd1b50a4550696d957cb4900f03a"),
+ common.HexToAddress("0x8b6c8fd93d6f4cea42bbb345dbc6f0dfdb5bec73"),
+ common.HexToAddress("0x9bb832254baf4e8b4cc26bd2b52b31389b56e98b"),
+ common.HexToAddress("0x9f8ccdafcc39f3c7d6ebf637c9151673cbc36b88"),
+ common.HexToAddress("0xa6f79b60359f141df90a0c745125b131caaffd12"),
+ common.HexToAddress("0xb8f7166496996a7da21cf1f1b04d9b3e26a3d077"),
+ common.HexToAddress("0xbe807dddb074639cd9fa61b47676c064fc50d62c"),
+ common.HexToAddress("0xce2fd7544e0b2cc94692d4a704debef7bcb61328"),
+ common.HexToAddress("0xe2d3a739effcd3a99387d015e260eefac72ebea1"),
+ common.HexToAddress("0xe9ae3261a475a27bb1028f140bc2a7c843318afd"),
+ common.HexToAddress("0xea0a6e3c511bbd10f4519ece37dc24887e11b55d"),
+ common.HexToAddress("0xee226379db83cffc681495730c11fdde79ba4c0c"),
+ })
+ // block 13082001 w/o validators in extra data
+ res = doVerifyParliaBlock(t, 56, hexutil.MustDecode("0xf9025ea0eafc056d4cd2355235e385604eb86c9fc3da295a5ac6b292dcd518aad81c96c7a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347947ae2f5b9e386cd1b50a4550696d957cb4900f03aa0250cf7c922d19a3fe86aa873f66847ec81fe3a9dd4b601ff4f3277ac96edbd2fa05df995f5895f9cf4923116c8272b5eff71e719be28fd5033d298d4c3954b3b35a092acd99501495017696d1f32624267613d5fcd1ae61a4316bb8c6f239fd2bb2fb90100f7febfbfe1ff7cdf9fed7fdeff1dbffef5b6bffbfffff76ff7ffafadfe6a77fdfddfaf2c7f7bbdee6fdbfbff6bbdb9fbf9dddbbffeffbffdffffaff9f3efffdff3f7fff9dfffffec7ff7fb7dcf3bfffdff96efffbbfdfcfbffffd7bfbfbffdffefffffe77bbff7fb7fff7dee397ffdffffafdbdfffdbfffffd7b7efffeefcabbbe7ff75fa7f5fbd7ffe7ffffdfabfebfdef79ffffce7dfbd77e3dbfbe1cdfebfebefb7fbebfffe6f719f7ffddffdbc7f7fbffdefbbfffdffffef7fa7fdfdbff7ff3f77e6d7bfdcfff7fefffffebfdfceef2bf65ecf7ff2d7df66fffabeffef77fdffbf5bf9d6fffcfff5f7be7dde9ff7fffef7ffdbfedaffef7ffa779fbf7fff0283c79d918405f5e100840482363c8461a65ec8b861d883010105846765746888676f312e31372e32856c696e7578000000c3167bdf98dad3b5a0c9f6e4f06e7eddec92065c8ba5630305a904d21822a6c15f2cf2e90916c19dfc6ac135021f44179f9dde2fde7904da16c5a488ceae534feec98ab500a00000000000000000000000000000000000000000000000000000000000000000880000000000000000"))
+ require.Equal(t, res.signer, common.HexToAddress("0x7ae2f5b9e386cd1b50a4550696d957cb4900f03a"))
+ require.Equal(t, res.validators, []common.Address{})
+ // block 13131313
+ res = doVerifyParliaBlock(t, 56, hexutil.MustDecode("0xf9025ea0a2069ff3eaafb4422687dc653b5b1d687b1c2f318682c691edd5ddca8e38f018a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d4934794a6f79b60359f141df90a0c745125b131caaffd12a0e4a5ac35e132110189e15e43f3b3304a984516366bf1bb11b454254b34f3696ea07c762f669d332b3b92f32c2c61ec5fb657e644866b917fe859bc602994c75601a0ece3f2a18b955f17fe468d8f8c318ca924722c8f32a713802420c05b5f44635eb90100b7fab7d7bf2dbe7fafdbeb7bf3ffadfff6fcdefbfffbf7dfff1edfffff739d77e867ffaadfdadfef779fdf39b9bfd3fffbe7ff93f7dfa7dfb0f78fbfffb77f897fffaf7dbb77ebfe997acf7ef7bf3bff7bb7ffafffdb7fefcde5ffedfefedf3eff6bdd7ff6dffff7ddfdefbfff6ffffefbf7ffeeb4beffe7f7bb63f3fdf3ec6f36bddff7cfb6f1bebcede7ffddbf38adf2f66eff6feedc5b7fe5fbdeffef79ffefffdfb7fadffef7dbebfb9f3e7fdfcbfcffbf2fffe9ff96dff7bf7ffb4feeffefb7ff8edfbfefde75fdffeffffe7d77bf7def7fcf7bebdfbf7cb3ef77f7efdf5dfe7fff7ffff83fefbfdfff1fee3ff7fffeef5afddafa69f67f7cfb7f7ff5f70283c85e318405f5350d840392ab268461a8c55fb861d983010106846765746889676f312e31362e3130856c696e75780000c3167bdf56c8d11fad0da9f8c18f13b49f132be98c5f5bc86f18968cd7197b662f0bdc852e22fc9bf4927ac4406850224da663e8405bf947ffdcd1f8607592831f770d5201a00000000000000000000000000000000000000000000000000000000000000000880000000000000000"))
+ require.Equal(t, res.signer, common.HexToAddress("0xa6f79b60359f141df90a0c745125b131caaffd12"))
+ require.Equal(t, res.validators, []common.Address{})
+ // block 0
+ res = doVerifyParliaBlock(t, 56, hexutil.MustDecode("0xf903fca00000000000000000000000000000000000000000000000000000000000000000a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d4934794fffffffffffffffffffffffffffffffffffffffea0919fcc7ad870b53db0aa76eb588da06bacb6d230195100699fc928511003b422a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421b901000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001808402625a0080845e9da7ceb9020500000000000000000000000000000000000000000000000000000000000000002a7cdd959bfe8d9487b2a43b33565295a698f7e26488aa4d1955ee33403f8ccb1d4de5fb97c7ade29ef9f4360c606c7ab4db26b016007d3ad0ab86a0ee01c3b1283aa067c58eab4709f85e99d46de5fe685b1ded8013785d6623cc18d214320b6bb6475978f3adfc719c99674c072166708589033e2d9afec2be4ec20253b8642161bc3f444f53679c1f3d472f7be8361c80a4c1e7e9aaf001d0877f1cfde218ce2fd7544e0b2cc94692d4a704debef7bcb61328b8f7166496996a7da21cf1f1b04d9b3e26a3d0772d4c407bbe49438ed859fe965b140dcf1aab71a96bbad7cf34b5fa511d8e963dbba288b1960e75d64430b3230294d12c6ab2aac5c2cd68e80b16b581ea0a6e3c511bbd10f4519ece37dc24887e11b55d7ae2f5b9e386cd1b50a4550696d957cb4900f03a82012708dafc9e1b880fd083b32182b869be8e0922b81f8e175ffde54d797fe11eb03f9e3bf75f1d68bf0b8b6fb4e317a0f9d6f03eaf8ce6675bc60d8c4d90829ce8f72d0163c1d5cf348a862d55063035e7a025f4da968de7e4d7e4004197917f4070f1d6caa02bbebaebb5d7e581e4b66559e635f805ff0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000880000000000000000"))
+ require.Equal(t, res.signer, common.HexToAddress("0x0000000000000000000000000000000000000000"))
+ require.Equal(t, res.validators, []common.Address{
+ common.HexToAddress("0x2a7cdd959bfe8d9487b2a43b33565295a698f7e2"),
+ common.HexToAddress("0x6488aa4d1955ee33403f8ccb1d4de5fb97c7ade2"),
+ common.HexToAddress("0x9ef9f4360c606c7ab4db26b016007d3ad0ab86a0"),
+ common.HexToAddress("0xee01c3b1283aa067c58eab4709f85e99d46de5fe"),
+ common.HexToAddress("0x685b1ded8013785d6623cc18d214320b6bb64759"),
+ common.HexToAddress("0x78f3adfc719c99674c072166708589033e2d9afe"),
+ common.HexToAddress("0xc2be4ec20253b8642161bc3f444f53679c1f3d47"),
+ common.HexToAddress("0x2f7be8361c80a4c1e7e9aaf001d0877f1cfde218"),
+ common.HexToAddress("0xce2fd7544e0b2cc94692d4a704debef7bcb61328"),
+ common.HexToAddress("0xb8f7166496996a7da21cf1f1b04d9b3e26a3d077"),
+ common.HexToAddress("0x2d4c407bbe49438ed859fe965b140dcf1aab71a9"),
+ common.HexToAddress("0x6bbad7cf34b5fa511d8e963dbba288b1960e75d6"),
+ common.HexToAddress("0x4430b3230294d12c6ab2aac5c2cd68e80b16b581"),
+ common.HexToAddress("0xea0a6e3c511bbd10f4519ece37dc24887e11b55d"),
+ common.HexToAddress("0x7ae2f5b9e386cd1b50a4550696d957cb4900f03a"),
+ common.HexToAddress("0x82012708dafc9e1b880fd083b32182b869be8e09"),
+ common.HexToAddress("0x22b81f8e175ffde54d797fe11eb03f9e3bf75f1d"),
+ common.HexToAddress("0x68bf0b8b6fb4e317a0f9d6f03eaf8ce6675bc60d"),
+ common.HexToAddress("0x8c4d90829ce8f72d0163c1d5cf348a862d550630"),
+ common.HexToAddress("0x35e7a025f4da968de7e4d7e4004197917f4070f1"),
+ common.HexToAddress("0xd6caa02bbebaebb5d7e581e4b66559e635f805ff"),
+ })
+ // latest known block with new validators (15946200)
+ res = doVerifyParliaBlock(t, 56, hexutil.MustDecode("0xf90403a062177e38673a43e86ef7804bd5272eaf5f133b9d6ee217c1ef2debca1f614c30a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d4934794ea0a6e3c511bbd10f4519ece37dc24887e11b55da0d45514f56749c9ad5034a1e81dee7ba1c8b2da69ba0525493b4bda3d6e375825a0b8789043c2e8807825077f5620df7df01c2326d4849ba91681a6fba8aab5a710a07c2f5c172d09ad3e10f514b421ce89ce74190e3248d12fc1e3df71e971d0742cb90100e261124194f80d14022b486f980c081ac19cbe7c8c70b2a8d953c63079804794d23d3a1a04409385cf7850313c9423182dc966bb3a1be93157e48c5f33bee7b028e534ad8628a3e2fdc2b0a8f0e3016632560ac5296c3882a57497359bb897005339d838ab02c008b1e3b3a61f73a8a05bc46e5886bd3612223490506f3a36d61016e01db538ee07b39026669991308f602fac45adaebaa9fb091c519de57b3cff6d5f63210461463d00e6109395e406a5228cc121ba47b528bbb216b20c6d5420461143138a412f380d121d58369460f3953224298b7a9b73f1c70e5d2de51852f0d1142d398d740f7d9158170836018510ae2854f6bd48a0b48a0ce05c8c680283f351d88404a2feaa840104cac184622a5b5bb90205d883010107846765746888676f312e31332e34856c696e7578000000c3167bdf2465176c461afb316ebc773c61faee85a6515daa295e26495cef6f69dfa69911d9d8e4f3bbadb89b29a97c6effb8a411dabc6adeefaa84f5067c8bbe2d4c407bbe49438ed859fe965b140dcf1aab71a93f349bbafec1551819b8be1efea2fc46ca749aa1685b1ded8013785d6623cc18d214320b6bb6475970f657164e5b75689b64b7fd1fa275f334f28e1872b61c6014342d914470ec7ac2975be345796c2b7ae2f5b9e386cd1b50a4550696d957cb4900f03a8b6c8fd93d6f4cea42bbb345dbc6f0dfdb5bec739f8ccdafcc39f3c7d6ebf637c9151673cbc36b88a6f79b60359f141df90a0c745125b131caaffd12aacf6a8119f7e11623b5a43da638e91f669a130fac0e15a038eedfc68ba3c35c73fed5be4a07afb5be807dddb074639cd9fa61b47676c064fc50d62cce2fd7544e0b2cc94692d4a704debef7bcb61328e2d3a739effcd3a99387d015e260eefac72ebea1e9ae3261a475a27bb1028f140bc2a7c843318afdea0a6e3c511bbd10f4519ece37dc24887e11b55dee226379db83cffc681495730c11fdde79ba4c0cef0274e31810c9df02f98fafde0f841f4e66a1cd7ee0a297d75806912fb259fe8efd114956704ec64cb790438c76d50e1fed0e921aa98030aa1d6f883d60b77dec2147cb7046ade017ebee5e2e0e4bc6a382391800a00000000000000000000000000000000000000000000000000000000000000000880000000000000000"))
+ require.Equal(t, res.signer, common.HexToAddress("0xea0a6e3c511bbd10f4519ece37dc24887e11b55d"))
+ require.Equal(t, res.validators, []common.Address{
+ common.HexToAddress("0x2465176c461afb316ebc773c61faee85a6515daa"),
+ common.HexToAddress("0x295e26495cef6f69dfa69911d9d8e4f3bbadb89b"),
+ common.HexToAddress("0x29a97c6effb8a411dabc6adeefaa84f5067c8bbe"),
+ common.HexToAddress("0x2d4c407bbe49438ed859fe965b140dcf1aab71a9"),
+ common.HexToAddress("0x3f349bbafec1551819b8be1efea2fc46ca749aa1"),
+ common.HexToAddress("0x685b1ded8013785d6623cc18d214320b6bb64759"),
+ common.HexToAddress("0x70f657164e5b75689b64b7fd1fa275f334f28e18"),
+ common.HexToAddress("0x72b61c6014342d914470ec7ac2975be345796c2b"),
+ common.HexToAddress("0x7ae2f5b9e386cd1b50a4550696d957cb4900f03a"),
+ common.HexToAddress("0x8b6c8fd93d6f4cea42bbb345dbc6f0dfdb5bec73"),
+ common.HexToAddress("0x9f8ccdafcc39f3c7d6ebf637c9151673cbc36b88"),
+ common.HexToAddress("0xa6f79b60359f141df90a0c745125b131caaffd12"),
+ common.HexToAddress("0xaacf6a8119f7e11623b5a43da638e91f669a130f"),
+ common.HexToAddress("0xac0e15a038eedfc68ba3c35c73fed5be4a07afb5"),
+ common.HexToAddress("0xbe807dddb074639cd9fa61b47676c064fc50d62c"),
+ common.HexToAddress("0xce2fd7544e0b2cc94692d4a704debef7bcb61328"),
+ common.HexToAddress("0xe2d3a739effcd3a99387d015e260eefac72ebea1"),
+ common.HexToAddress("0xe9ae3261a475a27bb1028f140bc2a7c843318afd"),
+ common.HexToAddress("0xea0a6e3c511bbd10f4519ece37dc24887e11b55d"),
+ common.HexToAddress("0xee226379db83cffc681495730c11fdde79ba4c0c"),
+ common.HexToAddress("0xef0274e31810c9df02f98fafde0f841f4e66a1cd"),
+ })
+ // 200's chapel block
+ res = doVerifyParliaBlock(t, 97, hexutil.MustDecode("0xf902d0a056991a71bc79fea816387a6076068274af17e08bbdc81c2f289447f60a19d310a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d4934794980a75ecd1309ea12fa2ed87a8744fbfc9b863d5a0be326f334378264bc98880f49863c9b4a0f1dbce836497a769e19fbfe291dee8a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421b90100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000281c88401c9c38080845f06d057b8d9d983010000846765746889676f312e31322e3137856c696e75780000000000001284214b9b9c85549ab3d2b972df0deef66ac2c935552c16704d214347f29fa77f77da6d75d7c752980a75ecd1309ea12fa2ed87a8744fbfc9b863d5a2959d3f95eae5dc7d70144ce1b73b403b7eb6e0b71b214cb885500844365e95cd9942c7276e7fd8f474cf03cceff28abc65c9cbae594f725c80e12d3674be94d2b375f63ae71920774699382646cf3ef8ba561a8bfad6f116b8b2e6220792aea7d71c7234d7e1d574948e492b42993dba91a4a9988009d607f0264c01a00000000000000000000000000000000000000000000000000000000000000000880000000000000000"))
+ require.Equal(t, res.signer, common.HexToAddress("0x980a75ecd1309ea12fa2ed87a8744fbfc9b863d5"))
+ require.Equal(t, res.validators, []common.Address{
+ common.HexToAddress("0x1284214b9b9c85549ab3d2b972df0deef66ac2c9"),
+ common.HexToAddress("0x35552c16704d214347f29fa77f77da6d75d7c752"),
+ common.HexToAddress("0x980a75ecd1309ea12fa2ed87a8744fbfc9b863d5"),
+ common.HexToAddress("0xa2959d3f95eae5dc7d70144ce1b73b403b7eb6e0"),
+ common.HexToAddress("0xb71b214cb885500844365e95cd9942c7276e7fd8"),
+ common.HexToAddress("0xf474cf03cceff28abc65c9cbae594f725c80e12d"),
+ })
+}
diff --git a/core/vm/contracts.go b/core/vm/contracts.go
index aa0eb831d..ef41e7e44 100644
--- a/core/vm/contracts.go
+++ b/core/vm/contracts.go
@@ -22,16 +22,18 @@ import (
"errors"
"math/big"
+ //lint:ignore SA1019 Needed for precompile
+ "github.com/prysmaticlabs/prysm/crypto/bls"
+ "golang.org/x/crypto/ripemd160"
+
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/math"
+ "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/crypto/blake2b"
"github.com/ethereum/go-ethereum/crypto/bls12381"
"github.com/ethereum/go-ethereum/crypto/bn256"
"github.com/ethereum/go-ethereum/params"
-
- //lint:ignore SA1019 Needed for precompile
- "golang.org/x/crypto/ripemd160"
)
// PrecompiledContract is the basic interface for native Go contracts. The implementation
@@ -92,6 +94,22 @@ var PrecompiledContractsBerlin = map[common.Address]PrecompiledContract{
common.BytesToAddress([]byte{9}): &blake2F{},
}
+// PrecompiledContractsBoneh contains the default set of pre-compiled Ethereum
+// contracts used in the Boneh release.
+var PrecompiledContractsBoneh = map[common.Address]PrecompiledContract{
+ common.BytesToAddress([]byte{1}): &ecrecover{},
+ common.BytesToAddress([]byte{2}): &sha256hash{},
+ common.BytesToAddress([]byte{3}): &ripemd160hash{},
+ common.BytesToAddress([]byte{4}): &dataCopy{},
+ common.BytesToAddress([]byte{5}): &bigModExp{eip2565: true},
+ common.BytesToAddress([]byte{6}): &bn256AddIstanbul{},
+ common.BytesToAddress([]byte{7}): &bn256ScalarMulIstanbul{},
+ common.BytesToAddress([]byte{8}): &bn256PairingIstanbul{},
+ common.BytesToAddress([]byte{9}): &blake2F{},
+
+ common.BytesToAddress([]byte{100}): &voteSignatureVerify{},
+}
+
// PrecompiledContractsBLS contains the set of pre-compiled Ethereum
// contracts specified in EIP-2537. These are exported for testing purposes.
var PrecompiledContractsBLS = map[common.Address]PrecompiledContract{
@@ -107,6 +125,7 @@ var PrecompiledContractsBLS = map[common.Address]PrecompiledContract{
}
var (
+ PrecompiledAddressesBoneh []common.Address
PrecompiledAddressesBerlin []common.Address
PrecompiledAddressesIstanbul []common.Address
PrecompiledAddressesByzantium []common.Address
@@ -126,11 +145,25 @@ func init() {
for k := range PrecompiledContractsBerlin {
PrecompiledAddressesBerlin = append(PrecompiledAddressesBerlin, k)
}
+ for k := range PrecompiledContractsBoneh {
+ PrecompiledAddressesBoneh = append(PrecompiledAddressesBoneh, k)
+ }
+}
+
+// set of BAS specific pre-compiled contracts (0x424153 means BAS)
+var verifyParliaBlockAddress = common.HexToAddress("0x0000000000000000000000000000004241530001")
+
+func enableBasContracts(contracts map[common.Address]PrecompiledContract, chainRules params.Rules) {
+ if chainRules.HasVerifyParliaBlock {
+ contracts[verifyParliaBlockAddress] = &verifyParliaBlock{}
+ }
}
// ActivePrecompiles returns the precompiles enabled with the current configuration.
func ActivePrecompiles(rules params.Rules) []common.Address {
switch {
+ case rules.IsBoneh:
+ return PrecompiledAddressesBoneh
case rules.IsBerlin:
return PrecompiledAddressesBerlin
case rules.IsIstanbul:
@@ -1049,3 +1082,52 @@ func (c *bls12381MapG2) Run(input []byte) ([]byte, error) {
// Encode the G2 point to 256 bytes
return g.EncodePoint(r), nil
}
+
+var errVoteSignatureVerify = errors.New("invalid signatures")
+
+// voteSignatureVerify implements BEP-126 finality signature verification precompile.
+type voteSignatureVerify struct{}
+
+// RequiredGas returns the gas required to execute the pre-compiled contract.
+func (c *voteSignatureVerify) RequiredGas(input []byte) uint64 {
+ return params.VoteSignatureVerifyGas
+}
+
+func (c *voteSignatureVerify) Run(input []byte) ([]byte, error) {
+ var (
+ srcNum = new(big.Int).SetBytes(getData(input, 0, 32)).Uint64()
+ tarNum = new(big.Int).SetBytes(getData(input, 32, 32)).Uint64()
+ srcHash = getData(input, 64, 32)
+ tarHash = getData(input, 96, 32)
+ sig = getData(input, 128, 96)
+ BLSKey = getData(input, 224, 48)
+ )
+
+ sigs := make([][]byte, 1)
+ msgs := make([][32]byte, 1)
+ pubKeys := make([]bls.PublicKey, 1)
+
+ voteData := &types.VoteData{
+ SourceNumber: srcNum,
+ SourceHash: common.BytesToHash(srcHash),
+ TargetNumber: tarNum,
+ TargetHash: common.BytesToHash(tarHash),
+ }
+ copy(msgs[0][:], voteData.Hash().Bytes())
+
+ pubKey, err := bls.PublicKeyFromBytes(BLSKey)
+ if err != nil {
+ return nil, err
+ }
+ pubKeys[0] = pubKey
+ sigs[0] = sig
+
+ success, err := bls.VerifyMultipleSignatures(sigs, msgs, pubKeys)
+ if err != nil {
+ return nil, err
+ }
+ if !success {
+ return nil, errVoteSignatureVerify
+ }
+ return big1.Bytes(), nil
+}
diff --git a/core/vm/contracts_test.go b/core/vm/contracts_test.go
index 30d9b49f7..0ada1db7a 100644
--- a/core/vm/contracts_test.go
+++ b/core/vm/contracts_test.go
@@ -65,6 +65,7 @@ var allPrecompiles = map[common.Address]PrecompiledContract{
common.BytesToAddress([]byte{16}): &bls12381Pairing{},
common.BytesToAddress([]byte{17}): &bls12381MapG1{},
common.BytesToAddress([]byte{18}): &bls12381MapG2{},
+ common.BytesToAddress([]byte{100}): &voteSignatureVerify{},
}
// EIP-152 test vectors
@@ -231,6 +232,16 @@ func BenchmarkPrecompiledIdentity(bench *testing.B) {
benchmarkPrecompiled("04", t, bench)
}
+// Benchmarks the sample inputs from the voteSignatureVerify precompile.
+func BenchmarkPrecompiledVoteSignatureVerify(bench *testing.B) {
+ t := precompiledTest{
+ Input: "000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000c555d45d77921e0f26487706179f73c5f8539744b55147c73a3621366bf809c066c8781959ce3621f67c9345da9b5e01ce5113d7b5ae3c6dcd3ca88ad4ed9023fa55c8188060c74f1791eefc78e8aacf0c044e3f6317fe5eadce8ba9db2d19da83e41364c3d6802175acaa392d576a95206d83e5bbfc6022b53c288dc5b60cfe1722298007f1a4b97f47383a9fe1cb7bb5250783e89f3720b7a37bec026ece0b6b32d4d46a7127dcc865f0d30f2ee3dcd5983b686f4e3a9202afc8b608652001c9938906ae1ff1417486096e32511f1bc",
+ Expected: "01",
+ Name: "",
+ }
+ benchmarkPrecompiled("64", t, bench)
+}
+
// Tests the sample inputs from the ModExp EIP 198.
func TestPrecompiledModExp(t *testing.T) { testJson("modexp", "05", t) }
func BenchmarkPrecompiledModExp(b *testing.B) { benchJson("modexp", "05", b) }
diff --git a/core/vm/evm.go b/core/vm/evm.go
index 856dcccad..cbd27cab7 100644
--- a/core/vm/evm.go
+++ b/core/vm/evm.go
@@ -18,7 +18,6 @@ package vm
import (
"errors"
- systemcontract2 "github.com/ethereum/go-ethereum/core/vm/systemcontract"
"math/big"
"sync"
"sync/atomic"
@@ -50,19 +49,11 @@ type (
GetHashFunc func(uint64) common.Hash
)
-func (evm *EVM) precompile(addr, caller common.Address) (PrecompiledContract, bool) {
- evmHook := systemcontract2.CreateEvmHook(addr, systemcontract2.EvmHookContext{
- CallerAddress: caller,
- StateDb: evm.StateDB,
- Evm: evm,
- ChainConfig: evm.chainConfig,
- ChainRules: evm.chainRules,
- })
- if evmHook != nil {
- return evmHook, true
- }
+func (evm *EVM) precompile(addr common.Address) (PrecompiledContract, bool) {
var precompiles map[common.Address]PrecompiledContract
switch {
+ case evm.chainRules.IsBoneh:
+ precompiles = PrecompiledContractsBoneh
case evm.chainRules.IsBerlin:
precompiles = PrecompiledContractsBerlin
case evm.chainRules.IsIstanbul:
@@ -72,6 +63,7 @@ func (evm *EVM) precompile(addr, caller common.Address) (PrecompiledContract, bo
default:
precompiles = PrecompiledContractsHomestead
}
+ enableBasContracts(precompiles, evm.chainRules)
p, ok := precompiles[addr]
return p, ok
}
@@ -238,7 +230,7 @@ func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas
return nil, gas, ErrInsufficientBalance
}
snapshot := evm.StateDB.Snapshot()
- p, isPrecompile := evm.precompile(addr, caller.Address())
+ p, isPrecompile := evm.precompile(addr)
if !evm.StateDB.Exist(addr) {
if !isPrecompile && evm.chainRules.IsEIP158 && value.Sign() == 0 {
@@ -340,7 +332,7 @@ func (evm *EVM) CallCode(caller ContractRef, addr common.Address, input []byte,
}
// It is allowed to call precompiles, even via delegatecall
- if p, isPrecompile := evm.precompile(addr, caller.Address()); isPrecompile {
+ if p, isPrecompile := evm.precompile(addr); isPrecompile {
ret, gas, err = RunPrecompiledContract(p, input, gas)
} else {
addrCopy := addr
@@ -384,7 +376,7 @@ func (evm *EVM) DelegateCall(caller ContractRef, addr common.Address, input []by
}
// It is allowed to call precompiles, even via delegatecall
- if p, isPrecompile := evm.precompile(addr, caller.Address()); isPrecompile {
+ if p, isPrecompile := evm.precompile(addr); isPrecompile {
ret, gas, err = RunPrecompiledContract(p, input, gas)
} else {
addrCopy := addr
@@ -436,7 +428,7 @@ func (evm *EVM) StaticCall(caller ContractRef, addr common.Address, input []byte
}(gas)
}
- if p, isPrecompile := evm.precompile(addr, caller.Address()); isPrecompile {
+ if p, isPrecompile := evm.precompile(addr); isPrecompile {
ret, gas, err = RunPrecompiledContract(p, input, gas)
} else {
// At this point, we use a copy of address. If we don't, the go compiler will
diff --git a/core/vm/instructions_test.go b/core/vm/instructions_test.go
index d17ccfab8..c881ba0ac 100644
--- a/core/vm/instructions_test.go
+++ b/core/vm/instructions_test.go
@@ -568,11 +568,11 @@ func BenchmarkOpSHA3(bench *testing.B) {
env.interpreter = evmInterpreter
mem.Resize(32)
pc := uint64(0)
- start := uint256.NewInt()
+ start := new(uint256.Int)
bench.ResetTimer()
for i := 0; i < bench.N; i++ {
- stack.pushN(*uint256.NewInt().SetUint64(32), *start)
+ stack.pushN(*uint256.NewInt(32), *start)
opSha3(&pc, evmInterpreter, &ScopeContext{mem, stack, nil})
}
}
diff --git a/core/vm/logger_test.go b/core/vm/logger_test.go
index 5d1aa8e54..4af2bb2b7 100644
--- a/core/vm/logger_test.go
+++ b/core/vm/logger_test.go
@@ -60,8 +60,8 @@ func TestStoreCapture(t *testing.T) {
Contract: contract,
}
)
- scope.Stack.push(uint256.NewInt().SetUint64(1))
- scope.Stack.push(uint256.NewInt())
+ scope.Stack.push(uint256.NewInt(1))
+ scope.Stack.push(new(uint256.Int))
var index common.Hash
logger.CaptureStart(env, common.Address{}, contract.Address(), false, nil, 0, nil)
logger.CaptureState(0, SSTORE, 0, 0, scope, nil, 0, nil)
diff --git a/core/vm/systemcontract/error.go b/core/vm/systemcontract/error.go
deleted file mode 100644
index 75e04d6d1..000000000
--- a/core/vm/systemcontract/error.go
+++ /dev/null
@@ -1,10 +0,0 @@
-package systemcontract
-
-import "fmt"
-
-var (
- errNotSupported = fmt.Errorf("not supported")
- errMethodNotFound = fmt.Errorf("method not found")
- errInvalidCaller = fmt.Errorf("invalid caller")
- errFailedToUnpack = fmt.Errorf("failed to unpack")
-)
diff --git a/core/vm/systemcontract/factory.go b/core/vm/systemcontract/factory.go
deleted file mode 100644
index 1d7ebb898..000000000
--- a/core/vm/systemcontract/factory.go
+++ /dev/null
@@ -1,13 +0,0 @@
-package systemcontract
-
-import (
- "github.com/ethereum/go-ethereum/common"
- "github.com/ethereum/go-ethereum/common/systemcontract"
-)
-
-func CreateEvmHook(address common.Address, context EvmHookContext) EvmHook {
- if address == systemcontract.EvmHookRuntimeUpgradeAddress {
- return &evmHookRuntimeUpgrade{context: context}
- }
- return nil
-}
diff --git a/core/vm/systemcontract/types.go b/core/vm/systemcontract/types.go
deleted file mode 100644
index 4dec890e4..000000000
--- a/core/vm/systemcontract/types.go
+++ /dev/null
@@ -1,31 +0,0 @@
-package systemcontract
-
-import (
- "github.com/ethereum/go-ethereum/common"
- "github.com/ethereum/go-ethereum/params"
- "math/big"
-)
-
-type EvmHook interface {
- RequiredGas(input []byte) uint64
- Run(input []byte) ([]byte, error)
-}
-
-type StateDB interface {
- GetCodeHash(common.Address) common.Hash
- GetCode(common.Address) []byte
- SetCode(common.Address, []byte)
- GetCodeSize(common.Address) int
-}
-
-type EVM interface {
- CreateWithAddress(caller common.Address, code []byte, gas uint64, value *big.Int, contractAddr common.Address) (ret []byte, leftOverGas uint64, err error)
-}
-
-type EvmHookContext struct {
- CallerAddress common.Address
- StateDb StateDB
- Evm EVM
- ChainConfig *params.ChainConfig
- ChainRules params.Rules
-}
diff --git a/core/vm/systemcontract/upgrade.go b/core/vm/systemcontract/upgrade.go
deleted file mode 100644
index 8564a55fa..000000000
--- a/core/vm/systemcontract/upgrade.go
+++ /dev/null
@@ -1,95 +0,0 @@
-package systemcontract
-
-import (
- "bytes"
- "github.com/ethereum/go-ethereum/accounts/abi"
- "github.com/ethereum/go-ethereum/common"
- "github.com/ethereum/go-ethereum/common/systemcontract"
- "math/big"
-)
-
-type evmHookRuntimeUpgrade struct {
- context EvmHookContext
-}
-
-func mustNewType(name string) abi.Type {
- typ, err := abi.NewType(name, name, nil)
- if err != nil {
- panic(err)
- }
- return typ
-}
-
-var (
- // abi types
- addressType = mustNewType("address")
- bytesType = mustNewType("bytes")
- // input args
- deployToMethod = abi.NewMethod("deployTo(address,bytes)", "deployTo", abi.Function, "", false, false, abi.Arguments{
- abi.Argument{Type: addressType}, // system contract address
- abi.Argument{Type: bytesType}, // new byte code
- }, abi.Arguments{})
- upgradeToMethod = abi.NewMethod("upgradeTo(address,bytes)", "upgradeTo", abi.Function, "", false, false, abi.Arguments{
- abi.Argument{Type: addressType}, // system contract address
- abi.Argument{Type: bytesType}, // new byte code
- }, abi.Arguments{})
-)
-
-func matchesMethod(input []byte, method abi.Method) []interface{} {
- // check does call matches
- if len(input) < 4 || !bytes.Equal(input[:4], method.ID) {
- return nil
- }
- values, err := method.Inputs.UnpackValues(input[4:])
- if err != nil || len(values) != len(method.Inputs) {
- return nil
- }
- return values
-}
-
-var runtimeUpgradeContract = common.HexToAddress(systemcontract.RuntimeUpgradeContract)
-
-func (sc *evmHookRuntimeUpgrade) Run(input []byte) ([]byte, error) {
- if !sc.context.ChainRules.HasRuntimeUpgrade {
- return nil, errNotSupported
- }
- // check the caller
- if sc.context.CallerAddress != runtimeUpgradeContract {
- return nil, errInvalidCaller
- }
- // if matches upgrade to method
- if values := matchesMethod(input, deployToMethod); values != nil {
- contractAddress, ok := values[0].(common.Address)
- if !ok {
- return nil, errFailedToUnpack
- }
- deployerByteCode, ok := values[1].([]byte)
- if !ok {
- return nil, errFailedToUnpack
- }
- byteCode, _, err := sc.context.Evm.CreateWithAddress(contractAddress, deployerByteCode, 0, big.NewInt(0), contractAddress)
- if err != nil {
- return nil, err
- }
- sc.context.StateDb.SetCode(contractAddress, byteCode)
- return nil, nil
- }
- if values := matchesMethod(input, upgradeToMethod); values != nil {
- contractAddress, ok := values[0].(common.Address)
- if !ok {
- return nil, errFailedToUnpack
- }
- byteCode, ok := values[1].([]byte)
- if !ok {
- return nil, errFailedToUnpack
- }
- sc.context.StateDb.SetCode(contractAddress, byteCode)
- return nil, nil
- }
- return nil, errMethodNotFound
-}
-
-func (sc *evmHookRuntimeUpgrade) RequiredGas(input []byte) uint64 {
- // don't charge gas for these cals
- return 0
-}
diff --git a/core/vm/systemcontract/upgrade_test.go b/core/vm/systemcontract/upgrade_test.go
deleted file mode 100644
index 15a25147f..000000000
--- a/core/vm/systemcontract/upgrade_test.go
+++ /dev/null
@@ -1,91 +0,0 @@
-package systemcontract
-
-import (
- "github.com/ethereum/go-ethereum/common"
- "github.com/ethereum/go-ethereum/common/hexutil"
- "github.com/ethereum/go-ethereum/params"
- "github.com/stretchr/testify/require"
- "testing"
-)
-
-type fakeStateDb struct {
- codeState map[common.Address][]byte
-}
-
-func (s *fakeStateDb) GetCodeHash(common.Address) common.Hash {
- panic("not supported")
-}
-
-func (s *fakeStateDb) GetCode(addr common.Address) []byte {
- if s.codeState == nil {
- s.codeState = make(map[common.Address][]byte)
- }
- return s.codeState[addr]
-}
-
-func (s *fakeStateDb) SetCode(addr common.Address, byteCode []byte) {
- if s.codeState == nil {
- s.codeState = make(map[common.Address][]byte)
- }
- s.codeState[addr] = byteCode
-}
-
-func (s *fakeStateDb) GetCodeSize(addr common.Address) int {
- if s.codeState == nil {
- s.codeState = make(map[common.Address][]byte)
- }
- return len(s.codeState[addr])
-}
-
-func TestEvmHookRuntimeUpgrade_UpgradeShouldWork(t *testing.T) {
- statedb := &fakeStateDb{}
- evmHook := &evmHookRuntimeUpgrade{
- context: EvmHookContext{
- CallerAddress: common.HexToAddress("0x0000000000000000000000000000000000007004"),
- StateDb: statedb,
- ChainConfig: nil,
- ChainRules: params.Rules{
- HasRuntimeUpgrade: true,
- },
- },
- }
- _, err := evmHook.Run(hexutil.MustDecode("0x6fbc15e900000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000008d60566037600b82828239805160001a607314602a57634e487b7160e01b600052600060045260246000fd5b30600052607381538281f3fe73000000000000000000000000000000000000000030146080604052600z080fdfea2646970667358221220e8e6b1d3408504bc107c141b02b247333ed5bfb36f4a2948a69815b2f5aec0f264736f6c634300080b003300000000000000000000000000000000000000"))
- require.NoError(t, err)
- require.Equal(t, statedb.codeState[common.HexToAddress("0x0000000000000000000000000000000000001000")], hexutil.MustDecode("0x60566037600b82828239805160001a607314602a57634e487b7160e01b600052600060045260246000fd5b30600052607381538281f3fe73000000000000000000000000000000000000000030146080604052600080fdfea2646970667358221220e8e6b1d3408504bc107c141b02b247333ed5bfb36f4a2948a69815b2f5aec0f264736f6c634300080b0033"))
-}
-
-func TestEvmHookRuntimeUpgrade_DoesntWorkWhenDisabled(t *testing.T) {
- statedb := &fakeStateDb{}
- evmHook := &evmHookRuntimeUpgrade{
- context: EvmHookContext{
- CallerAddress: common.HexToAddress("0x0000000000000000000000000000000000007004"),
- StateDb: statedb,
- ChainConfig: nil,
- ChainRules: params.Rules{
- HasRuntimeUpgrade: false,
- },
- },
- }
- _, err := evmHook.Run(hexutil.MustDecode("0x6fbc15e900000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000008d60566037600b82828239805160001a607314602a57634e487b7160e01b600052600060045260246000fd5b30600052607381538281f3fe73000000000000000000000000000000000000000030146080604052600080fdfea2646970667358221220e8e6b1d3408504bc107c141b02b247333ed5bfb36f4a2948a69815b2f5aec0f264736f6c634300080b003300000000000000000000000000000000000000"))
- require.Error(t, err)
-}
-
-func TestEvmHookRuntimeUpgrade_BadParams(t *testing.T) {
- statedb := &fakeStateDb{}
- evmHook := &evmHookRuntimeUpgrade{
- context: EvmHookContext{
- CallerAddress: common.HexToAddress("0x0000000000000000000000000000000000007004"),
- StateDb: statedb,
- ChainConfig: nil,
- ChainRules: params.Rules{
- HasRuntimeUpgrade: true,
- },
- },
- }
- _, err := evmHook.Run(hexutil.MustDecode("0x"))
- require.Error(t, err)
- _, err = evmHook.Run(hexutil.MustDecode("0x6fbc15e9"))
- require.Error(t, err)
- _, err = evmHook.Run(hexutil.MustDecode("0x6fbc15e90000000000000000000000000000000000000000000000000000000000001000"))
- require.Error(t, err)
-}
diff --git a/core/vote/vote_journal.go b/core/vote/vote_journal.go
new file mode 100644
index 000000000..3fede2407
--- /dev/null
+++ b/core/vote/vote_journal.go
@@ -0,0 +1,124 @@
+package vote
+
+import (
+ "encoding/json"
+
+ lru "github.com/hashicorp/golang-lru"
+ "github.com/tidwall/wal"
+
+ "github.com/ethereum/go-ethereum/core/types"
+ "github.com/ethereum/go-ethereum/log"
+ "github.com/ethereum/go-ethereum/metrics"
+)
+
+const (
+ maxSizeOfRecentEntry = 512
+)
+
+type VoteJournal struct {
+ journalPath string // file path of disk journal for saving the vote.
+
+ walLog *wal.Log
+
+ voteDataBuffer *lru.Cache
+}
+
+var voteJournalError = metrics.NewRegisteredGauge("voteJournal/local", nil)
+
+func NewVoteJournal(filePath string) (*VoteJournal, error) {
+ walLog, err := wal.Open(filePath, &wal.Options{
+ LogFormat: wal.JSON,
+ SegmentCacheSize: maxSizeOfRecentEntry,
+ })
+ if err != nil {
+ log.Error("Failed to open vote journal", "err", err)
+ return nil, err
+ }
+
+ voteDataBuffer, err := lru.New(maxSizeOfRecentEntry)
+ if err != nil {
+ return nil, err
+ }
+
+ firstIndex, err := walLog.FirstIndex()
+ if err != nil {
+ log.Error("Failed to get first index of votes journal", "err", err)
+ }
+
+ lastIndex, err := walLog.LastIndex()
+ if err != nil {
+ log.Error("Failed to get lastIndex of vote journal", "err", err)
+ return nil, err
+ }
+
+ voteJournal := &VoteJournal{
+ journalPath: filePath,
+ walLog: walLog,
+ }
+
+ // Reload all voteData from journal to lru memory everytime node reboot.
+ for index := firstIndex; index <= lastIndex; index++ {
+ if voteEnvelop, err := voteJournal.ReadVote(index); err == nil && voteEnvelop != nil {
+ voteData := voteEnvelop.Data
+ voteDataBuffer.Add(voteData.TargetNumber, voteData)
+ }
+ }
+ voteJournal.voteDataBuffer = voteDataBuffer
+
+ return voteJournal, nil
+}
+
+func (journal *VoteJournal) WriteVote(voteMessage *types.VoteEnvelope) error {
+ walLog := journal.walLog
+
+ vote, err := json.Marshal(voteMessage)
+ if err != nil {
+ log.Error("Failed to unmarshal vote", "err", err)
+ return err
+ }
+
+ lastIndex, err := walLog.LastIndex()
+ if err != nil {
+ log.Error("Failed to get lastIndex of vote journal", "err", err)
+ return err
+ }
+
+ lastIndex += 1
+ if err = walLog.Write(lastIndex, vote); err != nil {
+ log.Error("Failed to write vote journal", "err", err)
+ return err
+ }
+
+ firstIndex, err := walLog.FirstIndex()
+ if err != nil {
+ log.Error("Failed to get first index of votes journal", "err", err)
+ }
+
+ if lastIndex-firstIndex+1 > maxSizeOfRecentEntry {
+ if err := walLog.TruncateFront(lastIndex - maxSizeOfRecentEntry + 1); err != nil {
+ log.Error("Failed to truncate votes journal", "err", err)
+ }
+ }
+
+ journal.voteDataBuffer.Add(voteMessage.Data.TargetNumber, voteMessage.Data)
+ return nil
+}
+
+func (journal *VoteJournal) ReadVote(index uint64) (*types.VoteEnvelope, error) {
+ voteMessage, err := journal.walLog.Read(index)
+ if err != nil && err != wal.ErrNotFound {
+ log.Error("Failed to read votes journal", "err", err)
+ return nil, err
+ }
+
+ var vote *types.VoteEnvelope
+ if voteMessage != nil {
+ vote = &types.VoteEnvelope{}
+ if err := json.Unmarshal(voteMessage, vote); err != nil {
+ log.Error("Failed to read vote from voteJournal", "err", err)
+ return nil, err
+ }
+ }
+
+ return vote, nil
+}
diff --git a/core/vote/vote_manager.go b/core/vote/vote_manager.go
new file mode 100644
index 000000000..169063a8e
--- /dev/null
+++ b/core/vote/vote_manager.go
@@ -0,0 +1,223 @@
+package vote
+
+import (
+ "fmt"
+
+ "github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/consensus"
+ "github.com/ethereum/go-ethereum/core"
+ "github.com/ethereum/go-ethereum/core/types"
+ "github.com/ethereum/go-ethereum/eth/downloader"
+ "github.com/ethereum/go-ethereum/event"
+ "github.com/ethereum/go-ethereum/log"
+ "github.com/ethereum/go-ethereum/metrics"
+ "github.com/ethereum/go-ethereum/params"
+)
+
+const naturallyJustifiedDist = 15 // The distance to naturally justify a block
+
+// VoteManager will handle the vote produced by self.
+type VoteManager struct {
+ mux *event.TypeMux
+
+ chain *core.BlockChain
+ chainconfig *params.ChainConfig
+
+ chainHeadCh chan core.ChainHeadEvent
+ chainHeadSub event.Subscription
+
+ pool *VotePool
+ signer *VoteSigner
+ journal *VoteJournal
+
+ engine consensus.PoSA
+}
+
+func NewVoteManager(mux *event.TypeMux, chainconfig *params.ChainConfig, chain *core.BlockChain, pool *VotePool, journalPath, blsPasswordPath, blsWalletPath string, engine consensus.PoSA) (*VoteManager, error) {
+ voteManager := &VoteManager{
+ mux: mux,
+
+ chain: chain,
+ chainconfig: chainconfig,
+ chainHeadCh: make(chan core.ChainHeadEvent, chainHeadChanSize),
+
+ pool: pool,
+ engine: engine,
+ }
+
+ // Create voteSigner.
+ voteSigner, err := NewVoteSigner(blsPasswordPath, blsWalletPath)
+ if err != nil {
+ return nil, err
+ }
+ log.Info("Create voteSigner successfully")
+ voteManager.signer = voteSigner
+
+ // Create voteJournal
+ voteJournal, err := NewVoteJournal(journalPath)
+ if err != nil {
+ return nil, err
+ }
+ log.Info("Create voteJournal successfully")
+ voteManager.journal = voteJournal
+
+ // Subscribe to chain head event.
+ voteManager.chainHeadSub = voteManager.chain.SubscribeChainHeadEvent(voteManager.chainHeadCh)
+
+ go voteManager.loop()
+
+ return voteManager, nil
+}
+
+func (voteManager *VoteManager) loop() {
+ log.Debug("vote manager routine loop started")
+ events := voteManager.mux.Subscribe(downloader.StartEvent{}, downloader.DoneEvent{}, downloader.FailedEvent{})
+ defer func() {
+ log.Debug("vote manager loop defer func occur")
+ if !events.Closed() {
+ log.Debug("event not closed, unsubscribed by vote manager loop")
+ events.Unsubscribe()
+ }
+ }()
+
+ dlEventCh := events.Chan()
+
+ startVote := true
+ for {
+ select {
+ case ev := <-dlEventCh:
+ if ev == nil {
+ log.Debug("dlEvent is nil, continue")
+ continue
+ }
+ switch ev.Data.(type) {
+ case downloader.StartEvent:
+ log.Debug("downloader is in startEvent mode, will not startVote")
+ startVote = false
+ case downloader.FailedEvent:
+ log.Debug("downloader is in FailedEvent mode, set startVote flag as true")
+ startVote = true
+ case downloader.DoneEvent:
+ log.Debug("downloader is in DoneEvent mode, set the startVote flag to true")
+ startVote = true
+ }
+ case cHead := <-voteManager.chainHeadCh:
+ if !startVote {
+ log.Debug("startVote flag is false, continue")
+ continue
+ }
+
+ if cHead.Block == nil {
+ log.Debug("cHead.Block is nil, continue")
+ continue
+ }
+
+ curHead := cHead.Block.Header()
+ // Check if cur validator is within the validatorSet at curHead
+ if !voteManager.engine.IsActiveValidatorAt(voteManager.chain, curHead) {
+ log.Debug("cur validator is not within the validatorSet at curHead")
+ continue
+ }
+
+ // Vote for curBlockHeader block.
+ vote := &types.VoteData{
+ TargetNumber: curHead.Number.Uint64(),
+ TargetHash: curHead.Hash(),
+ }
+ voteMessage := &types.VoteEnvelope{
+ Data: vote,
+ }
+
+ // Put Vote into journal and VotesPool if we are active validator and allow to sign it.
+ if ok, sourceNumber, sourceHash := voteManager.UnderRules(curHead); ok {
+ log.Debug("curHead is underRules for voting")
+ if sourceHash == (common.Hash{}) {
+ log.Debug("sourceHash is empty")
+ continue
+ }
+
+ voteMessage.Data.SourceNumber = sourceNumber
+ voteMessage.Data.SourceHash = sourceHash
+
+ if err := voteManager.signer.SignVote(voteMessage); err != nil {
+ log.Error("Failed to sign vote", "err", err)
+ votesSigningErrorMetric(vote.TargetNumber, vote.TargetHash).Inc(1)
+ continue
+ }
+ if err := voteManager.journal.WriteVote(voteMessage); err != nil {
+ log.Error("Failed to write vote into journal", "err", err)
+ voteJournalError.Inc(1)
+ continue
+ }
+
+ log.Debug("vote manager produced vote", "votedBlockNumber", voteMessage.Data.TargetNumber, "votedBlockHash", voteMessage.Data.TargetHash, "voteMessageHash", voteMessage.Hash())
+ voteManager.pool.PutVote(voteMessage)
+ votesManagerMetric(vote.TargetNumber, vote.TargetHash).Inc(1)
+ }
+ case <-voteManager.chainHeadSub.Err():
+ log.Debug("voteManager subscribed chainHead failed")
+ return
+ }
+ }
+}
+
+// UnderRules checks if the produced header under the following rules:
+// A validator must not publish two distinct votes for the same height. (Rule 1)
+// A validator must not vote within the span of its other votes . (Rule 2)
+// Validators always vote for their canonical chain’s latest block. (Rule 3)
+func (voteManager *VoteManager) UnderRules(header *types.Header) (bool, uint64, common.Hash) {
+ justifiedHeader := voteManager.engine.GetJustifiedHeader(voteManager.chain, header)
+ if justifiedHeader == nil {
+ log.Error("highestJustifiedHeader at cur header is nil", "curHeader's BlockNumber", header.Number.Uint64(), "curHeader's BlockHash", header.Hash())
+ return false, 0, common.Hash{}
+ }
+
+ sourceNumber := justifiedHeader.Number.Uint64()
+ sourceHash := justifiedHeader.Hash()
+ targetNumber := header.Number.Uint64()
+
+ voteDataBuffer := voteManager.journal.voteDataBuffer
+ //Rule 1: A validator must not publish two distinct votes for the same height.
+ if voteDataBuffer.Contains(header.Number.Uint64()) {
+ log.Debug("err: A validator must not publish two distinct votes for the same height.")
+ return false, 0, common.Hash{}
+ }
+
+ //Rule 2: A validator must not vote within the span of its other votes.
+ for blockNumber := sourceNumber + 1; blockNumber < targetNumber; blockNumber++ {
+ if voteDataBuffer.Contains(blockNumber) {
+ voteData, ok := voteDataBuffer.Get(blockNumber)
+ if !ok {
+ log.Error("Failed to get voteData info from LRU cache.")
+ continue
+ }
+ if voteData.(*types.VoteData).SourceNumber > sourceNumber {
+ log.Debug("error: cur vote is within the span of other votes")
+ return false, 0, common.Hash{}
+ }
+ }
+ }
+ for blockNumber := targetNumber; blockNumber <= targetNumber+naturallyJustifiedDist; blockNumber++ {
+ if voteDataBuffer.Contains(blockNumber) {
+ voteData, ok := voteDataBuffer.Get(blockNumber)
+ if !ok {
+ log.Error("Failed to get voteData info from LRU cache.")
+ continue
+ }
+ if voteData.(*types.VoteData).SourceNumber < sourceNumber {
+ log.Debug("error: other votes are within span of cur vote")
+ return false, 0, common.Hash{}
+ }
+ }
+ }
+
+ // Rule 3: Validators always vote for their canonical chain’s latest block.
+ // Since the header subscribed to is the canonical chain, so this rule is satisified by default.
+ log.Debug("All three rules check passed")
+ return true, sourceNumber, sourceHash
+}
+
+// Metrics to monitor if voteManager worked in the expetected logic.
+func votesManagerMetric(blockNumber uint64, blockHash common.Hash) metrics.Gauge {
+ return metrics.GetOrRegisterGauge(fmt.Sprintf("voteManager/blockNumber/%d/blockHash/%s", blockNumber, blockHash), nil)
+}
diff --git a/core/vote/vote_pool.go b/core/vote/vote_pool.go
new file mode 100644
index 000000000..038420f89
--- /dev/null
+++ b/core/vote/vote_pool.go
@@ -0,0 +1,393 @@
+package vote
+
+import (
+ "container/heap"
+ "sync"
+
+ mapset "github.com/deckarep/golang-set"
+
+ "github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/consensus"
+ "github.com/ethereum/go-ethereum/core"
+ "github.com/ethereum/go-ethereum/core/types"
+ "github.com/ethereum/go-ethereum/event"
+ "github.com/ethereum/go-ethereum/log"
+ "github.com/ethereum/go-ethereum/metrics"
+ "github.com/ethereum/go-ethereum/params"
+)
+
+const (
+ maxCurVoteAmountPerBlock = 21
+ maxFutureVoteAmountPerBlock = 50
+
+ voteBufferForPut = 256
+ lowerLimitOfVoteBlockNumber = 256
+ upperLimitOfVoteBlockNumber = 11
+
+ chainHeadChanSize = 10 // chainHeadChanSize is the size of channel listening to ChainHeadEvent.
+)
+
+var (
+ localCurVotesGauge = metrics.NewRegisteredGauge("curVotes/local", nil)
+ localFutureVotesGauge = metrics.NewRegisteredGauge("futureVotes/local", nil)
+
+ localReceivedVotesGauge = metrics.NewRegisteredGauge("receivedVotes/local", nil)
+
+ localCurVotesPqGauge = metrics.NewRegisteredGauge("curVotesPq/local", nil)
+ localFutureVotesPqGauge = metrics.NewRegisteredGauge("futureVotesPq/local", nil)
+)
+
+type VoteBox struct {
+ blockNumber uint64
+ voteMessages []*types.VoteEnvelope
+}
+
+type VotePool struct {
+ chain *core.BlockChain
+ chainconfig *params.ChainConfig
+ mu sync.RWMutex
+
+ votesFeed event.Feed
+ scope event.SubscriptionScope
+
+ receivedVotes mapset.Set
+
+ curVotes map[common.Hash]*VoteBox
+ futureVotes map[common.Hash]*VoteBox
+
+ curVotesPq *votesPriorityQueue
+ futureVotesPq *votesPriorityQueue
+
+ chainHeadCh chan core.ChainHeadEvent
+ chainHeadSub event.Subscription
+
+ votesCh chan *types.VoteEnvelope
+
+ engine consensus.PoSA
+}
+
+type votesPriorityQueue []*types.VoteData
+
+func NewVotePool(chainconfig *params.ChainConfig, chain *core.BlockChain, engine consensus.PoSA) *VotePool {
+ votePool := &VotePool{
+ chain: chain,
+ chainconfig: chainconfig,
+ receivedVotes: mapset.NewSet(),
+ curVotes: make(map[common.Hash]*VoteBox),
+ futureVotes: make(map[common.Hash]*VoteBox),
+ curVotesPq: &votesPriorityQueue{},
+ futureVotesPq: &votesPriorityQueue{},
+ chainHeadCh: make(chan core.ChainHeadEvent, chainHeadChanSize),
+ votesCh: make(chan *types.VoteEnvelope, voteBufferForPut),
+ engine: engine,
+ }
+
+ // Subscribe events from blockchain and start the main event loop.
+ votePool.chainHeadSub = votePool.chain.SubscribeChainHeadEvent(votePool.chainHeadCh)
+
+ go votePool.loop()
+ return votePool
+}
+
+// loop is the vote pool's main even loop, waiting for and reacting to outside blockchain events and votes channel event.
+func (pool *VotePool) loop() {
+ for {
+ select {
+ // Handle ChainHeadEvent.
+ case ev := <-pool.chainHeadCh:
+ if ev.Block != nil {
+ latestBlockNumber := ev.Block.NumberU64()
+ pool.prune(latestBlockNumber)
+ pool.transferVotesFromFutureToCur(ev.Block.Header())
+ }
+ case <-pool.chainHeadSub.Err():
+ return
+
+ // Handle votes channel and put the vote into vote pool.
+ case vote := <-pool.votesCh:
+ pool.putIntoVotePool(vote)
+ }
+ }
+}
+
+func (pool *VotePool) PutVote(vote *types.VoteEnvelope) {
+ pool.votesCh <- vote
+}
+
+func (pool *VotePool) putIntoVotePool(vote *types.VoteEnvelope) bool {
+ targetNumber := vote.Data.TargetNumber
+ targetHash := vote.Data.TargetHash
+ header := pool.chain.CurrentBlock().Header()
+ headNumber := header.Number.Uint64()
+
+ // Make sure in the range currentHeight-256~currentHeight+11.
+ if targetNumber+lowerLimitOfVoteBlockNumber-1 < headNumber || targetNumber > headNumber+upperLimitOfVoteBlockNumber {
+ log.Debug("BlockNumber of vote is outside the range of header-256~header+13, will be discarded")
+ return false
+ }
+
+ voteData := &types.VoteData{
+ TargetNumber: targetNumber,
+ TargetHash: targetHash,
+ }
+
+ var votes map[common.Hash]*VoteBox
+ var votesPq *votesPriorityQueue
+ isFutureVote := false
+
+ voteBlock := pool.chain.GetHeaderByHash(targetHash)
+ if voteBlock == nil {
+ votes = pool.futureVotes
+ votesPq = pool.futureVotesPq
+ isFutureVote = true
+ } else {
+ votes = pool.curVotes
+ votesPq = pool.curVotesPq
+ }
+
+ voteHash := vote.Hash()
+ if ok := pool.basicVerify(vote, headNumber, votes, isFutureVote, voteHash); !ok {
+ return false
+ }
+
+ if !isFutureVote {
+ // Verify if the vote comes from valid validators based on voteAddress (BLSPublicKey), only verify curVotes here, will verify futureVotes in transfer process.
+ if pool.engine.VerifyVote(pool.chain, vote) != nil {
+ return false
+ }
+
+ // Send vote for handler usage of broadcasting to peers.
+ voteEv := core.NewVoteEvent{Vote: vote}
+ pool.votesFeed.Send(voteEv)
+ }
+
+ pool.putVote(votes, votesPq, vote, voteData, voteHash, isFutureVote)
+
+ return true
+}
+
+func (pool *VotePool) SubscribeNewVoteEvent(ch chan<- core.NewVoteEvent) event.Subscription {
+ return pool.scope.Track(pool.votesFeed.Subscribe(ch))
+}
+
+func (pool *VotePool) putVote(m map[common.Hash]*VoteBox, votesPq *votesPriorityQueue, vote *types.VoteEnvelope, voteData *types.VoteData, voteHash common.Hash, isFutureVote bool) {
+ targetHash := vote.Data.TargetHash
+ targetNumber := vote.Data.TargetNumber
+
+ log.Debug("The vote info to put is:", "voteBlockNumber", targetNumber, "voteBlockHash", targetHash)
+
+ pool.mu.Lock()
+ defer pool.mu.Unlock()
+ if _, ok := m[targetHash]; !ok {
+ // Push into votes priorityQueue if not exist in corresponding votes Map.
+ // To be noted: will not put into priorityQueue if exists in map to avoid duplicate element with the same voteData.
+ heap.Push(votesPq, voteData)
+ voteBox := &VoteBox{
+ blockNumber: targetNumber,
+ voteMessages: make([]*types.VoteEnvelope, 0, maxCurVoteAmountPerBlock),
+ }
+ m[targetHash] = voteBox
+
+ if isFutureVote {
+ localFutureVotesPqGauge.Update(int64(votesPq.Len()))
+ } else {
+ localCurVotesPqGauge.Update(int64(votesPq.Len()))
+ }
+ }
+
+ // Put into corresponding votes map.
+ m[targetHash].voteMessages = append(m[targetHash].voteMessages, vote)
+ // Add into received vote to avoid future duplicated vote comes.
+ pool.receivedVotes.Add(voteHash)
+ log.Debug("VoteHash put into votepool is:", "voteHash", voteHash)
+
+ if isFutureVote {
+ localFutureVotesGauge.Inc(1)
+ } else {
+ localCurVotesGauge.Inc(1)
+ }
+ localReceivedVotesGauge.Update(int64(pool.receivedVotes.Cardinality()))
+}
+
+func (pool *VotePool) transferVotesFromFutureToCur(latestBlockHeader *types.Header) {
+ pool.mu.Lock()
+ defer pool.mu.Unlock()
+
+ futurePq := pool.futureVotesPq
+ latestBlockNumber := latestBlockHeader.Number.Uint64()
+
+ // For vote before latestBlockHeader-13, transfer to cur if valid.
+ for futurePq.Len() > 0 && futurePq.Peek().TargetNumber+upperLimitOfVoteBlockNumber < latestBlockNumber {
+ blockHash := futurePq.Peek().TargetHash
+ pool.transfer(blockHash)
+ }
+
+ // For vote within latestBlockHeader-13 ~ latestBlockHeader, only transfer the the vote inside the local fork.
+ futurePqBuffer := make([]*types.VoteData, 0)
+ for futurePq.Len() > 0 && futurePq.Peek().TargetNumber <= latestBlockNumber {
+ blockHash := futurePq.Peek().TargetHash
+ header := pool.chain.GetHeaderByHash(blockHash)
+ if header == nil {
+ // Put into pq buffer used for later put again into futurePq
+ futurePqBuffer = append(futurePqBuffer, heap.Pop(futurePq).(*types.VoteData))
+ continue
+ }
+ pool.transfer(blockHash)
+ }
+
+ for _, voteData := range futurePqBuffer {
+ heap.Push(futurePq, voteData)
+ }
+}
+
+func (pool *VotePool) transfer(blockHash common.Hash) {
+ curPq, futurePq := pool.curVotesPq, pool.futureVotesPq
+ curVotes, futureVotes := pool.curVotes, pool.futureVotes
+ voteData := heap.Pop(futurePq)
+
+ defer localFutureVotesPqGauge.Update(int64(futurePq.Len()))
+
+ voteBox, ok := futureVotes[blockHash]
+ if !ok {
+ return
+ }
+
+ validVotes := make([]*types.VoteEnvelope, 0, len(voteBox.voteMessages))
+ for _, vote := range voteBox.voteMessages {
+ // Verify if the vote comes from valid validators based on voteAddress (BLSPublicKey).
+ if pool.engine.VerifyVote(pool.chain, vote) != nil {
+ continue
+ }
+
+ // In the process of transfer, send valid vote to votes channel for handler usage
+ voteEv := core.NewVoteEvent{Vote: vote}
+ pool.votesFeed.Send(voteEv)
+ validVotes = append(validVotes, vote)
+ }
+
+ if _, ok := curVotes[blockHash]; !ok {
+ heap.Push(curPq, voteData)
+ curVotes[blockHash] = &VoteBox{voteBox.blockNumber, validVotes}
+ localCurVotesPqGauge.Update(int64(curPq.Len()))
+ } else {
+ curVotes[blockHash].voteMessages = append(curVotes[blockHash].voteMessages, validVotes...)
+ }
+
+ delete(futureVotes, blockHash)
+
+ localCurVotesGauge.Inc(int64(len(validVotes)))
+ localFutureVotesGauge.Dec(int64(len(voteBox.voteMessages)))
+}
+
+// Prune old data of duplicationSet, curVotePq and curVotesMap.
+func (pool *VotePool) prune(latestBlockNumber uint64) {
+ pool.mu.Lock()
+ defer pool.mu.Unlock()
+ curVotes := pool.curVotes
+ curVotesPq := pool.curVotesPq
+
+ for curVotesPq.Len() > 0 && curVotesPq.Peek().TargetNumber+lowerLimitOfVoteBlockNumber-1 < latestBlockNumber {
+ // Prune curPriorityQueue.
+ blockHash := heap.Pop(curVotesPq).(*types.VoteData).TargetHash
+ localCurVotesPqGauge.Update(int64(curVotesPq.Len()))
+ if voteBox, ok := curVotes[blockHash]; ok {
+ voteMessages := voteBox.voteMessages
+ // Prune duplicationSet.
+ for _, voteMessage := range voteMessages {
+ voteHash := voteMessage.Hash()
+ pool.receivedVotes.Remove(voteHash)
+ }
+ // Prune curVotes Map.
+ delete(curVotes, blockHash)
+
+ localCurVotesGauge.Dec(int64(len(voteMessages)))
+ localReceivedVotesGauge.Update(int64(pool.receivedVotes.Cardinality()))
+ }
+ }
+}
+
+// GetVotes as batch.
+func (pool *VotePool) GetVotes() []*types.VoteEnvelope {
+ pool.mu.RLock()
+ defer pool.mu.RUnlock()
+
+ votesRes := make([]*types.VoteEnvelope, 0)
+ curVotes := pool.curVotes
+ for _, voteBox := range curVotes {
+ votesRes = append(votesRes, voteBox.voteMessages...)
+ }
+ return votesRes
+}
+
+func (pool *VotePool) FetchVoteByBlockHash(blockHash common.Hash) []*types.VoteEnvelope {
+ pool.mu.RLock()
+ defer pool.mu.RUnlock()
+ if _, ok := pool.curVotes[blockHash]; ok {
+ return pool.curVotes[blockHash].voteMessages
+ }
+ return nil
+}
+
+func (pool *VotePool) basicVerify(vote *types.VoteEnvelope, headNumber uint64, m map[common.Hash]*VoteBox, isFutureVote bool, voteHash common.Hash) bool {
+ targetHash := vote.Data.TargetHash
+ pool.mu.RLock()
+ defer pool.mu.RUnlock()
+
+ // Check duplicate voteMessage firstly.
+ if pool.receivedVotes.Contains(voteHash) {
+ log.Debug("Vote pool already contained the same vote", "voteHash", voteHash)
+ return false
+ }
+
+ // To prevent DOS attacks, make sure no more than 21 votes per blockHash if not futureVotes
+ // and no more than 50 votes per blockHash if futureVotes.
+ maxVoteAmountPerBlock := maxCurVoteAmountPerBlock
+ if isFutureVote {
+ maxVoteAmountPerBlock = maxFutureVoteAmountPerBlock
+ }
+ if voteBox, ok := m[targetHash]; ok {
+ if len(voteBox.voteMessages) > maxVoteAmountPerBlock {
+ return false
+ }
+ }
+
+ // Verify bls signature.
+ if err := vote.Verify(); err != nil {
+ log.Error("Failed to verify voteMessage", "err", err)
+ return false
+ }
+
+ return true
+}
+
+func (pq votesPriorityQueue) Less(i, j int) bool {
+ return pq[i].TargetNumber < pq[j].TargetNumber
+}
+
+func (pq votesPriorityQueue) Len() int {
+ return len(pq)
+}
+
+func (pq votesPriorityQueue) Swap(i, j int) {
+ pq[i], pq[j] = pq[j], pq[i]
+}
+
+func (pq *votesPriorityQueue) Push(vote interface{}) {
+ curVote := vote.(*types.VoteData)
+ *pq = append(*pq, curVote)
+}
+
+func (pq *votesPriorityQueue) Pop() interface{} {
+ tmp := *pq
+ l := len(tmp)
+ var res interface{} = tmp[l-1]
+ *pq = tmp[:l-1]
+ return res
+}
+
+func (pq *votesPriorityQueue) Peek() *types.VoteData {
+ if pq.Len() == 0 {
+ return nil
+ }
+ return (*pq)[0]
+}
diff --git a/core/vote/vote_pool_test.go b/core/vote/vote_pool_test.go
new file mode 100644
index 000000000..0d1446d95
--- /dev/null
+++ b/core/vote/vote_pool_test.go
@@ -0,0 +1,442 @@
+// Copyright 2014 The go-ethereum Authors
+// This file is part of the go-ethereum library.
+//
+// The go-ethereum library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The go-ethereum library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the go-ethereum library. If not, see .
+
+package vote
+
+import (
+ "container/heap"
+ "context"
+ "encoding/json"
+ "fmt"
+ "io/ioutil"
+ "math/big"
+ "os"
+ "path/filepath"
+ "testing"
+ "time"
+
+ "github.com/google/uuid"
+ "github.com/prysmaticlabs/prysm/crypto/bls"
+ "github.com/prysmaticlabs/prysm/validator/accounts"
+ "github.com/prysmaticlabs/prysm/validator/accounts/iface"
+ "github.com/prysmaticlabs/prysm/validator/accounts/wallet"
+ "github.com/prysmaticlabs/prysm/validator/keymanager"
+ keystorev4 "github.com/wealdtech/go-eth2-wallet-encryptor-keystorev4"
+
+ "github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/consensus"
+ "github.com/ethereum/go-ethereum/consensus/ethash"
+ "github.com/ethereum/go-ethereum/core"
+ "github.com/ethereum/go-ethereum/core/rawdb"
+ "github.com/ethereum/go-ethereum/core/types"
+ "github.com/ethereum/go-ethereum/core/vm"
+ "github.com/ethereum/go-ethereum/crypto"
+ "github.com/ethereum/go-ethereum/eth/downloader"
+ "github.com/ethereum/go-ethereum/event"
+ "github.com/ethereum/go-ethereum/params"
+)
+
+var (
+ // testKey is a private key to use for funding a tester account.
+ testKey, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
+
+ // testAddr is the Ethereum address of the tester account.
+ testAddr = crypto.PubkeyToAddress(testKey.PublicKey)
+
+ password = "secretPassword"
+
+ timeThreshold = 30
+)
+
+type mockPOSA struct {
+ consensus.PoSA
+}
+
+type mockInvalidPOSA struct {
+ consensus.PoSA
+}
+
+func (p *mockPOSA) GetJustifiedHeader(chain consensus.ChainHeaderReader, header *types.Header) *types.Header {
+ return chain.GetHeaderByHash(header.ParentHash)
+}
+
+func (p *mockInvalidPOSA) GetJustifiedHeader(chain consensus.ChainHeaderReader, header *types.Header) *types.Header {
+ return nil
+}
+
+func (m *mockPOSA) VerifyVote(chain consensus.ChainHeaderReader, vote *types.VoteEnvelope) error {
+ return nil
+}
+
+func (m *mockInvalidPOSA) VerifyVote(chain consensus.ChainHeaderReader, vote *types.VoteEnvelope) error {
+ return nil
+}
+
+func (m *mockPOSA) IsActiveValidatorAt(chain consensus.ChainHeaderReader, header *types.Header) bool {
+ return true
+}
+
+func (m *mockInvalidPOSA) IsActiveValidatorAt(chain consensus.ChainHeaderReader, header *types.Header) bool {
+ return true
+}
+
+func (pool *VotePool) verifyStructureSizeOfVotePool(receivedVotes, curVotes, futureVotes, curVotesPq, futureVotesPq int) bool {
+ for i := 0; i < timeThreshold; i++ {
+ time.Sleep(1 * time.Second)
+ if pool.receivedVotes.Cardinality() == receivedVotes && len(pool.curVotes) == curVotes && len(pool.futureVotes) == futureVotes && pool.curVotesPq.Len() == curVotesPq && pool.futureVotesPq.Len() == futureVotesPq {
+ return true
+ }
+ }
+ return false
+}
+
+func (journal *VoteJournal) verifyJournal(size, lastLatestVoteNumber int) bool {
+ for i := 0; i < timeThreshold; i++ {
+ time.Sleep(1 * time.Second)
+ lastIndex, _ := journal.walLog.LastIndex()
+ firstIndex, _ := journal.walLog.FirstIndex()
+ if int(lastIndex)-int(firstIndex)+1 == size {
+ return true
+ }
+ lastVote, _ := journal.ReadVote(lastIndex)
+ if lastVote != nil && lastVote.Data.TargetNumber == uint64(lastLatestVoteNumber) {
+ return true
+ }
+ }
+ return false
+}
+
+func TestValidVotePool(t *testing.T) {
+ testVotePool(t, true)
+}
+
+func TestInvalidVotePool(t *testing.T) {
+ testVotePool(t, false)
+}
+
+func testVotePool(t *testing.T, isValidRules bool) {
+ walletPasswordDir, walletDir := setUpKeyManager(t)
+
+ // Create a database pre-initialize with a genesis block
+ db := rawdb.NewMemoryDatabase()
+ (&core.Genesis{
+ Config: params.TestChainConfig,
+ Alloc: core.GenesisAlloc{testAddr: {Balance: big.NewInt(1000000)}},
+ }).MustCommit(db)
+
+ chain, _ := core.NewBlockChain(db, nil, params.TestChainConfig, ethash.NewFullFaker(), vm.Config{}, nil, nil)
+
+ mux := new(event.TypeMux)
+
+ var mockEngine consensus.PoSA
+ if isValidRules {
+ mockEngine = &mockPOSA{}
+ } else {
+ mockEngine = &mockInvalidPOSA{}
+ }
+
+ // Create vote pool
+ votePool := NewVotePool(params.TestChainConfig, chain, mockEngine)
+
+ // Create vote manager
+ // Create a temporary file for the votes journal
+ file, err := ioutil.TempFile("", "")
+ if err != nil {
+ t.Fatalf("failed to create temporary file path: %v", err)
+ }
+ journal := file.Name()
+ defer os.Remove(journal)
+
+ // Clean up the temporary file, we only need the path for now
+ file.Close()
+ os.Remove(journal)
+
+ voteManager, err := NewVoteManager(mux, params.TestChainConfig, chain, votePool, journal, walletPasswordDir, walletDir, mockEngine)
+ if err != nil {
+ t.Fatalf("failed to create vote managers")
+ }
+
+ voteJournal := voteManager.journal
+
+ // Send the done event of downloader
+ time.Sleep(10 * time.Millisecond)
+ mux.Post(downloader.DoneEvent{})
+
+ bs, _ := core.GenerateChain(params.TestChainConfig, chain.Genesis(), ethash.NewFaker(), db, 1, nil)
+ if _, err := chain.InsertChain(bs); err != nil {
+ panic(err)
+ }
+ for i := 0; i < 10; i++ {
+ bs, _ = core.GenerateChain(params.TestChainConfig, bs[len(bs)-1], ethash.NewFaker(), db, 1, nil)
+ if _, err := chain.InsertChain(bs); err != nil {
+ panic(err)
+ }
+ }
+
+ if !isValidRules {
+ if votePool.verifyStructureSizeOfVotePool(11, 11, 0, 11, 0) {
+ t.Fatalf("put vote failed")
+ }
+ return
+ }
+
+ if !votePool.verifyStructureSizeOfVotePool(11, 11, 0, 11, 0) {
+ t.Fatalf("put vote failed")
+ }
+
+ // Verify if votesPq is min heap
+ votesPq := votePool.curVotesPq
+ pqBuffer := make([]*types.VoteData, 0)
+ lastVotedBlockNumber := uint64(0)
+ for votesPq.Len() > 0 {
+ voteData := heap.Pop(votesPq).(*types.VoteData)
+ if voteData.TargetNumber < lastVotedBlockNumber {
+ t.Fatalf("votesPq verification failed")
+ }
+ lastVotedBlockNumber = voteData.TargetNumber
+ pqBuffer = append(pqBuffer, voteData)
+ }
+ for _, voteData := range pqBuffer {
+ heap.Push(votesPq, voteData)
+ }
+
+ // Verify journal
+ if !voteJournal.verifyJournal(11, 11) {
+ t.Fatalf("journal failed")
+ }
+
+ bs, _ = core.GenerateChain(params.TestChainConfig, bs[len(bs)-1], ethash.NewFaker(), db, 1, nil)
+ if _, err := chain.InsertChain(bs); err != nil {
+ panic(err)
+ }
+
+ if !votePool.verifyStructureSizeOfVotePool(12, 12, 0, 12, 0) {
+ t.Fatalf("put vote failed")
+ }
+
+ // Verify journal
+ if !voteJournal.verifyJournal(12, 12) {
+ t.Fatalf("journal failed")
+ }
+
+ for i := 0; i < 256; i++ {
+ bs, _ = core.GenerateChain(params.TestChainConfig, bs[len(bs)-1], ethash.NewFaker(), db, 1, nil)
+ if _, err := chain.InsertChain(bs); err != nil {
+ panic(err)
+ }
+ }
+
+ // Verify journal
+ if !voteJournal.verifyJournal(268, 268) {
+ t.Fatalf("journal failed")
+ }
+
+ // currently chain size is 268, and votePool should be pruned, so vote pool size should be 256!
+ if !votePool.verifyStructureSizeOfVotePool(256, 256, 0, 256, 0) {
+ t.Fatalf("put vote failed")
+ }
+
+ // Test invalid vote whose number larger than latestHeader + 13
+ invalidVote := &types.VoteEnvelope{
+ Data: &types.VoteData{
+ TargetNumber: 1000,
+ },
+ }
+ voteManager.pool.PutVote(invalidVote)
+
+ if !votePool.verifyStructureSizeOfVotePool(256, 256, 0, 256, 0) {
+ t.Fatalf("put vote failed")
+ }
+
+ votes := votePool.GetVotes()
+ if len(votes) != 256 {
+ t.Fatalf("get votes failed")
+ }
+
+ // Verify journal
+ if !voteJournal.verifyJournal(268, 268) {
+ t.Fatalf("journal failed")
+ }
+
+ // Test future votes scenario: votes number within latestBlockHeader ~ latestBlockHeader + 13
+ futureVote := &types.VoteEnvelope{
+ Data: &types.VoteData{
+ TargetNumber: 279,
+ },
+ }
+ if err := voteManager.signer.SignVote(futureVote); err != nil {
+ t.Fatalf("sign vote failed")
+ }
+ voteManager.pool.PutVote(futureVote)
+
+ if !votePool.verifyStructureSizeOfVotePool(257, 256, 1, 256, 1) {
+ t.Fatalf("put vote failed")
+ }
+
+ // Verify journal
+ if !voteJournal.verifyJournal(268, 268) {
+ t.Fatalf("journal failed")
+ }
+
+ // Test duplicate vote case, shouldn'd be put into vote pool
+ duplicateVote := &types.VoteEnvelope{
+ Data: &types.VoteData{
+ TargetNumber: 279,
+ },
+ }
+ if err := voteManager.signer.SignVote(duplicateVote); err != nil {
+ t.Fatalf("sign vote failed")
+ }
+ voteManager.pool.PutVote(duplicateVote)
+
+ if !votePool.verifyStructureSizeOfVotePool(257, 256, 1, 256, 1) {
+ t.Fatalf("put vote failed")
+ }
+
+ // Verify journal
+ if !voteJournal.verifyJournal(268, 268) {
+ t.Fatalf("journal failed")
+ }
+
+ // Test future votes larger than latestBlockNumber + 13 should be rejected
+ futureVote = &types.VoteEnvelope{
+ Data: &types.VoteData{
+ TargetNumber: 282,
+ TargetHash: common.Hash{},
+ },
+ }
+ voteManager.pool.PutVote(futureVote)
+ if !votePool.verifyStructureSizeOfVotePool(257, 256, 1, 256, 1) {
+ t.Fatalf("put vote failed")
+ }
+
+ // Test transfer votes from future to cur, latest block header is #288 after the following generation
+ // For the above BlockNumber 279, it did not have blockHash, should be assigned as well below.
+ curNumber := 268
+ var futureBlockHash common.Hash
+ for i := 0; i < 20; i++ {
+ bs, _ = core.GenerateChain(params.TestChainConfig, bs[len(bs)-1], ethash.NewFaker(), db, 1, nil)
+ curNumber += 1
+ if curNumber == 279 {
+ futureBlockHash = bs[0].Hash()
+ futureVotesMap := votePool.futureVotes
+ voteBox := futureVotesMap[common.Hash{}]
+ futureVotesMap[futureBlockHash] = voteBox
+ delete(futureVotesMap, common.Hash{})
+ futureVotesPq := votePool.futureVotesPq
+ futureVotesPq.Peek().TargetHash = futureBlockHash
+ }
+ if _, err := chain.InsertChain(bs); err != nil {
+ panic(err)
+ }
+ }
+
+ for i := 0; i < timeThreshold; i++ {
+ time.Sleep(1 * time.Second)
+ _, ok := votePool.curVotes[futureBlockHash]
+ if ok && len(votePool.curVotes[futureBlockHash].voteMessages) == 2 {
+ break
+ }
+ }
+ if votePool.curVotes[futureBlockHash] == nil || len(votePool.curVotes[futureBlockHash].voteMessages) != 2 {
+ t.Fatalf("transfer vote failed")
+ }
+
+ // Pruner will keep the size of votePool as latestBlockHeader-255~latestBlockHeader, then final result should be 256!
+ if !votePool.verifyStructureSizeOfVotePool(257, 256, 0, 256, 0) {
+ t.Fatalf("put vote failed")
+ }
+
+ // Verify journal
+ if !voteJournal.verifyJournal(288, 288) {
+ t.Fatalf("journal failed")
+ }
+
+ for i := 0; i < 224; i++ {
+ bs, _ = core.GenerateChain(params.TestChainConfig, bs[len(bs)-1], ethash.NewFaker(), db, 1, nil)
+ if _, err := chain.InsertChain(bs); err != nil {
+ panic(err)
+ }
+ }
+
+ // Verify journal
+ if !voteJournal.verifyJournal(512, 512) {
+ t.Fatalf("journal failed")
+ }
+
+ bs, _ = core.GenerateChain(params.TestChainConfig, bs[len(bs)-1], ethash.NewFaker(), db, 1, nil)
+ if _, err := chain.InsertChain(bs); err != nil {
+ panic(err)
+ }
+
+ // Verify if journal no longer than 512
+ if !voteJournal.verifyJournal(512, 513) {
+ t.Fatalf("journal failed")
+ }
+}
+
+func setUpKeyManager(t *testing.T) (string, string) {
+ walletDir := filepath.Join(t.TempDir(), "wallet")
+ walletConfig := &accounts.CreateWalletConfig{
+ WalletCfg: &wallet.Config{
+ WalletDir: walletDir,
+ KeymanagerKind: keymanager.Imported,
+ WalletPassword: password,
+ },
+ SkipMnemonicConfirm: true,
+ }
+ walletPasswordDir := filepath.Join(t.TempDir(), "password")
+ if err := os.MkdirAll(filepath.Dir(walletPasswordDir), 0700); err != nil {
+ t.Fatalf("failed to create walletPassword dir: %v", err)
+ }
+ if err := ioutil.WriteFile(walletPasswordDir, []byte(password), 0600); err != nil {
+ t.Fatalf("failed to write wallet password dir: %v", err)
+ }
+
+ w, err := accounts.CreateWalletWithKeymanager(context.Background(), walletConfig)
+ if err != nil {
+ t.Fatalf("failed to create wallet: %v", err)
+ }
+ km, _ := w.InitializeKeymanager(context.Background(), iface.InitKeymanagerConfig{ListenForChanges: false})
+ k, _ := km.(keymanager.Importer)
+ secretKey, _ := bls.RandKey()
+ encryptor := keystorev4.New()
+ pubKeyBytes := secretKey.PublicKey().Marshal()
+ cryptoFields, err := encryptor.Encrypt(secretKey.Marshal(), password)
+ if err != nil {
+ t.Fatalf("failed: %v", err)
+ }
+
+ id, _ := uuid.NewRandom()
+ keystore := &keymanager.Keystore{
+ Crypto: cryptoFields,
+ ID: id.String(),
+ Pubkey: fmt.Sprintf("%x", pubKeyBytes),
+ Version: encryptor.Version(),
+ Name: encryptor.Name(),
+ }
+
+ encodedFile, _ := json.MarshalIndent(keystore, "", "\t")
+ keyStoreDir := filepath.Join(t.TempDir(), "keystore")
+ keystoreFile, _ := os.Create(fmt.Sprintf("%s/keystore-%s.json", keyStoreDir, "publichh"))
+ keystoreFile.Write(encodedFile)
+ accounts.ImportAccounts(context.Background(), &accounts.ImportAccountsConfig{
+ Importer: k,
+ Keystores: []*keymanager.Keystore{keystore},
+ AccountPassword: password,
+ })
+ return walletPasswordDir, walletDir
+}
diff --git a/core/vote/vote_signer.go b/core/vote/vote_signer.go
new file mode 100644
index 000000000..d062ad446
--- /dev/null
+++ b/core/vote/vote_signer.go
@@ -0,0 +1,110 @@
+package vote
+
+import (
+ "context"
+ "fmt"
+ "io/ioutil"
+ "time"
+
+ "github.com/pkg/errors"
+
+ "github.com/prysmaticlabs/prysm/crypto/bls"
+ validatorpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1/validator-client"
+ "github.com/prysmaticlabs/prysm/validator/accounts/iface"
+ "github.com/prysmaticlabs/prysm/validator/accounts/wallet"
+ "github.com/prysmaticlabs/prysm/validator/keymanager"
+
+ "github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/core/types"
+ "github.com/ethereum/go-ethereum/log"
+ "github.com/ethereum/go-ethereum/metrics"
+)
+
+const (
+ voteSignerTimeout = time.Second * 5
+)
+
+type VoteSigner struct {
+ km *keymanager.IKeymanager
+ pubKey [48]byte
+}
+
+func NewVoteSigner(blsPasswordPath, blsWalletPath string) (*VoteSigner, error) {
+ dirExists, err := wallet.Exists(blsWalletPath)
+ if err != nil {
+ log.Error("Check BLS wallet exists error: %v.", err)
+ return nil, err
+ }
+ if !dirExists {
+ log.Error("BLS wallet did not exists.")
+ return nil, fmt.Errorf("BLS wallet did not exists.")
+ }
+
+ walletPassword, err := ioutil.ReadFile(blsPasswordPath)
+ if err != nil {
+ log.Error("Read BLS wallet password error: %v.", err)
+ return nil, err
+ }
+ log.Info("Read BLS wallet password successfully")
+
+ w, err := wallet.OpenWallet(context.Background(), &wallet.Config{
+ WalletDir: blsWalletPath,
+ WalletPassword: string(walletPassword),
+ })
+ if err != nil {
+ log.Error("Open BLS wallet failed: %v.", err)
+ return nil, err
+ }
+ log.Info("Open BLS wallet successfully")
+
+ km, err := w.InitializeKeymanager(context.Background(), iface.InitKeymanagerConfig{ListenForChanges: false})
+ if err != nil {
+ log.Error("Initialize key manager failed: %v.", err)
+ return nil, err
+ }
+ log.Info("Initialized keymanager successfully")
+
+ ctx, cancel := context.WithTimeout(context.Background(), voteSignerTimeout)
+ defer cancel()
+
+ pubKeys, err := km.FetchValidatingPublicKeys(ctx)
+ if err != nil {
+ return nil, errors.Wrap(err, "could not fetch validating public keys")
+ }
+
+ return &VoteSigner{
+ km: &km,
+ pubKey: pubKeys[0],
+ }, nil
+}
+
+func (signer *VoteSigner) SignVote(vote *types.VoteEnvelope) error {
+ // Sign the vote, fetch the first pubKey as validator's bls public key.
+ pubKey := signer.pubKey
+ blsPubKey, err := bls.PublicKeyFromBytes(pubKey[:])
+ if err != nil {
+ return errors.Wrap(err, "convert public key from bytes to bls failed")
+ }
+
+ voteDataHash := vote.Data.Hash()
+
+ ctx, cancel := context.WithTimeout(context.Background(), voteSignerTimeout)
+ defer cancel()
+
+ signature, err := (*signer.km).Sign(ctx, &validatorpb.SignRequest{
+ PublicKey: pubKey[:],
+ SigningRoot: voteDataHash[:],
+ })
+ if err != nil {
+ return err
+ }
+
+ copy(vote.VoteAddress[:], blsPubKey.Marshal()[:])
+ copy(vote.Signature[:], signature.Marshal()[:])
+ return nil
+}
+
+// Metrics to indicate if there's any failed signing.
+func votesSigningErrorMetric(blockNumber uint64, blockHash common.Hash) metrics.Gauge {
+ return metrics.GetOrRegisterGauge(fmt.Sprintf("voteSigning/blockNumber/%d/blockHash/%s", blockNumber, blockHash), nil)
+}
diff --git a/eth/api_backend.go b/eth/api_backend.go
index daf6c19ec..fbc0153be 100644
--- a/eth/api_backend.go
+++ b/eth/api_backend.go
@@ -218,6 +218,10 @@ func (b *EthAPIBackend) SubscribeChainHeadEvent(ch chan<- core.ChainHeadEvent) e
return b.eth.BlockChain().SubscribeChainHeadEvent(ch)
}
+func (b *EthAPIBackend) SubscribeFinalizedHeaderEvent(ch chan<- core.FinalizedHeaderEvent) event.Subscription {
+ return b.eth.BlockChain().SubscribeFinalizedHeaderEvent(ch)
+}
+
func (b *EthAPIBackend) SubscribeChainSideEvent(ch chan<- core.ChainSideEvent) event.Subscription {
return b.eth.BlockChain().SubscribeChainSideEvent(ch)
}
@@ -271,6 +275,13 @@ func (b *EthAPIBackend) SubscribeNewTxsEvent(ch chan<- core.NewTxsEvent) event.S
return b.eth.TxPool().SubscribeNewTxsEvent(ch)
}
+func (b *EthAPIBackend) SubscribeNewVoteEvent(ch chan<- core.NewVoteEvent) event.Subscription {
+ if b.eth.VotePool() == nil {
+ return nil
+ }
+ return b.eth.VotePool().SubscribeNewVoteEvent(ch)
+}
+
func (b *EthAPIBackend) Downloader() *downloader.Downloader {
return b.eth.Downloader()
}
diff --git a/eth/backend.go b/eth/backend.go
index 5cfa2998b..d6c41f830 100644
--- a/eth/backend.go
+++ b/eth/backend.go
@@ -21,6 +21,7 @@ import (
"errors"
"fmt"
"math/big"
+ "path/filepath"
"runtime"
"sync"
"sync/atomic"
@@ -38,6 +39,7 @@ import (
"github.com/ethereum/go-ethereum/core/state/pruner"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/vm"
+ "github.com/ethereum/go-ethereum/core/vote"
"github.com/ethereum/go-ethereum/eth/downloader"
"github.com/ethereum/go-ethereum/eth/ethconfig"
"github.com/ethereum/go-ethereum/eth/filters"
@@ -97,6 +99,8 @@ type Ethereum struct {
p2pServer *p2p.Server
lock sync.RWMutex // Protects the variadic fields (e.g. gas price and etherbase)
+
+ votePool *vote.VotePool
}
// New creates a new Ethereum object (including the
@@ -196,7 +200,8 @@ func New(stack *node.Node, config *ethconfig.Config) (*Ethereum, error) {
}
)
bcOps := make([]core.BlockChainOption, 0)
- if config.DiffSync {
+ // TODO diffsync performance is not as expected, disable it when pipecommit is enabled for now
+ if config.DiffSync && !config.PipeCommit {
bcOps = append(bcOps, core.EnableLightProcessor)
}
if config.PipeCommit {
@@ -222,6 +227,30 @@ func New(stack *node.Node, config *ethconfig.Config) (*Ethereum, error) {
}
eth.txPool = core.NewTxPool(config.TxPool, chainConfig, eth.blockchain)
+ conf := stack.Config()
+ blsPasswordPath := conf.BLSPasswordFile
+ blsWalletPath := filepath.Join(conf.DataDir, conf.BLSWalletDir)
+ voteJournalPath := stack.ResolvePath(conf.VoteJournalDir)
+
+ // Create voteManager instance
+ if posa, ok := eth.engine.(consensus.PoSA); ok {
+ // Create votePool instance
+ votePool := vote.NewVotePool(chainConfig, eth.blockchain, posa)
+ eth.votePool = votePool
+ if parlia, ok := eth.engine.(*parlia.Parlia); ok {
+ parlia.VotePool = votePool
+ } else {
+ return nil, fmt.Errorf("Engine is not Parlia type")
+ }
+ log.Info("Create votePool successfully")
+
+ if _, err := vote.NewVoteManager(eth.EventMux(), chainConfig, eth.blockchain, votePool, voteJournalPath, blsPasswordPath, blsWalletPath, posa); err != nil {
+ log.Error("Failed to Initialize voteManager", "error", err)
+ return nil, err
+ }
+ log.Info("Create voteManager successfully")
+ }
+
// Permit the downloader to use the trie cache allowance during fast sync
cacheLimit := cacheConfig.TrieCleanLimit + cacheConfig.TrieDirtyLimit + cacheConfig.SnapshotLimit
checkpoint := config.Checkpoint
@@ -242,6 +271,9 @@ func New(stack *node.Node, config *ethconfig.Config) (*Ethereum, error) {
}); err != nil {
return nil, err
}
+ if eth.votePool != nil {
+ eth.handler.votepool = eth.votePool
+ }
eth.miner = miner.New(eth, &config.Miner, chainConfig, eth.EventMux(), eth.engine, eth.isLocalBlock)
eth.miner.SetExtra(makeExtraData(config.Miner.ExtraData))
@@ -528,6 +560,7 @@ func (s *Ethereum) Miner() *miner.Miner { return s.miner }
func (s *Ethereum) AccountManager() *accounts.Manager { return s.accountManager }
func (s *Ethereum) BlockChain() *core.BlockChain { return s.blockchain }
func (s *Ethereum) TxPool() *core.TxPool { return s.txPool }
+func (s *Ethereum) VotePool() *vote.VotePool { return s.votePool }
func (s *Ethereum) EventMux() *event.TypeMux { return s.eventMux }
func (s *Ethereum) Engine() consensus.Engine { return s.engine }
func (s *Ethereum) ChainDb() ethdb.Database { return s.chainDb }
diff --git a/eth/catalyst/api_test.go b/eth/catalyst/api_test.go
index 593c62944..788612bbf 100644
--- a/eth/catalyst/api_test.go
+++ b/eth/catalyst/api_test.go
@@ -83,6 +83,9 @@ func generateTestChainWithFork(n int, fork int) (*core.Genesis, []*types.Block,
NielsBlock: big.NewInt(0),
MirrorSyncBlock: big.NewInt(0),
BrunoBlock: big.NewInt(0),
+ EulerBlock: big.NewInt(0),
+ BonehBlock: big.NewInt(0),
+ LynnBlock: big.NewInt(0),
Ethash: new(params.EthashConfig),
}
diff --git a/eth/downloader/downloader.go b/eth/downloader/downloader.go
index e1225e7a1..e81e57de8 100644
--- a/eth/downloader/downloader.go
+++ b/eth/downloader/downloader.go
@@ -395,8 +395,8 @@ func (d *Downloader) UnregisterPeer(id string) error {
return nil
}
-// Synchronise tries to sync up our local block chain with a remote peer, both
-// adding various sanity checks as well as wrapping it with various log entries.
+// Synchronise tries to sync up our local blockchain with a remote peer, both
+// adding various sanity checks and wrapping it with various log entries.
func (d *Downloader) Synchronise(id string, head common.Hash, td *big.Int, mode SyncMode) error {
err := d.synchronise(id, head, td, mode)
diff --git a/eth/downloader/peer.go b/eth/downloader/peer.go
index 297ba2fa5..63dc3808b 100644
--- a/eth/downloader/peer.go
+++ b/eth/downloader/peer.go
@@ -457,7 +457,7 @@ func (ps *peerSet) HeaderIdlePeers() ([]*peerConnection, int) {
defer p.lock.RUnlock()
return p.headerThroughput
}
- return ps.idlePeers(eth.ETH65, eth.ETH67, idle, throughput)
+ return ps.idlePeers(eth.ETH65, eth.ETH68, idle, throughput)
}
// BodyIdlePeers retrieves a flat list of all the currently body-idle peers within
@@ -471,7 +471,7 @@ func (ps *peerSet) BodyIdlePeers() ([]*peerConnection, int) {
defer p.lock.RUnlock()
return p.blockThroughput
}
- return ps.idlePeers(eth.ETH65, eth.ETH67, idle, throughput)
+ return ps.idlePeers(eth.ETH65, eth.ETH68, idle, throughput)
}
// ReceiptIdlePeers retrieves a flat list of all the currently receipt-idle peers
@@ -485,7 +485,7 @@ func (ps *peerSet) ReceiptIdlePeers() ([]*peerConnection, int) {
defer p.lock.RUnlock()
return p.receiptThroughput
}
- return ps.idlePeers(eth.ETH65, eth.ETH67, idle, throughput)
+ return ps.idlePeers(eth.ETH65, eth.ETH68, idle, throughput)
}
// NodeDataIdlePeers retrieves a flat list of all the currently node-data-idle
@@ -499,7 +499,7 @@ func (ps *peerSet) NodeDataIdlePeers() ([]*peerConnection, int) {
defer p.lock.RUnlock()
return p.stateThroughput
}
- return ps.idlePeers(eth.ETH65, eth.ETH67, idle, throughput)
+ return ps.idlePeers(eth.ETH65, eth.ETH68, idle, throughput)
}
// idlePeers retrieves a flat list of all currently idle peers satisfying the
diff --git a/eth/fetcher/tx_fetcher.go b/eth/fetcher/tx_fetcher.go
index d0c134801..d22da56f0 100644
--- a/eth/fetcher/tx_fetcher.go
+++ b/eth/fetcher/tx_fetcher.go
@@ -260,7 +260,7 @@ func (f *TxFetcher) Notify(peer string, hashes []common.Hash) error {
// Enqueue imports a batch of received transaction into the transaction pool
// and the fetcher. This method may be called by both transaction broadcasts and
// direct request replies. The differentiation is important so the fetcher can
-// re-shedule missing transactions as soon as possible.
+// re-schedule missing transactions as soon as possible.
func (f *TxFetcher) Enqueue(peer string, txs []*types.Transaction, direct bool) error {
// Keep track of all the propagated transactions
if direct {
diff --git a/eth/filters/api.go b/eth/filters/api.go
index ef360dd70..ce66b515b 100644
--- a/eth/filters/api.go
+++ b/eth/filters/api.go
@@ -177,6 +177,68 @@ func (api *PublicFilterAPI) NewPendingTransactions(ctx context.Context) (*rpc.Su
return rpcSub, nil
}
+// NewVotesFilter creates a filter that fetches votes that entered the vote pool.
+// It is part of the filter package since polling goes with eth_getFilterChanges.
+func (api *PublicFilterAPI) NewVotesFilter() rpc.ID {
+ var (
+ votes = make(chan *types.VoteEnvelope)
+ voteSub = api.events.SubscribeNewVotes(votes)
+ )
+ api.filtersMu.Lock()
+ api.filters[voteSub.ID] = &filter{typ: VotesSubscription, deadline: time.NewTimer(api.timeout), hashes: make([]common.Hash, 0), s: voteSub}
+ api.filtersMu.Unlock()
+
+ gopool.Submit(func() {
+ for {
+ select {
+ case vote := <-votes:
+ api.filtersMu.Lock()
+ if f, found := api.filters[voteSub.ID]; found {
+ f.hashes = append(f.hashes, vote.Hash())
+ }
+ api.filtersMu.Unlock()
+ case <-voteSub.Err():
+ api.filtersMu.Lock()
+ delete(api.filters, voteSub.ID)
+ api.filtersMu.Unlock()
+ return
+ }
+ }
+ })
+
+ return voteSub.ID
+}
+
+// NewVotes creates a subscription that is triggered each time a vote enters the vote pool.
+func (api *PublicFilterAPI) NewVotes(ctx context.Context) (*rpc.Subscription, error) {
+ notifier, supported := rpc.NotifierFromContext(ctx)
+ if !supported {
+ return &rpc.Subscription{}, rpc.ErrNotificationsUnsupported
+ }
+
+ rpcSub := notifier.CreateSubscription()
+
+ gopool.Submit(func() {
+ votes := make(chan *types.VoteEnvelope, 128)
+ voteSub := api.events.SubscribeNewVotes(votes)
+
+ for {
+ select {
+ case vote := <-votes:
+ notifier.Notify(rpcSub.ID, vote)
+ case <-rpcSub.Err():
+ voteSub.Unsubscribe()
+ return
+ case <-notifier.Closed():
+ voteSub.Unsubscribe()
+ return
+ }
+ }
+ })
+
+ return rpcSub, nil
+}
+
// NewBlockFilter creates a filter that fetches blocks that are imported into the chain.
// It is part of the filter package since polling goes with eth_getFilterChanges.
//
@@ -242,6 +304,68 @@ func (api *PublicFilterAPI) NewHeads(ctx context.Context) (*rpc.Subscription, er
return rpcSub, nil
}
+// NewFinalizedHeaderFilter creates a filter that fetches finalized headers that are reached.
+func (api *PublicFilterAPI) NewFinalizedHeaderFilter() rpc.ID {
+ var (
+ headers = make(chan *types.Header)
+ headerSub = api.events.SubscribeNewFinalizedHeaders(headers)
+ )
+
+ api.filtersMu.Lock()
+ api.filters[headerSub.ID] = &filter{typ: FinalizedHeadersSubscription, deadline: time.NewTimer(api.timeout), hashes: make([]common.Hash, 0), s: headerSub}
+ api.filtersMu.Unlock()
+
+ gopool.Submit(func() {
+ for {
+ select {
+ case h := <-headers:
+ api.filtersMu.Lock()
+ if f, found := api.filters[headerSub.ID]; found {
+ f.hashes = append(f.hashes, h.Hash())
+ }
+ api.filtersMu.Unlock()
+ case <-headerSub.Err():
+ api.filtersMu.Lock()
+ delete(api.filters, headerSub.ID)
+ api.filtersMu.Unlock()
+ return
+ }
+ }
+ })
+
+ return headerSub.ID
+}
+
+// NewFinalizedHeaders send a notification each time a new finalized header is reached.
+func (api *PublicFilterAPI) NewFinalizedHeaders(ctx context.Context) (*rpc.Subscription, error) {
+ notifier, supported := rpc.NotifierFromContext(ctx)
+ if !supported {
+ return &rpc.Subscription{}, rpc.ErrNotificationsUnsupported
+ }
+
+ rpcSub := notifier.CreateSubscription()
+
+ gopool.Submit(func() {
+ headers := make(chan *types.Header)
+ headersSub := api.events.SubscribeNewFinalizedHeaders(headers)
+
+ for {
+ select {
+ case h := <-headers:
+ notifier.Notify(rpcSub.ID, h)
+ case <-rpcSub.Err():
+ headersSub.Unsubscribe()
+ return
+ case <-notifier.Closed():
+ headersSub.Unsubscribe()
+ return
+ }
+ }
+ })
+
+ return rpcSub, nil
+}
+
// Logs creates a subscription that fires for all new log that match the given filter criteria.
func (api *PublicFilterAPI) Logs(ctx context.Context, crit FilterCriteria) (*rpc.Subscription, error) {
notifier, supported := rpc.NotifierFromContext(ctx)
@@ -433,7 +557,7 @@ func (api *PublicFilterAPI) GetFilterChanges(id rpc.ID) (interface{}, error) {
f.deadline.Reset(api.timeout)
switch f.typ {
- case PendingTransactionsSubscription, BlocksSubscription:
+ case PendingTransactionsSubscription, BlocksSubscription, VotesSubscription:
hashes := f.hashes
f.hashes = nil
return returnHashes(hashes), nil
diff --git a/eth/filters/filter.go b/eth/filters/filter.go
index 1f281a4ee..84501d897 100644
--- a/eth/filters/filter.go
+++ b/eth/filters/filter.go
@@ -42,9 +42,11 @@ type Backend interface {
SubscribeNewTxsEvent(chan<- core.NewTxsEvent) event.Subscription
SubscribeChainEvent(ch chan<- core.ChainEvent) event.Subscription
+ SubscribeFinalizedHeaderEvent(ch chan<- core.FinalizedHeaderEvent) event.Subscription
SubscribeRemovedLogsEvent(ch chan<- core.RemovedLogsEvent) event.Subscription
SubscribeLogsEvent(ch chan<- []*types.Log) event.Subscription
SubscribePendingLogsEvent(ch chan<- []*types.Log) event.Subscription
+ SubscribeNewVoteEvent(chan<- core.NewVoteEvent) event.Subscription
BloomStatus() (uint64, uint64)
ServiceFilter(ctx context.Context, session *bloombits.MatcherSession)
diff --git a/eth/filters/filter_system.go b/eth/filters/filter_system.go
index 12f037d0f..99873009b 100644
--- a/eth/filters/filter_system.go
+++ b/eth/filters/filter_system.go
@@ -52,7 +52,11 @@ const (
PendingTransactionsSubscription
// BlocksSubscription queries hashes for blocks that are imported
BlocksSubscription
- // LastSubscription keeps track of the last index
+ // VotesSubscription queries vote hashes for votes entering the vote pool
+ VotesSubscription
+ // FinalizedHeadersSubscription queries hashes for finalized headers that are reached
+ FinalizedHeadersSubscription
+ // LastIndexSubscription keeps track of the last index
LastIndexSubscription
)
@@ -66,18 +70,25 @@ const (
logsChanSize = 10
// chainEvChanSize is the size of channel listening to ChainEvent.
chainEvChanSize = 10
+ // finalizedHeaderEvChanSize is the size of channel listening to FinalizedHeaderEvent.
+ finalizedHeaderEvChanSize = 10
+ // voteChanSize is the size of channel listening to NewVoteEvent.
+ // The number is referenced from the size of vote pool.
+ voteChanSize = 256
)
type subscription struct {
- id rpc.ID
- typ Type
- created time.Time
- logsCrit ethereum.FilterQuery
- logs chan []*types.Log
- hashes chan []common.Hash
- headers chan *types.Header
- installed chan struct{} // closed when the filter is installed
- err chan error // closed when the filter is uninstalled
+ id rpc.ID
+ typ Type
+ created time.Time
+ logsCrit ethereum.FilterQuery
+ logs chan []*types.Log
+ hashes chan []common.Hash
+ headers chan *types.Header
+ finalizedHeaders chan *types.Header
+ votes chan *types.VoteEnvelope
+ installed chan struct{} // closed when the filter is installed
+ err chan error // closed when the filter is uninstalled
}
// EventSystem creates subscriptions, processes events and broadcasts them to the
@@ -88,20 +99,24 @@ type EventSystem struct {
lastHead *types.Header
// Subscriptions
- txsSub event.Subscription // Subscription for new transaction event
- logsSub event.Subscription // Subscription for new log event
- rmLogsSub event.Subscription // Subscription for removed log event
- pendingLogsSub event.Subscription // Subscription for pending log event
- chainSub event.Subscription // Subscription for new chain event
+ txsSub event.Subscription // Subscription for new transaction event
+ logsSub event.Subscription // Subscription for new log event
+ rmLogsSub event.Subscription // Subscription for removed log event
+ pendingLogsSub event.Subscription // Subscription for pending log event
+ chainSub event.Subscription // Subscription for new chain event
+ finalizedHeaderSub event.Subscription // Subscription for new finalized header
+ voteSub event.Subscription // Subscription for new vote event
// Channels
- install chan *subscription // install filter for event notification
- uninstall chan *subscription // remove filter for event notification
- txsCh chan core.NewTxsEvent // Channel to receive new transactions event
- logsCh chan []*types.Log // Channel to receive new log event
- pendingLogsCh chan []*types.Log // Channel to receive new log event
- rmLogsCh chan core.RemovedLogsEvent // Channel to receive removed log event
- chainCh chan core.ChainEvent // Channel to receive new chain event
+ install chan *subscription // install filter for event notification
+ uninstall chan *subscription // remove filter for event notification
+ txsCh chan core.NewTxsEvent // Channel to receive new transactions event
+ logsCh chan []*types.Log // Channel to receive new log event
+ pendingLogsCh chan []*types.Log // Channel to receive new log event
+ rmLogsCh chan core.RemovedLogsEvent // Channel to receive removed log event
+ chainCh chan core.ChainEvent // Channel to receive new chain event
+ finalizedHeaderCh chan core.FinalizedHeaderEvent // Channel to receive new finalized header event
+ voteCh chan core.NewVoteEvent // Channel to receive new vote event
}
// NewEventSystem creates a new manager that listens for event on the given mux,
@@ -112,15 +127,17 @@ type EventSystem struct {
// or by stopping the given mux.
func NewEventSystem(backend Backend, lightMode bool) *EventSystem {
m := &EventSystem{
- backend: backend,
- lightMode: lightMode,
- install: make(chan *subscription),
- uninstall: make(chan *subscription),
- txsCh: make(chan core.NewTxsEvent, txChanSize),
- logsCh: make(chan []*types.Log, logsChanSize),
- rmLogsCh: make(chan core.RemovedLogsEvent, rmLogsChanSize),
- pendingLogsCh: make(chan []*types.Log, logsChanSize),
- chainCh: make(chan core.ChainEvent, chainEvChanSize),
+ backend: backend,
+ lightMode: lightMode,
+ install: make(chan *subscription),
+ uninstall: make(chan *subscription),
+ txsCh: make(chan core.NewTxsEvent, txChanSize),
+ logsCh: make(chan []*types.Log, logsChanSize),
+ rmLogsCh: make(chan core.RemovedLogsEvent, rmLogsChanSize),
+ pendingLogsCh: make(chan []*types.Log, logsChanSize),
+ chainCh: make(chan core.ChainEvent, chainEvChanSize),
+ finalizedHeaderCh: make(chan core.FinalizedHeaderEvent, finalizedHeaderEvChanSize),
+ voteCh: make(chan core.NewVoteEvent, voteChanSize),
}
// Subscribe events
@@ -129,11 +146,16 @@ func NewEventSystem(backend Backend, lightMode bool) *EventSystem {
m.rmLogsSub = m.backend.SubscribeRemovedLogsEvent(m.rmLogsCh)
m.chainSub = m.backend.SubscribeChainEvent(m.chainCh)
m.pendingLogsSub = m.backend.SubscribePendingLogsEvent(m.pendingLogsCh)
+ m.finalizedHeaderSub = m.backend.SubscribeFinalizedHeaderEvent(m.finalizedHeaderCh)
+ m.voteSub = m.backend.SubscribeNewVoteEvent(m.voteCh)
// Make sure none of the subscriptions are empty
if m.txsSub == nil || m.logsSub == nil || m.rmLogsSub == nil || m.chainSub == nil || m.pendingLogsSub == nil {
log.Crit("Subscribe for event system failed")
}
+ if m.voteSub == nil {
+ log.Warn("Subscribe for vote event failed")
+ }
go m.eventLoop()
return m
@@ -167,6 +189,7 @@ func (sub *Subscription) Unsubscribe() {
case <-sub.f.logs:
case <-sub.f.hashes:
case <-sub.f.headers:
+ case <-sub.f.votes:
}
}
@@ -234,6 +257,7 @@ func (es *EventSystem) subscribeMinedPendingLogs(crit ethereum.FilterQuery, logs
logs: logs,
hashes: make(chan []common.Hash),
headers: make(chan *types.Header),
+ votes: make(chan *types.VoteEnvelope),
installed: make(chan struct{}),
err: make(chan error),
}
@@ -251,6 +275,7 @@ func (es *EventSystem) subscribeLogs(crit ethereum.FilterQuery, logs chan []*typ
logs: logs,
hashes: make(chan []common.Hash),
headers: make(chan *types.Header),
+ votes: make(chan *types.VoteEnvelope),
installed: make(chan struct{}),
err: make(chan error),
}
@@ -268,6 +293,7 @@ func (es *EventSystem) subscribePendingLogs(crit ethereum.FilterQuery, logs chan
logs: logs,
hashes: make(chan []common.Hash),
headers: make(chan *types.Header),
+ votes: make(chan *types.VoteEnvelope),
installed: make(chan struct{}),
err: make(chan error),
}
@@ -284,6 +310,24 @@ func (es *EventSystem) SubscribeNewHeads(headers chan *types.Header) *Subscripti
logs: make(chan []*types.Log),
hashes: make(chan []common.Hash),
headers: headers,
+ votes: make(chan *types.VoteEnvelope),
+ installed: make(chan struct{}),
+ err: make(chan error),
+ }
+ return es.subscribe(sub)
+}
+
+// SubscribeNewFinalizedHeaders creates a subscription that writes the finalized header of a block that is
+// reached recently.
+func (es *EventSystem) SubscribeNewFinalizedHeaders(headers chan *types.Header) *Subscription {
+ sub := &subscription{
+ id: rpc.NewID(),
+ typ: FinalizedHeadersSubscription,
+ created: time.Now(),
+ logs: make(chan []*types.Log),
+ hashes: make(chan []common.Hash),
+ headers: headers,
+ votes: make(chan *types.VoteEnvelope),
installed: make(chan struct{}),
err: make(chan error),
}
@@ -300,6 +344,24 @@ func (es *EventSystem) SubscribePendingTxs(hashes chan []common.Hash) *Subscript
logs: make(chan []*types.Log),
hashes: hashes,
headers: make(chan *types.Header),
+ votes: make(chan *types.VoteEnvelope),
+ installed: make(chan struct{}),
+ err: make(chan error),
+ }
+ return es.subscribe(sub)
+}
+
+// SubscribeNewVotes creates a subscription that writes transaction hashes for
+// transactions that enter the transaction pool.
+func (es *EventSystem) SubscribeNewVotes(votes chan *types.VoteEnvelope) *Subscription {
+ sub := &subscription{
+ id: rpc.NewID(),
+ typ: VotesSubscription,
+ created: time.Now(),
+ logs: make(chan []*types.Log),
+ hashes: make(chan []common.Hash),
+ headers: make(chan *types.Header),
+ votes: votes,
installed: make(chan struct{}),
err: make(chan error),
}
@@ -351,6 +413,12 @@ func (es *EventSystem) handleTxsEvent(filters filterIndex, ev core.NewTxsEvent)
}
}
+func (es *EventSystem) handleVoteEvent(filters filterIndex, ev core.NewVoteEvent) {
+ for _, f := range filters[VotesSubscription] {
+ f.votes <- ev.Vote
+ }
+}
+
func (es *EventSystem) handleChainEvent(filters filterIndex, ev core.ChainEvent) {
for _, f := range filters[BlocksSubscription] {
f.headers <- ev.Block.Header()
@@ -366,6 +434,12 @@ func (es *EventSystem) handleChainEvent(filters filterIndex, ev core.ChainEvent)
}
}
+func (es *EventSystem) handleFinalizedHeaderEvent(filters filterIndex, ev core.FinalizedHeaderEvent) {
+ for _, f := range filters[FinalizedHeadersSubscription] {
+ f.headers <- ev.Header
+ }
+}
+
func (es *EventSystem) lightFilterNewHead(newHeader *types.Header, callBack func(*types.Header, bool)) {
oldh := es.lastHead
es.lastHead = newHeader
@@ -448,6 +522,10 @@ func (es *EventSystem) eventLoop() {
es.rmLogsSub.Unsubscribe()
es.pendingLogsSub.Unsubscribe()
es.chainSub.Unsubscribe()
+ es.finalizedHeaderSub.Unsubscribe()
+ if es.voteSub != nil {
+ es.voteSub.Unsubscribe()
+ }
}()
index := make(filterIndex)
@@ -455,6 +533,10 @@ func (es *EventSystem) eventLoop() {
index[i] = make(map[rpc.ID]*subscription)
}
+ var voteSubErr <-chan error
+ if es.voteSub != nil {
+ voteSubErr = es.voteSub.Err()
+ }
for {
select {
case ev := <-es.txsCh:
@@ -467,6 +549,10 @@ func (es *EventSystem) eventLoop() {
es.handlePendingLogs(index, ev)
case ev := <-es.chainCh:
es.handleChainEvent(index, ev)
+ case ev := <-es.finalizedHeaderCh:
+ es.handleFinalizedHeaderEvent(index, ev)
+ case ev := <-es.voteCh:
+ es.handleVoteEvent(index, ev)
case f := <-es.install:
if f.typ == MinedAndPendingLogsSubscription {
@@ -497,6 +583,10 @@ func (es *EventSystem) eventLoop() {
return
case <-es.chainSub.Err():
return
+ case <-es.finalizedHeaderSub.Err():
+ return
+ case <-voteSubErr:
+ return
}
}
}
diff --git a/eth/filters/filter_system_test.go b/eth/filters/filter_system_test.go
index 050bbcc01..4d3597bfb 100644
--- a/eth/filters/filter_system_test.go
+++ b/eth/filters/filter_system_test.go
@@ -44,14 +44,16 @@ var (
)
type testBackend struct {
- mux *event.TypeMux
- db ethdb.Database
- sections uint64
- txFeed event.Feed
- logsFeed event.Feed
- rmLogsFeed event.Feed
- pendingLogsFeed event.Feed
- chainFeed event.Feed
+ mux *event.TypeMux
+ db ethdb.Database
+ sections uint64
+ txFeed event.Feed
+ logsFeed event.Feed
+ rmLogsFeed event.Feed
+ pendingLogsFeed event.Feed
+ chainFeed event.Feed
+ finalizedHeaderFeed event.Feed
+ voteFeed event.Feed
}
func (b *testBackend) ChainDb() ethdb.Database {
@@ -126,6 +128,14 @@ func (b *testBackend) SubscribeChainEvent(ch chan<- core.ChainEvent) event.Subsc
return b.chainFeed.Subscribe(ch)
}
+func (b *testBackend) SubscribeFinalizedHeaderEvent(ch chan<- core.FinalizedHeaderEvent) event.Subscription {
+ return b.finalizedHeaderFeed.Subscribe(ch)
+}
+
+func (b *testBackend) SubscribeNewVoteEvent(ch chan<- core.NewVoteEvent) event.Subscription {
+ return b.voteFeed.Subscribe(ch)
+}
+
func (b *testBackend) BloomStatus() (uint64, uint64) {
return params.BloomBitsBlocks, b.sections
}
@@ -680,3 +690,79 @@ func flattenLogs(pl [][]*types.Log) []*types.Log {
}
return logs
}
+
+func TestVoteSubscription(t *testing.T) {
+ t.Parallel()
+
+ var (
+ db = rawdb.NewMemoryDatabase()
+ backend = &testBackend{db: db}
+ api = NewPublicFilterAPI(backend, false, deadline, false)
+ votes = []*types.VoteEnvelope{
+ &types.VoteEnvelope{
+ VoteAddress: types.BLSPublicKey{},
+ Signature: types.BLSSignature{},
+ Data: &types.VoteData{
+ SourceNumber: uint64(0),
+ SourceHash: common.BytesToHash(common.Hex2Bytes(string(rune(0)))),
+ TargetNumber: uint64(1),
+ TargetHash: common.BytesToHash(common.Hex2Bytes(string(rune(1)))),
+ },
+ },
+ &types.VoteEnvelope{
+ VoteAddress: types.BLSPublicKey{},
+ Signature: types.BLSSignature{},
+ Data: &types.VoteData{
+ SourceNumber: uint64(0),
+ SourceHash: common.BytesToHash(common.Hex2Bytes(string(rune(0)))),
+ TargetNumber: uint64(2),
+ TargetHash: common.BytesToHash(common.Hex2Bytes(string(rune(2)))),
+ },
+ },
+ &types.VoteEnvelope{
+ VoteAddress: types.BLSPublicKey{},
+ Signature: types.BLSSignature{},
+ Data: &types.VoteData{
+ SourceNumber: uint64(0),
+ SourceHash: common.BytesToHash(common.Hex2Bytes(string(rune(0)))),
+ TargetNumber: uint64(3),
+ TargetHash: common.BytesToHash(common.Hex2Bytes(string(rune(3)))),
+ },
+ },
+ &types.VoteEnvelope{
+ VoteAddress: types.BLSPublicKey{},
+ Signature: types.BLSSignature{},
+ Data: &types.VoteData{
+ SourceNumber: uint64(0),
+ SourceHash: common.BytesToHash(common.Hex2Bytes(string(rune(0)))),
+ TargetNumber: uint64(4),
+ TargetHash: common.BytesToHash(common.Hex2Bytes(string(rune(4)))),
+ },
+ },
+ }
+ )
+
+ chan0 := make(chan *types.VoteEnvelope)
+ sub0 := api.events.SubscribeNewVotes(chan0)
+
+ go func() { // simulate client
+ i := 0
+ for i != len(votes) {
+ vote := <-chan0
+ if votes[i].Hash() != vote.Hash() {
+ t.Errorf("sub received invalid hash on index %d, want %x, got %x", i, votes[i].Hash(), vote.Hash())
+ }
+ i++
+ }
+
+ sub0.Unsubscribe()
+ }()
+
+ time.Sleep(1 * time.Second)
+ for _, v := range votes {
+ ev := core.NewVoteEvent{Vote: v}
+ backend.voteFeed.Send(ev)
+ }
+
+ <-sub0.Err()
+}
diff --git a/eth/handler.go b/eth/handler.go
index cbc6eca80..93fefad89 100644
--- a/eth/handler.go
+++ b/eth/handler.go
@@ -46,6 +46,11 @@ const (
// txChanSize is the size of channel listening to NewTxsEvent.
// The number is referenced from the size of tx pool.
txChanSize = 4096
+
+ // voteChanSize is the size of channel listening to NewVotesEvent.
+ voteChanSize = 256
+
+ deltaTdThreshold = 20
)
var (
@@ -79,13 +84,26 @@ type txPool interface {
SubscribeReannoTxsEvent(chan<- core.ReannoTxsEvent) event.Subscription
}
+// votePool defines the methods needed from a votes pool implementation to
+// support all the operations needed by the Ethereum chain protocols.
+type votePool interface {
+ PutVote(vote *types.VoteEnvelope)
+ FetchVoteByBlockHash(blockHash common.Hash) []*types.VoteEnvelope
+ GetVotes() []*types.VoteEnvelope
+
+ // SubscribeNewVoteEvent should return an event subscription of
+ // NewVotesEvent and send events to the given channel.
+ SubscribeNewVoteEvent(ch chan<- core.NewVoteEvent) event.Subscription
+}
+
// handlerConfig is the collection of initialization parameters to create a full
// node network handler.
type handlerConfig struct {
Database ethdb.Database // Database for direct sync insertions
Chain *core.BlockChain // Blockchain to serve data from
TxPool txPool // Transaction pool to propagate from
- Network uint64 // Network identifier to adfvertise
+ VotePool votePool // Votes pool to propagate from
+ Network uint64 // Network identifier to advertise
Sync downloader.SyncMode // Whether to fast or full sync
DiffSync bool // Whether to diff sync
BloomCache uint64 // Megabytes to alloc for fast sync bloom
@@ -112,6 +130,7 @@ type handler struct {
database ethdb.Database
txpool txPool
+ votepool votePool
chain *core.BlockChain
maxPeers int
@@ -127,6 +146,8 @@ type handler struct {
reannoTxsCh chan core.ReannoTxsEvent
reannoTxsSub event.Subscription
minedBlockSub *event.TypeMuxSubscription
+ voteCh chan core.NewVoteEvent
+ votesSub event.Subscription
whitelist map[uint64]common.Hash
@@ -152,6 +173,7 @@ func newHandler(config *handlerConfig) (*handler, error) {
eventMux: config.EventMux,
database: config.Database,
txpool: config.TxPool,
+ votepool: config.VotePool,
chain: config.Chain,
peers: newPeerSet(),
whitelist: config.Whitelist,
@@ -255,7 +277,7 @@ func newHandler(config *handlerConfig) (*handler, error) {
}
// runEthPeer registers an eth peer into the joint eth/snap peerset, adds it to
-// various subsistems and starts handling messages.
+// various subsystems and starts handling messages.
func (h *handler) runEthPeer(peer *eth.Peer, handler eth.Handler) error {
// If the peer has a `snap` extension, wait for it to connect so we can have
// a uniform initialization/teardown mechanism
@@ -313,7 +335,7 @@ func (h *handler) runEthPeer(peer *eth.Peer, handler eth.Handler) error {
peer.Log().Error("Ethereum peer registration failed", "err", err)
return err
}
- defer h.removePeer(peer.ID())
+ defer h.unregisterPeer(peer.ID())
p := h.peers.peer(peer.ID())
if p == nil {
@@ -332,9 +354,12 @@ func (h *handler) runEthPeer(peer *eth.Peer, handler eth.Handler) error {
}
h.chainSync.handlePeerEvent(peer)
- // Propagate existing transactions. new transactions appearing
+ // Propagate existing transactions and votes. new transactions and votes appearing
// after this will be sent via broadcasts.
h.syncTransactions(peer)
+ if h.votepool != nil {
+ h.syncVotes(peer)
+ }
// If we have a trusted CHT, reject all peers below that (avoid fast sync eclipse)
if h.checkpointHash != (common.Hash{}) {
@@ -395,9 +420,17 @@ func (h *handler) runDiffExtension(peer *diff.Peer, handler diff.Handler) error
return handler(peer)
}
-// removePeer unregisters a peer from the downloader and fetchers, removes it from
-// the set of tracked peers and closes the network connection to it.
+// removePeer requests disconnection of a peer.
func (h *handler) removePeer(id string) {
+ peer := h.peers.peer(id)
+ if peer != nil {
+ // Hard disconnect at the networking layer. Handler will get an EOF and terminate the peer. defer unregisterPeer will do the cleanup task after then.
+ peer.Peer.Disconnect(p2p.DiscUselessPeer)
+ }
+}
+
+// unregisterPeer removes a peer from the downloader, fetchers and main peer set.
+func (h *handler) unregisterPeer(id string) {
// Create a custom logger to avoid printing the entire id
var logger log.Logger
if len(id) < 16 {
@@ -425,8 +458,6 @@ func (h *handler) removePeer(id string) {
if err := h.peers.unregisterPeer(id); err != nil {
logger.Error("Ethereum peer removal failed", "err", err)
}
- // Hard disconnect at the networking layer
- peer.Peer.Disconnect(p2p.DiscUselessPeer)
}
func (h *handler) Start(maxPeers int) {
@@ -438,6 +469,14 @@ func (h *handler) Start(maxPeers int) {
h.txsSub = h.txpool.SubscribeNewTxsEvent(h.txsCh)
go h.txBroadcastLoop()
+ // broadcast votes
+ if h.votepool != nil {
+ h.wg.Add(1)
+ h.voteCh = make(chan core.NewVoteEvent, voteChanSize)
+ h.votesSub = h.votepool.SubscribeNewVoteEvent(h.voteCh)
+ go h.voteBroadcastLoop()
+ }
+
// announce local pending transactions again
h.wg.Add(1)
h.reannoTxsCh = make(chan core.ReannoTxsEvent, txChanSize)
@@ -459,6 +498,9 @@ func (h *handler) Stop() {
h.txsSub.Unsubscribe() // quits txBroadcastLoop
h.reannoTxsSub.Unsubscribe() // quits txReannounceLoop
h.minedBlockSub.Unsubscribe() // quits blockBroadcastLoop
+ if h.votepool != nil {
+ h.votesSub.Unsubscribe() // quits voteBroadcastLoop
+ }
// Quit chainSync and txsync64.
// After this is done, no new peers will be accepted.
@@ -510,7 +552,7 @@ func (h *handler) BroadcastBlock(block *types.Block, propagate bool) {
log.Trace("Propagated block", "hash", hash, "recipients", len(transfer), "duration", common.PrettyDuration(time.Since(block.ReceivedAt)))
return
}
- // Otherwise if the block is indeed in out own chain, announce it
+ // Otherwise if the block is indeed in our own chain, announce it
if h.chain.HasBlock(hash, block.NumberU64()) {
for _, peer := range peers {
peer.AsyncSendNewBlockHash(block)
@@ -580,6 +622,35 @@ func (h *handler) ReannounceTransactions(txs types.Transactions) {
"announce packs", peersCount, "announced hashes", peersCount*uint(len(hashes)))
}
+// BroadcastVote will propagate a batch of votes to all peers
+// which are not known to already have the given vote.
+func (h *handler) BroadcastVote(vote *types.VoteEnvelope) {
+ var (
+ directCount int // Count of announcements made
+ directPeers int
+
+ voteMap = make(map[*ethPeer]*types.VoteEnvelope) // Set peer->hash to transfer directly
+ )
+
+ // Broadcast vote to a batch of peers not knowing about it
+ peers := h.peers.peersWithoutVote(vote.Hash())
+ for _, peer := range peers {
+ _, peerTD := peer.Head()
+ deltaTD := new(big.Int).Abs(new(big.Int).Sub(h.chain.GetTdByHash(h.chain.CurrentHeader().Hash()), peerTD))
+ if deltaTD.Cmp(big.NewInt(deltaTdThreshold)) < 1 {
+ voteMap[peer] = vote
+ }
+ }
+
+ for peer, _vote := range voteMap {
+ directPeers++
+ directCount += 1
+ votes := []*types.VoteEnvelope{_vote}
+ peer.AsyncSendVotes(votes)
+ }
+ log.Debug("Vote broadcast", "vote packs", directPeers, "broadcast vote", directCount)
+}
+
// minedBroadcastLoop sends mined blocks to connected peers.
func (h *handler) minedBroadcastLoop() {
defer h.wg.Done()
@@ -617,3 +688,16 @@ func (h *handler) txReannounceLoop() {
}
}
}
+
+// voteBroadcastLoop announces new vote to connected peers.
+func (h *handler) voteBroadcastLoop() {
+ defer h.wg.Done()
+ for {
+ select {
+ case event := <-h.voteCh:
+ h.BroadcastVote(event.Vote)
+ case <-h.votesSub.Err():
+ return
+ }
+ }
+}
diff --git a/eth/handler_diff_test.go b/eth/handler_diff_test.go
index f6b967631..c42937d7c 100644
--- a/eth/handler_diff_test.go
+++ b/eth/handler_diff_test.go
@@ -83,11 +83,13 @@ func newTestBackendWithGenerator(blocks int) *testBackend {
panic(err)
}
txpool := newTestTxPool()
+ votepool := newTestVotePool()
handler, _ := newHandler(&handlerConfig{
Database: db,
Chain: chain,
TxPool: txpool,
+ VotePool: votepool,
Network: 1,
Sync: downloader.FullSync,
BloomCache: 1,
diff --git a/eth/handler_eth.go b/eth/handler_eth.go
index d2cf83fbf..a47f442ca 100644
--- a/eth/handler_eth.go
+++ b/eth/handler_eth.go
@@ -40,6 +40,7 @@ type ethHandler handler
func (h *ethHandler) Chain() *core.BlockChain { return h.chain }
func (h *ethHandler) StateBloom() *trie.SyncBloom { return h.stateBloom }
func (h *ethHandler) TxPool() eth.TxPool { return h.txpool }
+func (h *ethHandler) VotePool() eth.VotePool { return h.votepool }
// RunPeer is invoked when a peer joins on the `eth` protocol.
func (h *ethHandler) RunPeer(peer *eth.Peer, hand eth.Handler) error {
@@ -99,6 +100,9 @@ func (h *ethHandler) Handle(peer *eth.Peer, packet eth.Packet) error {
case *eth.PooledTransactionsPacket:
return h.txFetcher.Enqueue(peer.ID(), *packet, true)
+
+ case *eth.VotesPacket:
+ return h.handleVotesBroadcast(peer, packet.Votes)
default:
return fmt.Errorf("unexpected eth packet type: %T", packet)
}
@@ -225,3 +229,13 @@ func (h *ethHandler) handleBlockBroadcast(peer *eth.Peer, block *types.Block, td
}
return nil
}
+
+// handleVotesBroadcast is invoked from a peer's message handler when it transmits a
+// votes broadcast for the local node to process.
+func (h *ethHandler) handleVotesBroadcast(peer *eth.Peer, votes []*types.VoteEnvelope) error {
+ // Try to put votes into votepool
+ for _, vote := range votes {
+ h.votepool.PutVote(vote)
+ }
+ return nil
+}
diff --git a/eth/handler_eth_test.go b/eth/handler_eth_test.go
index e85c74e6f..78da89f12 100644
--- a/eth/handler_eth_test.go
+++ b/eth/handler_eth_test.go
@@ -46,6 +46,7 @@ type testEthHandler struct {
blockBroadcasts event.Feed
txAnnounces event.Feed
txBroadcasts event.Feed
+ voteBroadcasts event.Feed
}
func (h *testEthHandler) Chain() *core.BlockChain { panic("no backing chain") }
@@ -54,6 +55,7 @@ func (h *testEthHandler) TxPool() eth.TxPool { panic("no backi
func (h *testEthHandler) AcceptTxs() bool { return true }
func (h *testEthHandler) RunPeer(*eth.Peer, eth.Handler) error { panic("not used in tests") }
func (h *testEthHandler) PeerInfo(enode.ID) interface{} { panic("not used in tests") }
+func (h *testEthHandler) VotePool() eth.VotePool { panic("no backing vote pool") }
func (h *testEthHandler) Handle(peer *eth.Peer, packet eth.Packet) error {
switch packet := packet.(type) {
@@ -73,6 +75,10 @@ func (h *testEthHandler) Handle(peer *eth.Peer, packet eth.Packet) error {
h.txBroadcasts.Send(([]*types.Transaction)(*packet))
return nil
+ case *eth.VotesPacket:
+ h.voteBroadcasts.Send(packet.Votes)
+ return nil
+
default:
panic(fmt.Sprintf("unexpected eth packet type in tests: %T", packet))
}
@@ -82,6 +88,7 @@ func (h *testEthHandler) Handle(peer *eth.Peer, packet eth.Packet) error {
// fork IDs in the protocol handshake.
func TestForkIDSplit65(t *testing.T) { testForkIDSplit(t, eth.ETH65) }
func TestForkIDSplit66(t *testing.T) { testForkIDSplit(t, eth.ETH66) }
+func TestForkIDSplit68(t *testing.T) { testForkIDSplit(t, eth.ETH68) }
func testForkIDSplit(t *testing.T, protocol uint) {
t.Parallel()
@@ -91,11 +98,22 @@ func testForkIDSplit(t *testing.T, protocol uint) {
configNoFork = ¶ms.ChainConfig{HomesteadBlock: big.NewInt(1)}
configProFork = ¶ms.ChainConfig{
- HomesteadBlock: big.NewInt(1),
- EIP150Block: big.NewInt(2),
- EIP155Block: big.NewInt(2),
- EIP158Block: big.NewInt(2),
- ByzantiumBlock: big.NewInt(3),
+ HomesteadBlock: big.NewInt(1),
+ EIP150Block: big.NewInt(2),
+ EIP155Block: big.NewInt(2),
+ EIP158Block: big.NewInt(2),
+ ByzantiumBlock: big.NewInt(3),
+ ConstantinopleBlock: big.NewInt(4),
+ PetersburgBlock: big.NewInt(4),
+ IstanbulBlock: big.NewInt(4),
+ MuirGlacierBlock: big.NewInt(4),
+ RamanujanBlock: big.NewInt(4),
+ NielsBlock: big.NewInt(4),
+ MirrorSyncBlock: big.NewInt(4),
+ BrunoBlock: big.NewInt(4),
+ EulerBlock: big.NewInt(5),
+ BonehBlock: big.NewInt(5),
+ LynnBlock: big.NewInt(5),
}
dbNoFork = rawdb.NewMemoryDatabase()
dbProFork = rawdb.NewMemoryDatabase()
@@ -116,6 +134,7 @@ func testForkIDSplit(t *testing.T, protocol uint) {
Database: dbNoFork,
Chain: chainNoFork,
TxPool: newTestTxPool(),
+ VotePool: newTestVotePool(),
Network: 1,
Sync: downloader.FullSync,
BloomCache: 1,
@@ -124,6 +143,7 @@ func testForkIDSplit(t *testing.T, protocol uint) {
Database: dbProFork,
Chain: chainProFork,
TxPool: newTestTxPool(),
+ VotePool: newTestVotePool(),
Network: 1,
Sync: downloader.FullSync,
BloomCache: 1,
@@ -144,8 +164,8 @@ func testForkIDSplit(t *testing.T, protocol uint) {
defer p2pNoFork.Close()
defer p2pProFork.Close()
- peerNoFork := eth.NewPeer(protocol, p2p.NewPeer(enode.ID{1}, "", nil), p2pNoFork, nil)
- peerProFork := eth.NewPeer(protocol, p2p.NewPeer(enode.ID{2}, "", nil), p2pProFork, nil)
+ peerNoFork := eth.NewPeer(protocol, p2p.NewPeer(enode.ID{1}, "", nil), p2pNoFork, nil, nil)
+ peerProFork := eth.NewPeer(protocol, p2p.NewPeer(enode.ID{2}, "", nil), p2pProFork, nil, nil)
defer peerNoFork.Close()
defer peerProFork.Close()
@@ -175,8 +195,8 @@ func testForkIDSplit(t *testing.T, protocol uint) {
defer p2pNoFork.Close()
defer p2pProFork.Close()
- peerNoFork = eth.NewPeer(protocol, p2p.NewPeer(enode.ID{1}, "", nil), p2pNoFork, nil)
- peerProFork = eth.NewPeer(protocol, p2p.NewPeer(enode.ID{2}, "", nil), p2pProFork, nil)
+ peerNoFork = eth.NewPeer(protocol, p2p.NewPeer(enode.ID{1}, "", nil), p2pNoFork, nil, nil)
+ peerProFork = eth.NewPeer(protocol, p2p.NewPeer(enode.ID{2}, "", nil), p2pProFork, nil, nil)
defer peerNoFork.Close()
defer peerProFork.Close()
@@ -206,8 +226,8 @@ func testForkIDSplit(t *testing.T, protocol uint) {
defer p2pNoFork.Close()
defer p2pProFork.Close()
- peerNoFork = eth.NewPeer(protocol, p2p.NewPeer(enode.ID{1}, "", nil), p2pNoFork, nil)
- peerProFork = eth.NewPeer(protocol, p2p.NewPeer(enode.ID{2}, "", nil), p2pProFork, nil)
+ peerNoFork = eth.NewPeer(protocol, p2p.NewPeer(enode.ID{1}, "", nil), p2pNoFork, nil, nil)
+ peerProFork = eth.NewPeer(protocol, p2p.NewPeer(enode.ID{2}, "", nil), p2pProFork, nil, nil)
defer peerNoFork.Close()
defer peerProFork.Close()
@@ -229,7 +249,7 @@ func testForkIDSplit(t *testing.T, protocol uint) {
t.Fatalf("fork ID rejection didn't happen")
}
}
- case <-time.After(250 * time.Millisecond):
+ case <-time.After(10000 * time.Millisecond):
t.Fatalf("split peers not rejected")
}
}
@@ -238,43 +258,66 @@ func testForkIDSplit(t *testing.T, protocol uint) {
// Tests that received transactions are added to the local pool.
func TestRecvTransactions65(t *testing.T) { testRecvTransactions(t, eth.ETH65) }
func TestRecvTransactions66(t *testing.T) { testRecvTransactions(t, eth.ETH66) }
+func TestRecvTransactions68(t *testing.T) { testRecvTransactions(t, eth.ETH68) }
-func TestWaitDiffExtensionTimout(t *testing.T) {
+func testRecvTransactions(t *testing.T, protocol uint) {
t.Parallel()
// Create a message handler, configure it to accept transactions and watch them
handler := newTestHandler()
defer handler.close()
+ handler.handler.acceptTxs = 1 // mark synced to accept transactions
+
+ txs := make(chan core.NewTxsEvent)
+ sub := handler.txpool.SubscribeNewTxsEvent(txs)
+ defer sub.Unsubscribe()
+
// Create a source peer to send messages through and a sink handler to receive them
- _, p2pSink := p2p.MsgPipe()
+ p2pSrc, p2pSink := p2p.MsgPipe()
+ defer p2pSrc.Close()
defer p2pSink.Close()
- protos := []p2p.Protocol{
- {
- Name: "diff",
- Version: 1,
- },
- }
-
- sink := eth.NewPeer(eth.ETH67, p2p.NewPeerWithProtocols(enode.ID{2}, protos, "", []p2p.Cap{
- {
- Name: "diff",
- Version: 1,
- },
- }), p2pSink, nil)
+ src := eth.NewPeer(protocol, p2p.NewPeer(enode.ID{1}, "", nil), p2pSrc, handler.txpool, nil)
+ sink := eth.NewPeer(protocol, p2p.NewPeer(enode.ID{2}, "", nil), p2pSink, handler.txpool, nil)
+ defer src.Close()
defer sink.Close()
- err := handler.handler.runEthPeer(sink, func(peer *eth.Peer) error {
+ go handler.handler.runEthPeer(sink, func(peer *eth.Peer) error {
return eth.Handle((*ethHandler)(handler.handler), peer)
})
+ // Run the handshake locally to avoid spinning up a source handler
+ var (
+ genesis = handler.chain.Genesis()
+ head = handler.chain.CurrentBlock()
+ td = handler.chain.GetTd(head.Hash(), head.NumberU64())
+ )
+ if err := src.Handshake(1, td, head.Hash(), genesis.Hash(), forkid.NewIDWithChain(handler.chain), forkid.NewFilter(handler.chain), nil); err != nil {
+ t.Fatalf("failed to run protocol handshake")
+ }
+ // Send the transaction to the sink and verify that it's added to the tx pool
+ tx := types.NewTransaction(0, common.Address{}, big.NewInt(0), 100000, big.NewInt(0), nil)
+ tx, _ = types.SignTx(tx, types.HomesteadSigner{}, testKey)
- if err == nil || err.Error() != "peer wait timeout" {
- t.Fatalf("error should be `peer wait timeout`")
+ if err := src.SendTransactions([]*types.Transaction{tx}); err != nil {
+ t.Fatalf("failed to send transaction: %v", err)
+ }
+ select {
+ case event := <-txs:
+ if len(event.Txs) != 1 {
+ t.Errorf("wrong number of added transactions: got %d, want 1", len(event.Txs))
+ } else if event.Txs[0].Hash() != tx.Hash() {
+ t.Errorf("added wrong tx hash: got %v, want %v", event.Txs[0].Hash(), tx.Hash())
+ }
+ case <-time.After(2 * time.Second):
+ t.Errorf("no NewTxsEvent received within 2 seconds")
}
}
-func TestWaitSnapExtensionTimout(t *testing.T) {
+func TestWaitDiffExtensionTimout67(t *testing.T) { testWaitDiffExtensionTimout(t, eth.ETH67) }
+func TestWaitDiffExtensionTimout68(t *testing.T) { testWaitDiffExtensionTimout(t, eth.ETH68) }
+
+func testWaitDiffExtensionTimout(t *testing.T, protocol uint) {
t.Parallel()
// Create a message handler, configure it to accept transactions and watch them
@@ -287,17 +330,17 @@ func TestWaitSnapExtensionTimout(t *testing.T) {
protos := []p2p.Protocol{
{
- Name: "snap",
+ Name: "diff",
Version: 1,
},
}
- sink := eth.NewPeer(eth.ETH67, p2p.NewPeerWithProtocols(enode.ID{2}, protos, "", []p2p.Cap{
+ sink := eth.NewPeer(protocol, p2p.NewPeerWithProtocols(enode.ID{2}, protos, "", []p2p.Cap{
{
- Name: "snap",
+ Name: "diff",
Version: 1,
},
- }), p2pSink, nil)
+ }), p2pSink, nil, nil)
defer sink.Close()
err := handler.handler.runEthPeer(sink, func(peer *eth.Peer) error {
@@ -309,63 +352,48 @@ func TestWaitSnapExtensionTimout(t *testing.T) {
}
}
-func testRecvTransactions(t *testing.T, protocol uint) {
+func TestWaitSnapExtensionTimout67(t *testing.T) { testWaitSnapExtensionTimout(t, eth.ETH67) }
+func TestWaitSnapExtensionTimout68(t *testing.T) { testWaitDiffExtensionTimout(t, eth.ETH68) }
+
+func testWaitSnapExtensionTimout(t *testing.T, protocol uint) {
t.Parallel()
// Create a message handler, configure it to accept transactions and watch them
handler := newTestHandler()
defer handler.close()
- handler.handler.acceptTxs = 1 // mark synced to accept transactions
-
- txs := make(chan core.NewTxsEvent)
- sub := handler.txpool.SubscribeNewTxsEvent(txs)
- defer sub.Unsubscribe()
-
// Create a source peer to send messages through and a sink handler to receive them
- p2pSrc, p2pSink := p2p.MsgPipe()
- defer p2pSrc.Close()
+ _, p2pSink := p2p.MsgPipe()
defer p2pSink.Close()
- src := eth.NewPeer(protocol, p2p.NewPeer(enode.ID{1}, "", nil), p2pSrc, handler.txpool)
- sink := eth.NewPeer(protocol, p2p.NewPeer(enode.ID{2}, "", nil), p2pSink, handler.txpool)
- defer src.Close()
+ protos := []p2p.Protocol{
+ {
+ Name: "snap",
+ Version: 1,
+ },
+ }
+
+ sink := eth.NewPeer(protocol, p2p.NewPeerWithProtocols(enode.ID{2}, protos, "", []p2p.Cap{
+ {
+ Name: "snap",
+ Version: 1,
+ },
+ }), p2pSink, nil, nil)
defer sink.Close()
- go handler.handler.runEthPeer(sink, func(peer *eth.Peer) error {
+ err := handler.handler.runEthPeer(sink, func(peer *eth.Peer) error {
return eth.Handle((*ethHandler)(handler.handler), peer)
})
- // Run the handshake locally to avoid spinning up a source handler
- var (
- genesis = handler.chain.Genesis()
- head = handler.chain.CurrentBlock()
- td = handler.chain.GetTd(head.Hash(), head.NumberU64())
- )
- if err := src.Handshake(1, td, head.Hash(), genesis.Hash(), forkid.NewIDWithChain(handler.chain), forkid.NewFilter(handler.chain), nil); err != nil {
- t.Fatalf("failed to run protocol handshake")
- }
- // Send the transaction to the sink and verify that it's added to the tx pool
- tx := types.NewTransaction(0, common.Address{}, big.NewInt(0), 100000, big.NewInt(0), nil)
- tx, _ = types.SignTx(tx, types.HomesteadSigner{}, testKey)
- if err := src.SendTransactions([]*types.Transaction{tx}); err != nil {
- t.Fatalf("failed to send transaction: %v", err)
- }
- select {
- case event := <-txs:
- if len(event.Txs) != 1 {
- t.Errorf("wrong number of added transactions: got %d, want 1", len(event.Txs))
- } else if event.Txs[0].Hash() != tx.Hash() {
- t.Errorf("added wrong tx hash: got %v, want %v", event.Txs[0].Hash(), tx.Hash())
- }
- case <-time.After(2 * time.Second):
- t.Errorf("no NewTxsEvent received within 2 seconds")
+ if err == nil || err.Error() != "peer wait timeout" {
+ t.Fatalf("error should be `peer wait timeout`")
}
}
// This test checks that pending transactions are sent.
func TestSendTransactions65(t *testing.T) { testSendTransactions(t, eth.ETH65) }
func TestSendTransactions66(t *testing.T) { testSendTransactions(t, eth.ETH66) }
+func TestSendTransactions68(t *testing.T) { testSendTransactions(t, eth.ETH68) }
func testSendTransactions(t *testing.T, protocol uint) {
t.Parallel()
@@ -389,8 +417,8 @@ func testSendTransactions(t *testing.T, protocol uint) {
defer p2pSrc.Close()
defer p2pSink.Close()
- src := eth.NewPeer(protocol, p2p.NewPeer(enode.ID{1}, "", nil), p2pSrc, handler.txpool)
- sink := eth.NewPeer(protocol, p2p.NewPeer(enode.ID{2}, "", nil), p2pSink, handler.txpool)
+ src := eth.NewPeer(protocol, p2p.NewPeer(enode.ID{1}, "", nil), p2pSrc, handler.txpool, nil)
+ sink := eth.NewPeer(protocol, p2p.NewPeer(enode.ID{2}, "", nil), p2pSink, handler.txpool, nil)
defer src.Close()
defer sink.Close()
@@ -424,7 +452,7 @@ func testSendTransactions(t *testing.T, protocol uint) {
seen := make(map[common.Hash]struct{})
for len(seen) < len(insert) {
switch protocol {
- case 65, 66:
+ case 65, 66, 68:
select {
case hashes := <-anns:
for _, hash := range hashes {
@@ -452,6 +480,7 @@ func testSendTransactions(t *testing.T, protocol uint) {
// broadcasts or via announcements/retrievals.
func TestTransactionPropagation65(t *testing.T) { testTransactionPropagation(t, eth.ETH65) }
func TestTransactionPropagation66(t *testing.T) { testTransactionPropagation(t, eth.ETH66) }
+func TestTransactionPropagation68(t *testing.T) { testTransactionPropagation(t, eth.ETH68) }
func testTransactionPropagation(t *testing.T, protocol uint) {
t.Parallel()
@@ -477,8 +506,8 @@ func testTransactionPropagation(t *testing.T, protocol uint) {
defer sourcePipe.Close()
defer sinkPipe.Close()
- sourcePeer := eth.NewPeer(protocol, p2p.NewPeer(enode.ID{byte(i)}, "", nil), sourcePipe, source.txpool)
- sinkPeer := eth.NewPeer(protocol, p2p.NewPeer(enode.ID{0}, "", nil), sinkPipe, sink.txpool)
+ sourcePeer := eth.NewPeer(protocol, p2p.NewPeer(enode.ID{byte(i)}, "", nil), sourcePipe, source.txpool, nil)
+ sinkPeer := eth.NewPeer(protocol, p2p.NewPeer(enode.ID{0}, "", nil), sinkPipe, sink.txpool, nil)
defer sourcePeer.Close()
defer sinkPeer.Close()
@@ -537,8 +566,8 @@ func TestTransactionPendingReannounce(t *testing.T) {
defer sourcePipe.Close()
defer sinkPipe.Close()
- sourcePeer := eth.NewPeer(eth.ETH65, p2p.NewPeer(enode.ID{0}, "", nil), sourcePipe, source.txpool)
- sinkPeer := eth.NewPeer(eth.ETH65, p2p.NewPeer(enode.ID{0}, "", nil), sinkPipe, sink.txpool)
+ sourcePeer := eth.NewPeer(eth.ETH65, p2p.NewPeer(enode.ID{0}, "", nil), sourcePipe, source.txpool, nil)
+ sinkPeer := eth.NewPeer(eth.ETH65, p2p.NewPeer(enode.ID{0}, "", nil), sinkPipe, sink.txpool, nil)
defer sourcePeer.Close()
defer sinkPeer.Close()
@@ -641,14 +670,20 @@ func testCheckpointChallenge(t *testing.T, syncmode downloader.SyncMode, checkpo
defer p2pLocal.Close()
defer p2pRemote.Close()
- local := eth.NewPeer(eth.ETH65, p2p.NewPeer(enode.ID{1}, "", nil), p2pLocal, handler.txpool)
- remote := eth.NewPeer(eth.ETH65, p2p.NewPeer(enode.ID{2}, "", nil), p2pRemote, handler.txpool)
+ local := eth.NewPeer(eth.ETH65, p2p.NewPeerPipe(enode.ID{1}, "", nil, p2pLocal), p2pLocal, handler.txpool, nil)
+ remote := eth.NewPeer(eth.ETH65, p2p.NewPeerPipe(enode.ID{2}, "", nil, p2pRemote), p2pRemote, handler.txpool, nil)
defer local.Close()
defer remote.Close()
- go handler.handler.runEthPeer(local, func(peer *eth.Peer) error {
- return eth.Handle((*ethHandler)(handler.handler), peer)
- })
+ handlerDone := make(chan struct{})
+ go func() {
+ defer close(handlerDone)
+ handler.handler.runEthPeer(local, func(peer *eth.Peer) error {
+ err := eth.Handle((*ethHandler)(handler.handler), peer)
+ return err
+ })
+ }()
+
// Run the handshake locally to avoid spinning up a remote handler
var (
genesis = handler.chain.Genesis()
@@ -685,6 +720,7 @@ func testCheckpointChallenge(t *testing.T, syncmode downloader.SyncMode, checkpo
// Verify that the remote peer is maintained or dropped
if drop {
+ <-handlerDone
if peers := handler.handler.peers.len(); peers != 0 {
t.Fatalf("peer count mismatch: have %d, want %d", peers, 0)
}
@@ -731,8 +767,8 @@ func testBroadcastBlock(t *testing.T, peers, bcasts int) {
defer sourcePipe.Close()
defer sinkPipe.Close()
- sourcePeer := eth.NewPeer(eth.ETH65, p2p.NewPeer(enode.ID{byte(i)}, "", nil), sourcePipe, nil)
- sinkPeer := eth.NewPeer(eth.ETH65, p2p.NewPeer(enode.ID{0}, "", nil), sinkPipe, nil)
+ sourcePeer := eth.NewPeer(eth.ETH65, p2p.NewPeer(enode.ID{byte(i)}, "", nil), sourcePipe, nil, nil)
+ sinkPeer := eth.NewPeer(eth.ETH65, p2p.NewPeer(enode.ID{0}, "", nil), sinkPipe, nil, nil)
defer sourcePeer.Close()
defer sinkPeer.Close()
@@ -785,6 +821,7 @@ func testBroadcastBlock(t *testing.T, peers, bcasts int) {
// with the hashes in the header) gets discarded and not broadcast forward.
func TestBroadcastMalformedBlock65(t *testing.T) { testBroadcastMalformedBlock(t, eth.ETH65) }
func TestBroadcastMalformedBlock66(t *testing.T) { testBroadcastMalformedBlock(t, eth.ETH66) }
+func TestBroadcastMalformedBlock68(t *testing.T) { testBroadcastMalformedBlock(t, eth.ETH68) }
func testBroadcastMalformedBlock(t *testing.T, protocol uint) {
t.Parallel()
@@ -799,8 +836,8 @@ func testBroadcastMalformedBlock(t *testing.T, protocol uint) {
defer p2pSrc.Close()
defer p2pSink.Close()
- src := eth.NewPeer(protocol, p2p.NewPeer(enode.ID{1}, "", nil), p2pSrc, source.txpool)
- sink := eth.NewPeer(protocol, p2p.NewPeer(enode.ID{2}, "", nil), p2pSink, source.txpool)
+ src := eth.NewPeer(protocol, p2p.NewPeer(enode.ID{1}, "", nil), p2pSrc, source.txpool, nil)
+ sink := eth.NewPeer(protocol, p2p.NewPeer(enode.ID{2}, "", nil), p2pSink, source.txpool, nil)
defer src.Close()
defer sink.Close()
@@ -849,3 +886,145 @@ func testBroadcastMalformedBlock(t *testing.T, protocol uint) {
}
}
}
+
+func TestSendVotes68(t *testing.T) { testSendVotes(t, eth.ETH68) }
+
+func testSendVotes(t *testing.T, protocol uint) {
+ t.Parallel()
+
+ // Create a message handler and fill the pool with big votes
+ handler := newTestHandler()
+ defer handler.close()
+
+ insert := make([]*types.VoteEnvelope, 100)
+ for index := range insert {
+ vote := types.VoteEnvelope{
+ VoteAddress: types.BLSPublicKey{},
+ Signature: types.BLSSignature{},
+ Data: &types.VoteData{
+ SourceNumber: uint64(0),
+ SourceHash: common.BytesToHash(common.Hex2Bytes(string(rune(0)))),
+ TargetNumber: uint64(index),
+ TargetHash: common.BytesToHash(common.Hex2Bytes(string(rune(index)))),
+ },
+ }
+ insert[index] = &vote
+ go handler.votepool.PutVote(&vote)
+ }
+ time.Sleep(250 * time.Millisecond) // Wait until vote events get out of the system (can't use events, vote broadcaster races with peer join)
+
+ // Create a source handler to send messages through and a sink peer to receive them
+ p2pSrc, p2pSink := p2p.MsgPipe()
+ defer p2pSrc.Close()
+ defer p2pSink.Close()
+
+ src := eth.NewPeer(protocol, p2p.NewPeer(enode.ID{1}, "", nil), p2pSrc, nil, handler.votepool)
+ sink := eth.NewPeer(protocol, p2p.NewPeer(enode.ID{2}, "", nil), p2pSink, nil, handler.votepool)
+ defer src.Close()
+ defer sink.Close()
+
+ go handler.handler.runEthPeer(src, func(peer *eth.Peer) error {
+ return eth.Handle((*ethHandler)(handler.handler), peer)
+ })
+ // Run the handshake locally to avoid spinning up a source handler
+ var (
+ genesis = handler.chain.Genesis()
+ head = handler.chain.CurrentBlock()
+ td = handler.chain.GetTd(head.Hash(), head.NumberU64())
+ )
+ if err := sink.Handshake(1, td, head.Hash(), genesis.Hash(), forkid.NewIDWithChain(handler.chain), forkid.NewFilter(handler.chain), nil); err != nil {
+ t.Fatalf("failed to run protocol handshake")
+ }
+ // After the handshake completes, the source handler should stream the sink
+ // the transactions, subscribe to all inbound network events
+ backend := new(testEthHandler)
+
+ bcasts := make(chan []*types.VoteEnvelope)
+ bcastSub := backend.voteBroadcasts.Subscribe(bcasts)
+ defer bcastSub.Unsubscribe()
+
+ go eth.Handle(backend, sink)
+
+ // Make sure we get all the transactions on the correct channels
+ seen := make(map[common.Hash]struct{})
+ for len(seen) < len(insert) {
+ switch protocol {
+ case 65, 66, 68:
+ votes := <-bcasts
+ for _, vote := range votes {
+ if _, ok := seen[vote.Hash()]; ok {
+ t.Errorf("duplicate vote broadcast: %x", vote.Hash())
+ }
+ seen[vote.Hash()] = struct{}{}
+ }
+
+ default:
+ panic("unsupported protocol, please extend test")
+ }
+ }
+ for _, vote := range insert {
+ if _, ok := seen[vote.Hash()]; !ok {
+ t.Errorf("missing vote: %x", vote.Hash())
+ }
+ }
+}
+
+func TestRecvVotes68(t *testing.T) { testRecvVotes(t, eth.ETH68) }
+
+func testRecvVotes(t *testing.T, protocol uint) {
+ t.Parallel()
+
+ // Create a message handler, configure it to accept transactions and watch them
+ handler := newTestHandler()
+ defer handler.close()
+
+ votesCh := make(chan core.NewVoteEvent)
+ sub := handler.votepool.SubscribeNewVoteEvent(votesCh)
+ defer sub.Unsubscribe()
+
+ // Create a source peer to send messages through and a sink handler to receive them
+ p2pSrc, p2pSink := p2p.MsgPipe()
+ defer p2pSrc.Close()
+ defer p2pSink.Close()
+
+ src := eth.NewPeer(protocol, p2p.NewPeer(enode.ID{1}, "", nil), p2pSrc, nil, handler.votepool)
+ sink := eth.NewPeer(protocol, p2p.NewPeer(enode.ID{2}, "", nil), p2pSink, nil, handler.votepool)
+ defer src.Close()
+ defer sink.Close()
+
+ go handler.handler.runEthPeer(sink, func(peer *eth.Peer) error {
+ return eth.Handle((*ethHandler)(handler.handler), peer)
+ })
+ // Run the handshake locally to avoid spinning up a source handler
+ var (
+ genesis = handler.chain.Genesis()
+ head = handler.chain.CurrentBlock()
+ td = handler.chain.GetTd(head.Hash(), head.NumberU64())
+ )
+ if err := src.Handshake(1, td, head.Hash(), genesis.Hash(), forkid.NewIDWithChain(handler.chain), forkid.NewFilter(handler.chain), nil); err != nil {
+ t.Fatalf("failed to run protocol handshake")
+ }
+ // Send the vote to the sink and verify that it's added to the vote pool
+ vote := types.VoteEnvelope{
+ VoteAddress: types.BLSPublicKey{},
+ Signature: types.BLSSignature{},
+ Data: &types.VoteData{
+ SourceNumber: uint64(0),
+ SourceHash: common.BytesToHash(common.Hex2Bytes(string(rune(0)))),
+ TargetNumber: uint64(1),
+ TargetHash: common.BytesToHash(common.Hex2Bytes(string(rune(1)))),
+ },
+ }
+
+ if err := src.SendVotes([]*types.VoteEnvelope{&vote}); err != nil {
+ t.Fatalf("failed to send vote: %v", err)
+ }
+ select {
+ case event := <-votesCh:
+ if event.Vote.Hash() != vote.Hash() {
+ t.Errorf("added wrong vote hash: got %v, want %v", event.Vote.Hash(), vote.Hash())
+ }
+ case <-time.After(2 * time.Second):
+ t.Errorf("no NewVotesEvent received within 2 seconds")
+ }
+}
diff --git a/eth/handler_test.go b/eth/handler_test.go
index c3b7b769b..4b1780e18 100644
--- a/eth/handler_test.go
+++ b/eth/handler_test.go
@@ -135,10 +135,11 @@ func (p *testTxPool) SubscribeReannoTxsEvent(ch chan<- core.ReannoTxsEvent) even
// preinitialized with some sane testing defaults and the transaction pool mocked
// out.
type testHandler struct {
- db ethdb.Database
- chain *core.BlockChain
- txpool *testTxPool
- handler *handler
+ db ethdb.Database
+ chain *core.BlockChain
+ txpool *testTxPool
+ votepool *testVotePool
+ handler *handler
}
// newTestHandler creates a new handler for testing purposes with no blocks.
@@ -163,11 +164,13 @@ func newTestHandlerWithBlocks(blocks int) *testHandler {
panic(err)
}
txpool := newTestTxPool()
+ votepool := newTestVotePool()
handler, _ := newHandler(&handlerConfig{
Database: db,
Chain: chain,
TxPool: txpool,
+ VotePool: votepool,
Network: 1,
Sync: downloader.FastSync,
BloomCache: 1,
@@ -175,10 +178,11 @@ func newTestHandlerWithBlocks(blocks int) *testHandler {
handler.Start(1000)
return &testHandler{
- db: db,
- chain: chain,
- txpool: txpool,
- handler: handler,
+ db: db,
+ chain: chain,
+ txpool: txpool,
+ votepool: votepool,
+ handler: handler,
}
}
@@ -187,3 +191,45 @@ func (b *testHandler) close() {
b.handler.Stop()
b.chain.Stop()
}
+
+// newTestVotePool creates a mock vote pool.
+type testVotePool struct {
+ pool map[common.Hash]*types.VoteEnvelope // Hash map of collected votes
+
+ voteFeed event.Feed // Notification feed to allow waiting for inclusion
+ lock sync.RWMutex // Protects the vote pool
+}
+
+// newTestVotePool creates a mock vote pool.
+func newTestVotePool() *testVotePool {
+ return &testVotePool{
+ pool: make(map[common.Hash]*types.VoteEnvelope),
+ }
+}
+
+func (t *testVotePool) PutVote(vote *types.VoteEnvelope) {
+ t.lock.Lock()
+ defer t.lock.Unlock()
+
+ t.pool[vote.Hash()] = vote
+ t.voteFeed.Send(core.NewVoteEvent{Vote: vote})
+}
+
+func (t *testVotePool) FetchVoteByBlockHash(blockHash common.Hash) []*types.VoteEnvelope {
+ panic("implement me")
+}
+
+func (t *testVotePool) GetVotes() []*types.VoteEnvelope {
+ t.lock.RLock()
+ defer t.lock.RUnlock()
+
+ votes := make([]*types.VoteEnvelope, 0, len(t.pool))
+ for _, vote := range t.pool {
+ votes = append(votes, vote)
+ }
+ return votes
+}
+
+func (t *testVotePool) SubscribeNewVoteEvent(ch chan<- core.NewVoteEvent) event.Subscription {
+ return t.voteFeed.Subscribe(ch)
+}
diff --git a/eth/peerset.go b/eth/peerset.go
index 0f5245a05..fb2109bb0 100644
--- a/eth/peerset.go
+++ b/eth/peerset.go
@@ -311,7 +311,7 @@ func (ps *peerSet) headPeers(num uint) []*ethPeer {
}
// peersWithoutBlock retrieves a list of peers that do not have a given block in
-// their set of known hashes so it might be propagated to them.
+// their set of known hashes, so it might be propagated to them.
func (ps *peerSet) peersWithoutBlock(hash common.Hash) []*ethPeer {
ps.lock.RLock()
defer ps.lock.RUnlock()
@@ -340,6 +340,21 @@ func (ps *peerSet) peersWithoutTransaction(hash common.Hash) []*ethPeer {
return list
}
+// peersWithoutVote retrieves a list of peers that do not have a given
+// vote in their set of known hashes.
+func (ps *peerSet) peersWithoutVote(hash common.Hash) []*ethPeer {
+ ps.lock.RLock()
+ defer ps.lock.RUnlock()
+
+ list := make([]*ethPeer, 0, len(ps.peers))
+ for _, p := range ps.peers {
+ if !p.KnownVote(hash) {
+ list = append(list, p)
+ }
+ }
+ return list
+}
+
// len returns if the current number of `eth` peers in the set. Since the `snap`
// peers are tied to the existence of an `eth` connection, that will always be a
// subset of `eth`.
diff --git a/eth/protocols/diff/handler.go b/eth/protocols/diff/handler.go
index 18ec4e254..4bbf24f6f 100644
--- a/eth/protocols/diff/handler.go
+++ b/eth/protocols/diff/handler.go
@@ -101,7 +101,7 @@ func handleMessage(backend Backend, peer *Peer) error {
}
defer msg.Discard()
start := time.Now()
- // Track the emount of time it takes to serve the request and run the handler
+ // Track the amount of time it takes to serve the request and run the handler
if metrics.Enabled {
h := fmt.Sprintf("%s/%s/%d/%#02x", p2p.HandleHistName, ProtocolName, peer.Version(), msg.Code)
defer func(start time.Time) {
diff --git a/eth/protocols/eth/broadcast.go b/eth/protocols/eth/broadcast.go
index 132eac010..9976ff70b 100644
--- a/eth/protocols/eth/broadcast.go
+++ b/eth/protocols/eth/broadcast.go
@@ -200,3 +200,24 @@ func (p *Peer) announceTransactions() {
}
}
}
+
+// broadcastVotes is a write loop that schedules votes broadcasts
+// to the remote peer. The goal is to have an async writer that does not lock up
+// node internals and at the same time rate limits queued data.
+func (p *Peer) broadcastVotes() {
+ for {
+ select {
+ case votes := <-p.voteBroadcast:
+ if err := p.SendVotes(votes); err != nil {
+ return
+ }
+ p.Log().Trace("Sent votes", "count", len(votes))
+
+ case <-p.voteTerm:
+ return
+
+ case <-p.term:
+ return
+ }
+ }
+}
diff --git a/eth/protocols/eth/handler.go b/eth/protocols/eth/handler.go
index 6bbaa2f55..b4ab39ba8 100644
--- a/eth/protocols/eth/handler.go
+++ b/eth/protocols/eth/handler.go
@@ -79,6 +79,9 @@ type Backend interface {
// or if inbound transactions should simply be dropped.
AcceptTxs() bool
+ // VotePool retrieves the votes pool object to serve data.
+ VotePool() VotePool
+
// RunPeer is invoked when a peer joins on the `eth` protocol. The handler
// should do any peer maintenance work, handshakes and validations. If all
// is passed, control should be given back to the `handler` to process the
@@ -96,10 +99,12 @@ type Backend interface {
// TxPool defines the methods needed by the protocol handler to serve transactions.
type TxPool interface {
- // Get retrieves the the transaction from the local txpool with the given hash.
+ // Get retrieves the transaction from the local txpool with the given hash.
Get(hash common.Hash) *types.Transaction
}
+type VotePool interface{}
+
// MakeProtocols constructs the P2P protocol definitions for `eth`.
func MakeProtocols(backend Backend, network uint64, dnsdisc enode.Iterator) []p2p.Protocol {
protocols := make([]p2p.Protocol, len(ProtocolVersions))
@@ -111,7 +116,7 @@ func MakeProtocols(backend Backend, network uint64, dnsdisc enode.Iterator) []p2
Version: version,
Length: protocolLengths[version],
Run: func(p *p2p.Peer, rw p2p.MsgReadWriter) error {
- peer := NewPeer(version, p, rw, backend.TxPool())
+ peer := NewPeer(version, p, rw, backend.TxPool(), backend.VotePool())
defer peer.Close()
return backend.RunPeer(peer, func(peer *Peer) error {
@@ -206,6 +211,26 @@ var eth66 = map[uint64]msgHandler{
PooledTransactionsMsg: handlePooledTransactions66,
}
+var eth68 = map[uint64]msgHandler{
+ NewBlockHashesMsg: handleNewBlockhashes,
+ NewBlockMsg: handleNewBlock,
+ TransactionsMsg: handleTransactions,
+ NewPooledTransactionHashesMsg: handleNewPooledTransactionHashes,
+ // eth66 messages with request-id
+ GetBlockHeadersMsg: handleGetBlockHeaders66,
+ BlockHeadersMsg: handleBlockHeaders66,
+ GetBlockBodiesMsg: handleGetBlockBodies66,
+ BlockBodiesMsg: handleBlockBodies66,
+ GetNodeDataMsg: handleGetNodeData66,
+ NodeDataMsg: handleNodeData66,
+ GetReceiptsMsg: handleGetReceipts66,
+ ReceiptsMsg: handleReceipts66,
+ GetPooledTransactionsMsg: handleGetPooledTransactions66,
+ PooledTransactionsMsg: handlePooledTransactions66,
+ // eth68 messages
+ VotesMsg: handleVotes,
+}
+
// handleMessage is invoked whenever an inbound message is received from a remote
// peer. The remote connection is torn down upon returning any error.
func handleMessage(backend Backend, peer *Peer) error {
@@ -220,7 +245,9 @@ func handleMessage(backend Backend, peer *Peer) error {
defer msg.Discard()
var handlers = eth65
- if peer.Version() >= ETH66 {
+ if peer.Version() >= ETH68 {
+ handlers = eth68
+ } else if peer.Version() >= ETH66 {
handlers = eth66
}
// Track the amount of time it takes to serve the request and run the handler
diff --git a/eth/protocols/eth/handler_test.go b/eth/protocols/eth/handler_test.go
index 2dd2446e3..ea9321455 100644
--- a/eth/protocols/eth/handler_test.go
+++ b/eth/protocols/eth/handler_test.go
@@ -94,10 +94,10 @@ func (b *testBackend) close() {
func (b *testBackend) Chain() *core.BlockChain { return b.chain }
func (b *testBackend) StateBloom() *trie.SyncBloom { return nil }
func (b *testBackend) TxPool() TxPool { return b.txpool }
-
+func (b *testBackend) VotePool() VotePool { return nil }
func (b *testBackend) RunPeer(peer *Peer, handler Handler) error {
- // Normally the backend would do peer mainentance and handshakes. All that
- // is omitted and we will just give control back to the handler.
+ // Normally the backend would do peer maintenance and handshakes. All that
+ // is omitted, and we will just give control back to the handler.
return handler(peer)
}
func (b *testBackend) PeerInfo(enode.ID) interface{} { panic("not implemented") }
@@ -112,6 +112,7 @@ func (b *testBackend) Handle(*Peer, Packet) error {
// Tests that block headers can be retrieved from a remote chain based on user queries.
func TestGetBlockHeaders65(t *testing.T) { testGetBlockHeaders(t, ETH65) }
func TestGetBlockHeaders66(t *testing.T) { testGetBlockHeaders(t, ETH66) }
+func TestGetBlockHeaders68(t *testing.T) { testGetBlockHeaders(t, ETH68) }
func testGetBlockHeaders(t *testing.T, protocol uint) {
t.Parallel()
@@ -237,7 +238,7 @@ func testGetBlockHeaders(t *testing.T, protocol uint) {
backend.chain.GetBlockByNumber(1).Hash(),
},
},
- // Check that non existing headers aren't returned
+ // Check that non-existing headers aren't returned
{
&GetBlockHeadersPacket{Origin: HashOrNumber{Hash: unknown}, Amount: 1},
[]common.Hash{},
@@ -301,6 +302,7 @@ func testGetBlockHeaders(t *testing.T, protocol uint) {
// Tests that block contents can be retrieved from a remote chain based on their hashes.
func TestGetBlockBodies65(t *testing.T) { testGetBlockBodies(t, ETH65) }
func TestGetBlockBodies66(t *testing.T) { testGetBlockBodies(t, ETH66) }
+func TestGetBlockBodies68(t *testing.T) { testGetBlockBodies(t, ETH68) }
func testGetBlockBodies(t *testing.T, protocol uint) {
t.Parallel()
@@ -392,6 +394,7 @@ func testGetBlockBodies(t *testing.T, protocol uint) {
// Tests that the state trie nodes can be retrieved based on hashes.
func TestGetNodeData65(t *testing.T) { testGetNodeData(t, ETH65) }
func TestGetNodeData66(t *testing.T) { testGetNodeData(t, ETH66) }
+func TestGetNodeData68(t *testing.T) { testGetNodeData(t, ETH68) }
func testGetNodeData(t *testing.T, protocol uint) {
t.Parallel()
@@ -508,6 +511,7 @@ func testGetNodeData(t *testing.T, protocol uint) {
// Tests that the transaction receipts can be retrieved based on hashes.
func TestGetBlockReceipts65(t *testing.T) { testGetBlockReceipts(t, ETH65) }
func TestGetBlockReceipts66(t *testing.T) { testGetBlockReceipts(t, ETH66) }
+func TestGetBlockReceipts68(t *testing.T) { testGetBlockReceipts(t, ETH68) }
func testGetBlockReceipts(t *testing.T, protocol uint) {
t.Parallel()
diff --git a/eth/protocols/eth/handlers.go b/eth/protocols/eth/handlers.go
index d7d993a23..e1a5f566d 100644
--- a/eth/protocols/eth/handlers.go
+++ b/eth/protocols/eth/handlers.go
@@ -410,7 +410,7 @@ func handleNewPooledTransactionHashes(backend Backend, msg Decoder, peer *Peer)
}
func handleGetPooledTransactions(backend Backend, msg Decoder, peer *Peer) error {
- // Decode the pooled transactions retrieval message
+ // Decode the pooled transactions' retrieval message
var query GetPooledTransactionsPacket
if err := msg.Decode(&query); err != nil {
return fmt.Errorf("%w: message %v: %v", errDecode, msg, err)
@@ -420,7 +420,7 @@ func handleGetPooledTransactions(backend Backend, msg Decoder, peer *Peer) error
}
func handleGetPooledTransactions66(backend Backend, msg Decoder, peer *Peer) error {
- // Decode the pooled transactions retrieval message
+ // Decode the pooled transactions' retrieval message
var query GetPooledTransactionsPacket66
if err := msg.Decode(&query); err != nil {
return fmt.Errorf("%w: message %v: %v", errDecode, msg, err)
@@ -518,3 +518,15 @@ func handlePooledTransactions66(backend Backend, msg Decoder, peer *Peer) error
return backend.Handle(peer, &txs.PooledTransactionsPacket)
}
+
+func handleVotes(backend Backend, msg Decoder, peer *Peer) error {
+ ann := new(VotesPacket)
+ if err := msg.Decode(ann); err != nil {
+ return fmt.Errorf("%w: message %v: %v", errDecode, msg, err)
+ }
+ // Schedule all the unknown hashes for retrieval
+ for _, vote := range ann.Votes {
+ peer.markVote(vote.Hash())
+ }
+ return backend.Handle(peer, ann)
+}
diff --git a/eth/protocols/eth/handshake_test.go b/eth/protocols/eth/handshake_test.go
index 8a433f3ce..52cda2bb3 100644
--- a/eth/protocols/eth/handshake_test.go
+++ b/eth/protocols/eth/handshake_test.go
@@ -29,6 +29,7 @@ import (
// Tests that handshake failures are detected and reported correctly.
func TestHandshake65(t *testing.T) { testHandshake(t, ETH65) }
func TestHandshake66(t *testing.T) { testHandshake(t, ETH66) }
+func TestHandshake68(t *testing.T) { testHandshake68(t, ETH68) }
func testHandshake(t *testing.T, protocol uint) {
t.Parallel()
@@ -75,7 +76,71 @@ func testHandshake(t *testing.T, protocol uint) {
defer app.Close()
defer net.Close()
- peer := NewPeer(protocol, p2p.NewPeer(enode.ID{}, "peer", nil), net, nil)
+ peer := NewPeer(protocol, p2p.NewPeer(enode.ID{}, "peer", nil), net, nil, nil)
+ defer peer.Close()
+
+ // Send the junk test with one peer, check the handshake failure
+ go p2p.Send(app, test.code, test.data)
+
+ err := peer.Handshake(1, td, head.Hash(), genesis.Hash(), forkID, forkid.NewFilter(backend.chain), nil)
+ if err == nil {
+ t.Errorf("test %d: protocol returned nil error, want %q", i, test.want)
+ } else if !errors.Is(err, test.want) {
+ t.Errorf("test %d: wrong error: got %q, want %q", i, err, test.want)
+ }
+ }
+}
+
+func testHandshake68(t *testing.T, protocol uint) {
+ t.Parallel()
+
+ // Create a test backend only to have some valid genesis chain
+ backend := newTestBackend(3)
+ defer backend.close()
+
+ var (
+ genesis = backend.chain.Genesis()
+ head = backend.chain.CurrentBlock()
+ td = backend.chain.GetTd(head.Hash(), head.NumberU64())
+ forkID = forkid.NewID(backend.chain.Config(), backend.chain.Genesis().Hash(), backend.chain.CurrentHeader().Number.Uint64())
+ )
+ tests := []struct {
+ code uint64
+ data interface{}
+ want error
+ }{
+ {
+ code: TransactionsMsg, data: []interface{}{},
+ want: errNoStatusMsg,
+ },
+ {
+ code: StatusMsg, data: StatusPacket{10, 1, td, head.Hash(), genesis.Hash(), forkID},
+ want: errProtocolVersionMismatch,
+ },
+ {
+ code: StatusMsg, data: StatusPacket{uint32(protocol), 999, td, head.Hash(), genesis.Hash(), forkID},
+ want: errNetworkIDMismatch,
+ },
+ {
+ code: StatusMsg, data: StatusPacket{uint32(protocol), 1, td, head.Hash(), common.Hash{3}, forkID},
+ want: errGenesisMismatch,
+ },
+ {
+ code: StatusMsg, data: StatusPacket{uint32(protocol), 1, td, head.Hash(), genesis.Hash(), forkid.ID{Hash: [4]byte{0x00, 0x01, 0x02, 0x03}}},
+ want: errForkIDRejected,
+ },
+ {
+ code: VotesMsg, data: []interface{}{},
+ want: errNoStatusMsg,
+ },
+ }
+ for i, test := range tests {
+ // Create the two peers to shake with each other
+ app, net := p2p.MsgPipe()
+ defer app.Close()
+ defer net.Close()
+
+ peer := NewPeer(protocol, p2p.NewPeer(enode.ID{}, "peer", nil), net, nil, nil)
defer peer.Close()
// Send the junk test with one peer, check the handshake failure
diff --git a/eth/protocols/eth/peer.go b/eth/protocols/eth/peer.go
index 7ab4fa1a3..1a2ba00c3 100644
--- a/eth/protocols/eth/peer.go
+++ b/eth/protocols/eth/peer.go
@@ -34,6 +34,10 @@ const (
// before starting to randomly evict them.
maxKnownTxs = 32768
+ // maxKnownVotes is the maximum vote hashes to keep in the known list
+ // before starting to randomly evict them.
+ maxKnownVotes = 5376
+
// maxKnownBlocks is the maximum block hashes to keep in the known list
// before starting to randomly evict them.
maxKnownBlocks = 1024
@@ -86,14 +90,19 @@ type Peer struct {
txBroadcast chan []common.Hash // Channel used to queue transaction propagation requests
txAnnounce chan []common.Hash // Channel used to queue transaction announcement requests
- term chan struct{} // Termination channel to stop the broadcasters
- txTerm chan struct{} // Termination channel to stop the tx broadcasters
- lock sync.RWMutex // Mutex protecting the internal fields
+ votepool VotePool // Votes pool used by the broadcasters
+ knownVotes mapset.Set // Set of vote hashes known to be known by this peer
+ voteBroadcast chan []*types.VoteEnvelope // Channel used to queue votes propagation requests
+
+ term chan struct{} // Termination channel to stop the broadcasters
+ txTerm chan struct{} // Termination channel to stop the tx broadcasters
+ voteTerm chan struct{} // Termination channel to stop the votes broadcasters
+ lock sync.RWMutex // Mutex protecting the internal fields
}
// NewPeer create a wrapper for a network connection and negotiated protocol
// version.
-func NewPeer(version uint, p *p2p.Peer, rw p2p.MsgReadWriter, txpool TxPool) *Peer {
+func NewPeer(version uint, p *p2p.Peer, rw p2p.MsgReadWriter, txpool TxPool, votepool VotePool) *Peer {
peer := &Peer{
id: p.ID().String(),
Peer: p,
@@ -101,13 +110,17 @@ func NewPeer(version uint, p *p2p.Peer, rw p2p.MsgReadWriter, txpool TxPool) *Pe
version: version,
knownTxs: mapset.NewSet(),
knownBlocks: mapset.NewSet(),
+ knownVotes: mapset.NewSet(),
queuedBlocks: make(chan *blockPropagation, maxQueuedBlocks),
queuedBlockAnns: make(chan *types.Block, maxQueuedBlockAnns),
txBroadcast: make(chan []common.Hash),
txAnnounce: make(chan []common.Hash),
txpool: txpool,
+ voteBroadcast: make(chan []*types.VoteEnvelope),
+ votepool: votepool,
term: make(chan struct{}),
txTerm: make(chan struct{}),
+ voteTerm: make(chan struct{}),
}
// Start up all the broadcasters
go peer.broadcastBlocks()
@@ -115,6 +128,9 @@ func NewPeer(version uint, p *p2p.Peer, rw p2p.MsgReadWriter, txpool TxPool) *Pe
if version >= ETH65 {
go peer.announceTransactions()
}
+ if version >= ETH68 {
+ go peer.broadcastVotes()
+ }
return peer
}
@@ -141,7 +157,7 @@ func (p *Peer) ID() string {
return p.id
}
-// Version retrieves the peer's negoatiated `eth` protocol version.
+// Version retrieves the peer's negotiated `eth` protocol version.
func (p *Peer) Version() uint {
return p.version
}
@@ -174,6 +190,11 @@ func (p *Peer) KnownTransaction(hash common.Hash) bool {
return p.knownTxs.Contains(hash)
}
+// KnownVote returns whether peer is known to already have a vote.
+func (p *Peer) KnownVote(hash common.Hash) bool {
+ return p.knownVotes.Contains(hash)
+}
+
// markBlock marks a block as known for the peer, ensuring that the block will
// never be propagated to this particular peer.
func (p *Peer) markBlock(hash common.Hash) {
@@ -194,6 +215,16 @@ func (p *Peer) markTransaction(hash common.Hash) {
p.knownTxs.Add(hash)
}
+// markVote marks a vote as known for the peer, ensuring that it
+// will never be propagated to this particular peer.
+func (p *Peer) markVote(hash common.Hash) {
+ // If we reached the memory allowance, drop a previously known vote hash
+ for p.knownVotes.Cardinality() >= maxKnownVotes {
+ p.knownVotes.Pop()
+ }
+ p.knownVotes.Add(hash)
+}
+
// SendTransactions sends transactions to the peer and includes the hashes
// in its transaction hash set for future reference.
//
@@ -369,6 +400,39 @@ func (p *Peer) AsyncSendNewBlock(block *types.Block, td *big.Int) {
}
}
+// SendVotes propagates a batch of votes to the remote peer.
+func (p *Peer) SendVotes(votes []*types.VoteEnvelope) error {
+ // Mark all the votes as known, but ensure we don't overflow our limits
+ for p.knownVotes.Cardinality() > max(0, maxKnownTxs-len(votes)) {
+ p.knownVotes.Pop()
+ }
+ for _, vote := range votes {
+ p.knownVotes.Add(vote.Hash())
+ }
+ return p2p.Send(p.rw, VotesMsg, &VotesPacket{votes})
+}
+
+// AsyncSendVotes queues a batch of vote hashes for propagation to a remote peer. If
+// the peer's broadcast queue is full, the event is silently dropped.
+func (p *Peer) AsyncSendVotes(votes []*types.VoteEnvelope) {
+ select {
+ case p.voteBroadcast <- votes:
+ // Mark all the votes as known, but ensure we don't overflow our limits
+ for p.knownVotes.Cardinality() > max(0, maxKnownVotes-len(votes)) {
+ p.knownVotes.Pop()
+ }
+ for _, vote := range votes {
+ p.knownVotes.Add(vote.Hash())
+ }
+
+ case <-p.voteTerm:
+ p.Log().Debug("Dropping vote propagation", "count", len(votes))
+
+ case <-p.term:
+ p.Log().Debug("Dropping vote propagation", "count", len(votes))
+ }
+}
+
// SendBlockHeaders sends a batch of block headers to the remote peer.
func (p *Peer) SendBlockHeaders(headers []*types.Header) error {
return p2p.Send(p.rw, BlockHeadersMsg, BlockHeadersPacket(headers))
@@ -397,7 +461,7 @@ func (p *Peer) ReplyBlockBodiesRLP(id uint64, bodies []rlp.RawValue) error {
})
}
-// SendNodeDataRLP sends a batch of arbitrary internal data, corresponding to the
+// SendNodeData sends a batch of arbitrary internal data, corresponding to the
// hashes requested.
func (p *Peer) SendNodeData(data [][]byte) error {
return p2p.Send(p.rw, NodeDataMsg, NodeDataPacket(data))
diff --git a/eth/protocols/eth/peer_test.go b/eth/protocols/eth/peer_test.go
index 70e9959f8..e0e2717d9 100644
--- a/eth/protocols/eth/peer_test.go
+++ b/eth/protocols/eth/peer_test.go
@@ -43,7 +43,7 @@ func newTestPeer(name string, version uint, backend Backend) (*testPeer, <-chan
var id enode.ID
rand.Read(id[:])
- peer := NewPeer(version, p2p.NewPeer(id, name, nil), net, backend.TxPool())
+ peer := NewPeer(version, p2p.NewPeer(id, name, nil), net, backend.TxPool(), nil)
errc := make(chan error, 1)
go func() {
errc <- backend.RunPeer(peer, func(peer *Peer) error {
diff --git a/eth/protocols/eth/protocol.go b/eth/protocols/eth/protocol.go
index 3e0e0cf6e..0dd559431 100644
--- a/eth/protocols/eth/protocol.go
+++ b/eth/protocols/eth/protocol.go
@@ -33,6 +33,7 @@ const (
ETH65 = 65
ETH66 = 66
ETH67 = 67
+ ETH68 = 68
)
// ProtocolName is the official short name of the `eth` protocol used during
@@ -41,11 +42,11 @@ const ProtocolName = "eth"
// ProtocolVersions are the supported versions of the `eth` protocol (first
// is primary).
-var ProtocolVersions = []uint{ETH67, ETH66, ETH65}
+var ProtocolVersions = []uint{ETH68, ETH67, ETH66, ETH65}
// protocolLengths are the number of implemented message corresponding to
// different protocol versions.
-var protocolLengths = map[uint]uint64{ETH67: 18, ETH66: 17, ETH65: 17}
+var protocolLengths = map[uint]uint64{ETH68: 33, ETH67: 18, ETH66: 17, ETH65: 17}
// maxMessageSize is the maximum cap on the size of a protocol message.
const maxMessageSize = 10 * 1024 * 1024
@@ -72,6 +73,9 @@ const (
// Protocol messages overloaded in eth/66
UpgradeStatusMsg = 0x0b
+
+ // Protocol messages overloaded in eth/68
+ VotesMsg = 0x20
)
var (
@@ -161,7 +165,7 @@ type GetBlockHeadersPacket struct {
Reverse bool // Query direction (false = rising towards latest, true = falling towards genesis)
}
-// GetBlockHeadersPacket represents a block header query over eth/66
+// GetBlockHeadersPacket66 represents a block header query over eth/66
type GetBlockHeadersPacket66 struct {
RequestId uint64
*GetBlockHeadersPacket
@@ -206,7 +210,7 @@ func (hn *HashOrNumber) DecodeRLP(s *rlp.Stream) error {
// BlockHeadersPacket represents a block header response.
type BlockHeadersPacket []*types.Header
-// BlockHeadersPacket represents a block header response over eth/66.
+// BlockHeadersPacket66 represents a block header response over eth/66.
type BlockHeadersPacket66 struct {
RequestId uint64
BlockHeadersPacket
@@ -234,7 +238,7 @@ func (request *NewBlockPacket) sanityCheck() error {
// GetBlockBodiesPacket represents a block body query.
type GetBlockBodiesPacket []common.Hash
-// GetBlockBodiesPacket represents a block body query over eth/66.
+// GetBlockBodiesPacket66 represents a block body query over eth/66.
type GetBlockBodiesPacket66 struct {
RequestId uint64
GetBlockBodiesPacket
@@ -243,7 +247,7 @@ type GetBlockBodiesPacket66 struct {
// BlockBodiesPacket is the network packet for block content distribution.
type BlockBodiesPacket []*BlockBody
-// BlockBodiesPacket is the network packet for block content distribution over eth/66.
+// BlockBodiesPacket66 is the network packet for block content distribution over eth/66.
type BlockBodiesPacket66 struct {
RequestId uint64
BlockBodiesPacket
@@ -282,7 +286,7 @@ func (p *BlockBodiesPacket) Unpack() ([][]*types.Transaction, [][]*types.Header)
// GetNodeDataPacket represents a trie node data query.
type GetNodeDataPacket []common.Hash
-// GetNodeDataPacket represents a trie node data query over eth/66.
+// GetNodeDataPacket66 represents a trie node data query over eth/66.
type GetNodeDataPacket66 struct {
RequestId uint64
GetNodeDataPacket
@@ -291,7 +295,7 @@ type GetNodeDataPacket66 struct {
// NodeDataPacket is the network packet for trie node data distribution.
type NodeDataPacket [][]byte
-// NodeDataPacket is the network packet for trie node data distribution over eth/66.
+// NodeDataPacket66 is the network packet for trie node data distribution over eth/66.
type NodeDataPacket66 struct {
RequestId uint64
NodeDataPacket
@@ -300,7 +304,7 @@ type NodeDataPacket66 struct {
// GetReceiptsPacket represents a block receipts query.
type GetReceiptsPacket []common.Hash
-// GetReceiptsPacket represents a block receipts query over eth/66.
+// GetReceiptsPacket66 represents a block receipts query over eth/66.
type GetReceiptsPacket66 struct {
RequestId uint64
GetReceiptsPacket
@@ -309,7 +313,7 @@ type GetReceiptsPacket66 struct {
// ReceiptsPacket is the network packet for block receipts distribution.
type ReceiptsPacket [][]*types.Receipt
-// ReceiptsPacket is the network packet for block receipts distribution over eth/66.
+// ReceiptsPacket66 is the network packet for block receipts distribution over eth/66.
type ReceiptsPacket66 struct {
RequestId uint64
ReceiptsPacket
@@ -318,7 +322,7 @@ type ReceiptsPacket66 struct {
// ReceiptsRLPPacket is used for receipts, when we already have it encoded
type ReceiptsRLPPacket []rlp.RawValue
-// ReceiptsPacket66 is the eth-66 version of ReceiptsRLPPacket
+// ReceiptsRLPPacket66 is the eth-66 version of ReceiptsRLPPacket
type ReceiptsRLPPacket66 struct {
RequestId uint64
ReceiptsRLPPacket
@@ -338,13 +342,13 @@ type GetPooledTransactionsPacket66 struct {
// PooledTransactionsPacket is the network packet for transaction distribution.
type PooledTransactionsPacket []*types.Transaction
-// PooledTransactionsPacket is the network packet for transaction distribution over eth/66.
+// PooledTransactionsPacket66 is the network packet for transaction distribution over eth/66.
type PooledTransactionsPacket66 struct {
RequestId uint64
PooledTransactionsPacket
}
-// PooledTransactionsPacket is the network packet for transaction distribution, used
+// PooledTransactionsRLPPacket is the network packet for transaction distribution, used
// in the cases we already have them in rlp-encoded form
type PooledTransactionsRLPPacket []rlp.RawValue
@@ -354,6 +358,14 @@ type PooledTransactionsRLPPacket66 struct {
PooledTransactionsRLPPacket
}
+// VotesPacket is the network packet for votes record.
+type VotesPacket struct {
+ Votes []*types.VoteEnvelope
+}
+
+func (*VotesPacket) Name() string { return "Votes" }
+func (*VotesPacket) Kind() byte { return VotesMsg }
+
func (*StatusPacket) Name() string { return "Status" }
func (*StatusPacket) Kind() byte { return StatusMsg }
diff --git a/eth/protocols/eth/protocol_test.go b/eth/protocols/eth/protocol_test.go
index 7910c9b73..2ecfc2618 100644
--- a/eth/protocols/eth/protocol_test.go
+++ b/eth/protocols/eth/protocol_test.go
@@ -24,6 +24,7 @@ import (
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/rlp"
+ "github.com/prysmaticlabs/prysm/crypto/bls/blst"
)
// Tests that the custom union field encoder and decoder works correctly.
@@ -266,3 +267,111 @@ func TestEth66Messages(t *testing.T) {
}
}
}
+
+// TestEth68Messages tests the encoding of newly defined eth68 messages
+func TestEth68Messages(t *testing.T) {
+
+ // Some basic structs used during testing
+ var (
+ BLSPrivateKey = "4cf9fc19af38d1bbaf85b3639502f9eef4bc90c196fe36cc0252abf51551c8bd"
+
+ votesSet []*types.VoteEnvelope
+ )
+
+ // Init the vote data, taken from a local node
+ secretKey, _ := blst.SecretKeyFromBytes(common.Hex2Bytes(BLSPrivateKey))
+ {
+ for _, voteData := range []types.VoteData{
+ {
+ SourceNumber: 0,
+ SourceHash: common.HexToHash("0x6d3c66c5357ec91d5c43af47e234a939b22557cbb552dc45bebbceeed90fbe34"),
+ TargetNumber: 1,
+ TargetHash: common.HexToHash("0xd0bc67b50915467ada963c35ee00950f664788e47da8139d8c178653171034f1"),
+ },
+ {
+ SourceNumber: 0,
+ SourceHash: common.HexToHash("0x6d3c66c5357ec91d5c43af47e234a939b22557cbb552dc45bebbceeed90fbe34"),
+ TargetNumber: 2,
+ TargetHash: common.HexToHash("0xc2d18d5a59d65da573f70c4d30448482418894e018b0d189db24ea4fd02d7aa1"),
+ },
+ {
+ SourceNumber: 0,
+ SourceHash: common.HexToHash("0x6d3c66c5357ec91d5c43af47e234a939b22557cbb552dc45bebbceeed90fbe34"),
+ TargetNumber: 4,
+ TargetHash: common.HexToHash("0xbd1bdaf8a8f5c00c464df2856a9e2ef23b8dcc906e6490d3cd295ebb5eb124c3"),
+ },
+ {
+ SourceNumber: 0,
+ SourceHash: common.HexToHash("0x6d3c66c5357ec91d5c43af47e234a939b22557cbb552dc45bebbceeed90fbe34"),
+ TargetNumber: 8,
+ TargetHash: common.HexToHash("0x3073782ecabb5ef0673e95962273482347a2c7b30a0a7124c664443d0a43f1e1"),
+ },
+ {
+ SourceNumber: 0,
+ SourceHash: common.HexToHash("0x6d3c66c5357ec91d5c43af47e234a939b22557cbb552dc45bebbceeed90fbe34"),
+ TargetNumber: 16,
+ TargetHash: common.HexToHash("0xc119833266327fd7e0cd929c6a847ae7d1689df5066dfdde2e52f51c0ecbcc3f"),
+ },
+ {
+ SourceNumber: 0,
+ SourceHash: common.HexToHash("0x6d3c66c5357ec91d5c43af47e234a939b22557cbb552dc45bebbceeed90fbe34"),
+ TargetNumber: 32,
+ TargetHash: common.HexToHash("0x3b5650bcb98381e463871a15a3f601cdc26843d76f4d3461333d7feae38a1786"),
+ },
+ {
+ SourceNumber: 0,
+ SourceHash: common.HexToHash("0x6d3c66c5357ec91d5c43af47e234a939b22557cbb552dc45bebbceeed90fbe34"),
+ TargetNumber: 64,
+ TargetHash: common.HexToHash("0x5e38b4d98904178d60d58f5bc1687b0c7df114a51f2007d3ee3e6e732539f130"),
+ },
+ {
+ SourceNumber: 0,
+ SourceHash: common.HexToHash("0x6d3c66c5357ec91d5c43af47e234a939b22557cbb552dc45bebbceeed90fbe34"),
+ TargetNumber: 128,
+ TargetHash: common.HexToHash("0xa4a64a7d511d3ff6982b5a79e9a485508477b98996c570a220f9daea0c7682f8"),
+ },
+ {
+ SourceNumber: 0,
+ SourceHash: common.HexToHash("0x6d3c66c5357ec91d5c43af47e234a939b22557cbb552dc45bebbceeed90fbe34"),
+ TargetNumber: 256,
+ TargetHash: common.HexToHash("0xd313672c2653fc13e75a9dafdcee93f444caf2cffb04585d3e306fd15418b7e2"),
+ },
+ {
+ SourceNumber: 0,
+ SourceHash: common.HexToHash("0x6d3c66c5357ec91d5c43af47e234a939b22557cbb552dc45bebbceeed90fbe34"),
+ TargetNumber: 512,
+ TargetHash: common.HexToHash("0x3c5fe2e5439ca7a7f1a3de7d5c0914c37261451c87654397dd45f207109839ae"),
+ },
+ {
+ SourceNumber: 0,
+ SourceHash: common.HexToHash("0x6d3c66c5357ec91d5c43af47e234a939b22557cbb552dc45bebbceeed90fbe34"),
+ TargetNumber: 1024,
+ TargetHash: common.HexToHash("0x088eeeb07acff0db3ae2585195e9fd23bdf54b55077cab87d1632b08dd2c043b"),
+ },
+ } {
+ votes := new(types.VoteEnvelope)
+ voteAddress := new(types.BLSPublicKey)
+ signature := new(types.BLSSignature)
+ copy(voteAddress[:], secretKey.PublicKey().Marshal()[:])
+ copy(signature[:], secretKey.Sign(voteData.Hash().Bytes()).Marshal()[:])
+ votes.VoteAddress = *voteAddress
+ votes.Signature = *signature
+ votes.Data = &voteData
+ votesSet = append(votesSet, votes)
+ }
+ }
+
+ for i, tc := range []struct {
+ message interface{}
+ want []byte
+ }{
+ {
+ VotesPacket{votesSet},
+ common.FromHex("f90982f9097ff8dbb0b32d4d46a7127dcc865f0d30f2ee3dcd5983b686f4e3a9202afc8b608652001c9938906ae1ff1417486096e32511f1bcb86091f8a39f99a0b3632d248e635ab0e7b5ff68071fa4def5c2df9f249db7d393c0ba89eb28a65f2a6ba836baddb961b9c312c70a87d130edf944b340649218335c91078cce808da75ff69f673bab3ecdf068c33b1ab147c54298056b19e9cc625df84680a06d3c66c5357ec91d5c43af47e234a939b22557cbb552dc45bebbceeed90fbe34820400a0088eeeb07acff0db3ae2585195e9fd23bdf54b55077cab87d1632b08dd2c043bf8dbb0b32d4d46a7127dcc865f0d30f2ee3dcd5983b686f4e3a9202afc8b608652001c9938906ae1ff1417486096e32511f1bcb860a56cd257f9a4b4830c9bfadaa751c7b1d4e9c6899127c145987a55a7cfa0d1b7d114cb2523ea4e2efee0326cfc5a1cd912eaf7f0c4c0be3193677284533f1709fbd75471a9fb22aea358cdbf2e900628d7c504ce7245e8af6fdd1039dfa3c0bdf84680a06d3c66c5357ec91d5c43af47e234a939b22557cbb552dc45bebbceeed90fbe34820400a0088eeeb07acff0db3ae2585195e9fd23bdf54b55077cab87d1632b08dd2c043bf8dbb0b32d4d46a7127dcc865f0d30f2ee3dcd5983b686f4e3a9202afc8b608652001c9938906ae1ff1417486096e32511f1bcb860893f8aff7fc523a7aff006aaba71fbde5f1eee1f4683d405892ffb9ab9282a5dae01054210ff6873ee76f86b9afdef2e128932b26696e3f7e1de7fe7d3fdd1c41273912ff5d1002cba176dbf84e1fe2edb60b114129b89e1329a03f7d9843d04f84680a06d3c66c5357ec91d5c43af47e234a939b22557cbb552dc45bebbceeed90fbe34820400a0088eeeb07acff0db3ae2585195e9fd23bdf54b55077cab87d1632b08dd2c043bf8dbb0b32d4d46a7127dcc865f0d30f2ee3dcd5983b686f4e3a9202afc8b608652001c9938906ae1ff1417486096e32511f1bcb860b3585bf55f1e0d8bc0f544a386e6fc4ec37de52330f69b450f579050acda6279a8a38172ed2f01dfdb57cf7dd2a314970aa8a3168234cbd29adfc6a0efd080f57d7e195dafbf5b6db087e8b943aa634f797f8f6d4e5bf04681d7ce2218e59465f84680a06d3c66c5357ec91d5c43af47e234a939b22557cbb552dc45bebbceeed90fbe34820400a0088eeeb07acff0db3ae2585195e9fd23bdf54b55077cab87d1632b08dd2c043bf8dbb0b32d4d46a7127dcc865f0d30f2ee3dcd5983b686f4e3a9202afc8b608652001c9938906ae1ff1417486096e32511f1bcb8609366e823b456049cd10ed1aa8f02a58ce2fa4caea7e8c776d6aec9c42f4263b40b0f2d76cc55a598b378381f32ef33520d47e28707027c25e38eb971cddb379e0ded5e814ce70108d65855084a11484fd08447520b7ce79ac1e680020b243747f84680a06d3c66c5357ec91d5c43af47e234a939b22557cbb552dc45bebbceeed90fbe34820400a0088eeeb07acff0db3ae2585195e9fd23bdf54b55077cab87d1632b08dd2c043bf8dbb0b32d4d46a7127dcc865f0d30f2ee3dcd5983b686f4e3a9202afc8b608652001c9938906ae1ff1417486096e32511f1bcb860aafd383c9537d750358ea077d45476cf6c1541e717c690ebe5dc5442c2af732fba17b45c60b2c49de94f5121f318b6ae021c56ae06588c6552f1d5b87a166cb8050f287b528b20556033603f6a6649ccec4792c86ae5f6353cf6b7527ac40127f84680a06d3c66c5357ec91d5c43af47e234a939b22557cbb552dc45bebbceeed90fbe34820400a0088eeeb07acff0db3ae2585195e9fd23bdf54b55077cab87d1632b08dd2c043bf8dbb0b32d4d46a7127dcc865f0d30f2ee3dcd5983b686f4e3a9202afc8b608652001c9938906ae1ff1417486096e32511f1bcb86090d9dc467a64fe7852b2e0117806409a8f12a036849d69396282088f8d86adb3adcd46b1fde51b4630a6b64c2f8652f30a46609c49b33f50c9f4641e30900ee420f9b81b2ad59a2376dcf4e065ecf832fbf738ad5b73becd2f7add27e6c14d5ff84680a06d3c66c5357ec91d5c43af47e234a939b22557cbb552dc45bebbceeed90fbe34820400a0088eeeb07acff0db3ae2585195e9fd23bdf54b55077cab87d1632b08dd2c043bf8dbb0b32d4d46a7127dcc865f0d30f2ee3dcd5983b686f4e3a9202afc8b608652001c9938906ae1ff1417486096e32511f1bcb8608f7d6bc28626dc143208aaa7b97146510f01b1a108dead65f8fddf0ec07835bca91081f9e759656d52dd7d4adaac14220c8c62aa1dd09151fe8000ce4347b100ac496593058ae11b40c74b3049d38076d07301c9dc9586baf93d9c81b4e5d424f84680a06d3c66c5357ec91d5c43af47e234a939b22557cbb552dc45bebbceeed90fbe34820400a0088eeeb07acff0db3ae2585195e9fd23bdf54b55077cab87d1632b08dd2c043bf8dbb0b32d4d46a7127dcc865f0d30f2ee3dcd5983b686f4e3a9202afc8b608652001c9938906ae1ff1417486096e32511f1bcb860b6c17077217baa5930fb04e14b6ba7203b3c854e8f1363b48ad753d96db1b4ffed36d60d8b67e86f7f76504f0abefff80ed1e4f11ff192dbfc26a0f059f7b9f66f9e883fef208cc3f58c7ce49d8e854cf8a0e48c59c7407ebfe946cfd62bf3bef84680a06d3c66c5357ec91d5c43af47e234a939b22557cbb552dc45bebbceeed90fbe34820400a0088eeeb07acff0db3ae2585195e9fd23bdf54b55077cab87d1632b08dd2c043bf8dbb0b32d4d46a7127dcc865f0d30f2ee3dcd5983b686f4e3a9202afc8b608652001c9938906ae1ff1417486096e32511f1bcb860979b1d101e51731749c72fb160dd1245d69ebd6ca81c0519464d3bca9ec3db493cf4b45ebbb7f60fbd12f0705bd788000558bdedc335cedac2100169965b2794fae8a386b2e9ece86ea6952fadeb8501d9aad00e091713cc06d30c5885c3ecf0f84680a06d3c66c5357ec91d5c43af47e234a939b22557cbb552dc45bebbceeed90fbe34820400a0088eeeb07acff0db3ae2585195e9fd23bdf54b55077cab87d1632b08dd2c043bf8dbb0b32d4d46a7127dcc865f0d30f2ee3dcd5983b686f4e3a9202afc8b608652001c9938906ae1ff1417486096e32511f1bcb8608d035b04d8ef6c13117acc1ed9d0586a141f123494f21eeaaead5dd9f623933541b293eef403d2f3e8ede84f9dfe3dc10cbd3fa6bdf3e977dcf2d18a4dca84f8bd9b24fca8e7de4180b9aa6208ad6e756b1c81e98afc8e6994824b5c076857f8f84680a06d3c66c5357ec91d5c43af47e234a939b22557cbb552dc45bebbceeed90fbe34820400a0088eeeb07acff0db3ae2585195e9fd23bdf54b55077cab87d1632b08dd2c043b"),
+ },
+ } {
+ if have, _ := rlp.EncodeToBytes(tc.message); !bytes.Equal(have, tc.want) {
+ t.Errorf("test %d, type %T, have\n\t%x\nwant\n\t%x", i, tc.message, have, tc.want)
+ }
+ }
+}
diff --git a/eth/protocols/snap/range.go b/eth/protocols/snap/range.go
index dd380ff47..2627cb954 100644
--- a/eth/protocols/snap/range.go
+++ b/eth/protocols/snap/range.go
@@ -42,15 +42,15 @@ func newHashRange(start common.Hash, num uint64) *hashRange {
step256.SetFromBig(step)
return &hashRange{
- current: uint256.NewInt().SetBytes32(start[:]),
+ current: new(uint256.Int).SetBytes32(start[:]),
step: step256,
}
}
// Next pushes the hash range to the next interval.
func (r *hashRange) Next() bool {
- next := new(uint256.Int)
- if overflow := next.AddOverflow(r.current, r.step); overflow {
+ next, overflow := new(uint256.Int).AddOverflow(r.current, r.step)
+ if overflow {
return false
}
r.current = next
@@ -65,16 +65,17 @@ func (r *hashRange) Start() common.Hash {
// End returns the last hash in the current interval.
func (r *hashRange) End() common.Hash {
// If the end overflows (non divisible range), return a shorter interval
- next := new(uint256.Int)
- if overflow := next.AddOverflow(r.current, r.step); overflow {
+ next, overflow := new(uint256.Int).AddOverflow(r.current, r.step)
+ if overflow {
return common.HexToHash("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff")
}
- return new(uint256.Int).Sub(next, uint256.NewInt().SetOne()).Bytes32()
+ return next.SubUint64(next, 1).Bytes32()
}
// incHash returns the next hash, in lexicographical order (a.k.a plus one)
func incHash(h common.Hash) common.Hash {
- a := uint256.NewInt().SetBytes32(h[:])
- a.Add(a, uint256.NewInt().SetOne())
+ var a uint256.Int
+ a.SetBytes32(h[:])
+ a.AddUint64(&a, 1)
return common.Hash(a.Bytes32())
}
diff --git a/eth/state_accessor.go b/eth/state_accessor.go
index 24a0e776f..156e3f1da 100644
--- a/eth/state_accessor.go
+++ b/eth/state_accessor.go
@@ -74,6 +74,9 @@ func (eth *Ethereum) stateAtBlock(block *types.Block, reexec uint64, base *state
// The optional base statedb is given, mark the start point as parent block
statedb, database, report = base, base.Database(), false
current = eth.blockchain.GetBlock(block.ParentHash(), block.NumberU64()-1)
+ if current == nil {
+ return nil, fmt.Errorf("missing parent block %v %d", block.ParentHash(), block.NumberU64()-1)
+ }
} else {
// Otherwise try to reexec blocks until we find a state or reach our limit
current = block
diff --git a/eth/sync.go b/eth/sync.go
index 4128f775b..85fb91ec5 100644
--- a/eth/sync.go
+++ b/eth/sync.go
@@ -80,6 +80,17 @@ func (h *handler) syncTransactions(p *eth.Peer) {
}
}
+func (h *handler) syncVotes(p *eth.Peer) {
+ // Vote is introduces since the eth/68 protocol.
+ if p.Version() >= eth.ETH68 {
+ votes := h.votepool.GetVotes()
+ if len(votes) == 0 {
+ return
+ }
+ p.AsyncSendVotes(votes)
+ }
+}
+
// txsyncLoop64 takes care of the initial transaction sync for each new
// connection. When a new peer appears, we relay all currently pending
// transactions. In order to minimise egress bandwidth usage, we send
diff --git a/eth/sync_test.go b/eth/sync_test.go
index a0c6f8602..2a253930c 100644
--- a/eth/sync_test.go
+++ b/eth/sync_test.go
@@ -30,6 +30,7 @@ import (
// Tests that fast sync is disabled after a successful sync cycle.
func TestFastSyncDisabling65(t *testing.T) { testFastSyncDisabling(t, eth.ETH65) }
func TestFastSyncDisabling66(t *testing.T) { testFastSyncDisabling(t, eth.ETH66) }
+func TestFastSyncDisabling68(t *testing.T) { testFastSyncDisabling(t, eth.ETH68) }
// Tests that fast sync gets disabled as soon as a real block is successfully
// imported into the blockchain.
@@ -55,8 +56,8 @@ func testFastSyncDisabling(t *testing.T, protocol uint) {
defer emptyPipe.Close()
defer fullPipe.Close()
- emptyPeer := eth.NewPeer(protocol, p2p.NewPeer(enode.ID{1}, "", nil), emptyPipe, empty.txpool)
- fullPeer := eth.NewPeer(protocol, p2p.NewPeer(enode.ID{2}, "", nil), fullPipe, full.txpool)
+ emptyPeer := eth.NewPeer(protocol, p2p.NewPeer(enode.ID{1}, "", nil), emptyPipe, empty.txpool, nil)
+ fullPeer := eth.NewPeer(protocol, p2p.NewPeer(enode.ID{2}, "", nil), fullPipe, full.txpool, nil)
defer emptyPeer.Close()
defer fullPeer.Close()
diff --git a/ethclient/ethclient.go b/ethclient/ethclient.go
index 578b10f09..32ce15f66 100644
--- a/ethclient/ethclient.go
+++ b/ethclient/ethclient.go
@@ -200,6 +200,28 @@ func (ec *Client) GetDiffAccountsWithScope(ctx context.Context, number *big.Int,
return &result, err
}
+// GetJustifiedHeader returns the highest justified header before a specific block number. If number is nil, the
+// latest justified block header is returned.
+func (ec *Client) GetJustifiedHeader(ctx context.Context, blockNumber *big.Int) (*types.Header, error) {
+ var head *types.Header
+ err := ec.c.CallContext(ctx, &head, "eth_getJustifiedHeader", toBlockNumArg(blockNumber))
+ if err == nil && head == nil {
+ err = ethereum.NotFound
+ }
+ return head, err
+}
+
+// GetFinalizedHeader returns the highest finalized block header before a specific block number. If header is nil, the
+// latest finalized block header is returned.
+func (ec *Client) GetFinalizedHeader(ctx context.Context, blockNumber *big.Int) (*types.Header, error) {
+ var head *types.Header
+ err := ec.c.CallContext(ctx, &head, "eth_getFinalizedHeader", toBlockNumArg(blockNumber))
+ if err == nil && head == nil {
+ err = ethereum.NotFound
+ }
+ return head, err
+}
+
type rpcTransaction struct {
tx *types.Transaction
txExtraInfo
@@ -386,6 +408,17 @@ func (ec *Client) SubscribeNewHead(ctx context.Context, ch chan<- *types.Header)
return ec.c.EthSubscribe(ctx, ch, "newHeads")
}
+// SubscribeNewFinalizedHeader subscribes to notifications about the current blockchain finalized header
+// on the given channel.
+func (ec *Client) SubscribeNewFinalizedHeader(ctx context.Context, ch chan<- *types.Header) (ethereum.Subscription, error) {
+ return ec.c.EthSubscribe(ctx, ch, "newFinalizedHeaders")
+}
+
+// SubscribeNewVotes subscribes to notifications about the new votes into vote pool
+func (ec *Client) SubscribeNewVotes(ctx context.Context, ch chan<- *types.VoteEnvelope) (ethereum.Subscription, error) {
+ return ec.c.EthSubscribe(ctx, ch, "newVotes")
+}
+
// State Access
// NetworkID returns the network ID (also known as the chain ID) for this chain.
diff --git a/genesis b/genesis
index d6b090b5c..e33afb5de 160000
--- a/genesis
+++ b/genesis
@@ -1 +1 @@
-Subproject commit d6b090b5c8ee2dad08cbbf47ed3a6d39901ba865
+Subproject commit e33afb5dea16b6e903197c53380de2ff405adc2f
diff --git a/go.mod b/go.mod
index 14ce921d4..89499c3bf 100644
--- a/go.mod
+++ b/go.mod
@@ -1,92 +1,75 @@
module github.com/ethereum/go-ethereum
-go 1.17
+go 1.15
require (
github.com/Azure/azure-storage-blob-go v0.7.0
- github.com/VictoriaMetrics/fastcache v1.5.7
+ github.com/VictoriaMetrics/fastcache v1.6.0
github.com/aws/aws-sdk-go-v2 v1.4.0
github.com/aws/aws-sdk-go-v2/config v1.1.7
github.com/aws/aws-sdk-go-v2/credentials v1.1.7
github.com/aws/aws-sdk-go-v2/service/route53 v1.5.0
- github.com/btcsuite/btcd v0.20.1-beta
- github.com/cespare/cp v0.1.0
+ github.com/btcsuite/btcd v0.22.0-beta
+ github.com/cespare/cp v1.1.1
github.com/cloudflare/cloudflare-go v0.14.0
github.com/consensys/gnark-crypto v0.4.1-0.20210426202927-39ac3d4b3f1f
github.com/davecgh/go-spew v1.1.1
- github.com/deckarep/golang-set v0.0.0-20180603214616-504e848d77ea
+ github.com/deckarep/golang-set v1.7.1
github.com/docker/docker v1.4.2-0.20180625184442-8e610b2b55bf
- github.com/dop251/goja v0.0.0-20200721192441-a695b0cdd498
+ github.com/dop251/goja v0.0.0-20211011172007-d99e4b8cbf48
github.com/edsrzf/mmap-go v1.0.0
- github.com/fatih/color v1.7.0
+ github.com/fatih/color v1.9.0
github.com/fjl/memsize v0.0.0-20190710130421-bcb5799ab5e5
- github.com/gballet/go-libpcsclite v0.0.0-20190607065134-2772fd86a8ff
+ github.com/gballet/go-libpcsclite v0.0.0-20191108122812-4678299bea08
github.com/go-stack/stack v1.8.0
- github.com/golang/protobuf v1.4.3
+ github.com/golang/protobuf v1.5.2
github.com/golang/snappy v0.0.4
- github.com/google/gofuzz v1.1.1-0.20200604201612-c04b05f3adfa
- github.com/google/uuid v1.1.5
+ github.com/google/gofuzz v1.2.0
+ github.com/google/uuid v1.3.0
github.com/gorilla/websocket v1.4.2
github.com/graph-gophers/graphql-go v0.0.0-20201113091052-beb923fada29
github.com/hashicorp/golang-lru v0.5.5-0.20210104140557-80c98217689d
github.com/holiman/bloomfilter/v2 v2.0.3
- github.com/holiman/uint256 v1.1.1
- github.com/huin/goupnp v1.0.1-0.20210310174557-0ca763054c88
+ github.com/holiman/uint256 v1.2.0
+ github.com/huin/goupnp v1.0.2
github.com/influxdata/influxdb v1.8.3
- github.com/jackpal/go-nat-pmp v1.0.2-0.20160603034137-1fa385a6f458
+ github.com/jackpal/go-nat-pmp v1.0.2
github.com/jedisct1/go-minisign v0.0.0-20190909160543-45766022959e
- github.com/julienschmidt/httprouter v1.2.0
- github.com/karalabe/usb v0.0.0-20190919080040-51dc0efba356
- github.com/mattn/go-colorable v0.1.0
- github.com/mattn/go-isatty v0.0.5-0.20180830101745-3fb116b82035
+ github.com/julienschmidt/httprouter v1.3.0
+ github.com/karalabe/usb v0.0.0-20211005121534-4c5740d64559
+ github.com/logrusorgru/aurora v2.0.3+incompatible
+ github.com/mattn/go-colorable v0.1.8
+ github.com/mattn/go-isatty v0.0.14
github.com/naoina/toml v0.1.2-0.20170918210437-9fafd6967416
github.com/olekukonko/tablewriter v0.0.5
github.com/oxtoacart/bpool v0.0.0-20190530202638-03653db5a59c
github.com/panjf2000/ants/v2 v2.4.5
- github.com/peterh/liner v1.1.1-0.20190123174540-a2c9a5303de7
- github.com/prometheus/tsdb v0.7.1
+ github.com/peterh/liner v1.2.0
+ github.com/pkg/errors v0.9.1
+ github.com/prometheus/tsdb v0.10.0
+ github.com/prysmaticlabs/prysm v0.0.0-20220124113610-e26cde5e091b
github.com/rjeczalik/notify v0.9.1
github.com/rs/cors v1.7.0
github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible
- github.com/status-im/keycard-go v0.0.0-20190316090335-8537d3370df4
+ github.com/status-im/keycard-go v0.0.0-20200402102358-957c09536969
github.com/stretchr/testify v1.7.0
github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7
- github.com/tyler-smith/go-bip39 v1.0.1-0.20181017060643-dbb3b84ba2ef
- golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2
+ github.com/tidwall/gjson v1.14.0 // indirect
+ github.com/tidwall/wal v1.1.7
+ github.com/tyler-smith/go-bip39 v1.1.0
+ github.com/wealdtech/go-eth2-wallet-encryptor-keystorev4 v1.1.3
+ github.com/willf/bitset v1.1.3
+ golang.org/x/crypto v0.0.0-20211117183948-ae814b36b871
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c
- golang.org/x/sys v0.0.0-20210420205809-ac73e9fd8988
- golang.org/x/text v0.3.4
- golang.org/x/time v0.0.0-20201208040808-7e3f01d25324
+ golang.org/x/sys v0.0.0-20211124211545-fe61309f8881
+ golang.org/x/text v0.3.7
+ golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba
gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce
gopkg.in/olebedev/go-duktape.v3 v3.0.0-20200619000410-60c24ae608a6
gopkg.in/urfave/cli.v1 v1.20.0
)
-require (
- github.com/Azure/azure-pipeline-go v0.2.2 // indirect
- github.com/Azure/go-autorest/autorest/adal v0.8.0 // indirect
- github.com/StackExchange/wmi v0.0.0-20180116203802-5d049714c4a6 // indirect
- github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.0.7 // indirect
- github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.0.7 // indirect
- github.com/aws/aws-sdk-go-v2/service/sso v1.1.6 // indirect
- github.com/aws/aws-sdk-go-v2/service/sts v1.3.1 // indirect
- github.com/aws/smithy-go v1.4.0 // indirect
- github.com/cespare/xxhash/v2 v2.1.1 // indirect
- github.com/dlclark/regexp2 v1.2.0 // indirect
- github.com/go-ole/go-ole v1.2.1 // indirect
- github.com/go-sourcemap/sourcemap v2.1.2+incompatible // indirect
- github.com/kr/pretty v0.2.0 // indirect
- github.com/kylelemons/godebug v1.1.0 // indirect
- github.com/mattn/go-ieproxy v0.0.0-20190702010315-6dee0af9227d // indirect
- github.com/mattn/go-runewidth v0.0.9 // indirect
- github.com/naoina/go-stringutil v0.1.0 // indirect
- github.com/opentracing/opentracing-go v1.1.0 // indirect
- github.com/pkg/errors v0.9.1 // indirect
- github.com/pmezard/go-difflib v1.0.0 // indirect
- github.com/tklauser/go-sysconf v0.3.5 // indirect
- github.com/tklauser/numcpus v0.2.2 // indirect
- golang.org/x/net v0.0.0-20210226172049-e18ecbb05110 // indirect
- google.golang.org/protobuf v1.23.0 // indirect
- gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c // indirect
- gotest.tools v2.2.0+incompatible // indirect
+replace (
+ github.com/ferranbt/fastssz => github.com/prysmaticlabs/fastssz v0.0.0-20220110145812-fafb696cae88
+ github.com/grpc-ecosystem/grpc-gateway/v2 => github.com/prysmaticlabs/grpc-gateway/v2 v2.3.1-0.20210702154020-550e1cd83ec1
)
diff --git a/go.sum b/go.sum
index 8f94f1ce9..da22aaf92 100644
--- a/go.sum
+++ b/go.sum
@@ -1,5 +1,8 @@
+cloud.google.com/go v0.16.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
+cloud.google.com/go v0.31.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
+cloud.google.com/go v0.37.0/go.mod h1:TS1dMSSfndXH133OKGwekG838Om/cQT0BUHV3HcBgoo=
cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=
cloud.google.com/go v0.43.0/go.mod h1:BOSR3VbTLkk6FDC/TcffxP4NF/FFBGA5ku+jvKOP7pg=
cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU=
@@ -8,16 +11,43 @@ cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTj
cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0=
cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To=
cloud.google.com/go v0.51.0/go.mod h1:hWtGJ6gnXH+KgDv+V0zFGDvpi07n3z8ZNj3T1RW0Gcw=
+cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4=
+cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M=
+cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc=
+cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk=
+cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs=
+cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc=
+cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY=
cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=
cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE=
+cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc=
+cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg=
+cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc=
+cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ=
cloud.google.com/go/bigtable v1.2.0/go.mod h1:JcVAOl45lrTmQfLj7T6TxyMzIN/3FGGcFm+2xVAli2o=
cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE=
+cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk=
+cloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk=
cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I=
cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw=
+cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA=
+cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU=
cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw=
cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos=
+cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk=
+cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs=
+cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0=
collectd.org v0.3.0/go.mod h1:A/8DzQBkF6abtvrT2j/AU/4tiBgJWYyh0y/oB/4MlWE=
+contrib.go.opencensus.io/exporter/jaeger v0.2.1 h1:yGBYzYMewVL0yO9qqJv3Z5+IRhPdU7e9o/2oKpX4YvI=
+contrib.go.opencensus.io/exporter/jaeger v0.2.1/go.mod h1:Y8IsLgdxqh1QxYxPC5IgXVmBaeLUeQFfBeBi9PbeZd0=
+dmitri.shuralyov.com/app/changes v0.0.0-20180602232624-0a106ad413e3/go.mod h1:Yl+fi1br7+Rr3LqpNJf1/uxUdtRUV+Tnj0o93V2B9MU=
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
+dmitri.shuralyov.com/html/belt v0.0.0-20180602232347-f7d459c86be0/go.mod h1:JLBrvjyP0v+ecvNYvCpyZgu5/xkfAUhi6wJj28eUfSU=
+dmitri.shuralyov.com/service/change v0.0.0-20181023043359-a85b471d5412/go.mod h1:a1inKt/atXimZ4Mv927x+r7UpyzRUf4emIoiiSC2TN4=
+dmitri.shuralyov.com/state v0.0.0-20180228185332-28bcc343414c/go.mod h1:0PRwlb0D6DFvNNtx+9ybjezNCa8XF0xaYcETyp6rHWU=
+git.apache.org/thrift.git v0.0.0-20180902110319-2566ecd5d999/go.mod h1:fPE2ZNJGynbRyZ4dJvy6G277gSllfV2HJqblrnkyeyg=
+github.com/AndreasBriese/bbloom v0.0.0-20190306092124-e2d15f34fcf9/go.mod h1:bOvUY6CB00SOBii9/FifXqc0awNKxLFCL/+pkDPuyl8=
+github.com/AndreasBriese/bbloom v0.0.0-20190825152654-46b345b51c96/go.mod h1:bOvUY6CB00SOBii9/FifXqc0awNKxLFCL/+pkDPuyl8=
github.com/Azure/azure-pipeline-go v0.2.1/go.mod h1:UGSo8XybXnIGZ3epmeBw7Jdz+HiUVpqIlpz/HKHylF4=
github.com/Azure/azure-pipeline-go v0.2.2 h1:6oiIS9yaG6XCCzhgAgKFfIWyo4LLCiDhZot6ltoThhY=
github.com/Azure/azure-pipeline-go v0.2.2/go.mod h1:4rQ/NZncSvGqNkkOsNpOU1tgoNuIlp9AfUH5G1tvCHc=
@@ -33,367 +63,1266 @@ github.com/Azure/go-autorest/autorest/date v0.2.0 h1:yW+Zlqf26583pE43KhfnhFcdmSW
github.com/Azure/go-autorest/autorest/date v0.2.0/go.mod h1:vcORJHLJEh643/Ioh9+vPmf1Ij9AEBM5FuBIXLmIy0g=
github.com/Azure/go-autorest/autorest/mocks v0.1.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0=
github.com/Azure/go-autorest/autorest/mocks v0.2.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0=
+github.com/Azure/go-autorest/autorest/mocks v0.3.0 h1:qJumjCaCudz+OcqE9/XtEPfvtOjOmKaui4EOpFI6zZc=
github.com/Azure/go-autorest/autorest/mocks v0.3.0/go.mod h1:a8FDP3DYzQ4RYfVAxAN3SVSiiO77gL2j2ronKKP0syM=
github.com/Azure/go-autorest/logger v0.1.0 h1:ruG4BSDXONFRrZZJ2GUXDiUyVpayPmb1GnWeHDdaNKY=
github.com/Azure/go-autorest/logger v0.1.0/go.mod h1:oExouG+K6PryycPJfVSxi/koC6LSNgds39diKLz7Vrc=
github.com/Azure/go-autorest/tracing v0.5.0 h1:TRn4WjSnkcSy5AEG3pnbtFSwNtwzjr4VYyQflFE619k=
github.com/Azure/go-autorest/tracing v0.5.0/go.mod h1:r/s2XiOKccPW3HrqB+W0TQzfbtp2fGCgRFtBroKn4Dk=
+github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
github.com/DATA-DOG/go-sqlmock v1.3.3/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q5eFN3EC/SaM=
+github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0=
+github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ=
+github.com/OneOfOne/xxhash v1.2.2 h1:KMrpdQIwFcEqXDklaen+P1axHaj9BSKzvpUUfnHldSE=
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
-github.com/StackExchange/wmi v0.0.0-20180116203802-5d049714c4a6 h1:fLjPD/aNc3UIOA6tDi6QXUemppXK3P9BI7mr2hd6gx8=
+github.com/PuerkitoBio/purell v1.0.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
+github.com/PuerkitoBio/urlesc v0.0.0-20160726150825-5bd2802263f2/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
+github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo=
+github.com/Shopify/sarama v1.26.1/go.mod h1:NbSGBSSndYaIhRcBtY9V0U7AyH+x71bG668AuWys/yU=
+github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI=
github.com/StackExchange/wmi v0.0.0-20180116203802-5d049714c4a6/go.mod h1:3eOhrUMpNV+6aFIbp5/iudMxNCF27Vw2OZgy4xEx0Fg=
-github.com/VictoriaMetrics/fastcache v1.5.7 h1:4y6y0G8PRzszQUYIQHHssv/jgPHAb5qQuuDNdCbyAgw=
-github.com/VictoriaMetrics/fastcache v1.5.7/go.mod h1:ptDBkNMQI4RtmVo8VS/XwRY6RoTu1dAWCbrk+6WsEM8=
+github.com/StackExchange/wmi v0.0.0-20210224194228-fe8f1750fd46 h1:5sXbqlSomvdjlRbWyNqkPsJ3Fg+tQZCbgeX1VGljbQY=
+github.com/StackExchange/wmi v0.0.0-20210224194228-fe8f1750fd46/go.mod h1:3eOhrUMpNV+6aFIbp5/iudMxNCF27Vw2OZgy4xEx0Fg=
+github.com/VictoriaMetrics/fastcache v1.6.0 h1:C/3Oi3EiBCqufydp1neRZkqcwmEiuRT9c3fqvvgKm5o=
+github.com/VictoriaMetrics/fastcache v1.6.0/go.mod h1:0qHz5QP0GMX4pfmMA/zt5RgfNuXJrTP0zS7DqpHGGTw=
+github.com/VividCortex/gohistogram v1.0.0/go.mod h1:Pf5mBqqDxYaXu3hDrrU+w6nw50o/4+TcAqDqk/vUH7g=
github.com/aead/siphash v1.0.1/go.mod h1:Nywa3cDsYNNK3gaciGTWPwHt0wlpNV15vwmswBAUSII=
+github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5/go.mod h1:SkGFH1ia65gfNATL8TAiHDNxPzPdmEL5uirI2Uyuz6c=
github.com/ajstarks/svgo v0.0.0-20180226025133-644b8db467af/go.mod h1:K08gAheRH3/J6wwsYMMT4xOr94bZjxIelGM0+d/wbFw=
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
+github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
-github.com/allegro/bigcache v1.2.1-0.20190218064605-e24eb225f156 h1:eMwmnE/GDgah4HI848JfFxHt+iPb26b4zyfspmqY0/8=
+github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
+github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho=
github.com/allegro/bigcache v1.2.1-0.20190218064605-e24eb225f156/go.mod h1:Cb/ax3seSYIx7SuZdm2G2xzfwmv3TPSk2ucNfQESPXM=
+github.com/allegro/bigcache v1.2.1 h1:hg1sY1raCwic3Vnsvje6TT7/pnZba83LeFck5NrFKSc=
+github.com/allegro/bigcache v1.2.1/go.mod h1:Cb/ax3seSYIx7SuZdm2G2xzfwmv3TPSk2ucNfQESPXM=
github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8=
+github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c=
+github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
github.com/apache/arrow/go/arrow v0.0.0-20191024131854-af6fa24be0db/go.mod h1:VTxUBvSJ3s3eHAg65PNgrsn5BtqCRPdmyXh6rAfdxN0=
+github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ=
+github.com/apache/thrift v0.13.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ=
+github.com/aristanetworks/fsnotify v1.4.2/go.mod h1:D/rtu7LpjYM8tRJphJ0hUBYpjai8SfX+aSNsWDTq/Ks=
+github.com/aristanetworks/glog v0.0.0-20191112221043-67e8567f59f3/go.mod h1:KASm+qXFKs/xjSoWn30NrWBBvdTTQq+UjkhjEJHfSFA=
+github.com/aristanetworks/goarista v0.0.0-20200521140103-6c3304613b30 h1:cgk6xsRVshE29qzHDCQ+tqmu7ny8GnjPQhAw/RTk/Co=
+github.com/aristanetworks/goarista v0.0.0-20200521140103-6c3304613b30/go.mod h1:QZe5Yh80Hp1b6JxQdpfSEEe8X7hTyTEZSosSrFf/oJE=
+github.com/aristanetworks/splunk-hec-go v0.3.3/go.mod h1:1VHO9r17b0K7WmOlLb9nTk/2YanvOEnLMUgsFrxBROc=
+github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=
+github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
+github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY=
+github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
+github.com/aryann/difflib v0.0.0-20170710044230-e206f873d14a/go.mod h1:DAHtR1m6lCRdSC2Tm3DSWRPvIPr6xNKyeHdqDQSQT+A=
+github.com/aws/aws-lambda-go v1.13.3/go.mod h1:4UKl9IzQMoD+QF79YdCuzCwp8VbmG4VAQwij/eHl5CU=
+github.com/aws/aws-sdk-go v1.27.0/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
+github.com/aws/aws-sdk-go-v2 v0.18.0/go.mod h1:JWVYvqSMppoMJC0x5wdwiImzgXTI9FuZwxzkQq9wy+g=
+github.com/aws/aws-sdk-go-v2 v1.2.0/go.mod h1:zEQs02YRBw1DjK0PoJv3ygDYOFTre1ejlJWl8FwAuQo=
github.com/aws/aws-sdk-go-v2 v1.4.0 h1:Ryh4fNebT9SwLyCKPSk83dyEZj+KB6KzDyb1gXii7EI=
github.com/aws/aws-sdk-go-v2 v1.4.0/go.mod h1:tI4KhsR5VkzlUa2DZAdwx7wCAYGwkZZ1H31PYrBFx1w=
+github.com/aws/aws-sdk-go-v2/config v1.1.1/go.mod h1:0XsVy9lBI/BCXm+2Tuvt39YmdHwS5unDQmxZOYe8F5Y=
github.com/aws/aws-sdk-go-v2/config v1.1.7 h1:I9AsaodDiw1WbUBn8b4Ktvr2ltPBe7QfLq4UUZY4GsY=
github.com/aws/aws-sdk-go-v2/config v1.1.7/go.mod h1:6GFyKv06rDCBCIOmWe9vLi7ofCkE7y8aqI6a3tFWNQ0=
+github.com/aws/aws-sdk-go-v2/credentials v1.1.1/go.mod h1:mM2iIjwl7LULWtS6JCACyInboHirisUUdkBPoTHMOUo=
github.com/aws/aws-sdk-go-v2/credentials v1.1.7 h1:hpg13GGT/j7Zv+xkJ+0p2Pj1NvnRSNIX1XI4WI02kq8=
github.com/aws/aws-sdk-go-v2/credentials v1.1.7/go.mod h1:xYCvIyeVCRC9DmG3Zv/pxlZEEIBYf4fY/jSUVSrr58M=
+github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.0.2/go.mod h1:3hGg3PpiEjHnrkrlasTfxFqUsZ2GCk/fMUn4CbKgSkM=
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.0.7 h1:WdPPgbL9Kl4UrEdiy6IdQioPuXSBg8HJcU9eihiCWOE=
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.0.7/go.mod h1:51hY5nMAiL2EF8ny/pFovWYoKZTcEfOw0WWKcq2E9AQ=
+github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.0.2/go.mod h1:45MfaXZ0cNbeuT0KQ1XJylq8A6+OpVV2E5kvY/Kq+u8=
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.0.7 h1:/XUEk2eY/QGzCHSX5LH4vp+UMKTSVVCroUfgkzPGIzE=
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.0.7/go.mod h1:Fl3kxN4ucBIPuAHKHRn+wTGylFzMCEjRfmfHqZuEh8o=
+github.com/aws/aws-sdk-go-v2/service/route53 v1.1.1/go.mod h1:rLiOUrPLW/Er5kRcQ7NkwbjlijluLsrIbu/iyl35RO4=
github.com/aws/aws-sdk-go-v2/service/route53 v1.5.0 h1:pElmeJjj+4a/eyOiY7Fhj9cBTuvQP16FrRHcY4YBmVE=
github.com/aws/aws-sdk-go-v2/service/route53 v1.5.0/go.mod h1:rc8BFfeKArrCF8I1wmYOF3Bhm+DW/LWVOZEpSG12v2o=
+github.com/aws/aws-sdk-go-v2/service/sso v1.1.1/go.mod h1:SuZJxklHxLAXgLTc1iFXbEWkXs7QRTQpCLGaKIprQW0=
github.com/aws/aws-sdk-go-v2/service/sso v1.1.6 h1:oS6Jdb6ZwD7U1OqJYqyGbgcU0VJ15E3XbiIIp0clge8=
github.com/aws/aws-sdk-go-v2/service/sso v1.1.6/go.mod h1:EO4s+JzAllrWKgNjS/Q4mj43vGimSvhWCB6BLognegc=
+github.com/aws/aws-sdk-go-v2/service/sts v1.1.1/go.mod h1:Wi0EBZwiz/K44YliU0EKxqTCJGUfYTWXrrBwkq736bM=
github.com/aws/aws-sdk-go-v2/service/sts v1.3.1 h1:tAn9Kvjfq2F2rCeTbMOLtDBQVjLLSIvm21BTUFoswss=
github.com/aws/aws-sdk-go-v2/service/sts v1.3.1/go.mod h1:QPpnumNNgybBiz3HXGgDRf/QypAMOOsh4+JdOhG5mLU=
+github.com/aws/smithy-go v1.1.0/go.mod h1:EzMw8dbp/YJL4A5/sbhGddag+NPT7q084agLbB9LgIw=
github.com/aws/smithy-go v1.4.0 h1:3rsQpgRe+OoQgJhEwGNpIkosl0fJLdmQqF4gSFRjg+4=
github.com/aws/smithy-go v1.4.0/go.mod h1:SObp3lf9smib00L/v3U2eAKG8FyQ7iLrJnQiAmR5n+E=
+github.com/bazelbuild/rules_go v0.23.2 h1:Wxu7JjqnF78cKZbsBsARLSXx/jlGaSLCnUV3mTlyHvM=
+github.com/bazelbuild/rules_go v0.23.2/go.mod h1:MC23Dc/wkXEyk3Wpq6lCqz0ZAYOZDw2DR5y3N1q2i7M=
+github.com/benbjohnson/clock v1.0.2/go.mod h1:bGMdMPoPVvcYyt1gHDf4J2KE153Yf9BuiUKYMaxlTDM=
+github.com/benbjohnson/clock v1.0.3/go.mod h1:bGMdMPoPVvcYyt1gHDf4J2KE153Yf9BuiUKYMaxlTDM=
+github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
+github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
+github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
+github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d/go.mod h1:6QX/PXZ00z/TKoufEY6K/a0k6AhaJrQKdFe6OfVXsa4=
+github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
+github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84=
github.com/bmizerany/pat v0.0.0-20170815010413-6226ea591a40/go.mod h1:8rLXio+WjiTceGBHIoTvn60HIbs7Hm7bcHjyrSqYB9c=
github.com/boltdb/bolt v1.3.1/go.mod h1:clJnj/oiGkjum5o1McbSZDSLxVThjynRyGBgiAx27Ps=
-github.com/btcsuite/btcd v0.20.1-beta h1:Ik4hyJqN8Jfyv3S4AGBOmyouMsYE3EdYODkMbQjwPGw=
+github.com/bradfitz/go-smtpd v0.0.0-20170404230938-deb6d6237625/go.mod h1:HYsPBTaaSFSlLx/70C2HPIMNZpVV8+vt/A+FMnYP11g=
+github.com/bradfitz/gomemcache v0.0.0-20170208213004-1952afaa557d/go.mod h1:PmM6Mmwb0LSuEubjR8N7PtNe1KxZLtOUHtbeikc5h60=
+github.com/btcsuite/btcd v0.0.0-20190213025234-306aecffea32/go.mod h1:DrZx5ec/dmnfpw9KyYoQyYo7d0KEvTkk/5M/vbZjAr8=
+github.com/btcsuite/btcd v0.0.0-20190523000118-16327141da8c/go.mod h1:3J08xEfcugPacsc34/LKRU2yO7YmuT8yt28J8k2+rrI=
github.com/btcsuite/btcd v0.20.1-beta/go.mod h1:wVuoA8VJLEcwgqHBwHmzLRazpKxTv13Px/pDuV7OomQ=
+github.com/btcsuite/btcd v0.21.0-beta/go.mod h1:ZSWyehm27aAuS9bvkATT+Xte3hjHZ+MRgMY/8NJ7K94=
+github.com/btcsuite/btcd v0.22.0-beta h1:LTDpDKUM5EeOFBPM8IXpinEcmZ6FWfNZbE3lfrfdnWo=
+github.com/btcsuite/btcd v0.22.0-beta/go.mod h1:9n5ntfhhHQBIhUvlhDvD3Qg6fRUj4jkN0VB8L8svzOA=
github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f/go.mod h1:TdznJufoqS23FtqVCzL0ZqgP5MqXbb4fg/WgDys70nA=
+github.com/btcsuite/btcutil v0.0.0-20190207003914-4c204d697803/go.mod h1:+5NJ2+qvTyV9exUAL/rxXi3DcLg2Ts+ymUAY5y4NvMg=
github.com/btcsuite/btcutil v0.0.0-20190425235716-9e5f4b9a998d/go.mod h1:+5NJ2+qvTyV9exUAL/rxXi3DcLg2Ts+ymUAY5y4NvMg=
+github.com/btcsuite/btcutil v1.0.2/go.mod h1:j9HUFwoQRsZL3V4n+qG+CUnEGHOarIxfC3Le2Yhbcts=
+github.com/btcsuite/btcutil v1.0.3-0.20201208143702-a53e38424cce/go.mod h1:0DVlHczLPewLcPGEIeUEzfOJhqGPQ0mJJRDBtD307+o=
github.com/btcsuite/go-socks v0.0.0-20170105172521-4720035b7bfd/go.mod h1:HHNXQzUsZCxOoE+CPiyCTO6x34Zs86zZUiwtpXoGdtg=
github.com/btcsuite/goleveldb v0.0.0-20160330041536-7834afc9e8cd/go.mod h1:F+uVaaLLH7j4eDXPRvw78tMflu7Ie2bzYOH4Y8rRKBY=
+github.com/btcsuite/goleveldb v1.0.0/go.mod h1:QiK9vBlgftBg6rWQIj6wFzbPfRjiykIEhBH4obrXJ/I=
github.com/btcsuite/snappy-go v0.0.0-20151229074030-0bdef8d06723/go.mod h1:8woku9dyThutzjeg+3xrA5iCpBRH8XEEg3lh6TiUghc=
+github.com/btcsuite/snappy-go v1.0.0/go.mod h1:8woku9dyThutzjeg+3xrA5iCpBRH8XEEg3lh6TiUghc=
github.com/btcsuite/websocket v0.0.0-20150119174127-31079b680792/go.mod h1:ghJtEyQwv5/p4Mg4C0fgbePVuGr935/5ddU9Z3TmDRY=
github.com/btcsuite/winsvc v1.0.0/go.mod h1:jsenWakMcC0zFBFurPLEAyrnc/teJEM1O46fmI40EZs=
+github.com/bufbuild/buf v0.37.0/go.mod h1:lQ1m2HkIaGOFba6w/aC3KYBHhKEOESP3gaAEpS3dAFM=
+github.com/buger/jsonparser v0.0.0-20181115193947-bf1c66bbce23/go.mod h1:bbYlZJ7hK1yFx9hf58LP0zeX7UjIGs20ufpu3evjr+s=
github.com/c-bata/go-prompt v0.2.2/go.mod h1:VzqtzE2ksDBcdln8G7mk2RX9QyGjH+OVqOCSiVIqS34=
+github.com/casbin/casbin/v2 v2.1.2/go.mod h1:YcPU1XXisHhLzuxH9coDNf2FbKpjGlbCg3n9yuLkIJQ=
+github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
-github.com/cespare/cp v0.1.0 h1:SE+dxFebS7Iik5LK0tsi1k9ZCxEaFX4AjQmoyA+1dJk=
github.com/cespare/cp v0.1.0/go.mod h1:SOGHArjBr4JWaSDEVpWpo/hNg6RoKrls6Oh40hiwW+s=
+github.com/cespare/cp v1.1.1 h1:nCb6ZLdB7NRaqsm91JtQTAme2SKJzXVsdPIPkyJr1MU=
+github.com/cespare/cp v1.1.1/go.mod h1:SOGHArjBr4JWaSDEVpWpo/hNg6RoKrls6Oh40hiwW+s=
github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko=
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
github.com/cespare/xxhash/v2 v2.1.1 h1:6MnRN8NT7+YBpUIWxHtefFZOKTAPgGjpQSxqLNn0+qY=
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
+github.com/cheekybits/genny v1.0.0/go.mod h1:+tQajlRqAUrPI7DOSpB0XAqZYtQakVtB7wXkRAgjxjQ=
+github.com/chzyer/logex v1.1.10 h1:Swpa1K6QvQznwJRcfTfQJmTE72DqScAa40E+fbHEXEE=
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
+github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e h1:fY5BOSpyZCqRo5OhCuC+XN+r/bBCmeuuJtjz+bCNIf8=
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
+github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1 h1:q763qf9huN11kDQavWsoZXJNW3xEE4JJyHa5Q25/sd8=
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
+github.com/clbanning/x2j v0.0.0-20191024224557-825249438eec/go.mod h1:jMjuTZXRI4dUb/I5gc9Hdhagfvm9+RyrPryS/auMzxE=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/cloudflare/cloudflare-go v0.14.0 h1:gFqGlGl/5f9UGXAaKapCGUfaTCgRKKnzu2VvzMZlOFA=
github.com/cloudflare/cloudflare-go v0.14.0/go.mod h1:EnwdgGMaFOruiPZRFSgn+TsQ3hQ7C/YWzIGLeu5c304=
+github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
+github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
+github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
+github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8=
+github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd/go.mod h1:sE/e/2PUdi/liOCUjSTXgM1o87ZssimdTWN964YiIeI=
github.com/consensys/bavard v0.1.8-0.20210406032232-f3452dc9b572/go.mod h1:Bpd0/3mZuaj6Sj+PqrmIquiOKy397AKGThQPaGzNXAQ=
github.com/consensys/gnark-crypto v0.4.1-0.20210426202927-39ac3d4b3f1f h1:C43yEtQ6NIf4ftFXD/V55gnGFgPbMQobd//YlnLjUJ8=
github.com/consensys/gnark-crypto v0.4.1-0.20210426202927-39ac3d4b3f1f/go.mod h1:815PAHg3wvysy0SyIqanF8gZ0Y1wjk/hrDHD/iT88+Q=
+github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
+github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
+github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
+github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk=
+github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
+github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
+github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
+github.com/coreos/go-systemd v0.0.0-20181012123002-c6f51f82210d/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
+github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
+github.com/coreos/go-systemd v0.0.0-20191104093116-d3cd4ed1dbcf/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
+github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
+github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
+github.com/cpuguy83/go-md2man v1.0.10 h1:BSKMNlYxDvnunlTymqtgONjNnaRV1sTpcovwwjF22jk=
+github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE=
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
+github.com/cpuguy83/go-md2man/v2 v2.0.0 h1:EoUDS0afbrsXAZ9YQ9jdu/mZ2sXgT1/2yyNng4PGlyM=
+github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
+github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY=
+github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
+github.com/cyberdelia/templates v0.0.0-20141128023046-ca7fffd4298c/go.mod h1:GyV+0YP4qX0UQ7r2MoYZ+AvYDp12OF5yg4q8rGnyNh4=
+github.com/d4l3k/messagediff v1.2.1 h1:ZcAIMYsUg0EAp9X+tt8/enBE/Q8Yd5kzPynLyKptt9U=
+github.com/d4l3k/messagediff v1.2.1/go.mod h1:Oozbb1TVXFac9FtSIxHBMnBCq2qeH/2KkEQxENCrlLo=
github.com/dave/jennifer v1.2.0/go.mod h1:fIb+770HOpJ2fmN9EPPKOqm1vMGhB+TwXKMZhrIygKg=
github.com/davecgh/go-spew v0.0.0-20171005155431-ecdeabc65495/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
-github.com/deckarep/golang-set v0.0.0-20180603214616-504e848d77ea h1:j4317fAZh7X6GqbFowYdYdI0L9bwxL07jyPZIdepyZ0=
+github.com/davidlazar/go-crypto v0.0.0-20170701192655-dcfb0a7ac018/go.mod h1:rQYf4tfk5sSwFsnDg3qYaBxSjsD9S8+59vW0dKUgme4=
+github.com/davidlazar/go-crypto v0.0.0-20200604182044-b73af7476f6c/go.mod h1:6UhI8N9EjYm1c2odKpFpAYeR8dsBeM7PtzQhRgxRr9U=
github.com/deckarep/golang-set v0.0.0-20180603214616-504e848d77ea/go.mod h1:93vsz/8Wt4joVM7c2AVqh+YRMiUSc14yDtF28KmMOgQ=
+github.com/deckarep/golang-set v1.7.1 h1:SCQV0S6gTtp6itiFrTqI+pfmJ4LN85S1YzhDf9rTHJQ=
+github.com/deckarep/golang-set v1.7.1/go.mod h1:93vsz/8Wt4joVM7c2AVqh+YRMiUSc14yDtF28KmMOgQ=
+github.com/decred/dcrd/lru v1.0.0/go.mod h1:mxKOwFd7lFjN2GZYsiz/ecgqR6kkYAl+0pz0tEMk218=
+github.com/deepmap/oapi-codegen v1.6.0/go.mod h1:ryDa9AgbELGeB+YEXE1dR53yAjHwFvE9iAUlWl9Al3M=
+github.com/deepmap/oapi-codegen v1.8.2/go.mod h1:YLgSKSDv/bZQB7N4ws6luhozi3cEdRktEqrX88CvjIw=
+github.com/dgraph-io/badger v1.6.1/go.mod h1:FRmFw3uxvcpa8zG3Rxs0th+hCLIuaQg8HlNV5bjgnuU=
+github.com/dgraph-io/badger v1.6.2/go.mod h1:JW2yswe3V058sS0kZ2h/AXeDSqFjxnZcRrVH//y2UQE=
+github.com/dgraph-io/ristretto v0.0.2/go.mod h1:KPxhHT9ZxKefz+PCeOGsrHpl1qZ7i70dGTu2u+Ahh6E=
+github.com/dgraph-io/ristretto v0.0.4-0.20210318174700-74754f61e018 h1:cNcG4c2n5xanQzp2hMyxDxPYVQmZ91y4WN6fJFlndLo=
+github.com/dgraph-io/ristretto v0.0.4-0.20210318174700-74754f61e018/go.mod h1:MIonLggsKgZLUSt414ExgwNtlOL5MuEoAJP514mwGe8=
github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM=
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
github.com/dgryski/go-bitstream v0.0.0-20180413035011-3522498ce2c8/go.mod h1:VMaSuZ+SZcx/wljOQKvp5srsbCiKDEb6K2wC4+PiBmQ=
+github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2 h1:tdlZCpZ/P9DhczCTSixgIKmwPv6+wP5DGjqLYw5SUiA=
+github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw=
github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
-github.com/dlclark/regexp2 v1.2.0 h1:8sAhBGEM0dRWogWqWyQeIJnxjWO6oIjl8FKqREDsGfk=
-github.com/dlclark/regexp2 v1.2.0/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc=
+github.com/dlclark/regexp2 v1.4.1-0.20201116162257-a2a8dda75c91 h1:Izz0+t1Z5nI16/II7vuEo/nHjodOg0p7+OiDpjX5t1E=
+github.com/dlclark/regexp2 v1.4.1-0.20201116162257-a2a8dda75c91/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc=
github.com/docker/docker v1.4.2-0.20180625184442-8e610b2b55bf h1:sh8rkQZavChcmakYiSlqu2425CHyFXLZZnvm7PDpU8M=
github.com/docker/docker v1.4.2-0.20180625184442-8e610b2b55bf/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
-github.com/dop251/goja v0.0.0-20200721192441-a695b0cdd498 h1:Y9vTBSsV4hSwPSj4bacAU/eSnV3dAxVpepaghAdhGoQ=
-github.com/dop251/goja v0.0.0-20200721192441-a695b0cdd498/go.mod h1:Mw6PkjjMXWbTj+nnj4s3QPXq1jaT0s5pC0iFD4+BOAA=
+github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96/go.mod h1:Qh8CwZgvJUkLughtfhJv5dyTYa91l1fOUCrgjqmcifM=
+github.com/dop251/goja v0.0.0-20211011172007-d99e4b8cbf48 h1:iZOop7pqsg+56twTopWgwCGxdB5SI2yDO8Ti7eTRliQ=
+github.com/dop251/goja v0.0.0-20211011172007-d99e4b8cbf48/go.mod h1:R9ET47fwRVRPZnOGvHxxhuZcbrMCuiqOz3Rlrh4KSnk=
+github.com/dop251/goja_nodejs v0.0.0-20210225215109-d91c329300e7/go.mod h1:hn7BA7c8pLvoGndExHudxTDKZ84Pyvv+90pbBjbTz0Y=
+github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
+github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo=
+github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
+github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs=
+github.com/eapache/go-resiliency v1.2.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs=
+github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU=
+github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I=
github.com/eclipse/paho.mqtt.golang v1.2.0/go.mod h1:H9keYFcgq3Qr5OUJm/JZI/i6U7joQ8SYLhZwfeOo6Ts=
github.com/edsrzf/mmap-go v1.0.0 h1:CEBF7HpRnUCSJgGUb5h1Gm7e3VkmVDrR8lvWVLtrOFw=
github.com/edsrzf/mmap-go v1.0.0/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M=
+github.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc=
+github.com/emicklei/dot v0.11.0/go.mod h1:DeV7GvQtIw4h2u73RKBkkFdvVAz0D9fzeJrgPW6gy/s=
+github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs=
+github.com/envoyproxy/go-control-plane v0.6.9/go.mod h1:SBwIajubJHhxtWwsL9s8ss4safvEdbitLhGGK48rN6g=
+github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
+github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
+github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
+github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
+github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ=
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
-github.com/fatih/color v1.7.0 h1:DkWD4oS2D8LGGgTQ6IvwJJXSL5Vp2ffcQg58nFV38Ys=
+github.com/ethereum/go-ethereum v1.10.13/go.mod h1:W3yfrFyL9C1pHcwY5hmRHVDaorTiQxhYBkKyu5mEDHw=
+github.com/evanphx/json-patch v4.2.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
+github.com/fatih/color v1.9.0 h1:8xPHl4/q1VyqGIPif1F+1V3Y3lSmrq01EabUW3CoW5s=
+github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU=
github.com/fjl/memsize v0.0.0-20190710130421-bcb5799ab5e5 h1:FtmdgXiUlNeRsoNMFlKLDt+S+6hbjVMEW6RGQ7aUf7c=
github.com/fjl/memsize v0.0.0-20190710130421-bcb5799ab5e5/go.mod h1:VvhXpOYNQvB+uIk2RvXzuaQtkQJzzIx6lSBe1xv7hi0=
+github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc=
+github.com/flynn/noise v1.0.0/go.mod h1:xbMo+0i6+IGbYdJhF31t2eR1BIU0CYc12+BNAKwUTag=
github.com/fogleman/gg v1.2.1-0.20190220221249-0403632d5b90/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k=
+github.com/fortytw2/leaktest v1.3.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g=
+github.com/francoispqt/gojay v1.2.13/go.mod h1:ehT5mTG4ua4581f1++1WLG0vPdaA9HaiDsoyrBGkyDY=
+github.com/franela/goblin v0.0.0-20200105215937-c9ffbefa60db/go.mod h1:7dvUGVsVBjqR7JHJk0brhHOZYGmfBYOrK0ZhYMEtBr4=
+github.com/franela/goreq v0.0.0-20171204163338-bcd34c9993f8/go.mod h1:ZhphrRTfi2rbfLwlschooIH4+wKKDR4Pdxhh+TRoA20=
+github.com/frankban/quicktest v1.7.2/go.mod h1:jaStnuzAqU1AJdCO0l53JDCJrVDKcS03DbaAcR7Ks/o=
+github.com/fsnotify/fsnotify v1.4.3-0.20170329110642-4da3e2cfbabc/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
-github.com/gballet/go-libpcsclite v0.0.0-20190607065134-2772fd86a8ff h1:tY80oXqGNY4FhTFhk+o9oFHGINQ/+vhlm8HFzi6znCI=
+github.com/garyburd/redigo v1.1.1-0.20170914051019-70e1b1943d4f/go.mod h1:NR3MbYisc3/PwhQ00EMzDiPmrwpPxAn5GI05/YaO1SY=
+github.com/garyburd/redigo v1.6.0/go.mod h1:NR3MbYisc3/PwhQ00EMzDiPmrwpPxAn5GI05/YaO1SY=
github.com/gballet/go-libpcsclite v0.0.0-20190607065134-2772fd86a8ff/go.mod h1:x7DCsMOv1taUwEWCzT4cmDeAkigA5/QCwUodaVOe8Ww=
+github.com/gballet/go-libpcsclite v0.0.0-20191108122812-4678299bea08 h1:f6D9Hr8xV8uYKlyuj8XIruxlh9WjVjdh1gIicAS7ays=
+github.com/gballet/go-libpcsclite v0.0.0-20191108122812-4678299bea08/go.mod h1:x7DCsMOv1taUwEWCzT4cmDeAkigA5/QCwUodaVOe8Ww=
+github.com/getkin/kin-openapi v0.53.0/go.mod h1:7Yn5whZr5kJi6t+kShccXS8ae1APpYTW6yheSwk8Yi4=
+github.com/getkin/kin-openapi v0.61.0/go.mod h1:7Yn5whZr5kJi6t+kShccXS8ae1APpYTW6yheSwk8Yi4=
+github.com/ghodss/yaml v0.0.0-20150909031657-73d445a93680/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
+github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
+github.com/gliderlabs/ssh v0.1.1/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0=
github.com/glycerine/go-unsnap-stream v0.0.0-20180323001048-9f0cb55181dd/go.mod h1:/20jfyN9Y5QPEAprSgKAUr+glWDY39ZiUEAYOEv5dsE=
github.com/glycerine/goconvey v0.0.0-20190410193231-58a59202ab31/go.mod h1:Ogl1Tioa0aV7gstGFO7KhffUsb9M4ydbEbbxpcEDc24=
+github.com/go-chi/chi/v5 v5.0.0/go.mod h1:BBug9lr0cqtdAhsu6R4AAdvufI0/XBzAQSsUqJpoZOs=
+github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q=
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
-github.com/go-kit/kit v0.8.0 h1:Wz+5lgoB0kkuqLEc6NVmwRknTKP6dTGbSqvhZtBI/j0=
+github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
+github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
+github.com/go-kit/kit v0.10.0 h1:dXFJfIHVvUcpSgDOV+Ne6t7jXri8Tfv2uOLHUZ2XNuo=
+github.com/go-kit/kit v0.10.0/go.mod h1:xUsJbQ/Fp4kEt7AFgCuvyX4a71u8h9jB8tj/ORgOZ7o=
+github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY=
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
-github.com/go-logfmt/logfmt v0.4.0 h1:MP4Eh7ZCb31lleYCFuwm0oe4/YGak+5l1vA2NOE80nA=
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
-github.com/go-ole/go-ole v1.2.1 h1:2lOsA72HgjxAuMlKpFiCbHTvu44PIVkZ5hqm3RSdI/E=
+github.com/go-logfmt/logfmt v0.5.0 h1:TrB8swr/68K7m9CcGut2g3UOihhbcbiMAYiuTXdEih4=
+github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A=
+github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas=
+github.com/go-logr/logr v0.2.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU=
+github.com/go-logr/logr v0.2.1 h1:fV3MLmabKIZ383XifUjFSwcoGee0v9qgPp8wy5svibE=
+github.com/go-logr/logr v0.2.1/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU=
github.com/go-ole/go-ole v1.2.1/go.mod h1:7FAglXiTm7HKlQRDeOQ6ZNUHidzCWXuZWq/1dTyBNF8=
-github.com/go-sourcemap/sourcemap v2.1.2+incompatible h1:0b/xya7BKGhXuqFESKM4oIiRo9WOt2ebz7KxfreD6ug=
-github.com/go-sourcemap/sourcemap v2.1.2+incompatible/go.mod h1:F8jJfvm2KbVjc5NqelyYJmf/v5J0dwNLS2mL4sNA1Jg=
+github.com/go-ole/go-ole v1.2.5 h1:t4MGB5xEDZvXI+0rMjjsfBsD7yAgp/s9ZDkL1JndXwY=
+github.com/go-ole/go-ole v1.2.5/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
+github.com/go-openapi/jsonpointer v0.0.0-20160704185906-46af16f9f7b1/go.mod h1:+35s3my2LFTysnkMfxsJBAMHj/DoqoB9knIWoYG/Vk0=
+github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
+github.com/go-openapi/jsonreference v0.0.0-20160704190145-13c6e3589ad9/go.mod h1:W3Z9FmVs9qj+KR4zFKmDPGiLdk1D9Rlm7cyMvf57TTg=
+github.com/go-openapi/spec v0.0.0-20160808142527-6aced65f8501/go.mod h1:J8+jY1nAiCcj+friV/PDoE1/3eeccG9LYBs0tYvLOWc=
+github.com/go-openapi/swag v0.0.0-20160704191624-1d0bd113de87/go.mod h1:DXUve3Dpr1UfpPtxFw+EFuQ41HhCWZfha5jSVRG7C7I=
+github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
+github.com/go-sourcemap/sourcemap v2.1.3+incompatible h1:W1iEw64niKVGogNgBN3ePyLFfuisuzeidWPMPWmECqU=
+github.com/go-sourcemap/sourcemap v2.1.3+incompatible/go.mod h1:F8jJfvm2KbVjc5NqelyYJmf/v5J0dwNLS2mL4sNA1Jg=
+github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
+github.com/go-stack/stack v1.6.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
+github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
+github.com/go-yaml/yaml v2.1.0+incompatible h1:RYi2hDdss1u4YE7GwixGzWwVo47T8UQwnTLB6vQiq+o=
+github.com/go-yaml/yaml v2.1.0+incompatible/go.mod h1:w2MrLa16VYP0jy6N7M5kHaCkaLENm+P+Tv+MfurjSw0=
+github.com/gofrs/flock v0.8.0/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU=
github.com/gofrs/uuid v3.3.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
+github.com/gofrs/uuid v4.0.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
+github.com/gogo/googleapis v1.1.0/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFGgqEef3s=
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
+github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
+github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=
+github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
+github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
+github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I=
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k=
+github.com/golang/gddo v0.0.0-20200528160355-8d077c1d8f4c/go.mod h1:sam69Hju0uq+5uvLJUMDlsKlQ21Vrs1Kd/1YFPNYdOU=
github.com/golang/geo v0.0.0-20190916061304-5b978397cfec/go.mod h1:QZ0nwyI2jOfgRAoBvP+ab5aRr7c9x7lhGEJrKvBwjWI=
+github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
+github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
+github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
+github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e h1:1r7pUrabqp18hOBcwBwiTsbnFeTZHV9eER/QT5JVZxY=
+github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
+github.com/golang/lint v0.0.0-20170918230701-e5d664eb928e/go.mod h1:tluoj9z5200jBnyusfRPU2LqT6J+DAorxEvtC7LHB+E=
+github.com/golang/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:tluoj9z5200jBnyusfRPU2LqT6J+DAorxEvtC7LHB+E=
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y=
+github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
+github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
+github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
+github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4=
+github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc=
+github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs=
+github.com/golang/protobuf v0.0.0-20161109072736-4bd1920723d7/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
+github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
+github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
+github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk=
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
+github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
-github.com/golang/protobuf v1.4.3 h1:JjCZWpVbqXDqFVmTfYWEVTMIYrL/NPdPSCHPJ0T/raM=
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
+github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
+github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=
+github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
+github.com/golang/snappy v0.0.0-20170215233205-553a64147049/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
+github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM=
github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
+github.com/golangci/lint-1 v0.0.0-20181222135242-d2cdd8c08219/go.mod h1:/X8TswGSh1pIozq4ZwCfxS0WA5JGXguxk94ar/4c87Y=
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/flatbuffers v1.11.0/go.mod h1:1AeVuKshWv4vARoZatz6mlQ0JxURH0Kv5+zNeJKJCa8=
+github.com/google/go-cmp v0.1.1-0.20171103154506-982329095285/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
-github.com/google/go-cmp v0.5.4 h1:L8R9j+yAqZuZjsqh/z+F1NCffTKKLShY6zXTItVIZ8M=
+github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
+github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
+github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
+github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
+github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
-github.com/google/gofuzz v1.1.1-0.20200604201612-c04b05f3adfa h1:Q75Upo5UN4JbPFURXZ8nLKYUvF85dyFRop/vQ0Rv+64=
+github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=
+github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
+github.com/google/go-github v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+umXrS52loVEgC2AApnigrVQ=
+github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
+github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
+github.com/google/gofuzz v1.1.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/gofuzz v1.1.1-0.20200604201612-c04b05f3adfa/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
+github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0=
+github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
+github.com/google/gopacket v1.1.17/go.mod h1:UdDNZ1OO62aGYVnPhxT1U6aI7ukYtA/kB8vaU0diBUM=
+github.com/google/gopacket v1.1.19/go.mod h1:iJ8V8n6KS+z2U1A8pUwu8bW5SyEMkXJB8Yo/Vo+TKTo=
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
+github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
+github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
+github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
+github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
+github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
-github.com/google/uuid v1.1.5 h1:kxhtnfFVi+rYdOALN0B3k9UT86zVJKfBimRaciULW4I=
+github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
+github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
+github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.1.5/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
+github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
+github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
+github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
+github.com/googleapis/gax-go v2.0.0+incompatible/go.mod h1:SFVmujtThgffbyetf+mdk2eWhX2bMyUtNHzFKcPA9HY=
+github.com/googleapis/gax-go/v2 v2.0.3/go.mod h1:LLvjysVCY1JZeum8Z6l8qUty8fiNwE08qbEPm1M08qg=
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
+github.com/googleapis/gnostic v0.0.0-20170729233727-0c5108395e2d/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY=
+github.com/googleapis/gnostic v0.1.0/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY=
+github.com/gophercloud/gophercloud v0.1.0/go.mod h1:vxM41WHh5uqHVBMZHzuwNOHh8XEoIEcSTewFxm1c5g8=
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
+github.com/gordonklaus/ineffassign v0.0.0-20200309095847-7953dde2c7bf/go.mod h1:cuNKsD1zp2v6XfE/orVX2QE1LC+i254ceGcVeDT3pTU=
+github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg=
+github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
+github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
+github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
+github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc=
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/graph-gophers/graphql-go v0.0.0-20201113091052-beb923fada29 h1:sezaKhEfPFg8W0Enm61B9Gs911H8iesGY5R8NDPtd1M=
github.com/graph-gophers/graphql-go v0.0.0-20201113091052-beb923fada29/go.mod h1:9CQHMSxwO4MprSdzoIEobiHpoLtHm77vfxsvsIN5Vuc=
+github.com/gregjones/httpcache v0.0.0-20170920190843-316c5e0ff04e/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA=
+github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA=
+github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
+github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
+github.com/grpc-ecosystem/go-grpc-middleware v1.2.2 h1:FlFbCRLd5Jr4iYXZufAvgWN6Ao0JrI5chLINnUXDDr0=
+github.com/grpc-ecosystem/go-grpc-middleware v1.2.2/go.mod h1:EaizFBKfUKtMIF5iaDEhniwNedqGo9FuLFzppDr3uwI=
+github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 h1:Ovs26xHkKqVztRpIrF/92BcuyuQ/YW4NSIpoGtfXNho=
+github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
+github.com/grpc-ecosystem/grpc-gateway v1.5.0/go.mod h1:RSKVYQBd5MCa4OVpNdGskqpgL2+G+NZTnrVHpWWfpdw=
+github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
+github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
+github.com/grpc-ecosystem/grpc-gateway v1.16.0 h1:gmcG1KaJ57LophUzW0Hy8NmPhnMZb4M0+kPpLofRdBo=
+github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=
+github.com/gxed/hashland/keccakpg v0.0.1/go.mod h1:kRzw3HkwxFU1mpmPP8v1WyQzwdGfmKFJ6tItnhQ67kU=
+github.com/gxed/hashland/murmur3 v0.0.1/go.mod h1:KjXop02n4/ckmZSnY2+HKcLud/tcmvhST0bie/0lS48=
+github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q=
+github.com/hashicorp/consul/api v1.3.0/go.mod h1:MmDNSzIMUjNpY/mQ398R4bk2FnqQLoPndWW5VkKPlCE=
+github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8=
+github.com/hashicorp/consul/sdk v0.3.0/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8=
+github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
+github.com/hashicorp/go-bexpr v0.1.10/go.mod h1:oxlubA2vC/gFVfX1A6JGp7ls7uCDlfJn732ehYYg+g0=
+github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
+github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
+github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM=
+github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=
+github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU=
+github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU=
+github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4=
+github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
+github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
+github.com/hashicorp/go-uuid v1.0.2/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
+github.com/hashicorp/go-version v1.2.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
+github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90=
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
+github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4=
github.com/hashicorp/golang-lru v0.5.5-0.20210104140557-80c98217689d h1:dg1dEPuWpEqDnvIw251EVy4zlP8gWbsGj4BsUKCRpYs=
github.com/hashicorp/golang-lru v0.5.5-0.20210104140557-80c98217689d/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4=
+github.com/hashicorp/hcl v0.0.0-20170914154624-68e816d1c783/go.mod h1:oZtUIOe8dh44I2q6ScRibXws4Ajl+d+nod3AaR9vL5w=
+github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
+github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64=
+github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ=
+github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I=
+github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc=
+github.com/herumi/bls-eth-go-binary v0.0.0-20210130185500-57372fb27371/go.mod h1:luAnRm3OsMQeokhGzpYmc0ZKwawY7o87PUEP11Z7r7U=
+github.com/herumi/bls-eth-go-binary v0.0.0-20210917013441-d37c07cfda4e h1:wCMygKUQhmcQAjlk2Gquzq6dLmyMv2kF+llRspoRgrk=
+github.com/herumi/bls-eth-go-binary v0.0.0-20210917013441-d37c07cfda4e/go.mod h1:luAnRm3OsMQeokhGzpYmc0ZKwawY7o87PUEP11Z7r7U=
github.com/holiman/bloomfilter/v2 v2.0.3 h1:73e0e/V0tCydx14a0SCYS/EWCxgwLZ18CZcZKVu0fao=
github.com/holiman/bloomfilter/v2 v2.0.3/go.mod h1:zpoh+gs7qcpqrHr3dB55AMiJwo0iURXE7ZOP9L9hSkA=
-github.com/holiman/uint256 v1.1.1 h1:4JywC80b+/hSfljFlEBLHrrh+CIONLDz9NuFl0af4Mw=
-github.com/holiman/uint256 v1.1.1/go.mod h1:y4ga/t+u+Xwd7CpDgZESaRcWy0I7XMlTMA25ApIH5Jw=
+github.com/holiman/uint256 v1.2.0 h1:gpSYcPLWGv4sG43I2mVLiDZCNDh/EpGjSk8tmtxitHM=
+github.com/holiman/uint256 v1.2.0/go.mod h1:y4ga/t+u+Xwd7CpDgZESaRcWy0I7XMlTMA25ApIH5Jw=
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
-github.com/huin/goupnp v1.0.1-0.20210310174557-0ca763054c88 h1:bcAj8KroPf552TScjFPIakjH2/tdIrIH8F+cc4v4SRo=
-github.com/huin/goupnp v1.0.1-0.20210310174557-0ca763054c88/go.mod h1:nNs7wvRfN1eKaMknBydLNQU6146XQim8t4h+q90biWo=
+github.com/hudl/fargo v1.3.0/go.mod h1:y3CKSmjA+wD2gak7sUSXTAoopbhU08POFhmITJgmKTg=
+github.com/huin/goupnp v1.0.0/go.mod h1:n9v9KO1tAxYH82qOn+UTIFQDmx5n1Zxd/ClZDMX7Bnc=
+github.com/huin/goupnp v1.0.2 h1:RfGLP+h3mvisuWEyybxNq5Eft3NWhHLPeUN72kpKZoI=
+github.com/huin/goupnp v1.0.2/go.mod h1:0dxJBVBHqTMjIUMkESDTNgOOx/Mw5wYIfyFmdzSamkM=
github.com/huin/goutil v0.0.0-20170803182201-1ca381bf3150/go.mod h1:PpLOETDnJ0o3iZrZfqZzyLl6l7F3c6L1oWn7OICBi6o=
+github.com/ianlancetaylor/cgosymbolizer v0.0.0-20200424224625-be1b05b0b279/go.mod h1:a5aratAVTWyz+nJMmDsN8O4XTfaLfdAsB1ysCmZX5Bw=
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
+github.com/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
+github.com/inconshreveable/log15 v0.0.0-20170622235902-74a0988b5f80/go.mod h1:cOaXtrgN4ScfRrD9Bre7U1thNq5RtJ8ZoP4iXVGRj6o=
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
github.com/influxdata/flux v0.65.1/go.mod h1:J754/zds0vvpfwuq7Gc2wRdVwEodfpCFM7mYlOw2LqY=
github.com/influxdata/influxdb v1.8.3 h1:WEypI1BQFTT4teLM+1qkEcvUi0dAvopAI/ir0vAiBg8=
github.com/influxdata/influxdb v1.8.3/go.mod h1:JugdFhsvvI8gadxOI6noqNeeBHvWNTbfYGtiAn+2jhI=
+github.com/influxdata/influxdb-client-go/v2 v2.4.0/go.mod h1:vLNHdxTJkIf2mSLvGrpj8TCcISApPoXkaxP8g9uRlW8=
+github.com/influxdata/influxdb1-client v0.0.0-20191209144304-8bf82d3c094d/go.mod h1:qj24IKcXYK6Iy9ceXlo3Tc+vtHo9lIhSX5JddghvEPo=
github.com/influxdata/influxql v1.1.1-0.20200828144457-65d3ef77d385/go.mod h1:gHp9y86a/pxhjJ+zMjNXiQAA197Xk9wLxaz+fGG+kWk=
github.com/influxdata/line-protocol v0.0.0-20180522152040-32c6aa80de5e/go.mod h1:4kt73NQhadE3daL3WhR5EJ/J2ocX0PZzwxQ0gXJ7oFE=
+github.com/influxdata/line-protocol v0.0.0-20200327222509-2487e7298839/go.mod h1:xaLFMmpvUxqXtVkUJfg9QmT88cDaCJ3ZKgdZ78oO8Qo=
+github.com/influxdata/line-protocol v0.0.0-20210311194329-9aa0e372d097/go.mod h1:xaLFMmpvUxqXtVkUJfg9QmT88cDaCJ3ZKgdZ78oO8Qo=
github.com/influxdata/promql/v2 v2.12.0/go.mod h1:fxOPu+DY0bqCTCECchSRtWfc+0X19ybifQhZoQNF5D8=
github.com/influxdata/roaring v0.4.13-0.20180809181101-fc520f41fab6/go.mod h1:bSgUQ7q5ZLSO+bKBGqJiCBGAl+9DxyW63zLTujjUlOE=
github.com/influxdata/tdigest v0.0.0-20181121200506-bf2b5ad3c0a9/go.mod h1:Js0mqiSBE6Ffsg94weZZ2c+v/ciT8QRHFOap7EKDrR0=
github.com/influxdata/usage-client v0.0.0-20160829180054-6d3895376368/go.mod h1:Wbbw6tYNvwa5dlB6304Sd+82Z3f7PmVZHVKU637d4po=
-github.com/jackpal/go-nat-pmp v1.0.2-0.20160603034137-1fa385a6f458 h1:6OvNmYgJyexcZ3pYbTI9jWx5tHo1Dee/tWbLMfPe2TA=
+github.com/ipfs/go-cid v0.0.1/go.mod h1:GHWU/WuQdMPmIosc4Yn1bcCT7dSeX4lBafM7iqUPQvM=
+github.com/ipfs/go-cid v0.0.2/go.mod h1:GHWU/WuQdMPmIosc4Yn1bcCT7dSeX4lBafM7iqUPQvM=
+github.com/ipfs/go-cid v0.0.3/go.mod h1:GHWU/WuQdMPmIosc4Yn1bcCT7dSeX4lBafM7iqUPQvM=
+github.com/ipfs/go-cid v0.0.4/go.mod h1:4LLaPOQwmk5z9LBgQnpkivrx8BJjUyGwTXCd5Xfj6+M=
+github.com/ipfs/go-cid v0.0.5/go.mod h1:plgt+Y5MnOey4vO4UlUazGqdbEXuFYitED67FexhXog=
+github.com/ipfs/go-cid v0.0.7/go.mod h1:6Ux9z5e+HpkQdckYoX1PG/6xqKspzlEIR5SDmgqgC/I=
+github.com/ipfs/go-datastore v0.4.1/go.mod h1:SX/xMIKoCszPqp+z9JhPYCmoOoXTvaa13XEbGtsFUhA=
+github.com/ipfs/go-datastore v0.4.4/go.mod h1:SX/xMIKoCszPqp+z9JhPYCmoOoXTvaa13XEbGtsFUhA=
+github.com/ipfs/go-datastore v0.5.0/go.mod h1:9zhEApYMTl17C8YDp7JmU7sQZi2/wqiYh73hakZ90Bk=
+github.com/ipfs/go-detect-race v0.0.1/go.mod h1:8BNT7shDZPo99Q74BpGMK+4D8Mn4j46UU0LZ723meps=
+github.com/ipfs/go-ds-badger v0.2.3/go.mod h1:pEYw0rgg3FIrywKKnL+Snr+w/LjJZVMTBRn4FS6UHUk=
+github.com/ipfs/go-ds-badger v0.3.0/go.mod h1:1ke6mXNqeV8K3y5Ak2bAA0osoTfmxUdupVCGm4QUIek=
+github.com/ipfs/go-ds-leveldb v0.4.2/go.mod h1:jpbku/YqBSsBc1qgME8BkWS4AxzF2cEu1Ii2r79Hh9s=
+github.com/ipfs/go-ds-leveldb v0.5.0/go.mod h1:d3XG9RUDzQ6V4SHi8+Xgj9j1XuEk1z82lquxrVbml/Q=
+github.com/ipfs/go-ipfs-delay v0.0.0-20181109222059-70721b86a9a8/go.mod h1:8SP1YXK1M1kXuc4KJZINY3TQQ03J2rwBG9QfXmbRPrw=
+github.com/ipfs/go-ipfs-util v0.0.2/go.mod h1:CbPtkWJzjLdEcezDns2XYaehFVNXG9zrdrtMecczcsQ=
+github.com/ipfs/go-log v0.0.1/go.mod h1:kL1d2/hzSpI0thNYjiKfjanbVNU+IIGA/WnNESY9leM=
+github.com/ipfs/go-log v1.0.2/go.mod h1:1MNjMxe0u6xvJZgeqbJ8vdo2TKaGwZ1a0Bpza+sr2Sk=
+github.com/ipfs/go-log v1.0.3/go.mod h1:OsLySYkwIbiSUR/yBTdv1qPtcE4FW3WPWk/ewz9Ru+A=
+github.com/ipfs/go-log v1.0.4/go.mod h1:oDCg2FkjogeFOhqqb+N39l2RpTNPL6F/StPkB3kPgcs=
+github.com/ipfs/go-log v1.0.5/go.mod h1:j0b8ZoR+7+R99LD9jZ6+AJsrzkPbSXbZfGakb5JPtIo=
+github.com/ipfs/go-log/v2 v2.0.2/go.mod h1:O7P1lJt27vWHhOwQmcFEvlmo49ry2VY2+JfBWFaa9+0=
+github.com/ipfs/go-log/v2 v2.0.3/go.mod h1:O7P1lJt27vWHhOwQmcFEvlmo49ry2VY2+JfBWFaa9+0=
+github.com/ipfs/go-log/v2 v2.0.5/go.mod h1:eZs4Xt4ZUJQFM3DlanGhy7TkwwawCZcSByscwkWG+dw=
+github.com/ipfs/go-log/v2 v2.1.1/go.mod h1:2v2nsGfZsvvAJz13SyFzf9ObaqwHiHxsPLEHntrv9KM=
+github.com/ipfs/go-log/v2 v2.1.3/go.mod h1:/8d0SH3Su5Ooc31QlL1WysJhvyOTDCjcCZ9Axpmri6g=
+github.com/ipfs/go-log/v2 v2.3.0/go.mod h1:QqGoj30OTpnKaG/LKTGTxoP2mmQtjVMEnK72gynbe/g=
+github.com/ipfs/go-log/v2 v2.4.0/go.mod h1:nPZnh7Cj7lwS3LpRU5Mwr2ol1c2gXIEXuF6aywqrtmo=
github.com/jackpal/go-nat-pmp v1.0.2-0.20160603034137-1fa385a6f458/go.mod h1:QPH045xvCAeXUZOxsnwmrtiCoxIr9eob+4orBN1SBKc=
+github.com/jackpal/go-nat-pmp v1.0.2 h1:KzKSgb7qkJvOUTqYl9/Hg/me3pWgBmERKrTGD7BdWus=
+github.com/jackpal/go-nat-pmp v1.0.2/go.mod h1:QPH045xvCAeXUZOxsnwmrtiCoxIr9eob+4orBN1SBKc=
+github.com/jbenet/go-cienv v0.1.0/go.mod h1:TqNnHUmJgXau0nCzC7kXWeotg3J9W34CUv5Djy1+FlA=
+github.com/jbenet/go-temp-err-catcher v0.0.0-20150120210811-aac704a3f4f2/go.mod h1:8GXXJV31xl8whumTzdZsTt3RnUIiPqzkyf7mxToRCMs=
+github.com/jbenet/go-temp-err-catcher v0.1.0/go.mod h1:0kJRvmDZXNMIiJirNPEYfhpPwbGVtZVWC34vc5WLsDk=
+github.com/jbenet/goprocess v0.0.0-20160826012719-b497e2f366b8/go.mod h1:Ly/wlsjFq/qrU3Rar62tu1gASgGw6chQbSh/XgIIXCY=
+github.com/jbenet/goprocess v0.1.3/go.mod h1:5yspPrukOVuOLORacaBi858NqyClJPQxYZlqdZVfqY4=
+github.com/jbenet/goprocess v0.1.4/go.mod h1:5yspPrukOVuOLORacaBi858NqyClJPQxYZlqdZVfqY4=
+github.com/jcmturner/gofork v1.0.0/go.mod h1:MK8+TM0La+2rjBD4jE12Kj1pCCxK7d2LK/UM3ncEo0o=
github.com/jedisct1/go-minisign v0.0.0-20190909160543-45766022959e h1:UvSe12bq+Uj2hWd8aOlwPmoZ+CITRFrdit+sDGfAg8U=
github.com/jedisct1/go-minisign v0.0.0-20190909160543-45766022959e/go.mod h1:G1CVv03EnqU1wYL2dFwXxW2An0az9JTl/ZsqXQeBlkU=
+github.com/jellevandenhooff/dkim v0.0.0-20150330215556-f50fe3d243e1/go.mod h1:E0B/fFc00Y+Rasa88328GlI/XbtyysCtTHZS8h7IrBU=
github.com/jessevdk/go-flags v0.0.0-20141203071132-1679536dcc89/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
+github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
+github.com/jhump/protoreflect v1.8.1/go.mod h1:7GcYQDdMU/O/BBrl/cX6PNHpXh6cenjd8pneu5yW7Tg=
+github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo=
github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U=
+github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
+github.com/joonix/log v0.0.0-20200409080653-9c1d2ceb5f1d/go.mod h1:fS54ONkjDV71zS9CDx3V9K21gJg7byKSvI4ajuWFNJw=
+github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4=
github.com/jrick/logrotate v1.0.0/go.mod h1:LNinyqDIJnpAur+b8yyulnQw/wDuN1+BYKlTRt3OuAQ=
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
+github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
+github.com/json-iterator/go v1.1.8/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
+github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
+github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
+github.com/json-iterator/go v1.1.11 h1:uVUAXhF2To8cbw/3xN3pxj6kk7TYKs98NIrTqPlMWAQ=
+github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=
github.com/jsternberg/zap-logfmt v1.0.0/go.mod h1:uvPs/4X51zdkcm5jXl5SYoN+4RK21K8mysFmDaM/h+o=
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
-github.com/julienschmidt/httprouter v1.2.0 h1:TDTW5Yz1mjftljbcKqRcrYhd4XeOoI98t+9HbQbYf7g=
+github.com/juju/ansiterm v0.0.0-20180109212912-720a0952cc2a h1:FaWFmfWdAUKbSCtOU2QjDaorUexogfaMgbipgYATUMU=
+github.com/juju/ansiterm v0.0.0-20180109212912-720a0952cc2a/go.mod h1:UJSiEoRfvx3hP73CvoARgeLjaIOjybY9vj8PUPPFGeU=
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
+github.com/julienschmidt/httprouter v1.3.0 h1:U0609e9tgbseu3rBINet9P48AI/D3oJs4dN7jwJOQ1U=
+github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM=
github.com/jung-kurt/gofpdf v1.0.3-0.20190309125859-24315acbbda5/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes=
github.com/jwilder/encoding v0.0.0-20170811194829-b4e1701a28ef/go.mod h1:Ct9fl0F6iIOGgxJ5npU/IUOhOhqlVrGjyIZc8/MagT0=
-github.com/karalabe/usb v0.0.0-20190919080040-51dc0efba356 h1:I/yrLt2WilKxlQKCM52clh5rGzTKpVctGT1lH4Dc8Jw=
-github.com/karalabe/usb v0.0.0-20190919080040-51dc0efba356/go.mod h1:Od972xHfMJowv7NGVDiWVxk2zxnWgjLlJzE+F4F7AGU=
+github.com/k0kubun/go-ansi v0.0.0-20180517002512-3bf9e2903213 h1:qGQQKEcAR99REcMpsXCp3lJ03zYT1PkRd3kQGPn9GVg=
+github.com/k0kubun/go-ansi v0.0.0-20180517002512-3bf9e2903213/go.mod h1:vNUNkEQ1e29fT/6vq2aBdFsgNPmy8qMdSay1npru+Sw=
+github.com/kami-zh/go-capturer v0.0.0-20171211120116-e492ea43421d/go.mod h1:P2viExyCEfeWGU259JnaQ34Inuec4R38JCyBx2edgD0=
+github.com/karalabe/usb v0.0.0-20211005121534-4c5740d64559 h1:0VWDXPNE0brOek1Q8bLfzKkvOzwbQE/snjGojlCr8CY=
+github.com/karalabe/usb v0.0.0-20211005121534-4c5740d64559/go.mod h1:Od972xHfMJowv7NGVDiWVxk2zxnWgjLlJzE+F4F7AGU=
+github.com/kevinms/leakybucket-go v0.0.0-20200115003610-082473db97ca/go.mod h1:ph+C5vpnCcQvKBwJwKLTK3JLNGnBXYlG7m7JjoC/zYA=
+github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00=
+github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/kkdai/bstream v0.0.0-20161212061736-f391b8402d23/go.mod h1:J+Gs4SYgM6CZQHDETBtE9HaSEkGmuNXF86RwHhHUvq4=
github.com/klauspost/compress v1.4.0/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=
+github.com/klauspost/compress v1.9.8/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=
+github.com/klauspost/compress v1.10.1/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
+github.com/klauspost/compress v1.11.7/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
github.com/klauspost/cpuid v0.0.0-20170728055534-ae7887de9fa5/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek=
+github.com/klauspost/cpuid v1.2.3 h1:CCtW0xUnWGVINKvE/WWOYKdsPV6mawAtvQuSl8guwQs=
+github.com/klauspost/cpuid v1.2.3/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek=
+github.com/klauspost/cpuid/v2 v2.0.4/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
+github.com/klauspost/cpuid/v2 v2.0.9 h1:lgaqFMSdTdQYdZ04uHyN2d/eKdOMyi2YLSvlQIBFYa4=
+github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
github.com/klauspost/crc32 v0.0.0-20161016154125-cb6bfca970f6/go.mod h1:+ZoRqAPRLkC4NPOvfYeR5KNOrY6TD+/sAC3HXPZgDYg=
github.com/klauspost/pgzip v1.0.2-0.20170402124221-0bf5dcad4ada/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs=
+github.com/klauspost/pgzip v1.2.5/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs=
+github.com/klauspost/reedsolomon v1.9.3/go.mod h1:CwCi+NUr9pqSVktrkN+Ondf06rkhYZ/pcNv7fu+8Un4=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
-github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515 h1:T+h1c/A9Gawja4Y9mFVWj2vyii2bbUNDw3kt9VxK2EY=
+github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
+github.com/konsorten/go-windows-terminal-sequences v1.0.3 h1:CE8S1cTafDpPvMhIxNJKvHsGVBgn1xWYf1NbHQhywc8=
+github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
+github.com/koron/go-ssdp v0.0.0-20191105050749-2e1c40ed0b5d/go.mod h1:5Ky9EC2xfoUKUor0Hjgi2BJhCSXJfMOFlmyYrVKGQMk=
+github.com/koron/go-ssdp v0.0.2/go.mod h1:XoLfkAiA2KeZsYh4DbHxD7h3nR2AZNqVQOa+LJuqPYs=
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
-github.com/kr/pretty v0.2.0 h1:s5hAObm+yFO5uHYt5dYjxi2rXrsnmRpJx4OYvIWUaQs=
github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
+github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI=
+github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
-github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
+github.com/kr/pty v1.1.3/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
+github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
+github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
+github.com/labstack/echo/v4 v4.2.1/go.mod h1:AA49e0DZ8kk5jTOOCKNuPR6oTnBS0dYiM4FW1e6jwpg=
+github.com/labstack/gommon v0.3.0/go.mod h1:MULnywXg0yavhxWKc+lOruYdAhDwPK9wf0OL7NoOu+k=
github.com/leanovate/gopter v0.2.9 h1:fQjYxZaynp97ozCzfOyOuAGOU4aU/z37zf/tOujFk7c=
github.com/leanovate/gopter v0.2.9/go.mod h1:U2L/78B+KVFIx2VmW6onHJQzXtFb+p5y3y2Sh+Jxxv8=
github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
+github.com/libp2p/go-addr-util v0.0.2/go.mod h1:Ecd6Fb3yIuLzq4bD7VcywcVSBtefcAwnUISBM3WG15E=
+github.com/libp2p/go-addr-util v0.1.0/go.mod h1:6I3ZYuFr2O/9D+SoyM0zEw0EF3YkldtTX406BpdQMqw=
+github.com/libp2p/go-buffer-pool v0.0.1/go.mod h1:xtyIz9PMobb13WaxR6Zo1Pd1zXJKYg0a8KiIvDp3TzQ=
+github.com/libp2p/go-buffer-pool v0.0.2/go.mod h1:MvaB6xw5vOrDl8rYZGLFdKAuk/hRoRZd1Vi32+RXyFM=
+github.com/libp2p/go-cidranger v1.1.0/go.mod h1:KWZTfSr+r9qEo9OkI9/SIEeAtw+NNoU0dXIXt15Okic=
+github.com/libp2p/go-conn-security-multistream v0.2.0/go.mod h1:hZN4MjlNetKD3Rq5Jb/P5ohUnFLNzEAR4DLSzpn2QLU=
+github.com/libp2p/go-conn-security-multistream v0.2.1/go.mod h1:cR1d8gA0Hr59Fj6NhaTpFhJZrjSYuNmhpT2r25zYR70=
+github.com/libp2p/go-conn-security-multistream v0.3.0/go.mod h1:EEP47t4fw/bTelVmEzIDqSe69hO/ip52xBEhZMLWAHM=
+github.com/libp2p/go-eventbus v0.2.1/go.mod h1:jc2S4SoEVPP48H9Wpzm5aiGwUCBMfGhVhhBjyhhCJs8=
+github.com/libp2p/go-flow-metrics v0.0.1/go.mod h1:Iv1GH0sG8DtYN3SVJ2eG221wMiNpZxBdp967ls1g+k8=
+github.com/libp2p/go-flow-metrics v0.0.3/go.mod h1:HeoSNUrOJVK1jEpDqVEiUOIXqhbnS27omG0uWU5slZs=
+github.com/libp2p/go-libp2p v0.17.0/go.mod h1:Fkin50rsGdv5mm5BshBUtPRZknt9esfmYXBOYcwOTgw=
+github.com/libp2p/go-libp2p-asn-util v0.1.0/go.mod h1:wu+AnM9Ii2KgO5jMmS1rz9dvzTdj8BXqsPR9HR0XB7I=
+github.com/libp2p/go-libp2p-autonat v0.7.0/go.mod h1:uPvPn6J7cN+LCfFwW5tpOYvAz5NvPTc4iBamTV/WDMg=
+github.com/libp2p/go-libp2p-blankhost v0.2.0/go.mod h1:eduNKXGTioTuQAUcZ5epXi9vMl+t4d8ugUBRQ4SqaNQ=
+github.com/libp2p/go-libp2p-blankhost v0.3.0/go.mod h1:urPC+7U01nCGgJ3ZsV8jdwTp6Ji9ID0dMTvq+aJ+nZU=
+github.com/libp2p/go-libp2p-circuit v0.4.0/go.mod h1:t/ktoFIUzM6uLQ+o1G6NuBl2ANhBKN9Bc8jRIk31MoA=
+github.com/libp2p/go-libp2p-connmgr v0.2.4/go.mod h1:YV0b/RIm8NGPnnNWM7hG9Q38OeQiQfKhHCCs1++ufn0=
+github.com/libp2p/go-libp2p-core v0.0.1/go.mod h1:g/VxnTZ/1ygHxH3dKok7Vno1VfpvGcGip57wjTU4fco=
+github.com/libp2p/go-libp2p-core v0.2.0/go.mod h1:X0eyB0Gy93v0DZtSYbEM7RnMChm9Uv3j7yRXjO77xSI=
+github.com/libp2p/go-libp2p-core v0.3.0/go.mod h1:ACp3DmS3/N64c2jDzcV429ukDpicbL6+TrrxANBjPGw=
+github.com/libp2p/go-libp2p-core v0.3.1/go.mod h1:thvWy0hvaSBhnVBaW37BvzgVV68OUhgJJLAa6almrII=
+github.com/libp2p/go-libp2p-core v0.5.0/go.mod h1:49XGI+kc38oGVwqSBhDEwytaAxgZasHhFfQKibzTls0=
+github.com/libp2p/go-libp2p-core v0.5.1/go.mod h1:uN7L2D4EvPCvzSH5SrhR72UWbnSGpt5/a35Sm4upn4Y=
+github.com/libp2p/go-libp2p-core v0.5.4/go.mod h1:uN7L2D4EvPCvzSH5SrhR72UWbnSGpt5/a35Sm4upn4Y=
+github.com/libp2p/go-libp2p-core v0.5.5/go.mod h1:vj3awlOr9+GMZJFH9s4mpt9RHHgGqeHCopzbYKZdRjM=
+github.com/libp2p/go-libp2p-core v0.5.7/go.mod h1:txwbVEhHEXikXn9gfC7/UDDw7rkxuX0bJvM49Ykaswo=
+github.com/libp2p/go-libp2p-core v0.6.0/go.mod h1:txwbVEhHEXikXn9gfC7/UDDw7rkxuX0bJvM49Ykaswo=
+github.com/libp2p/go-libp2p-core v0.7.0/go.mod h1:FfewUH/YpvWbEB+ZY9AQRQ4TAD8sJBt/G1rVvhz5XT8=
+github.com/libp2p/go-libp2p-core v0.8.0/go.mod h1:FfewUH/YpvWbEB+ZY9AQRQ4TAD8sJBt/G1rVvhz5XT8=
+github.com/libp2p/go-libp2p-core v0.8.1/go.mod h1:FfewUH/YpvWbEB+ZY9AQRQ4TAD8sJBt/G1rVvhz5XT8=
+github.com/libp2p/go-libp2p-core v0.8.2/go.mod h1:FfewUH/YpvWbEB+ZY9AQRQ4TAD8sJBt/G1rVvhz5XT8=
+github.com/libp2p/go-libp2p-core v0.8.6/go.mod h1:dgHr0l0hIKfWpGpqAMbpo19pen9wJfdCGv51mTmdpmM=
+github.com/libp2p/go-libp2p-core v0.9.0/go.mod h1:ESsbz31oC3C1AvMJoGx26RTuCkNhmkSRCqZ0kQtJ2/8=
+github.com/libp2p/go-libp2p-core v0.10.0/go.mod h1:ECdxehoYosLYHgDDFa2N4yE8Y7aQRAMf0sX9mf2sbGg=
+github.com/libp2p/go-libp2p-core v0.11.0/go.mod h1:ECdxehoYosLYHgDDFa2N4yE8Y7aQRAMf0sX9mf2sbGg=
+github.com/libp2p/go-libp2p-core v0.12.0/go.mod h1:ECdxehoYosLYHgDDFa2N4yE8Y7aQRAMf0sX9mf2sbGg=
+github.com/libp2p/go-libp2p-core v0.13.0/go.mod h1:ECdxehoYosLYHgDDFa2N4yE8Y7aQRAMf0sX9mf2sbGg=
+github.com/libp2p/go-libp2p-discovery v0.6.0/go.mod h1:/u1voHt0tKIe5oIA1RHBKQLVCWPna2dXmPNHc2zR9S8=
+github.com/libp2p/go-libp2p-loggables v0.1.0/go.mod h1:EyumB2Y6PrYjr55Q3/tiJ/o3xoDasoRYM7nOzEpoa90=
+github.com/libp2p/go-libp2p-mplex v0.2.1/go.mod h1:SC99Rxs8Vuzrf/6WhmH41kNn13TiYdAWNYHrwImKLnE=
+github.com/libp2p/go-libp2p-mplex v0.2.3/go.mod h1:CK3p2+9qH9x+7ER/gWWDYJ3QW5ZxWDkm+dVvjfuG3ek=
+github.com/libp2p/go-libp2p-mplex v0.4.1/go.mod h1:cmy+3GfqfM1PceHTLL7zQzAAYaryDu6iPSC+CIb094g=
+github.com/libp2p/go-libp2p-nat v0.1.0/go.mod h1:DQzAG+QbDYjN1/C3B6vXucLtz3u9rEonLVPtZVzQqks=
+github.com/libp2p/go-libp2p-netutil v0.1.0/go.mod h1:3Qv/aDqtMLTUyQeundkKsA+YCThNdbQD54k3TqjpbFU=
+github.com/libp2p/go-libp2p-noise v0.3.0/go.mod h1:JNjHbociDJKHD64KTkzGnzqJ0FEV5gHJa6AB00kbCNQ=
+github.com/libp2p/go-libp2p-peerstore v0.2.6/go.mod h1:ss/TWTgHZTMpsU/oKVVPQCGuDHItOpf2W8RxAi50P2s=
+github.com/libp2p/go-libp2p-peerstore v0.4.0/go.mod h1:rDJUFyzEWPpXpEwywkcTYYzDHlwza8riYMaUzaN6hX0=
+github.com/libp2p/go-libp2p-peerstore v0.6.0/go.mod h1:DGEmKdXrcYpK9Jha3sS7MhqYdInxJy84bIPtSu65bKc=
+github.com/libp2p/go-libp2p-pnet v0.2.0/go.mod h1:Qqvq6JH/oMZGwqs3N1Fqhv8NVhrdYcO0BW4wssv21LA=
+github.com/libp2p/go-libp2p-pubsub v0.6.1/go.mod h1:nJv87QM2cU0w45KPR1rZicq+FmFIOD16zmT+ep1nOmg=
+github.com/libp2p/go-libp2p-quic-transport v0.13.0/go.mod h1:39/ZWJ1TW/jx1iFkKzzUg00W6tDJh73FC0xYudjr7Hc=
+github.com/libp2p/go-libp2p-quic-transport v0.15.2/go.mod h1:wv4uGwjcqe8Mhjj7N/Ic0aKjA+/10UnMlSzLO0yRpYQ=
+github.com/libp2p/go-libp2p-swarm v0.3.0/go.mod h1:hdv95GWCTmzkgeJpP+GK/9D9puJegb7H57B5hWQR5Kk=
+github.com/libp2p/go-libp2p-swarm v0.8.0/go.mod h1:sOMp6dPuqco0r0GHTzfVheVBh6UEL0L1lXUZ5ot2Fvc=
+github.com/libp2p/go-libp2p-swarm v0.9.0/go.mod h1:2f8d8uxTJmpeqHF/1ujjdXZp+98nNIbujVOMEZxCbZ8=
+github.com/libp2p/go-libp2p-testing v0.0.3/go.mod h1:gvchhf3FQOtBdr+eFUABet5a4MBLK8jM3V4Zghvmi+E=
+github.com/libp2p/go-libp2p-testing v0.1.1/go.mod h1:xaZWMJrPUM5GlDBxCeGUi7kI4eqnjVyavGroI2nxEM0=
+github.com/libp2p/go-libp2p-testing v0.1.2-0.20200422005655-8775583591d8/go.mod h1:Qy8sAncLKpwXtS2dSnDOP8ktexIAHKu+J+pnZOFZLTc=
+github.com/libp2p/go-libp2p-testing v0.3.0/go.mod h1:efZkql4UZ7OVsEfaxNHZPzIehtsBXMrXnCfJIgDti5g=
+github.com/libp2p/go-libp2p-testing v0.4.0/go.mod h1:Q+PFXYoiYFN5CAEG2w3gLPEzotlKsNSbKQ/lImlOWF0=
+github.com/libp2p/go-libp2p-testing v0.4.2/go.mod h1:Q+PFXYoiYFN5CAEG2w3gLPEzotlKsNSbKQ/lImlOWF0=
+github.com/libp2p/go-libp2p-testing v0.5.0/go.mod h1:QBk8fqIL1XNcno/l3/hhaIEn4aLRijpYOR+zVjjlh+A=
+github.com/libp2p/go-libp2p-testing v0.6.0/go.mod h1:QBk8fqIL1XNcno/l3/hhaIEn4aLRijpYOR+zVjjlh+A=
+github.com/libp2p/go-libp2p-tls v0.3.0/go.mod h1:fwF5X6PWGxm6IDRwF3V8AVCCj/hOd5oFlg+wo2FxJDY=
+github.com/libp2p/go-libp2p-tls v0.3.1/go.mod h1:fwF5X6PWGxm6IDRwF3V8AVCCj/hOd5oFlg+wo2FxJDY=
+github.com/libp2p/go-libp2p-transport-upgrader v0.2.0/go.mod h1:mQcrHj4asu6ArfSoMuyojOdjx73Q47cYD7s5+gZOlns=
+github.com/libp2p/go-libp2p-transport-upgrader v0.3.0/go.mod h1:i+SKzbRnvXdVbU3D1dwydnTmKRPXiAR/fyvi1dXuL4o=
+github.com/libp2p/go-libp2p-transport-upgrader v0.4.3/go.mod h1:bpkldbOWXMrXhpZbSV1mQxTrefOg2Fi+k1ClDSA4ppw=
+github.com/libp2p/go-libp2p-transport-upgrader v0.5.0/go.mod h1:Rc+XODlB3yce7dvFV4q/RmyJGsFcCZRkeZMu/Zdg0mo=
+github.com/libp2p/go-libp2p-transport-upgrader v0.6.0/go.mod h1:1e07y1ZSZdHo9HPbuU8IztM1Cj+DR5twgycb4pnRzRo=
+github.com/libp2p/go-libp2p-yamux v0.4.0/go.mod h1:+DWDjtFMzoAwYLVkNZftoucn7PelNoy5nm3tZ3/Zw30=
+github.com/libp2p/go-libp2p-yamux v0.5.0/go.mod h1:AyR8k5EzyM2QN9Bbdg6X1SkVVuqLwTGf0L4DFq9g6po=
+github.com/libp2p/go-libp2p-yamux v0.7.0/go.mod h1:fMyA0CsPfHkIuBU0wjRGrCjTBFiXTXxG0k5M4ETv+08=
+github.com/libp2p/go-maddr-filter v0.0.5/go.mod h1:Jk+36PMfIqCJhAnaASRH83bdAvfDRp/w6ENFaC9bG+M=
+github.com/libp2p/go-maddr-filter v0.1.0/go.mod h1:VzZhTXkMucEGGEOSKddrwGiOv0tUhgnKqNEmIAz/bPU=
+github.com/libp2p/go-mplex v0.1.0/go.mod h1:SXgmdki2kwCUlCCbfGLEgHjC4pFqhTp0ZoV6aiKgxDU=
+github.com/libp2p/go-mplex v0.1.2/go.mod h1:Xgz2RDCi3co0LeZfgjm4OgUF15+sVR8SRcu3SFXI1lk=
+github.com/libp2p/go-mplex v0.3.0/go.mod h1:0Oy/A9PQlwBytDRp4wSkFnzHYDKcpLot35JQ6msjvYQ=
+github.com/libp2p/go-msgio v0.0.4/go.mod h1:63lBBgOTDKQL6EWazRMCwXsEeEeK9O2Cd+0+6OOuipQ=
+github.com/libp2p/go-msgio v0.0.6/go.mod h1:4ecVB6d9f4BDSL5fqvPiC4A3KivjWn+Venn/1ALLMWA=
+github.com/libp2p/go-msgio v0.1.0/go.mod h1:eNlv2vy9V2X/kNldcZ+SShFE++o2Yjxwx6RAYsmgJnE=
+github.com/libp2p/go-nat v0.1.0/go.mod h1:X7teVkwRHNInVNWQiO/tAiAVRwSr5zoRz4YSTC3uRBM=
+github.com/libp2p/go-netroute v0.1.2/go.mod h1:jZLDV+1PE8y5XxBySEBgbuVAXbhtuHSdmLPL2n9MKbk=
+github.com/libp2p/go-netroute v0.1.3/go.mod h1:jZLDV+1PE8y5XxBySEBgbuVAXbhtuHSdmLPL2n9MKbk=
+github.com/libp2p/go-netroute v0.1.5/go.mod h1:V1SR3AaECRkEQCoFFzYwVYWvYIEtlxx89+O3qcpCl4A=
+github.com/libp2p/go-netroute v0.1.6/go.mod h1:AqhkMh0VuWmfgtxKPp3Oc1LdU5QSWS7wl0QLhSZqXxQ=
+github.com/libp2p/go-openssl v0.0.4/go.mod h1:unDrJpgy3oFr+rqXsarWifmJuNnJR4chtO1HmaZjggc=
+github.com/libp2p/go-openssl v0.0.5/go.mod h1:unDrJpgy3oFr+rqXsarWifmJuNnJR4chtO1HmaZjggc=
+github.com/libp2p/go-openssl v0.0.7/go.mod h1:unDrJpgy3oFr+rqXsarWifmJuNnJR4chtO1HmaZjggc=
+github.com/libp2p/go-reuseport v0.0.1/go.mod h1:jn6RmB1ufnQwl0Q1f+YxAj8isJgDCQzaaxIFYDhcYEA=
+github.com/libp2p/go-reuseport v0.1.0/go.mod h1:bQVn9hmfcTaoo0c9v5pBhOarsU1eNOBZdaAd2hzXRKU=
+github.com/libp2p/go-reuseport-transport v0.0.3/go.mod h1:Spv+MPft1exxARzP2Sruj2Wb5JSyHNncjf1Oi2dEbzM=
+github.com/libp2p/go-reuseport-transport v0.1.0/go.mod h1:vev0C0uMkzriDY59yFHD9v+ujJvYmDQVLowvAjEOmfw=
+github.com/libp2p/go-sockaddr v0.0.2/go.mod h1:syPvOmNs24S3dFVGJA1/mrqdeijPxLV2Le3BRLKd68k=
+github.com/libp2p/go-sockaddr v0.1.0/go.mod h1:syPvOmNs24S3dFVGJA1/mrqdeijPxLV2Le3BRLKd68k=
+github.com/libp2p/go-sockaddr v0.1.1/go.mod h1:syPvOmNs24S3dFVGJA1/mrqdeijPxLV2Le3BRLKd68k=
+github.com/libp2p/go-stream-muxer-multistream v0.3.0/go.mod h1:yDh8abSIzmZtqtOt64gFJUXEryejzNb0lisTt+fAMJA=
+github.com/libp2p/go-tcp-transport v0.2.0/go.mod h1:vX2U0CnWimU4h0SGSEsg++AzvBcroCGYw28kh94oLe0=
+github.com/libp2p/go-tcp-transport v0.4.0/go.mod h1:0y52Rwrn4076xdJYu/51/qJIdxz+EWDAOG2S45sV3VI=
+github.com/libp2p/go-ws-transport v0.5.0/go.mod h1:I2juo1dNTbl8BKSBYo98XY85kU2xds1iamArLvl8kNg=
+github.com/libp2p/go-yamux v1.4.0/go.mod h1:fr7aVgmdNGJK+N1g+b6DW6VxzbRCjCOejR/hkmpooHE=
+github.com/libp2p/go-yamux v1.4.1/go.mod h1:fr7aVgmdNGJK+N1g+b6DW6VxzbRCjCOejR/hkmpooHE=
+github.com/libp2p/go-yamux/v2 v2.3.0/go.mod h1:iTU+lOIn/2h0AgKcL49clNTwfEw+WSfDYrXe05EyKIs=
+github.com/libp2p/zeroconf/v2 v2.1.1/go.mod h1:fuJqLnUwZTshS3U/bMRJ3+ow/v9oid1n0DmyYyNO1Xs=
+github.com/lightstep/lightstep-tracer-common/golang/gogo v0.0.0-20190605223551-bc2310a04743/go.mod h1:qklhhLq1aX+mtWk9cPHPzaBjWImj5ULL6C7HFJtXQMM=
+github.com/lightstep/lightstep-tracer-go v0.18.1/go.mod h1:jlF1pusYV4pidLvZ+XD0UBX0ZE6WURAspgAczcDHrL4=
+github.com/logrusorgru/aurora v2.0.3+incompatible h1:tOpm7WcpBTn4fjmVfgpQq0EfczGlG91VSDkswnjF5A8=
+github.com/logrusorgru/aurora v2.0.3+incompatible/go.mod h1:7rIyQOR62GCctdiQpZ/zOJlFyk6y+94wXzv6RNZgaR4=
+github.com/lucas-clemente/quic-go v0.23.0/go.mod h1:paZuzjXCE5mj6sikVLMvqXk8lJV2AsqtJ6bDhjEfxx0=
+github.com/lucas-clemente/quic-go v0.24.0/go.mod h1:paZuzjXCE5mj6sikVLMvqXk8lJV2AsqtJ6bDhjEfxx0=
+github.com/lunixbochs/vtclean v0.0.0-20180621232353-2d01aacdc34a/go.mod h1:pHhQNgMf3btfWnGBVipUOjRYhoOsdGqdm/+2c2E2WMI=
+github.com/lunixbochs/vtclean v1.0.0 h1:xu2sLAri4lGiovBDQKxl5mrXyESr3gUr5m5SM5+LVb8=
+github.com/lunixbochs/vtclean v1.0.0/go.mod h1:pHhQNgMf3btfWnGBVipUOjRYhoOsdGqdm/+2c2E2WMI=
+github.com/lyft/protoc-gen-validate v0.0.13/go.mod h1:XbGvPuh87YZc5TdIa2/I4pLk0QoUACkjt2znoq26NVQ=
+github.com/magiconair/properties v1.7.4-0.20170902060319-8d7837e64d3c/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
+github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
+github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
+github.com/mailru/easyjson v0.0.0-20160728113105-d5b7844b561a/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
+github.com/mailru/easyjson v0.0.0-20180823135443-60711f1a8329/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
+github.com/mailru/easyjson v0.0.0-20190312143242-1de009706dbe/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
+github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
+github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
+github.com/manifoldco/promptui v0.7.0 h1:3l11YT8tm9MnwGFQ4kETwkzpAwY2Jt9lCrumCUW4+z4=
+github.com/manifoldco/promptui v0.7.0/go.mod h1:n4zTdgP0vr0S3w7/O/g98U+e0gwLScEXGwov2nIKuGQ=
+github.com/marten-seemann/qpack v0.2.1/go.mod h1:F7Gl5L1jIgN1D11ucXefiuJS9UMVP2opoCp2jDKb7wc=
+github.com/marten-seemann/qtls-go1-15 v0.1.4/go.mod h1:GyFwywLKkRt+6mfU99csTEY1joMZz5vmB1WNZH3P81I=
+github.com/marten-seemann/qtls-go1-16 v0.1.4/go.mod h1:gNpI2Ol+lRS3WwSOtIUUtRwZEQMXjYK+dQSBFbethAk=
+github.com/marten-seemann/qtls-go1-17 v0.1.0/go.mod h1:fz4HIxByo+LlWcreM4CZOYNuz3taBQ8rN2X6FqvaWo8=
+github.com/marten-seemann/tcp v0.0.0-20210406111302-dfbc87cc63fd/go.mod h1:QuCEs1Nt24+FYQEqAAncTDPJIuGs+LxK1MCiFL25pMU=
+github.com/matryer/moq v0.0.0-20190312154309-6cfb0558e1bd/go.mod h1:9ELz6aaclSIGnZBoaSLZ3NAl1VTufbOrXBPvtcy6WiQ=
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
-github.com/mattn/go-colorable v0.1.0 h1:v2XXALHHh6zHfYTJ+cSkwtyffnaOyR1MXaA91mTrb8o=
-github.com/mattn/go-colorable v0.1.0/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
+github.com/mattn/go-colorable v0.0.10-0.20170816031813-ad5389df28cd/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
+github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ=
+github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
+github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
+github.com/mattn/go-colorable v0.1.7/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
+github.com/mattn/go-colorable v0.1.8 h1:c1ghPdyEDarC70ftn0y+A/Ee++9zz8ljHG1b13eJ0s8=
+github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
github.com/mattn/go-ieproxy v0.0.0-20190610004146-91bb50d98149/go.mod h1:31jz6HNzdxOmlERGGEc4v/dMssOfmp2p5bT/okiKFFc=
github.com/mattn/go-ieproxy v0.0.0-20190702010315-6dee0af9227d h1:oNAwILwmgWKFpuU+dXvI6dl9jG2mAWAZLX3r9s0PPiw=
github.com/mattn/go-ieproxy v0.0.0-20190702010315-6dee0af9227d/go.mod h1:31jz6HNzdxOmlERGGEc4v/dMssOfmp2p5bT/okiKFFc=
+github.com/mattn/go-isatty v0.0.2/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
+github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
-github.com/mattn/go-isatty v0.0.5-0.20180830101745-3fb116b82035 h1:USWjF42jDCSEeikX/G1g40ZWnsPXN5WkZ4jMHZWyBK4=
-github.com/mattn/go-isatty v0.0.5-0.20180830101745-3fb116b82035/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
+github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
+github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
+github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ=
+github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE=
+github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
+github.com/mattn/go-isatty v0.0.13/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
+github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y=
+github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
+github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
github.com/mattn/go-runewidth v0.0.3/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
github.com/mattn/go-runewidth v0.0.9 h1:Lm995f3rfxdpd6TSmuVCHVb/QhupuXlYr8sCI/QdE+0=
github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
github.com/mattn/go-sqlite3 v1.11.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
github.com/mattn/go-tty v0.0.0-20180907095812-13ff1204f104/go.mod h1:XPvLUNfbS4fJH25nqRHfWLMa1ONC8Amw+mIA639KxkE=
+github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU=
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
+github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE=
+github.com/microcosm-cc/bluemonday v1.0.1/go.mod h1:hsXNsILzKxV+sX77C5b8FSuKF00vh2OMYv+xgHpAMF4=
+github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
+github.com/miekg/dns v1.1.41/go.mod h1:p6aan82bvRIyn+zDIv9xYNUpwa73JcSh9BKwknJysuI=
+github.com/miekg/dns v1.1.43/go.mod h1:+evo5L0630/F6ca/Z9+GAqzhjGyn8/c+TBaOyfEl0V4=
+github.com/mikioh/tcp v0.0.0-20190314235350-803a9b46060c/go.mod h1:0SQS9kMwD2VsyFEB++InYyBJroV/FRmBgcydeSUcJms=
+github.com/mikioh/tcpinfo v0.0.0-20190314235526-30a79bb1804b/go.mod h1:lxPUiZwKoFL8DUUmalo2yJJUCxbPKtm8OKfqr2/FTNU=
+github.com/mikioh/tcpopt v0.0.0-20190314235656-172688c1accc/go.mod h1:cGKTAVKx4SxOuR/czcZ/E2RSJ3sfHs8FpHhQ5CWMf9s=
+github.com/minio/blake2b-simd v0.0.0-20160723061019-3f5f724cb5b1/go.mod h1:pD8RvIylQ358TN4wwqatJ8rNavkEINozVn9DtGI3dfQ=
+github.com/minio/highwayhash v1.0.1 h1:dZ6IIu8Z14VlC0VpfKofAhCy74wu/Qb5gcn52yWoz/0=
+github.com/minio/highwayhash v1.0.1/go.mod h1:BQskDq+xkJ12lmlUUi7U0M5Swg3EWR+dLTk+kldvVxY=
+github.com/minio/sha256-simd v0.0.0-20190131020904-2d45a736cd16/go.mod h1:2FMWW+8GMoPweT6+pI63m9YE3Lmw4J71hV56Chs1E/U=
+github.com/minio/sha256-simd v0.0.0-20190328051042-05b4dd3047e5/go.mod h1:2FMWW+8GMoPweT6+pI63m9YE3Lmw4J71hV56Chs1E/U=
+github.com/minio/sha256-simd v0.1.0/go.mod h1:2FMWW+8GMoPweT6+pI63m9YE3Lmw4J71hV56Chs1E/U=
+github.com/minio/sha256-simd v0.1.1-0.20190913151208-6de447530771/go.mod h1:B5e1o+1/KgNmWrSQK08Y6Z1Vb5pwIktudl0J58iy0KM=
+github.com/minio/sha256-simd v0.1.1/go.mod h1:B5e1o+1/KgNmWrSQK08Y6Z1Vb5pwIktudl0J58iy0KM=
+github.com/minio/sha256-simd v1.0.0 h1:v1ta+49hkWZyvaKwrQB8elexRqm6Y0aMLjCNsrYxo6g=
+github.com/minio/sha256-simd v1.0.0/go.mod h1:OuYzVNI5vcoYIAmbIvHPl3N3jUzVedXbKy5RFepssQM=
+github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc=
+github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db h1:62I3jR2EmQ4l5rM/4FEfDWcRD+abF5XlKShorW5LRoQ=
+github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db/go.mod h1:l0dey0ia/Uv7NcFFVbCLtqEBQbrT4OCwCSKTEv6enCw=
+github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
+github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
+github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI=
+github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg=
+github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY=
+github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
+github.com/mitchellh/mapstructure v0.0.0-20170523030023-d0303fe80992/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
+github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
+github.com/mitchellh/mapstructure v1.3.2/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
+github.com/mitchellh/mapstructure v1.4.1 h1:CpVNEelQCZBooIPDn+AR3NpivK/TIKU8bDxdASFVQag=
+github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
+github.com/mitchellh/pointerstructure v1.2.0/go.mod h1:BRAsLI5zgXmw97Lf6s25bs8ohIXc3tViBH44KcwB2g4=
+github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
+github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
+github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
+github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI=
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
+github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 h1:RWengNIwukTxcDr9M+97sNutRR1RKhG96O6jWumTTnw=
+github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8=
+github.com/mr-tron/base58 v1.1.0/go.mod h1:xcD2VGqlgYjBdcBLw+TuYLr8afG+Hj8g2eTVqeSzSU8=
+github.com/mr-tron/base58 v1.1.1/go.mod h1:xcD2VGqlgYjBdcBLw+TuYLr8afG+Hj8g2eTVqeSzSU8=
+github.com/mr-tron/base58 v1.1.2/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc=
+github.com/mr-tron/base58 v1.1.3/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc=
+github.com/mr-tron/base58 v1.2.0/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc=
github.com/mschoch/smat v0.0.0-20160514031455-90eadee771ae/go.mod h1:qAyveg+e4CE+eKJXWVjKXM4ck2QobLqTDytGJbLLhJg=
+github.com/multiformats/go-base32 v0.0.3/go.mod h1:pLiuGC8y0QR3Ue4Zug5UzK9LjgbkL8NSQj0zQ5Nz/AA=
+github.com/multiformats/go-base36 v0.1.0/go.mod h1:kFGE83c6s80PklsHO9sRn2NCoffoRdUUOENyW/Vv6sM=
+github.com/multiformats/go-multiaddr v0.0.1/go.mod h1:xKVEak1K9cS1VdmPZW3LSIb6lgmoS58qz/pzqmAxV44=
+github.com/multiformats/go-multiaddr v0.0.2/go.mod h1:xKVEak1K9cS1VdmPZW3LSIb6lgmoS58qz/pzqmAxV44=
+github.com/multiformats/go-multiaddr v0.0.4/go.mod h1:xKVEak1K9cS1VdmPZW3LSIb6lgmoS58qz/pzqmAxV44=
+github.com/multiformats/go-multiaddr v0.1.1/go.mod h1:aMKBKNEYmzmDmxfX88/vz+J5IU55txyt0p4aiWVohjo=
+github.com/multiformats/go-multiaddr v0.2.0/go.mod h1:0nO36NvPpyV4QzvTLi/lafl2y95ncPj0vFwVF6k6wJ4=
+github.com/multiformats/go-multiaddr v0.2.1/go.mod h1:s/Apk6IyxfvMjDafnhJgJ3/46z7tZ04iMk5wP4QMGGE=
+github.com/multiformats/go-multiaddr v0.2.2/go.mod h1:NtfXiOtHvghW9KojvtySjH5y0u0xW5UouOmQQrn6a3Y=
+github.com/multiformats/go-multiaddr v0.3.0/go.mod h1:dF9kph9wfJ+3VLAaeBqo9Of8x4fJxp6ggJGteB8HQTI=
+github.com/multiformats/go-multiaddr v0.3.1/go.mod h1:uPbspcUPd5AfaP6ql3ujFY+QWzmBD8uLLL4bXW0XfGc=
+github.com/multiformats/go-multiaddr v0.3.3/go.mod h1:lCKNGP1EQ1eZ35Za2wlqnabm9xQkib3fyB+nZXHLag0=
+github.com/multiformats/go-multiaddr v0.4.0/go.mod h1:YcpyLH8ZPudLxQlemYBPhSm0/oCXAT8Z4mzFpyoPyRc=
+github.com/multiformats/go-multiaddr-dns v0.3.1/go.mod h1:G/245BRQ6FJGmryJCrOuTdB37AMA5AMOVuO6NY3JwTk=
+github.com/multiformats/go-multiaddr-fmt v0.1.0/go.mod h1:hGtDIW4PU4BqJ50gW2quDuPVjyWNZxToGUh/HwTZYJo=
+github.com/multiformats/go-multiaddr-net v0.1.2/go.mod h1:QsWt3XK/3hwvNxZJp92iMQKME1qHfpYmyIjFVsSOY6Y=
+github.com/multiformats/go-multiaddr-net v0.1.3/go.mod h1:ilNnaM9HbmVFqsb/qcNysjCu4PVONlrBZpHIrw/qQuA=
+github.com/multiformats/go-multiaddr-net v0.1.4/go.mod h1:ilNnaM9HbmVFqsb/qcNysjCu4PVONlrBZpHIrw/qQuA=
+github.com/multiformats/go-multiaddr-net v0.1.5/go.mod h1:ilNnaM9HbmVFqsb/qcNysjCu4PVONlrBZpHIrw/qQuA=
+github.com/multiformats/go-multiaddr-net v0.2.0/go.mod h1:gGdH3UXny6U3cKKYCvpXI5rnK7YaOIEOPVDI9tsJbEA=
+github.com/multiformats/go-multibase v0.0.1/go.mod h1:bja2MqRZ3ggyXtZSEDKpl0uO/gviWFaSteVbWT51qgs=
+github.com/multiformats/go-multibase v0.0.3/go.mod h1:5+1R4eQrT3PkYZ24C3W2Ue2tPwIdYQD509ZjSb5y9Oc=
+github.com/multiformats/go-multihash v0.0.1/go.mod h1:w/5tugSrLEbWqlcgJabL3oHFKTwfvkofsjW2Qa1ct4U=
+github.com/multiformats/go-multihash v0.0.5/go.mod h1:lt/HCbqlQwlPBz7lv0sQCdtfcMtlJvakRUn/0Ual8po=
+github.com/multiformats/go-multihash v0.0.8/go.mod h1:YSLudS+Pi8NHE7o6tb3D8vrpKa63epEDmG8nTduyAew=
+github.com/multiformats/go-multihash v0.0.10/go.mod h1:YSLudS+Pi8NHE7o6tb3D8vrpKa63epEDmG8nTduyAew=
+github.com/multiformats/go-multihash v0.0.13/go.mod h1:VdAWLKTwram9oKAatUcLxBNUjdtcVwxObEQBtRfuyjc=
+github.com/multiformats/go-multihash v0.0.14/go.mod h1:VdAWLKTwram9oKAatUcLxBNUjdtcVwxObEQBtRfuyjc=
+github.com/multiformats/go-multihash v0.0.15/go.mod h1:D6aZrWNLFTV/ynMpKsNtB40mJzmCl4jb1alC0OvHiHg=
+github.com/multiformats/go-multistream v0.1.1/go.mod h1:KmHZ40hzVxiaiwlj3MEbYgK9JFk2/9UktWZAF54Du38=
+github.com/multiformats/go-multistream v0.2.1/go.mod h1:5GZPQZbkWOLOn3J2y4Y99vVW7vOfsAflxARk3x14o6k=
+github.com/multiformats/go-multistream v0.2.2/go.mod h1:UIcnm7Zuo8HKG+HkWgfQsGL+/MIEhyTqbODbIUwSXKs=
+github.com/multiformats/go-varint v0.0.1/go.mod h1:3Ls8CIEsrijN6+B7PbrXRPxHRPuXSrVKRY101jdMZYE=
+github.com/multiformats/go-varint v0.0.2/go.mod h1:3Ls8CIEsrijN6+B7PbrXRPxHRPuXSrVKRY101jdMZYE=
+github.com/multiformats/go-varint v0.0.5/go.mod h1:3Ls8CIEsrijN6+B7PbrXRPxHRPuXSrVKRY101jdMZYE=
+github.com/multiformats/go-varint v0.0.6/go.mod h1:3Ls8CIEsrijN6+B7PbrXRPxHRPuXSrVKRY101jdMZYE=
+github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
+github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
+github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw=
github.com/naoina/go-stringutil v0.1.0 h1:rCUeRUHjBjGTSHl0VC00jUPLz8/F9dDzYI70Hzifhks=
github.com/naoina/go-stringutil v0.1.0/go.mod h1:XJ2SJL9jCtBh+P9q5btrd/Ylo8XwT/h1USek5+NqSA0=
github.com/naoina/toml v0.1.2-0.20170918210437-9fafd6967416 h1:shk/vn9oCoOTmwcouEdwIeOtOGA/ELRUw/GwvxwfT+0=
github.com/naoina/toml v0.1.2-0.20170918210437-9fafd6967416/go.mod h1:NBIhNtsFMo3G2szEBne+bO4gS192HuIYRqfvOWb4i1E=
-github.com/nxadm/tail v1.4.4 h1:DQuhQpB1tVlglWS2hLQ5OV6B5r8aGxSrPc5Qo6uTN78=
+github.com/nats-io/jwt v0.3.0/go.mod h1:fRYCDE99xlTsqUzISS1Bi75UBJ6ljOJQOAAu5VglpSg=
+github.com/nats-io/jwt v0.3.2/go.mod h1:/euKqTS1ZD+zzjYrY7pseZrTtWQSjujC7xjPc8wL6eU=
+github.com/nats-io/nats-server/v2 v2.1.2/go.mod h1:Afk+wRZqkMQs/p45uXdrVLuab3gwv3Z8C4HTBu8GD/k=
+github.com/nats-io/nats.go v1.9.1/go.mod h1:ZjDU1L/7fJ09jvUSRVBR2e7+RnLiiIQyqyzEE/Zbp4w=
+github.com/nats-io/nkeys v0.1.0/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w=
+github.com/nats-io/nkeys v0.1.3/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w=
+github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c=
+github.com/neelance/astrewrite v0.0.0-20160511093645-99348263ae86/go.mod h1:kHJEU3ofeGjhHklVoIGuVj85JJwZ6kWPaJwCIxgnFmo=
+github.com/neelance/sourcemap v0.0.0-20151028013722-8c68805598ab/go.mod h1:Qr6/a/Q4r9LP1IltGz7tA7iOK1WonHEYhu1HRBA7ZiM=
+github.com/nishanths/predeclared v0.0.0-20200524104333-86fad755b4d3/go.mod h1:nt3d53pc1VYcphSCIaYAJtnPYnr3Zyn8fMq2wvPGPso=
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
+github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE=
+github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU=
+github.com/oklog/oklog v0.3.2/go.mod h1:FCV+B7mhrz4o+ueLpx+KqkyXRGMWOYEvfiXtdGtbWGs=
+github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA=
github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
+github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo=
github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec=
github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY=
+github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
+github.com/onsi/ginkgo v1.10.1/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
+github.com/onsi/ginkgo v1.11.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
+github.com/onsi/ginkgo v1.12.0/go.mod h1:oUhWkIvk5aDxtKvDDuw8gItl8pKl42LzjC9KZE0HfGg=
github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=
-github.com/onsi/ginkgo v1.14.0 h1:2mOpI4JVVPBN+WQRa0WKH2eXR+Ey+uK4n7Zj0aYpIQA=
github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY=
+github.com/onsi/ginkgo v1.16.2/go.mod h1:CObGmKUOKaSC0RjmoAK7tKyn4Azo5P2IWuoMnvwxz1E=
+github.com/onsi/ginkgo v1.16.4 h1:29JGrr5oVBm5ulCWet69zQkzWipVXIol6ygQUe/EzNc=
+github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0=
+github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA=
+github.com/onsi/gomega v1.4.1/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA=
github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
+github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
-github.com/onsi/gomega v1.10.1 h1:o0+MgICZLuZ7xjH7Vx6zS/zcu93/BEp1VwkIW1mEXCE=
+github.com/onsi/gomega v1.9.0/go.mod h1:Ho0h+IUsWyvy1OpqCwxlQ/21gkhVunqlU8fDGcoTdcA=
github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
+github.com/onsi/gomega v1.13.0 h1:7lLHu94wT9Ij0o6EWWclhu0aOh32VxhkwEJvzuWPeak=
+github.com/onsi/gomega v1.13.0/go.mod h1:lRk9szgn8TxENtWd0Tp4c3wjlRfMTMH27I+3Je41yGY=
+github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk=
+github.com/openconfig/gnmi v0.0.0-20190823184014-89b2bf29312c/go.mod h1:t+O9It+LKzfOAhKTT5O0ehDix+MTqbtT0T9t+7zzOvc=
+github.com/openconfig/reference v0.0.0-20190727015836-8dfd928c9696/go.mod h1:ym2A+zigScwkSEb/cVQB0/ZMpU3rqiH6X7WRRsxgOGw=
+github.com/opentracing-contrib/go-observer v0.0.0-20170622124052-a52f23424492/go.mod h1:Ngi6UdF0k5OKD5t5wlmGhe/EDKPoUM3BXZSSfIuJbis=
+github.com/opentracing/basictracer-go v1.0.0/go.mod h1:QfBfYuafItcjQuMwinw9GhYKwFXS9KnPs5lxoYwgW74=
github.com/opentracing/opentracing-go v1.0.2/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
github.com/opentracing/opentracing-go v1.0.3-0.20180606204148-bd9c31933947/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
-github.com/opentracing/opentracing-go v1.1.0 h1:pWlfV3Bxv7k65HYwkikxat0+s3pV4bsqf19k25Ur8rU=
github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
+github.com/opentracing/opentracing-go v1.2.0 h1:uEJPy/1a5RIPAJ0Ov+OIO8OxWu77jEv+1B0VhjKrZUs=
+github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYrxe9dPLANfrWvHYVTgc=
+github.com/openzipkin-contrib/zipkin-go-opentracing v0.4.5/go.mod h1:/wsWhb9smxSfWAKL3wpBW7V8scJMt8N8gnaMCS9E/cA=
+github.com/openzipkin/zipkin-go v0.1.1/go.mod h1:NtoC/o8u3JlF1lSlyPNswIbeQH9bJTmOf0Erfk+hxe8=
+github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw=
+github.com/openzipkin/zipkin-go v0.2.1/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4=
+github.com/openzipkin/zipkin-go v0.2.2/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4=
github.com/oxtoacart/bpool v0.0.0-20190530202638-03653db5a59c h1:rp5dCmg/yLR3mgFuSOe4oEnDDmGLROTvMragMUXpTQw=
github.com/oxtoacart/bpool v0.0.0-20190530202638-03653db5a59c/go.mod h1:X07ZCGwUbLaax7L0S3Tw4hpejzu63ZrrQiUe6W0hcy0=
+github.com/pact-foundation/pact-go v1.0.4/go.mod h1:uExwJY4kCzNPcHRj+hCR/HBbOOIwwtUjcrb0b5/5kLM=
github.com/panjf2000/ants/v2 v2.4.5 h1:kcGvjXB7ea0MrzzszpnlVFthhYKoFxLi75nRbsq01HY=
github.com/panjf2000/ants/v2 v2.4.5/go.mod h1:f6F0NZVFsGCp5A7QW/Zj/m92atWwOkY0OIhFxRNFr4A=
+github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
+github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc=
+github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ=
github.com/paulbellamy/ratecounter v0.2.0/go.mod h1:Hfx1hDpSGoqxkVVpBi/IlYD7kChlfo5C6hzIHwPqfFE=
+github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k=
+github.com/pborman/uuid v1.2.1/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k=
+github.com/pelletier/go-toml v1.0.1-0.20170904195809-1d6b12b7cb29/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
+github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
+github.com/performancecopilot/speed v3.0.0+incompatible/go.mod h1:/CLtqpZ5gBg1M9iaPbIdPPGyKcA8hKdoy6hAWba7Yac=
+github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU=
github.com/peterh/liner v1.0.1-0.20180619022028-8c1271fcf47f/go.mod h1:xIteQHvHuaLYG9IFj6mSxM0fCKrs34IrEQUhOYuGPHc=
-github.com/peterh/liner v1.1.1-0.20190123174540-a2c9a5303de7 h1:oYW+YCJ1pachXTQmzR3rNLYGGz4g/UgFcjb28p/viDM=
github.com/peterh/liner v1.1.1-0.20190123174540-a2c9a5303de7/go.mod h1:CRroGNssyjTd/qIG2FyxByd2S8JEAZXBl4qUrZf8GS0=
+github.com/peterh/liner v1.2.0 h1:w/UPXyl5GfahFxcTOz2j9wCIHNI+pUPr2laqpojKNCg=
+github.com/peterh/liner v1.2.0/go.mod h1:CRroGNssyjTd/qIG2FyxByd2S8JEAZXBl4qUrZf8GS0=
github.com/philhofer/fwd v1.0.0/go.mod h1:gk3iGcWd9+svBvR0sR+KPcfE+RNWozjowpeBVG3ZVNU=
+github.com/pierrec/lz4 v1.0.2-0.20190131084431-473cd7ce01a1/go.mod h1:3/3N9NVKO0jef7pBehbT1qWhCMrIgbYNnFAZCqQ5LRc=
github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY=
+github.com/pierrec/lz4 v2.4.1+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY=
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
+github.com/pkg/profile v1.2.1/go.mod h1:hJw3o1OdXxsrSjjVksARp5W95eeEaEfptyVZyv6JUPA=
+github.com/pkg/profile v1.5.0/go.mod h1:qBsxPvzyUincmltOk6iyRVxHYg4adc0OFOv72ZdLa18=
github.com/pkg/term v0.0.0-20180730021639-bffc007b7fd5/go.mod h1:eCbImbZ95eXtAUIbLAuAVnBnwf83mjf6QIVH8SHYwqQ=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=
+github.com/prometheus/client_golang v0.8.0/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
+github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829/go.mod h1:p2iRAGwDERtqlqzRXnrOVns+ignqQo//hLXqYxZYVNs=
+github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso=
github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
+github.com/prometheus/client_golang v1.3.0/go.mod h1:hJaj2vgQTGQmVCsAACORcieXFeDPbaTKGT+JTgUa3og=
+github.com/prometheus/client_golang v1.4.1/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3OK1iX/F2sw+iXX5zU=
+github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M=
+github.com/prometheus/client_golang v1.9.0/go.mod h1:FqZLKOZnGdFAhOK4nqGHa7D66IdsO+O441Eve7ptJDU=
+github.com/prometheus/client_golang v1.10.0/go.mod h1:WJM3cc3yu7XKBKa/I8WeZm+V3eltZnBwfENSU7mdogU=
+github.com/prometheus/client_golang v1.11.0 h1:HNkLOAEQMIDv/K+04rukrLx6ch7msSRwf3/SASFAGtQ=
+github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0=
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
+github.com/prometheus/client_model v0.0.0-20190115171406-56726106282f/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
+github.com/prometheus/client_model v0.1.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
+github.com/prometheus/client_model v0.2.0 h1:uq5h0d+GuxiXLJLNABMgp2qUWDPiLvgCzz2dUR+/W/M=
+github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
+github.com/prometheus/common v0.0.0-20180801064454-c7de2306084e/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
+github.com/prometheus/common v0.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
+github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
github.com/prometheus/common v0.6.0/go.mod h1:eBmuwkDJBwy6iBfxCBob6t6dR6ENT/y+J+Zk0j9GMYc=
+github.com/prometheus/common v0.7.0/go.mod h1:DjGbpBbp5NYNiECxcL/VnbXCCaQpKd3tt26CguLLsqA=
+github.com/prometheus/common v0.9.1/go.mod h1:yhUN8i9wzaXS3w1O07YhxHEBxD+W35wd8bs7vj7HSQ4=
+github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo=
+github.com/prometheus/common v0.15.0/go.mod h1:U+gB1OBLb1lF3O42bTCL+FK18tX9Oar16Clt/msog/s=
+github.com/prometheus/common v0.18.0/go.mod h1:U+gB1OBLb1lF3O42bTCL+FK18tX9Oar16Clt/msog/s=
+github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc=
+github.com/prometheus/common v0.30.0 h1:JEkYlQnpzrzQFxi6gnukFPdQ+ac82oRhzMcIduJu/Ug=
+github.com/prometheus/common v0.30.0/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls=
+github.com/prometheus/procfs v0.0.0-20180725123919-05ee40e3a273/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
+github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
+github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
-github.com/prometheus/tsdb v0.7.1 h1:YZcsG11NqnK4czYLrWd9mpEuAJIHVQLwdrleYfszMAA=
+github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A=
+github.com/prometheus/procfs v0.0.10/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A=
+github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
+github.com/prometheus/procfs v0.2.0/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
+github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=
+github.com/prometheus/procfs v0.7.3 h1:4jVXhlkAyzOScmCkXBTOLRLTz8EeU+eyjrwB/EPq0VU=
+github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=
+github.com/prometheus/prom2json v1.3.0/go.mod h1:rMN7m0ApCowcoDlypBHlkNbp5eJQf/+1isKykIP5ZnM=
github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
+github.com/prometheus/tsdb v0.10.0 h1:If5rVCMTp6W2SiRAQFlbpJNgVlgMEd+U2GZckwK38ic=
+github.com/prometheus/tsdb v0.10.0/go.mod h1:oi49uRhEe9dPUTlS3JRZOwJuVi6tmh10QSgwXEyGCt4=
+github.com/prysmaticlabs/eth2-types v0.0.0-20210303084904-c9735a06829d h1:1dN7YAqMN3oAJ0LceWcyv/U4jHLh+5urnSnr4br6zg4=
+github.com/prysmaticlabs/eth2-types v0.0.0-20210303084904-c9735a06829d/go.mod h1:kOmQ/zdobQf7HUohDTifDNFEZfNaSCIY5fkONPL+dWU=
+github.com/prysmaticlabs/fastssz v0.0.0-20220110145812-fafb696cae88 h1:MRQwO/qZtHJFQA7M3uQBadrscFC5org7fWm8CCBRzMM=
+github.com/prysmaticlabs/fastssz v0.0.0-20220110145812-fafb696cae88/go.mod h1:ASoCYXOqVPSr7KRfiBbbAOxyOwRBfl9gpwTvEKqbnkc=
+github.com/prysmaticlabs/go-bitfield v0.0.0-20210108222456-8e92c3709aa0/go.mod h1:hCwmef+4qXWjv0jLDbQdWnL0Ol7cS7/lCSS26WR+u6s=
+github.com/prysmaticlabs/go-bitfield v0.0.0-20210809151128-385d8c5e3fb7 h1:0tVE4tdWQK9ZpYygoV7+vS6QkDvQVySboMVEIxBJmXw=
+github.com/prysmaticlabs/go-bitfield v0.0.0-20210809151128-385d8c5e3fb7/go.mod h1:wmuf/mdK4VMD+jA9ThwcUKjg3a2XWM9cVfFYjDyY4j4=
+github.com/prysmaticlabs/grpc-gateway/v2 v2.3.1-0.20210702154020-550e1cd83ec1 h1:xcu59yYL6AWWTl6jtejBfE0y8uF35fArCBeZjRlvJss=
+github.com/prysmaticlabs/grpc-gateway/v2 v2.3.1-0.20210702154020-550e1cd83ec1/go.mod h1:IOyTYjcIO0rkmnGBfJTL0NJ11exy/Tc2QEuv7hCXp24=
+github.com/prysmaticlabs/prombbolt v0.0.0-20210126082820-9b7adba6db7c h1:9PHRCuO/VN0s9k+RmLykho7AjDxblNYI5bYKed16NPU=
+github.com/prysmaticlabs/prombbolt v0.0.0-20210126082820-9b7adba6db7c/go.mod h1:ZRws458tYHS/Zs936OQ6oCrL+Ict5O4Xpwve1UQ6C9M=
+github.com/prysmaticlabs/protoc-gen-go-cast v0.0.0-20211014160335-757fae4f38c6 h1:+jhXLjEYVW4qU2z5SOxlxN+Hv/A9FDf0HpfDurfMEz0=
+github.com/prysmaticlabs/protoc-gen-go-cast v0.0.0-20211014160335-757fae4f38c6/go.mod h1:ZVEbRdnMkGhp/pu35zq4SXxtvUwWK0J1MATtekZpH2Y=
+github.com/prysmaticlabs/prysm v0.0.0-20220124113610-e26cde5e091b h1:XULhE6PdzCYSe5OEVFhuixNqL3mYVOq/3M+SUGnKr1Y=
+github.com/prysmaticlabs/prysm v0.0.0-20220124113610-e26cde5e091b/go.mod h1:bFzDfaj4xtisRey9RPkMJOhOJVwmtH3FChV7NPKV1Nk=
+github.com/r3labs/sse v0.0.0-20210224172625-26fe804710bc/go.mod h1:S8xSOnV3CgpNrWd0GQ/OoQfMtlg2uPRSuTzcSGrzwK8=
+github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
+github.com/rcrowley/go-metrics v0.0.0-20190826022208-cac0b30c2563/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
github.com/retailnext/hllpp v1.0.1-0.20180308014038-101a6d2f8b52/go.mod h1:RDpi1RftBQPUCDRw6SmxeaREsAaRKnOclghuzp/WRzc=
github.com/rjeczalik/notify v0.9.1 h1:CLCKso/QK1snAlnhNR/CNvNiFU2saUtjV0bx3EwNeCE=
github.com/rjeczalik/notify v0.9.1/go.mod h1:rKwnCoCGeuQnwBtTSPL9Dad03Vh2n40ePRrjvIXnJho=
+github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
+github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/rs/cors v1.7.0 h1:+88SsELBHx5r+hZ8TCkggzSstaWNbDvThkVK8H6f9ik=
github.com/rs/cors v1.7.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU=
+github.com/russross/blackfriday v1.5.2 h1:HyvC0ARfnZBqnXwABFeSZHpKvJHJJfPz81GNueLj0oo=
+github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
+github.com/russross/blackfriday/v2 v2.0.1 h1:lPqVAte+HuHNfhJ/0LC98ESWRz8afy9tM/0RK8m9o+Q=
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
+github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
+github.com/samuel/go-zookeeper v0.0.0-20190923202752-2cc03de413da/go.mod h1:gi+0XIa01GRL2eRQVjQkKGqKF3SF9vZR/HnPullcV2E=
+github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
+github.com/schollz/progressbar/v3 v3.3.4 h1:nMinx+JaEm/zJz4cEyClQeAw5rsYSB5th3xv+5lV6Vg=
+github.com/schollz/progressbar/v3 v3.3.4/go.mod h1:Rp5lZwpgtYmlvmGo1FyDwXMqagyRBQYSDwzlP9QDu84=
+github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
github.com/segmentio/kafka-go v0.1.0/go.mod h1:X6itGqS9L4jDletMsxZ7Dz+JFWxM6JHfPOCvTvk+EJo=
github.com/segmentio/kafka-go v0.2.0/go.mod h1:X6itGqS9L4jDletMsxZ7Dz+JFWxM6JHfPOCvTvk+EJo=
github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible h1:Bn1aCHHRnjv4Bl16T8rcaFjYSrGrIZvpiGO6P3Q4GpU=
github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA=
+github.com/shurcooL/component v0.0.0-20170202220835-f88ec8f54cc4/go.mod h1:XhFIlyj5a1fBNx5aJTbKoIq0mNaPvOagO+HjB3EtxrY=
+github.com/shurcooL/events v0.0.0-20181021180414-410e4ca65f48/go.mod h1:5u70Mqkb5O5cxEA8nxTsgrgLehJeAw6Oc4Ab1c/P1HM=
+github.com/shurcooL/github_flavored_markdown v0.0.0-20181002035957-2122de532470/go.mod h1:2dOwnU2uBioM+SGy2aZoq1f/Sd1l9OkAeAUvjSyvgU0=
+github.com/shurcooL/go v0.0.0-20180423040247-9e1955d9fb6e/go.mod h1:TDJrrUr11Vxrven61rcy3hJMUqaf/CLWYhHNPmT14Lk=
+github.com/shurcooL/go-goon v0.0.0-20170922171312-37c2f522c041/go.mod h1:N5mDOmsrJOB+vfqUK+7DmDyjhSLIIBnXo9lvZJj3MWQ=
+github.com/shurcooL/gofontwoff v0.0.0-20180329035133-29b52fc0a18d/go.mod h1:05UtEgK5zq39gLST6uB0cf3NEHjETfB4Fgr3Gx5R9Vw=
+github.com/shurcooL/gopherjslib v0.0.0-20160914041154-feb6d3990c2c/go.mod h1:8d3azKNyqcHP1GaQE/c6dDgjkgSx2BZ4IoEi4F1reUI=
+github.com/shurcooL/highlight_diff v0.0.0-20170515013008-09bb4053de1b/go.mod h1:ZpfEhSmds4ytuByIcDnOLkTHGUI6KNqRNPDLHDk+mUU=
+github.com/shurcooL/highlight_go v0.0.0-20181028180052-98c3abbbae20/go.mod h1:UDKB5a1T23gOMUJrI+uSuH0VRDStOiUVSjBTRDVBVag=
+github.com/shurcooL/home v0.0.0-20181020052607-80b7ffcb30f9/go.mod h1:+rgNQw2P9ARFAs37qieuu7ohDNQ3gds9msbT2yn85sg=
+github.com/shurcooL/htmlg v0.0.0-20170918183704-d01228ac9e50/go.mod h1:zPn1wHpTIePGnXSHpsVPWEktKXHr6+SS6x/IKRb7cpw=
+github.com/shurcooL/httperror v0.0.0-20170206035902-86b7830d14cc/go.mod h1:aYMfkZ6DWSJPJ6c4Wwz3QtW22G7mf/PEgaB9k/ik5+Y=
+github.com/shurcooL/httpfs v0.0.0-20171119174359-809beceb2371/go.mod h1:ZY1cvUeJuFPAdZ/B6v7RHavJWZn2YPVFQ1OSXhCGOkg=
+github.com/shurcooL/httpgzip v0.0.0-20180522190206-b1c53ac65af9/go.mod h1:919LwcH0M7/W4fcZ0/jy0qGght1GIhqyS/EgWGH2j5Q=
+github.com/shurcooL/issues v0.0.0-20181008053335-6292fdc1e191/go.mod h1:e2qWDig5bLteJ4fwvDAc2NHzqFEthkqn7aOZAOpj+PQ=
+github.com/shurcooL/issuesapp v0.0.0-20180602232740-048589ce2241/go.mod h1:NPpHK2TI7iSaM0buivtFUc9offApnI0Alt/K8hcHy0I=
+github.com/shurcooL/notifications v0.0.0-20181007000457-627ab5aea122/go.mod h1:b5uSkrEVM1jQUspwbixRBhaIjIzL2xazXp6kntxYle0=
+github.com/shurcooL/octicon v0.0.0-20181028054416-fa4f57f9efb2/go.mod h1:eWdoE5JD4R5UVWDucdOPg1g2fqQRq78IQa9zlOV1vpQ=
+github.com/shurcooL/reactions v0.0.0-20181006231557-f2e0b4ca5b82/go.mod h1:TCR1lToEk4d2s07G3XGfz2QrgHXg4RJBvjrOozvoWfk=
+github.com/shurcooL/sanitized_anchor_name v0.0.0-20170918181015-86672fcb3f95/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
+github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo=
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
+github.com/shurcooL/users v0.0.0-20180125191416-49c67e49c537/go.mod h1:QJTqeLYEDaXHZDBsXlPCDqdhQuJkuw4NOtaxYe3xii4=
+github.com/shurcooL/webdavfs v0.0.0-20170829043945-18c3829fa133/go.mod h1:hKmq5kWdCj2z2KEozexVbfEZIWiTjhE0+UjmZgPqehw=
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
+github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
+github.com/sirupsen/logrus v1.6.0 h1:UBcNElsrwanuuMsnGSlYmtmgbb23qDR5dG+6X6Oo89I=
+github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88=
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
+github.com/smola/gocompat v0.2.0/go.mod h1:1B0MlxbmoZNo3h8guHp8HztB3BSYR5itql9qtVc0ypY=
+github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
+github.com/sony/gobreaker v0.4.1/go.mod h1:ZKptC7FHNvhBz7dN2LGjPVBz2sZJmc0/PkyDJOjmxWY=
+github.com/sourcegraph/annotate v0.0.0-20160123013949-f4cad6c6324d/go.mod h1:UdhH50NIW0fCiwBSr0co2m7BnFLdv4fQTgdqdJTHFeE=
+github.com/sourcegraph/syntaxhighlight v0.0.0-20170531221838-bd320f5d308e/go.mod h1:HuIsMU8RRBOtsCgI77wP899iHVBQpCmg4ErYMZB+2IA=
+github.com/spacemonkeygo/openssl v0.0.0-20181017203307-c2dcc5cca94a/go.mod h1:7AyxJNCJ7SBZ1MfVQCWD6Uqo2oubI2Eq2y2eqf+A5r0=
+github.com/spacemonkeygo/spacelog v0.0.0-20180420211403-2296661a0572/go.mod h1:w0SWMsp6j9O/dk4/ZpIhL+3CkG8ofA2vuv7k+ltqUMc=
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
+github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI=
+github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
+github.com/spf13/afero v0.0.0-20170901052352-ee1bd8ee15a1/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
+github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
+github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk=
+github.com/spf13/cast v1.1.0/go.mod h1:r2rcYCSwa1IExKTDiTfzaxqT2FNHs8hODu4LnUfgKEg=
github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ=
+github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU=
+github.com/spf13/cobra v1.0.1-0.20201006035406-b97b5ead31f7/go.mod h1:yk5b0mALVusDL5fMM6Rd1wgnoO5jUPhwsQ6LQAJTidQ=
+github.com/spf13/jwalterweatherman v0.0.0-20170901151539-12bd96e66386/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
+github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
+github.com/spf13/pflag v0.0.0-20170130214245-9ff6c6923cff/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
+github.com/spf13/pflag v1.0.1-0.20170901120850-7aff26db30c1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
+github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
-github.com/status-im/keycard-go v0.0.0-20190316090335-8537d3370df4 h1:Gb2Tyox57NRNuZ2d3rmvB3pcmbu7O1RS3m8WRx7ilrg=
+github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
+github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
+github.com/spf13/viper v1.0.0/go.mod h1:A8kyI5cUJhb8N+3pkfONlcEcZbueH6nhAm0Fq7SrnBM=
+github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s=
+github.com/spf13/viper v1.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg=
+github.com/src-d/envconfig v1.0.0/go.mod h1:Q9YQZ7BKITldTBnoxsE5gOeB5y66RyPXeue/R4aaNBc=
github.com/status-im/keycard-go v0.0.0-20190316090335-8537d3370df4/go.mod h1:RZLeN1LMWmRsyYjvAu+I6Dm9QmlDaIIt+Y+4Kd7Tp+Q=
+github.com/status-im/keycard-go v0.0.0-20200402102358-957c09536969 h1:Oo2KZNP70KE0+IUJSidPj/BFS/RXNHmKIJOdckzml2E=
+github.com/status-im/keycard-go v0.0.0-20200402102358-957c09536969/go.mod h1:RZLeN1LMWmRsyYjvAu+I6Dm9QmlDaIIt+Y+4Kd7Tp+Q=
+github.com/streadway/amqp v0.0.0-20190404075320-75d898a42a94/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw=
+github.com/streadway/amqp v0.0.0-20190827072141-edfb9018d271/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw=
+github.com/streadway/handy v0.0.0-20190108123426-d5acb3125c2a/go.mod h1:qNTQ5P5JnDBl6z3cMAg/SywNDC5ABu5ApDIw6lUbRmI=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.2.0/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
+github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
+github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
+github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
+github.com/supranational/blst v0.3.5 h1:/pey7U712GgJBSD1XTiJ5iBqjYIH3QNdrjRoGXlJJ60=
+github.com/supranational/blst v0.3.5/go.mod h1:jZJtfjgudtNl4en1tzwPIV3KjUnQUvG3/j+w+fVonLw=
+github.com/syndtr/goleveldb v1.0.0/go.mod h1:ZVVdQEZoIme9iO1Ch2Jdy24qqXrMMOU6lpPAyBWyWuQ=
github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 h1:epCh84lMvA70Z7CTTCmYQn2CKbY8j86K7/FAIr141uY=
github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7/go.mod h1:q4W45IWZaF22tdD+VEXcAWRA037jwmWEB5VWYORlTpc=
+github.com/tarm/serial v0.0.0-20180830185346-98f6abe2eb07/go.mod h1:kDXzergiv9cbyO7IOYJZWg1U88JhDg3PB6klq9Hg2pA=
+github.com/templexxx/cpufeat v0.0.0-20180724012125-cef66df7f161/go.mod h1:wM7WEvslTq+iOEAMDLSzhVuOt5BRZ05WirO+b09GHQU=
+github.com/templexxx/xor v0.0.0-20191217153810-f85b25db303b/go.mod h1:5XA7W9S6mni3h5uvOC75dA3m9CCCaS83lltmc0ukdi4=
+github.com/thomaso-mirodin/intmath v0.0.0-20160323211736-5dc6d854e46e h1:cR8/SYRgyQCt5cNCMniB/ZScMkhI9nk8U5C7SbISXjo=
+github.com/thomaso-mirodin/intmath v0.0.0-20160323211736-5dc6d854e46e/go.mod h1:Tu4lItkATkonrYuvtVjG0/rhy15qrNGNTjPdaphtZ/8=
+github.com/tidwall/gjson v1.10.2/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
+github.com/tidwall/gjson v1.14.0 h1:6aeJ0bzojgWLa82gDQHcx3S0Lr/O51I9bJ5nv6JFx5w=
+github.com/tidwall/gjson v1.14.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
+github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA=
+github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM=
+github.com/tidwall/pretty v1.2.0 h1:RWIZEg2iJ8/g6fDDYzMpobmaoGh5OLl4AXtGUGPcqCs=
+github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
+github.com/tidwall/tinylru v1.1.0 h1:XY6IUfzVTU9rpwdhKUF6nQdChgCdGjkMfLzbWyiau6I=
+github.com/tidwall/tinylru v1.1.0/go.mod h1:3+bX+TJ2baOLMWTnlyNWHh4QMnFyARg2TLTQ6OFbzw8=
+github.com/tidwall/wal v1.1.7 h1:emc1TRjIVsdKKSnpwGBAcsAGg0767SvUk8+ygx7Bb+4=
+github.com/tidwall/wal v1.1.7/go.mod h1:r6lR1j27W9EPalgHiB7zLJDYu3mzW5BQP5KrzBpYY/E=
github.com/tinylib/msgp v1.0.2/go.mod h1:+d+yLhGm8mzTaHzB+wgMYrodPfmZrzkirds8fDWklFE=
+github.com/tjfoc/gmsm v1.3.0/go.mod h1:HaUcFuY0auTiaHB9MHFGCPx5IaLhTUd2atbCFBQXn9w=
github.com/tklauser/go-sysconf v0.3.5 h1:uu3Xl4nkLzQfXNsWn15rPc/HQCJKObbt1dKJeWp3vU4=
github.com/tklauser/go-sysconf v0.3.5/go.mod h1:MkWzOF4RMCshBAMXuhXJs64Rte09mITnppBXY/rYEFI=
github.com/tklauser/numcpus v0.2.2 h1:oyhllyrScuYI6g+h/zUvNXNp1wy7x8qQy3t/piefldA=
github.com/tklauser/numcpus v0.2.2/go.mod h1:x3qojaO3uyYt0i56EW/VUYs7uBvdl2fkfZFu0T9wgjM=
-github.com/tyler-smith/go-bip39 v1.0.1-0.20181017060643-dbb3b84ba2ef h1:wHSqTBrZW24CsNJDfeh9Ex6Pm0Rcpc7qrgKBiL44vF4=
+github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
+github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
+github.com/trailofbits/go-mutexasserts v0.0.0-20200708152505-19999e7d3cef/go.mod h1:+SV/613m53DNAmlXPTWGZhIyt4E/qDvn9g/lOPRiy0A=
+github.com/twitchtv/twirp v7.1.0+incompatible/go.mod h1:RRJoFSAmTEh2weEqWtpPE3vFK5YBhA6bqp2l1kfCC5A=
github.com/tyler-smith/go-bip39 v1.0.1-0.20181017060643-dbb3b84ba2ef/go.mod h1:sJ5fKU0s6JVwZjjcUEX2zFOnvq0ASQ2K9Zr6cf67kNs=
+github.com/tyler-smith/go-bip39 v1.1.0 h1:5eUemwrMargf3BSLRRCalXT93Ns6pQJIjYQN2nyfOP8=
+github.com/tyler-smith/go-bip39 v1.1.0/go.mod h1:gUYDtqQw1JS3ZJ8UWVcGTGqqr6YIN3CWg+kkNaLt55U=
+github.com/uber/jaeger-client-go v2.25.0+incompatible h1:IxcNZ7WRY1Y3G4poYlx24szfsn/3LvK9QHCq9oQw8+U=
+github.com/uber/jaeger-client-go v2.25.0+incompatible/go.mod h1:WVhlPFC8FDjOFMMWRy2pZqQJSXxYSwNYOkTr/Z6d3Kk=
+github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0=
+github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA=
+github.com/urfave/cli v1.22.1 h1:+mkCCcOFKPnCmVYVcURKps1Xe+3zP90gSYGNfRkjoIY=
+github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
+github.com/urfave/cli/v2 v2.3.0 h1:qph92Y649prgesehzOrQjdWyxFOp/QVM+6imKHad91M=
github.com/urfave/cli/v2 v2.3.0/go.mod h1:LJmUH05zAU44vOAcrfzZQKsZbVcdbOG8rtL3/XcUArI=
+github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
+github.com/valyala/fasttemplate v1.0.1/go.mod h1:UQGH1tvbgY+Nz5t2n7tXsz52dQxojPUpymEIMZ47gx8=
+github.com/valyala/fasttemplate v1.2.1/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ=
+github.com/viant/assertly v0.4.8/go.mod h1:aGifi++jvCrUaklKEKT0BU95igDNaqkvz+49uaYMPRU=
+github.com/viant/toolbox v0.24.0/go.mod h1:OxMCG57V0PXuIP2HNQrtJf2CjqdmbrOx5EkMILuUhzM=
+github.com/wealdtech/go-bytesutil v1.1.1 h1:ocEg3Ke2GkZ4vQw5lp46rmO+pfqCCTgq35gqOy8JKVc=
+github.com/wealdtech/go-bytesutil v1.1.1/go.mod h1:jENeMqeTEU8FNZyDFRVc7KqBdRKSnJ9CCh26TcuNb9s=
+github.com/wealdtech/go-eth2-types/v2 v2.5.2 h1:tiA6T88M6XQIbrV5Zz53l1G5HtRERcxQfmET225V4Ls=
+github.com/wealdtech/go-eth2-types/v2 v2.5.2/go.mod h1:8lkNUbgklSQ4LZ2oMSuxSdR7WwJW3L9ge1dcoCVyzws=
+github.com/wealdtech/go-eth2-util v1.6.3 h1:2INPeOR35x5LdFFpSzyw954WzTD+DFyHe3yKlJnG5As=
+github.com/wealdtech/go-eth2-util v1.6.3/go.mod h1:0hFMj/qtio288oZFHmAbCnPQ9OB3c4WFzs5NVPKTY4k=
+github.com/wealdtech/go-eth2-wallet-encryptor-keystorev4 v1.1.3 h1:SxrDVSr+oXuT1x8kZt4uWqNCvv5xXEGV9zd7cuSrZS8=
+github.com/wealdtech/go-eth2-wallet-encryptor-keystorev4 v1.1.3/go.mod h1:qiIimacW5NhVRy8o+YxWo9YrecXqDAKKbL0+sOa0SJ4=
+github.com/wealdtech/go-eth2-wallet-types/v2 v2.8.2 h1:264/meVYWt1wFw6Mtn+xwkZkXjID42gNra4rycoiDXI=
+github.com/wealdtech/go-eth2-wallet-types/v2 v2.8.2/go.mod h1:k6kmiKWSWBTd4OxFifTEkPaBLhZspnO2KFD5XJY9nqg=
+github.com/wercker/journalhook v0.0.0-20180428041537-5d0a5ae867b3/go.mod h1:XCsSkdKK4gwBMNrOCZWww0pX6AOt+2gYc5Z6jBRrNVg=
+github.com/whyrusleeping/go-keyspace v0.0.0-20160322163242-5b898ac5add1/go.mod h1:8UvriyWtv5Q5EOgjHaSseUEdkQfvwFv1I/In/O2M9gc=
+github.com/whyrusleeping/go-logging v0.0.0-20170515211332-0457bb6b88fc/go.mod h1:bopw91TMyo8J3tvftk8xmU2kPmlrt4nScJQZU2hE5EM=
+github.com/whyrusleeping/mdns v0.0.0-20190826153040-b9b60ed33aa9/go.mod h1:j4l84WPFclQPj320J9gp0XwNKBb3U0zt5CBqjPp22G4=
+github.com/whyrusleeping/multiaddr-filter v0.0.0-20160516205228-e903e4adabd7/go.mod h1:X2c0RVCI1eSUFI8eLcY3c0423ykwiUdxLJtkDvruhjI=
+github.com/whyrusleeping/timecache v0.0.0-20160911033111-cfcb2f1abfee/go.mod h1:m2aV4LZI4Aez7dP5PMyVKEHhUyEJ/RjmPEDOpDvudHg=
+github.com/willf/bitset v1.1.3 h1:ekJIKh6+YbUIVt9DfNbkR5d6aFcFTLDRyJNAACURBg8=
github.com/willf/bitset v1.1.3/go.mod h1:RjeCKbqT1RxIR/KWY6phxZiaY1IyutSBfGjNPySAYV4=
+github.com/x-cray/logrus-prefixed-formatter v0.5.2/go.mod h1:2duySbKsL6M18s5GU7VPsoEPHyzalCE06qoARUCeBBE=
+github.com/xdg/scram v0.0.0-20180814205039-7eeb5667e42c/go.mod h1:lB8K/P019DLNhemzwFU4jHLhdvlE6uDZjXFejJXr49I=
+github.com/xdg/stringprep v1.0.0/go.mod h1:Jhud4/sHMO4oL310DaZAKk9ZaJ08SJfe+sJh0HrGL1Y=
+github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
github.com/xlab/treeprint v0.0.0-20180616005107-d6fb6747feb6/go.mod h1:ce1O1j6UtZfjr22oyGxGLbauSBp2YVXpARAosm7dHBg=
+github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
+github.com/xtaci/kcp-go v5.4.20+incompatible/go.mod h1:bN6vIwHQbfHaHtFpEssmWsN45a+AZwO7eyRCmEIbtvE=
+github.com/xtaci/lossyconn v0.0.0-20190602105132-8df528c0c9ae/go.mod h1:gXtu8J62kEgmN++bm9BVICuT/e8yiLI2KFobd/TRFsE=
+github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
+github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
+github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
+github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
+github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
+go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
+go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
+go.etcd.io/bbolt v1.3.5 h1:XAzx9gjCb0Rxj7EoqcClPD1d5ZBxZJk0jbuoPHenBt0=
+go.etcd.io/bbolt v1.3.5/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ=
+go.etcd.io/etcd v0.0.0-20191023171146-3cf2f69b5738/go.mod h1:dnLIgRNXwCJa5e+c6mIZCrds/GIG4ncV9HhK5PX7jPg=
+go.opencensus.io v0.18.0/go.mod h1:vKdFvxhtzZ9onBp9VKHK8z/sRpBMnKAsufL7wlDrCOA=
+go.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk=
+go.opencensus.io v0.20.2/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk=
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
+go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
+go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
+go.opencensus.io v0.22.6/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E=
+go.opencensus.io v0.23.0 h1:gqCw0LfLxScz8irSi8exQc7fyQ0fKQU/qnC/X8+V/1M=
+go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E=
+go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI=
go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
+go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
+go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
+go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
+go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
+go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
+go.uber.org/automaxprocs v1.3.0/go.mod h1:9CWT6lKIep8U41DDaPiH6eFscnTyjfTANNQNx6LrIcA=
+go.uber.org/goleak v1.0.0/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A=
+go.uber.org/goleak v1.1.10/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A=
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
+go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4=
+go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU=
+go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU=
+go.uber.org/multierr v1.7.0/go.mod h1:7EAYxJLBy9rStEaz58O2t4Uvip6FSURkq8/ppBp95ak=
+go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA=
go.uber.org/zap v1.9.1/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
+go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
+go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM=
+go.uber.org/zap v1.14.1/go.mod h1:Mb2vm2krFEG5DV0W9qcHBYFtp/Wku1cvYaqPsS/WYfc=
+go.uber.org/zap v1.15.0/go.mod h1:Mb2vm2krFEG5DV0W9qcHBYFtp/Wku1cvYaqPsS/WYfc=
+go.uber.org/zap v1.16.0/go.mod h1:MA8QOfq0BHJwdXa996Y4dYkAqRKB8/1K1QMMZVaNZjQ=
+go.uber.org/zap v1.19.0/go.mod h1:xg/QME4nWcxGxrpdeYfq7UvYrLh66cuVKdrbD1XF/NI=
+go4.org v0.0.0-20180809161055-417644f6feb5/go.mod h1:MkTOUMDaeVYJUOUsaDXIhWPZYa1yOyC1qaOBpL57BhE=
+golang.org/x/build v0.0.0-20190111050920-041ab4dc3f9d/go.mod h1:OWs+y06UdEOHN4y+MfF/py+xQ/tYqIWW03b70/CG9Rw=
golang.org/x/crypto v0.0.0-20170930174604-9419663f5a44/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
+golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
+golang.org/x/crypto v0.0.0-20181030102418-4d3f4d9ffa16/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
+golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
+golang.org/x/crypto v0.0.0-20190211182817-74369b46fc67/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
+golang.org/x/crypto v0.0.0-20190225124518-7f87c0fbb88b/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
+golang.org/x/crypto v0.0.0-20190313024323-a1f597ede03a/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
+golang.org/x/crypto v0.0.0-20190426145343-a29dc8fdc734/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
+golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
+golang.org/x/crypto v0.0.0-20190618222545-ea8f1a30c443/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
+golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190909091759-094676da4a83/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
+golang.org/x/crypto v0.0.0-20191219195013-becbf705a915/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
+golang.org/x/crypto v0.0.0-20200115085410-6d4e4cb37c7d/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
+golang.org/x/crypto v0.0.0-20200204104054-c9f3fb736b72/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
+golang.org/x/crypto v0.0.0-20200220183623-bac4c82f6975/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
+golang.org/x/crypto v0.0.0-20200221231518-2aa609cf4a9d/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
+golang.org/x/crypto v0.0.0-20200510223506-06a226fb4e37/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
+golang.org/x/crypto v0.0.0-20200602180216-279210d13fed/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
-golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2 h1:It14KIkyBFYkHkwZ7k45minvA9aorojkyjGk9KJ5B/w=
+golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
+golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
+golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
+golang.org/x/crypto v0.0.0-20210506145944-38f3c27a63bf/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8=
+golang.org/x/crypto v0.0.0-20210813211128-0a44fdfbc16e/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
+golang.org/x/crypto v0.0.0-20211117183948-ae814b36b871 h1:/pEO3GD/ABYAjuakUS6xSEmmlyVS4kxBNkeA9tLJiTI=
+golang.org/x/crypto v0.0.0-20211117183948-ae814b36b871/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
@@ -404,9 +1333,15 @@ golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm0
golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY=
golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
+golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
+golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM=
+golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU=
+golang.org/x/exp v0.0.0-20200331195152-e8c3332aa8e5/go.mod h1:4M0jN8W1tt0AVLNr8HDosyJCDCDuyL9N9+3m7wDWgKw=
+golang.org/x/exp v0.0.0-20200513190911-00229845015e/go.mod h1:4M0jN8W1tt0AVLNr8HDosyJCDCDuyL9N9+3m7wDWgKw=
golang.org/x/image v0.0.0-20180708004352-c73c2afc3b81/go.mod h1:ux5Hcp/YLpHSI86hEcLt0YII63i6oz57MZXIpbrjZUs=
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
+golang.org/x/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
@@ -415,40 +1350,96 @@ golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHl
golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs=
+golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
+golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
+golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
+golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
+golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
+golang.org/x/mod v0.5.1 h1:OJxoQ/rynoF0dcCdI7cLPktw/hR2cueqYfjm43oqK38=
+golang.org/x/mod v0.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro=
+golang.org/x/net v0.0.0-20170114055629-f2499483f923/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20180719180050-a680a1efc54d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181011144130-49bb7cea24b1/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20181029044818-c44066c5c816/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20181106065722-10aee1819953/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20190125091013-d26f9f9a57f3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20190227160552-c95aed5357e7/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+golang.org/x/net v0.0.0-20190313220215-9f648a60d977/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20191004110552-13f9640d40b9/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20191116160921-f9c825593386/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
+golang.org/x/net v0.0.0-20200421231249-e086a090c8fd/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
+golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
+golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
+golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
+golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
+golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
+golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20200813134508-3edf25e44fcc/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
+golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
+golang.org/x/net v0.0.0-20200904194848-62affa334b73/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
+golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
+golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210220033124-5f55cee0dc0d/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
-golang.org/x/net v0.0.0-20210226172049-e18ecbb05110 h1:qWPm9rbaAMKs8Bq/9LRpbMqxWRVUAQwMI9fVrssnTfw=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
+golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc=
+golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
+golang.org/x/net v0.0.0-20210423184538-5f58ad60dda6/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk=
+golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk=
+golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
+golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
+golang.org/x/net v0.0.0-20210813160813-60bc85c4be6d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
+golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
+golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2 h1:CIJ76btIcR3eFI5EgSo6k1qKw9KJexJuRLI9G7Hp5wE=
+golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
+golang.org/x/oauth2 v0.0.0-20170912212905-13449ad91cb2/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
+golang.org/x/oauth2 v0.0.0-20181017192945-9dcd33a902f4/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
+golang.org/x/oauth2 v0.0.0-20181203162652-d668ce993890/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
+golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
+golang.org/x/oauth2 v0.0.0-20210427180440-81ed05c6b58c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
+golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c h1:pkQiBZBvdos9qq4wBAHqlzuZHEXo07pqV06ef90u1WI=
+golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
+golang.org/x/perf v0.0.0-20180704124530-6e6d33e29852/go.mod h1:JLpeXjPJfIyPr5TlbXLkXWLhP8nz10XfvxElABhCtcw=
+golang.org/x/sync v0.0.0-20170517211232-f52d1811a629/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@@ -456,51 +1447,133 @@ golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJ
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sys v0.0.0-20170830134202-bb24a47a89ea/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20181029174526-d69651ed3497/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20190130150945-aca44879d564/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20190209173611-3b5209105503/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20190219092855-153ac476189d/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20190228124157-a34e9553db1e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190316082340-a2f829d7f35f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190405154228-4b34438f7a67/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20191022100944-742c48ecaeb7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20191206220618-eeba5f6aabab/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20191220142924-d4481acd189f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200107162124-548cf772de50/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200219091948-cb0a6d8edb6c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200420163511-1957bb5e6d1f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200602225109-6fdc65e7d980/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200814200057-3d37ad5750ed/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200826173525-f9321e4c35a6/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20201214210602-f9fddec55a1e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210303074136-134d130e1a04/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210309074719-68d13333faf2/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210316164454-77fc1eacc6aa/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20210420205809-ac73e9fd8988 h1:EjgCl+fVlIaPJSori0ikSz3uV0DOHKWOJFpv1sAAhBM=
+golang.org/x/sys v0.0.0-20210317225723-c4fcb01b228e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210324051608-47abb6519492/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210420205809-ac73e9fd8988/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210426080607-c94f62235c83/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20210511113859-b0526f3d8744/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20210816183151-1e6c022a8912/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20211124211545-fe61309f8881 h1:TyHqChC80pFkXWraUUf6RuB5IqFdQieMLwwCJokV2pc=
+golang.org/x/sys v0.0.0-20211124211545-fe61309f8881/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
+golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1 h1:v+OssWQX+hTHEmOBgwxdZxK4zHq3yOs8F9J7mk0PY8E=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
+golang.org/x/text v0.0.0-20160726164857-2910a502d2bf/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
+golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
-golang.org/x/text v0.3.4 h1:0YWbFKbhXG/wIiuHDSKpS0Iy7FSA+u45VtBMfQcFTTc=
golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
+golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
+golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
+golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
+golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
+golang.org/x/time v0.0.0-20170424234030-8be79e1e0910/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
+golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
-golang.org/x/time v0.0.0-20201208040808-7e3f01d25324 h1:Hir2P/De0WpUhtrKGGjvSb2YxUgyZ7EFOSLIcSSpiwE=
+golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20201208040808-7e3f01d25324/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
+golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba h1:O8mE0/t419eoIwhTFpKVkHiTs/Igowgfkj25AcZrtiE=
+golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
+golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20180525024113-a5b4c53f6e8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/tools v0.0.0-20181011042414-1f849cf54d09/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/tools v0.0.0-20181030000716-a0a13e073c7b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/tools v0.0.0-20181130052023-1c3d964395ce/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190206041539-40960b6deb8e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
@@ -517,14 +1590,48 @@ golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgw
golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.0.0-20191108193012-7d206e10da11/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
+golang.org/x/tools v0.0.0-20200103221440-774c71fcf114/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200108203644-89082a384178/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
+golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
+golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
+golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
+golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
+golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
+golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
+golang.org/x/tools v0.0.0-20200221224223-e1da425f72fd/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
+golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
+golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
+golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=
+golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=
+golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8=
+golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
+golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
+golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
+golang.org/x/tools v0.0.0-20200522201501-cb1345f3a375/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
+golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
+golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
+golang.org/x/tools v0.0.0-20200717024301-6ddee64345a6/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
+golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
+golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
+golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
+golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE=
+golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
+golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
+golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
+golang.org/x/tools v0.1.8 h1:P1HhGGuLW4aAclzjtmJdf0mJOjVUZUzOTqkAkWL+l6w=
+golang.org/x/tools v0.1.8/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
@@ -536,6 +1643,11 @@ gonum.org/v1/gonum v0.6.0/go.mod h1:9mxDZsDKxgMAuccQkewq682L+0eCu4dCN2yonUJTCLU=
gonum.org/v1/netlib v0.0.0-20181029234149-ec6d1f5cefe6/go.mod h1:wa6Ws7BG/ESfp6dHfk7C6KdzKA7wR7u/rKwOGE66zvw=
gonum.org/v1/netlib v0.0.0-20190313105609-8cb42192e0e0/go.mod h1:wa6Ws7BG/ESfp6dHfk7C6KdzKA7wR7u/rKwOGE66zvw=
gonum.org/v1/plot v0.0.0-20190515093506-e2840ee46a6b/go.mod h1:Wt8AAjI+ypCyYX3nZBvf6cAIx93T+c/OS2HFAYskSZc=
+google.golang.org/api v0.0.0-20170921000349-586095a6e407/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0=
+google.golang.org/api v0.0.0-20180910000450-7ca32eb868bf/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0=
+google.golang.org/api v0.0.0-20181030000543-1d582fd0359e/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0=
+google.golang.org/api v0.1.0/go.mod h1:UGEZY7KEX120AnNLIHFMKIo4obdJhkp2tPbaPlQx13Y=
+google.golang.org/api v0.3.1/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMtkk=
google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=
google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
@@ -543,16 +1655,39 @@ google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEn
google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
+google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
+google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
+google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
+google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
+google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
+google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE=
+google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE=
+google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM=
+google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc=
+google.golang.org/api v0.34.0 h1:k40adF3uR+6x/+hO5Dh4ZFUqFp67vxvbpafFiJxl10A=
+google.golang.org/api v0.34.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
+google.golang.org/appengine v1.2.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
+google.golang.org/appengine v1.3.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
+google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
+google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c=
+google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
+google.golang.org/genproto v0.0.0-20170918111702-1e559d0a00ee/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
+google.golang.org/genproto v0.0.0-20180831171423-11092d34479b/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
+google.golang.org/genproto v0.0.0-20181029155118-b69ba1387ce2/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
+google.golang.org/genproto v0.0.0-20181202183823-bd91e49a0898/go.mod h1:7Ep/1NZk928CDR8SjdVbjWNpdIf6nzjE3BTgJDr2Atg=
+google.golang.org/genproto v0.0.0-20190306203927-b5d61aea6440/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
+google.golang.org/genproto v0.0.0-20190522204451-c2c4e71fbf69/go.mod h1:z3L6/3dTEVtUr6QSP8miRzeRqwQOioJ9I66odjN4I7s=
+google.golang.org/genproto v0.0.0-20190530194941-fb225487d101/go.mod h1:z3L6/3dTEVtUr6QSP8miRzeRqwQOioJ9I66odjN4I7s=
google.golang.org/genproto v0.0.0-20190716160619-c506a9f90610/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
@@ -562,49 +1697,166 @@ google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvx
google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
google.golang.org/genproto v0.0.0-20200108215221-bd8f9a0ef82f/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
+google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
+google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
+google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA=
+google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
+google.golang.org/genproto v0.0.0-20200218151345-dad8c97a84f5/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
+google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
+google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
+google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
+google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
+google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
+google.golang.org/genproto v0.0.0-20200423170343-7949de9c1215/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
+google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
+google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
+google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
+google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U=
+google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
+google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA=
+google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
+google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
+google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
+google.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
+google.golang.org/genproto v0.0.0-20210207032614-bba0dbe2a9ea/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
+google.golang.org/genproto v0.0.0-20210426193834-eac7f76ac494 h1:KMgpo2lWy1vfrYjtxPAzR0aNWeAR1UdQykt6sj/hpBY=
+google.golang.org/genproto v0.0.0-20210426193834-eac7f76ac494/go.mod h1:P3QM42oQyzQSnHPnZ/vqoCdDmzH28fzWByN9asMeM8A=
+google.golang.org/grpc v1.2.1-0.20170921194603-d4b75ebd4f9f/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=
+google.golang.org/grpc v1.14.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=
+google.golang.org/grpc v1.16.0/go.mod h1:0JHn/cJsOMiMfNA9+DeHDlAU7KAAB5GDlYFpa9MZMio=
+google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
+google.golang.org/grpc v1.20.0/go.mod h1:chYK+tFQF0nDUGJgXMSgLCQk3phJEuONr2DCgLDdAQM=
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
+google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
+google.golang.org/grpc v1.22.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
+google.golang.org/grpc v1.23.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
+google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
+google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
+google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
+google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60=
+google.golang.org/grpc v1.28.1/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60=
+google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk=
+google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
+google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
+google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
+google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0=
+google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc=
+google.golang.org/grpc v1.35.0-dev.0.20201218190559-666aea1fb34c/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
+google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
+google.golang.org/grpc v1.36.1/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
+google.golang.org/grpc v1.37.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM=
+google.golang.org/grpc v1.40.0 h1:AGJ0Ih4mHjSeibYkFGh1dD9KJ/eOtZ93I6hoHhukQ5Q=
+google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34=
+google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.0.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw=
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
-google.golang.org/protobuf v1.23.0 h1:4MY060fB1DLGMB/7MBTLnwQUY6+F09GEiz6SsrNqyzM=
+google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
+google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
+google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4=
+google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
+google.golang.org/protobuf v1.25.1-0.20200805231151-a709e31e5d12/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
+google.golang.org/protobuf v1.25.1-0.20201208041424-160c7477e0e8/go.mod h1:hFxJC2f0epmp1elRCiEGJTKAWbwxZ2nvqZdHl3FQXCY=
+google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
+google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
+google.golang.org/protobuf v1.27.1 h1:SnqbnDw1V7RiZcXPx5MEeqPv2s79L9i7BJUlG/+RurQ=
+google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
+gopkg.in/bsm/ratelimit.v1 v1.0.0-20160220154919-db14e161995a/go.mod h1:KF9sEfUPAXdG8Oev9e99iLGnl2uJMjc5B+4y3O7x610=
+gopkg.in/cenkalti/backoff.v1 v1.1.0/go.mod h1:J6Vskwqd+OMVJl8C33mmtxTBs2gyzfv7UDAkHu8BrjI=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
-gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
+gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
+gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw=
+gopkg.in/d4l3k/messagediff.v1 v1.2.1 h1:70AthpjunwzUiarMHyED52mj9UwtAnE89l1Gmrt3EU0=
+gopkg.in/d4l3k/messagediff.v1 v1.2.1/go.mod h1:EUzikiKadqXWcD1AzJLagx0j/BeeWGtn++04Xniyg44=
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
+gopkg.in/gcfg.v1 v1.2.3/go.mod h1:yesOnuUOFQAhST5vPY4nbZsb/huCgGGXlipJsBn0b3o=
+gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc=
+gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
+gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
+gopkg.in/jcmturner/aescts.v1 v1.0.1/go.mod h1:nsR8qBOg+OucoIW+WMhB3GspUQXq9XorLnQb9XtvcOo=
+gopkg.in/jcmturner/dnsutils.v1 v1.0.1/go.mod h1:m3v+5svpVOhtFAP/wSz+yzh4Mc0Fg7eRhxkJMWSIz9Q=
+gopkg.in/jcmturner/goidentity.v3 v3.0.0/go.mod h1:oG2kH0IvSYNIu80dVAyu/yoefjq1mNfM5bm88whjWx4=
+gopkg.in/jcmturner/gokrb5.v7 v7.5.0/go.mod h1:l8VISx+WGYp+Fp7KRbsiUuXTTOnxIc3Tuvyavf11/WM=
+gopkg.in/jcmturner/rpc.v1 v1.1.0/go.mod h1:YIdkC4XfD6GXbzje11McwsDuOlZQSb9W4vfLvuNnlv8=
gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce h1:+JknDZhAj8YMt7GC73Ei8pv4MzjDUNPHgQWJdtMAaDU=
gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce/go.mod h1:5AcXVHNjg+BDxry382+8OKon8SEWiKktQR07RKPsv1c=
gopkg.in/olebedev/go-duktape.v3 v3.0.0-20200619000410-60c24ae608a6 h1:a6cXbcDDUkSBlpnkWV1bJ+vv3mOgQEltEJ2rPxroVu0=
gopkg.in/olebedev/go-duktape.v3 v3.0.0-20200619000410-60c24ae608a6/go.mod h1:uAJfkITjFhyEEuUfm7bsmCZRbW5WRq8s9EY8HZ6hCns=
+gopkg.in/redis.v4 v4.2.4/go.mod h1:8KREHdypkCEojGKQcjMqAODMICIVwZAONWq8RowTITA=
+gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
+gopkg.in/src-d/go-cli.v0 v0.0.0-20181105080154-d492247bbc0d/go.mod h1:z+K8VcOYVYcSwSjGebuDL6176A1XskgbtNl64NSg+n8=
+gopkg.in/src-d/go-log.v1 v1.0.1/go.mod h1:GN34hKP0g305ysm2/hctJ0Y8nWP3zxXXJ8GFabTyABE=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
gopkg.in/urfave/cli.v1 v1.20.0 h1:NdAVW6RYxDif9DhDHaAortIu956m2c0v+09AZBPTbE0=
gopkg.in/urfave/cli.v1 v1.20.0/go.mod h1:vuBzUtMdQeixQj8LVd+/98pzhxNGQoyuPBlsXHOQNO0=
+gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI=
+gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.7/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
-gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU=
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
-gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
+gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
+gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
+gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo=
+gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo=
gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw=
+grpc.go4.org v0.0.0-20170609214715-11d0a25b4919/go.mod h1:77eQGdRu53HpSqPFJFmuJdjuHRquDANNeA4x7B8WQ9o=
+honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
+honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
+honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
honnef.co/go/tools v0.1.3/go.mod h1:NgwopIslSNH47DimFoV78dnkksY2EFtX0ajyb3K/las=
+k8s.io/api v0.18.3 h1:2AJaUQdgUZLoDZHrun21PW2Nx9+ll6cUzvn3IKhSIn0=
+k8s.io/api v0.18.3/go.mod h1:UOaMwERbqJMfeeeHc8XJKawj4P9TgDRnViIqqBeH2QA=
+k8s.io/apimachinery v0.18.3 h1:pOGcbVAhxADgUYnjS08EFXs9QMl8qaH5U4fr5LGUrSk=
+k8s.io/apimachinery v0.18.3/go.mod h1:OaXp26zu/5J7p0f92ASynJa1pZo06YlV9fG7BoWbCko=
+k8s.io/client-go v0.18.3 h1:QaJzz92tsN67oorwzmoB0a9r9ZVHuD5ryjbCKP0U22k=
+k8s.io/client-go v0.18.3/go.mod h1:4a/dpQEvzAhT1BbuWW09qvIaGw6Gbu1gZYiQZIi1DMw=
+k8s.io/gengo v0.0.0-20190128074634-0689ccc1d7d6/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0=
+k8s.io/klog v0.0.0-20181102134211-b9b56d5dfc92/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk=
+k8s.io/klog v0.3.0/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk=
+k8s.io/klog v1.0.0 h1:Pt+yjF5aB1xDSVbau4VsWe+dQNzA0qv1LlXdC2dF6Q8=
+k8s.io/klog v1.0.0/go.mod h1:4Bi6QPql/J/LkTDqv7R/cd3hPo4k2DG6Ptcz060Ez5I=
+k8s.io/klog/v2 v2.0.0/go.mod h1:PBfzABfn139FHAV07az/IF9Wp1bkk3vpT2XSJ76fSDE=
+k8s.io/klog/v2 v2.3.0 h1:WmkrnW7fdrm0/DMClc+HIxtftvxVIPAhlVwMQo5yLco=
+k8s.io/klog/v2 v2.3.0/go.mod h1:Od+F08eJP+W3HUb4pSrPpgp9DGU4GzlpG/TmITuYh/Y=
+k8s.io/kube-openapi v0.0.0-20200410145947-61e04a5be9a6/go.mod h1:GRQhZsXIAJ1xR0C9bd8UpWHZ5plfAS9fzPjJuQ6JL3E=
+k8s.io/utils v0.0.0-20200324210504-a9aa75ae1b89/go.mod h1:sZAwmy6armz5eXlNoLmJcl4F1QuKu7sr+mFQ0byX7Ew=
+k8s.io/utils v0.0.0-20200520001619-278ece378a50 h1:ZtTUW5+ZWaoqjR3zOpRa7oFJ5d4aA22l4me/xArfOIc=
+k8s.io/utils v0.0.0-20200520001619-278ece378a50/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA=
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=
+rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
+rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
+sigs.k8s.io/structured-merge-diff/v3 v3.0.0-20200116222232-67a7b8c61874/go.mod h1:PlARxl6Hbt/+BC80dRLi1qAmnMqwqDg62YvvVkZjemw=
+sigs.k8s.io/structured-merge-diff/v3 v3.0.0 h1:dOmIZBMfhcHS09XZkMyUgkq5trg3/jRyJYFZUiaOp8E=
+sigs.k8s.io/structured-merge-diff/v3 v3.0.0/go.mod h1:PlARxl6Hbt/+BC80dRLi1qAmnMqwqDg62YvvVkZjemw=
+sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o=
+sigs.k8s.io/yaml v1.2.0 h1:kr/MCeFWJWTwyaHoR9c8EjH9OumOmoF9YGiZd7lFm/Q=
+sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc=
+sourcegraph.com/sourcegraph/appdash v0.0.0-20190731080439-ebfcffb1b5c0/go.mod h1:hI742Nqp5OhwiqlzhgfbWU4mW4yO10fP+LoT9WOswdU=
+sourcegraph.com/sourcegraph/go-diff v0.5.0/go.mod h1:kuch7UrkMzY0X+p9CRK03kfuPQ2zzQcaEFbx8wA8rck=
+sourcegraph.com/sqs/pbtypes v0.0.0-20180604144634-d3ebe8f20ae4/go.mod h1:ketZ/q3QxT9HOBeFhu6RdvsftgpsbFHBF5Cas6cDKZ0=
diff --git a/internal/ethapi/api.go b/internal/ethapi/api.go
index 371e7bbf3..0970e90b1 100644
--- a/internal/ethapi/api.go
+++ b/internal/ethapi/api.go
@@ -1286,6 +1286,54 @@ func (s *PublicBlockChainAPI) GetDiffAccountsWithScope(ctx context.Context, bloc
return result, err
}
+// GetJustifiedHeader returns the highest justified block before the input block.
+func (s *PublicBlockChainAPI) GetJustifiedHeader(ctx context.Context, blockNrOrHash *rpc.BlockNumberOrHash) (*types.Header, error) {
+ bNrOrHash := rpc.BlockNumberOrHashWithNumber(rpc.LatestBlockNumber)
+ if blockNrOrHash != nil {
+ bNrOrHash = *blockNrOrHash
+ }
+
+ // Retrieve the block to act as the gas ceiling
+ header, err := s.b.HeaderByNumberOrHash(ctx, bNrOrHash)
+ if err != nil {
+ return nil, err
+ }
+ if header == nil {
+ return nil, errors.New("block header not found")
+ }
+
+ if posa, ok := s.b.Engine().(consensus.PoSA); ok {
+ return posa.GetJustifiedHeader(s.b.Chain(), header), nil
+ }
+
+ // Not support.
+ return nil, nil
+}
+
+// GetFinalizedHeader returns the highest finalized block header before the input block.
+func (s *PublicBlockChainAPI) GetFinalizedHeader(ctx context.Context, blockNrOrHash *rpc.BlockNumberOrHash) (*types.Header, error) {
+ bNrOrHash := rpc.BlockNumberOrHashWithNumber(rpc.LatestBlockNumber)
+ if blockNrOrHash != nil {
+ bNrOrHash = *blockNrOrHash
+ }
+
+ // Retrieve the block to act as the gas ceiling
+ header, err := s.b.HeaderByNumberOrHash(ctx, bNrOrHash)
+ if err != nil {
+ return nil, err
+ }
+ if header == nil {
+ return nil, errors.New("block header not found")
+ }
+
+ if posa, ok := s.b.Engine().(consensus.PoSA); ok {
+ return posa.GetFinalizedHeader(s.b.Chain(), header, types.NaturallyFinalizedDist), nil
+ }
+
+ // Not support.
+ return nil, nil
+}
+
// ExecutionResult groups all structured logs emitted by the EVM
// while replaying a transaction in debug mode as well as transaction
// execution status, the amount of gas used and the return value
@@ -1773,10 +1821,16 @@ func (s *PublicTransactionPoolAPI) GetTransactionReceiptsByBlockNumber(ctx conte
if err != nil {
return nil, err
}
+ if receipts == nil {
+ return nil, fmt.Errorf("block %d receipts not found", blockNumber)
+ }
block, err := s.b.BlockByHash(ctx, blockHash)
if err != nil {
return nil, err
}
+ if block == nil {
+ return nil, fmt.Errorf("block %d not found", blockNumber)
+ }
txs := block.Transactions()
if len(txs) != len(receipts) {
return nil, fmt.Errorf("txs length doesn't equal to receipts' length")
diff --git a/internal/ethapi/backend.go b/internal/ethapi/backend.go
index ca5a55d5e..d60de8bee 100644
--- a/internal/ethapi/backend.go
+++ b/internal/ethapi/backend.go
@@ -86,6 +86,8 @@ type Backend interface {
SubscribeLogsEvent(ch chan<- []*types.Log) event.Subscription
SubscribePendingLogsEvent(ch chan<- []*types.Log) event.Subscription
SubscribeRemovedLogsEvent(ch chan<- core.RemovedLogsEvent) event.Subscription
+ SubscribeNewVoteEvent(chan<- core.NewVoteEvent) event.Subscription
+ SubscribeFinalizedHeaderEvent(ch chan<- core.FinalizedHeaderEvent) event.Subscription
ChainConfig() *params.ChainConfig
Engine() consensus.Engine
diff --git a/les/api_backend.go b/les/api_backend.go
index 243a0ac81..1c164153a 100644
--- a/les/api_backend.go
+++ b/les/api_backend.go
@@ -35,6 +35,7 @@ import (
"github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/event"
"github.com/ethereum/go-ethereum/light"
+ "github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/rpc"
)
@@ -216,6 +217,11 @@ func (b *LesApiBackend) SubscribeNewTxsEvent(ch chan<- core.NewTxsEvent) event.S
return b.eth.txPool.SubscribeNewTxsEvent(ch)
}
+func (b *LesApiBackend) SubscribeNewVoteEvent(ch chan<- core.NewVoteEvent) event.Subscription {
+ log.Error("light ethereum does not support SubscribeNewVoteEvent")
+ return nil
+}
+
func (b *LesApiBackend) SubscribeChainEvent(ch chan<- core.ChainEvent) event.Subscription {
return b.eth.blockchain.SubscribeChainEvent(ch)
}
@@ -224,6 +230,10 @@ func (b *LesApiBackend) SubscribeChainHeadEvent(ch chan<- core.ChainHeadEvent) e
return b.eth.blockchain.SubscribeChainHeadEvent(ch)
}
+func (b *LesApiBackend) SubscribeFinalizedHeaderEvent(ch chan<- core.FinalizedHeaderEvent) event.Subscription {
+ return b.eth.blockchain.SubscribeFinalizedHeaderEvent(ch)
+}
+
func (b *LesApiBackend) SubscribeChainSideEvent(ch chan<- core.ChainSideEvent) event.Subscription {
return b.eth.blockchain.SubscribeChainSideEvent(ch)
}
diff --git a/light/lightchain.go b/light/lightchain.go
index 99644355a..f2ce94b92 100644
--- a/light/lightchain.go
+++ b/light/lightchain.go
@@ -50,16 +50,17 @@ var (
// headers, downloading block bodies and receipts on demand through an ODR
// interface. It only does header validation during chain insertion.
type LightChain struct {
- hc *core.HeaderChain
- indexerConfig *IndexerConfig
- chainDb ethdb.Database
- engine consensus.Engine
- odr OdrBackend
- chainFeed event.Feed
- chainSideFeed event.Feed
- chainHeadFeed event.Feed
- scope event.SubscriptionScope
- genesisBlock *types.Block
+ hc *core.HeaderChain
+ indexerConfig *IndexerConfig
+ chainDb ethdb.Database
+ engine consensus.Engine
+ odr OdrBackend
+ chainFeed event.Feed
+ chainSideFeed event.Feed
+ chainHeadFeed event.Feed
+ finalizedHeaderFeed event.Feed
+ scope event.SubscriptionScope
+ genesisBlock *types.Block
bodyCache *lru.Cache // Cache for the most recent block bodies
bodyRLPCache *lru.Cache // Cache for the most recent block bodies in RLP encoded format
@@ -368,6 +369,8 @@ func (lc *LightChain) postChainEvents(events []interface{}) {
lc.chainHeadFeed.Send(core.ChainHeadEvent{Block: ev.Block})
}
lc.chainFeed.Send(ev)
+ case core.FinalizedHeaderEvent:
+ lc.finalizedHeaderFeed.Send(ev)
case core.ChainSideEvent:
lc.chainSideFeed.Send(ev)
}
@@ -415,6 +418,9 @@ func (lc *LightChain) InsertHeaderChain(chain []*types.Header, checkFreq int) (i
switch status {
case core.CanonStatTy:
events = append(events, core.ChainEvent{Block: block, Hash: block.Hash()})
+ if posa, ok := lc.Engine().(consensus.PoSA); ok {
+ events = append(events, core.FinalizedHeaderEvent{Header: posa.GetFinalizedHeader(lc, block.Header(), types.NaturallyFinalizedDist)})
+ }
case core.SideStatTy:
events = append(events, core.ChainSideEvent{Block: block})
}
@@ -561,6 +567,11 @@ func (lc *LightChain) SubscribeChainHeadEvent(ch chan<- core.ChainHeadEvent) eve
return lc.scope.Track(lc.chainHeadFeed.Subscribe(ch))
}
+// SubscribeFinalizedHeaderEvent registers a subscription of FinalizedHeaderEvent.
+func (lc *LightChain) SubscribeFinalizedHeaderEvent(ch chan<- core.FinalizedHeaderEvent) event.Subscription {
+ return lc.scope.Track(lc.finalizedHeaderFeed.Subscribe(ch))
+}
+
// SubscribeChainSideEvent registers a subscription of ChainSideEvent.
func (lc *LightChain) SubscribeChainSideEvent(ch chan<- core.ChainSideEvent) event.Subscription {
return lc.scope.Track(lc.chainSideFeed.Subscribe(ch))
diff --git a/miner/worker.go b/miner/worker.go
index b9a06208b..493baf682 100644
--- a/miner/worker.go
+++ b/miner/worker.go
@@ -127,6 +127,7 @@ type intervalAdjust struct {
// worker is the main object which takes care of submitting new work to consensus engine
// and gathering the sealing result.
type worker struct {
+ prefetcher core.Prefetcher
config *Config
chainConfig *params.ChainConfig
engine consensus.Engine
@@ -193,6 +194,7 @@ type worker struct {
func newWorker(config *Config, chainConfig *params.ChainConfig, engine consensus.Engine, eth Backend, mux *event.TypeMux, isLocalBlock func(*types.Block) bool, init bool) *worker {
worker := &worker{
+ prefetcher: core.NewStatePrefetcher(chainConfig, eth.BlockChain(), engine),
config: config,
chainConfig: chainConfig,
engine: engine,
@@ -656,7 +658,7 @@ func (w *worker) resultLoop() {
func (w *worker) makeCurrent(parent *types.Block, header *types.Header) error {
// Retrieve the parent state to execute on top and start a prefetcher for
// the miner to speed block sealing up a bit
- state, err := w.chain.StateAt(parent.Root())
+ state, err := w.chain.StateAtWithSharedPool(parent.Root())
if err != nil {
return err
}
@@ -756,7 +758,12 @@ func (w *worker) commitTransactions(txs *types.TransactionsByPriceAndNonce, coin
if w.current.gasPool == nil {
w.current.gasPool = new(core.GasPool).AddGas(w.current.header.GasLimit)
- w.current.gasPool.SubGas(params.SystemTxsGas)
+ if w.chain.Config().IsEuler(w.current.header.Number) {
+ w.current.gasPool.SubGas(params.SystemTxsGas * 3)
+ } else {
+ w.current.gasPool.SubGas(params.SystemTxsGas)
+ }
+
}
var coalescedLogs []*types.Log
@@ -775,6 +782,14 @@ func (w *worker) commitTransactions(txs *types.TransactionsByPriceAndNonce, coin
}
bloomProcessors := core.NewAsyncReceiptBloomGenerator(processorCapacity)
+ interruptCh := make(chan struct{})
+ defer close(interruptCh)
+ //prefetch txs from all pending txs
+ txsPrefetch := txs.Copy()
+ tx := txsPrefetch.Peek()
+ txCurr := &tx
+ w.prefetcher.PrefetchMining(txsPrefetch, w.current.header, w.current.gasPool.Gas(), w.current.state.Copy(), *w.chain.GetVMConfig(), interruptCh, txCurr)
+
LOOP:
for {
// In the following three cases, we will interrupt the execution of the transaction.
@@ -811,7 +826,7 @@ LOOP:
}
}
// Retrieve the next transaction and abort if all done
- tx := txs.Peek()
+ tx = txs.Peek()
if tx == nil {
break
}
@@ -966,7 +981,11 @@ func (w *worker) commitNewWork(interrupt *int32, noempty bool, timestamp int64)
commitTxsTimer.UpdateSince(start)
log.Info("Gas pool", "height", header.Number.String(), "pool", w.current.gasPool.String())
}
- w.commit(uncles, w.fullTaskHook, false, tstart)
+ err = w.commit(uncles, w.fullTaskHook, false, tstart)
+ if err != nil {
+ log.Error("Failed to commit mining block", "err", err)
+ return
+ }
}
// commit runs any post-transaction state modifications, assembles the final block
@@ -977,6 +996,9 @@ func (w *worker) commit(uncles []*types.Header, interval func(), update bool, st
if err != nil {
return err
}
+
+ s.CorrectAccountsRoot(w.chain.CurrentBlock().Root())
+
block, receipts, err := w.engine.FinalizeAndAssemble(w.chain, types.CopyHeader(w.current.header), s, w.current.txs, uncles, w.current.receipts)
if err != nil {
return err
diff --git a/mobile/ethclient.go b/mobile/ethclient.go
index 662125c4a..59e39e53e 100644
--- a/mobile/ethclient.go
+++ b/mobile/ethclient.go
@@ -148,6 +148,74 @@ func (ec *EthereumClient) SubscribeNewHead(ctx *Context, handler NewHeadHandler,
return &Subscription{rawSub}, nil
}
+// NewFinalizedHeaderHandler is a client-side subscription callback to invoke on events and
+// subscription failure.
+type NewFinalizedHeaderHandler interface {
+ OnNewFinalizedHeader(header *Header)
+ OnError(failure string)
+}
+
+// SubscribeNewFinalizedHeader subscribes to notifications about the current blockchain finalized header
+// on the given channel.
+func (ec *EthereumClient) SubscribeNewFinalizedHeader(ctx *Context, handler NewFinalizedHeaderHandler, buffer int) (sub *Subscription, _ error) {
+ // Subscribe to the event internally
+ ch := make(chan *types.Header, buffer)
+ rawSub, err := ec.client.SubscribeNewFinalizedHeader(ctx.context, ch)
+ if err != nil {
+ return nil, err
+ }
+ // Start up a dispatcher to feed into the callback
+ go func() {
+ for {
+ select {
+ case header := <-ch:
+ handler.OnNewFinalizedHeader(&Header{header})
+
+ case err := <-rawSub.Err():
+ if err != nil {
+ handler.OnError(err.Error())
+ }
+ return
+ }
+ }
+ }()
+ return &Subscription{rawSub}, nil
+}
+
+// NewVoteHandler is a client-side subscription callback to invoke on events and
+// subscription failure.
+type NewVoteHandler interface {
+ OnNewVote(vote *types.VoteEnvelope)
+ OnError(failure string)
+}
+
+// SubscribeNewVotes subscribes to notifications about the new votes into the vote pool
+// on the given channel.
+func (ec *EthereumClient) SubscribeNewVotes(ctx *Context, handler NewVoteHandler, buffer int) (sub *Subscription, _ error) {
+ // Subscribe to the event internally
+ ch := make(chan *types.VoteEnvelope, buffer)
+ rawSub, err := ec.client.SubscribeNewVotes(ctx.context, ch)
+ if err != nil {
+ return nil, err
+ }
+ // Start up a dispatcher to feed into the callback
+ go func() {
+ for {
+ select {
+ case vote := <-ch:
+ handler.OnNewVote(vote)
+
+ case err := <-rawSub.Err():
+ if err != nil {
+ handler.OnError(err.Error())
+ }
+ return
+ }
+ }
+ }()
+ return &Subscription{rawSub}, nil
+}
+
// State Access
// GetBalanceAt returns the wei balance of the given account.
diff --git a/node/config.go b/node/config.go
index 527cd3641..d0ec92723 100644
--- a/node/config.go
+++ b/node/config.go
@@ -205,6 +205,17 @@ type Config struct {
// AllowUnprotectedTxs allows non EIP-155 protected transactions to be send over RPC.
AllowUnprotectedTxs bool `toml:",omitempty"`
+
+ // BLSPasswordFile is the file that contains BLS wallet password.
+ BLSPasswordFile string `toml:",omitempty"`
+
+ // BLSWalletDir is the file system folder of BLS wallet. The directory can
+ // be specified as a relative path, in which case it is resolved relative to the
+ // current directory.
+ BLSWalletDir string `toml:",omitempty"`
+
+ // VoteJournalDir is the directory to store votes in the fast finality feature.
+ VoteJournalDir string `toml:",omitempty"`
}
// IPCEndpoint resolves an IPC endpoint based on a configured value, taking into
diff --git a/p2p/discover/v5_udp.go b/p2p/discover/v5_udp.go
index 71a39ea5a..01fa6dc46 100644
--- a/p2p/discover/v5_udp.go
+++ b/p2p/discover/v5_udp.go
@@ -54,7 +54,7 @@ type codecV5 interface {
// Encode encodes a packet.
Encode(enode.ID, string, v5wire.Packet, *v5wire.Whoareyou) ([]byte, v5wire.Nonce, error)
- // decode decodes a packet. It returns a *v5wire.Unknown packet if decryption fails.
+ // Decode decodes a packet. It returns a *v5wire.Unknown packet if decryption fails.
// The *enode.Node return value is non-nil when the input contains a handshake response.
Decode([]byte, string) (enode.ID, *enode.Node, v5wire.Packet, error)
}
diff --git a/p2p/enode/node.go b/p2p/enode/node.go
index c2429e0e8..d747ca331 100644
--- a/p2p/enode/node.go
+++ b/p2p/enode/node.go
@@ -121,7 +121,7 @@ func (n *Node) UDP() int {
return int(port)
}
-// UDP returns the TCP port of the node.
+// TCP returns the TCP port of the node.
func (n *Node) TCP() int {
var port enr.TCP
n.Load(&port)
diff --git a/p2p/peer.go b/p2p/peer.go
index 3b633108d..7b9e4d594 100644
--- a/p2p/peer.go
+++ b/p2p/peer.go
@@ -116,7 +116,8 @@ type Peer struct {
disc chan DiscReason
// events receives message send / receive events if set
- events *event.Feed
+ events *event.Feed
+ testPipe *MsgPipeRW // for testing
}
// NewPeer returns a peer for testing purposes.
@@ -129,6 +130,15 @@ func NewPeer(id enode.ID, name string, caps []Cap) *Peer {
return peer
}
+// NewPeerPipe creates a peer for testing purposes.
+// The message pipe given as the last parameter is closed when
+// Disconnect is called on the peer.
+func NewPeerPipe(id enode.ID, name string, caps []Cap, pipe *MsgPipeRW) *Peer {
+ p := NewPeer(id, name, caps)
+ p.testPipe = pipe
+ return p
+}
+
// NewPeerWithProtocols returns a peer for testing purposes.
func NewPeerWithProtocols(id enode.ID, protocols []Protocol, name string, caps []Cap) *Peer {
pipe, _ := net.Pipe()
@@ -196,6 +206,10 @@ func (p *Peer) LocalAddr() net.Addr {
// Disconnect terminates the peer connection with the given reason.
// It returns immediately and does not wait until the connection is closed.
func (p *Peer) Disconnect(reason DiscReason) {
+ if p.testPipe != nil {
+ p.testPipe.Close()
+ }
+
select {
case p.disc <- reason:
case <-p.closed:
diff --git a/p2p/transport.go b/p2p/transport.go
index 502983a11..e6e17e151 100644
--- a/p2p/transport.go
+++ b/p2p/transport.go
@@ -131,7 +131,7 @@ func (t *rlpxTransport) doProtoHandshake(our *protoHandshake) (their *protoHands
// Writing our handshake happens concurrently, we prefer
// returning the handshake read error. If the remote side
// disconnects us early with a valid reason, we should return it
- // as the error so it can be tracked elsewhere.
+ // as the error, so it can be tracked elsewhere.
werr := make(chan error, 1)
gopool.Submit(func() { werr <- Send(t, handshakeMsg, our) })
if their, err = readProtocolHandshake(t); err != nil {
diff --git a/params/config.go b/params/config.go
index d8c8459eb..114f8dd65 100644
--- a/params/config.go
+++ b/params/config.go
@@ -53,6 +53,7 @@ var (
big.NewInt(0),
nil,
nil,
+ nil,
}
// AllCliqueProtocolChanges contains every protocol change (EIPs) introduced
@@ -79,6 +80,7 @@ var (
big.NewInt(0),
big.NewInt(0),
big.NewInt(0),
+ nil,
&CliqueConfig{Period: 0, Epoch: 30000},
nil,
}
@@ -102,8 +104,12 @@ var (
big.NewInt(0),
big.NewInt(0),
big.NewInt(0),
+ nil,
nil, nil,
}
+
+ // GoerliChainConfig prysm relays on this field, lets keep it just to let it compile properly
+ GoerliChainConfig = AllCliqueProtocolChanges
)
// TrustedCheckpoint represents a set of post-processed trie roots (CHT and
@@ -178,9 +184,6 @@ type ChainConfig struct {
MuirGlacierBlock *big.Int `json:"muirGlacierBlock,omitempty"` // Eip-2384 (bomb delay) switch block (nil = no fork, 0 = already activated)
BerlinBlock *big.Int `json:"berlinBlock,omitempty"` // Berlin switch block (nil = no fork, 0 = already on berlin)
- RuntimeUpgradeBlock *big.Int `json:"runtimeUpgradeBlock,omitempty"`
- DeployerProxyBlock *big.Int `json:"deployerProxyBlock,omitempty"`
-
YoloV3Block *big.Int `json:"yoloV3Block,omitempty"` // YOLO v3: Gas repricings TODO @holiman add EIP references
EWASMBlock *big.Int `json:"ewasmBlock,omitempty"` // EWASM switch block (nil = no fork, 0 = already activated) RamanujanBlock *big.Int `json:"ramanujanBlock,omitempty" toml:",omitempty"` // ramanujanBlock switch block (nil = no fork, 0 = already activated)
CatalystBlock *big.Int `json:"catalystBlock,omitempty"` // Catalyst switch block (nil = no fork, 0 = already on catalyst)
@@ -190,6 +193,10 @@ type ChainConfig struct {
MirrorSyncBlock *big.Int `json:"mirrorSyncBlock,omitempty" toml:",omitempty"` // mirrorSyncBlock switch block (nil = no fork, 0 = already activated)
BrunoBlock *big.Int `json:"brunoBlock,omitempty" toml:",omitempty"` // brunoBlock switch block (nil = no fork, 0 = already activated)
+ VerifyParliaBlock *big.Int `json:"verifyParliaBlock,omitempty" toml:",omitempty"`
+ BlockRewardsBlock *big.Int `json:"blockRewardsBlock,omitempty" toml:",omitempty"`
+ FastFinalityBlock *big.Int `json:"fastFinalityBlock,omitempty" toml:",omitempty"`
+
// Various consensus engines
Clique *CliqueConfig `json:"clique,omitempty" toml:",omitempty"`
Parlia *ParliaConfig `json:"parlia,omitempty" toml:",omitempty"`
@@ -208,8 +215,9 @@ func (c *CliqueConfig) String() string {
// ParliaConfig is the consensus engine configs for proof-of-staked-authority based sealing.
type ParliaConfig struct {
- Period uint64 `json:"period"` // Number of seconds between blocks to enforce
- Epoch uint64 `json:"epoch"` // Epoch length to update validatorSet
+ Period uint64 `json:"period"` // Number of seconds between blocks to enforce
+ Epoch uint64 `json:"epoch"` // Epoch length to update validatorSet
+ BlockRewards *big.Int `json:"blockRewards,omitempty"` // Block rewards to be paid for each produced block
}
// String implements the stringer interface, returning the consensus engine details.
@@ -319,6 +327,36 @@ func (c *ChainConfig) IsOnBruno(num *big.Int) bool {
return configNumEqual(c.BrunoBlock, num)
}
+// IsEuler returns whether num is either equal to the euler fork block or greater.
+func (c *ChainConfig) IsEuler(num *big.Int) bool {
+ return isForked(c.FastFinalityBlock, num)
+}
+
+// IsOnEuler returns whether num is equal to the euler fork block
+func (c *ChainConfig) IsOnEuler(num *big.Int) bool {
+ return configNumEqual(c.FastFinalityBlock, num)
+}
+
+// IsBoneh returns whether num is either equal to the first fast finality fork block or greater.
+func (c *ChainConfig) IsBoneh(num *big.Int) bool {
+ return isForked(c.FastFinalityBlock, num)
+}
+
+// IsOnBoneh returns whether num is equal to the first fast finality fork block.
+func (c *ChainConfig) IsOnBoneh(num *big.Int) bool {
+ return configNumEqual(c.FastFinalityBlock, num)
+}
+
+// IsLynn returns whether num is either equal to the second fast finality fork block or greater.
+func (c *ChainConfig) IsLynn(num *big.Int) bool {
+ return isForked(c.FastFinalityBlock, num)
+}
+
+// IsOnLynn returns whether num is equal to the second fast finality fork block.
+func (c *ChainConfig) IsOnLynn(num *big.Int) bool {
+ return configNumEqual(c.FastFinalityBlock, num)
+}
+
// IsMuirGlacier returns whether num is either equal to the Muir Glacier (EIP-2384) fork block or greater.
func (c *ChainConfig) IsMuirGlacier(num *big.Int) bool {
return isForked(c.MuirGlacierBlock, num)
@@ -351,12 +389,12 @@ func (c *ChainConfig) IsEWASM(num *big.Int) bool {
return isForked(c.EWASMBlock, num)
}
-func (c *ChainConfig) HasRuntimeUpgrade(num *big.Int) bool {
- return isForked(c.RuntimeUpgradeBlock, num)
+func (c *ChainConfig) IsVerifyParliaBlock(num *big.Int) bool {
+ return isForked(c.VerifyParliaBlock, num)
}
-func (c *ChainConfig) HasDeployerProxy(num *big.Int) bool {
- return isForked(c.DeployerProxyBlock, num)
+func (c *ChainConfig) IsBlockRewardsBlock(num *big.Int) bool {
+ return isForked(c.BlockRewardsBlock, num)
}
// CheckCompatible checks whether scheduled fork transitions have been imported
@@ -389,6 +427,7 @@ func (c *ChainConfig) CheckConfigForkOrder() error {
for _, cur := range []fork{
{name: "mirrorSyncBlock", block: c.MirrorSyncBlock},
{name: "brunoBlock", block: c.BrunoBlock},
+ {name: "fastFinalityBlock", block: c.FastFinalityBlock},
{name: "berlinBlock", block: c.BerlinBlock},
} {
if lastFork.name != "" {
@@ -533,7 +572,9 @@ type Rules struct {
IsHomestead, IsEIP150, IsEIP155, IsEIP158 bool
IsByzantium, IsConstantinople, IsPetersburg, IsIstanbul bool
IsBerlin, IsCatalyst bool
- HasRuntimeUpgrade, HasDeployerProxy bool
+ HasVerifyParliaBlock bool
+ HasBlockRewards bool
+ IsBoneh bool
}
// Rules ensures c's ChainID is not nil.
@@ -543,18 +584,19 @@ func (c *ChainConfig) Rules(num *big.Int) Rules {
chainID = new(big.Int)
}
return Rules{
- ChainID: new(big.Int).Set(chainID),
- IsHomestead: c.IsHomestead(num),
- IsEIP150: c.IsEIP150(num),
- IsEIP155: c.IsEIP155(num),
- IsEIP158: c.IsEIP158(num),
- IsByzantium: c.IsByzantium(num),
- IsConstantinople: c.IsConstantinople(num),
- IsPetersburg: c.IsPetersburg(num),
- IsIstanbul: c.IsIstanbul(num),
- IsBerlin: c.IsBerlin(num),
- IsCatalyst: c.IsCatalyst(num),
- HasRuntimeUpgrade: c.HasRuntimeUpgrade(num),
- HasDeployerProxy: c.HasDeployerProxy(num),
+ ChainID: new(big.Int).Set(chainID),
+ IsHomestead: c.IsHomestead(num),
+ IsEIP150: c.IsEIP150(num),
+ IsEIP155: c.IsEIP155(num),
+ IsEIP158: c.IsEIP158(num),
+ IsByzantium: c.IsByzantium(num),
+ IsConstantinople: c.IsConstantinople(num),
+ IsPetersburg: c.IsPetersburg(num),
+ IsIstanbul: c.IsIstanbul(num),
+ IsBerlin: c.IsBerlin(num),
+ IsCatalyst: c.IsCatalyst(num),
+ IsBoneh: c.IsBoneh(num),
+ HasVerifyParliaBlock: c.IsVerifyParliaBlock(num),
+ HasBlockRewards: c.IsBlockRewardsBlock(num),
}
}
diff --git a/params/protocol_params.go b/params/protocol_params.go
index 3b3eec4e0..f1441b235 100644
--- a/params/protocol_params.go
+++ b/params/protocol_params.go
@@ -113,13 +113,14 @@ const (
MaxCodeSize = 24576 // Maximum bytecode to permit for a contract
// Precompiled contract gas prices
- EcrecoverGas uint64 = 3000 // Elliptic curve sender recovery gas price
- Sha256BaseGas uint64 = 60 // Base price for a SHA256 operation
- Sha256PerWordGas uint64 = 12 // Per-word price for a SHA256 operation
- Ripemd160BaseGas uint64 = 600 // Base price for a RIPEMD160 operation
- Ripemd160PerWordGas uint64 = 120 // Per-word price for a RIPEMD160 operation
- IdentityBaseGas uint64 = 15 // Base price for a data copy operation
- IdentityPerWordGas uint64 = 3 // Per-work price for a data copy operation
+ EcrecoverGas uint64 = 3000 // Elliptic curve sender recovery gas price
+ Sha256BaseGas uint64 = 60 // Base price for a SHA256 operation
+ Sha256PerWordGas uint64 = 12 // Per-word price for a SHA256 operation
+ Ripemd160BaseGas uint64 = 600 // Base price for a RIPEMD160 operation
+ Ripemd160PerWordGas uint64 = 120 // Per-word price for a RIPEMD160 operation
+ IdentityBaseGas uint64 = 15 // Base price for a data copy operation
+ IdentityPerWordGas uint64 = 3 // Per-work price for a data copy operation
+ VoteSignatureVerifyGas uint64 = 35000 // Finality signature verify gas price
Bn256AddGasByzantium uint64 = 500 // Byzantium gas needed for an elliptic curve addition
Bn256AddGasIstanbul uint64 = 150 // Gas needed for an elliptic curve addition
@@ -138,6 +139,8 @@ const (
Bls12381PairingPerPairGas uint64 = 23000 // Per-point pair gas price for BLS12-381 elliptic curve pairing check
Bls12381MapG1Gas uint64 = 5500 // Gas price for BLS12-381 mapping field element to G1 operation
Bls12381MapG2Gas uint64 = 110000 // Gas price for BLS12-381 mapping field element to G2 operation
+
+ VerifyParliaBlockGas uint64 = 3000 // Native function ~12 times faster comparing to the Solidity version
)
// Gas discount table for BLS12-381 G1 and G2 multi exponentiation operations
diff --git a/params/version.go b/params/version.go
index 8faa4bb64..d4a04cfba 100644
--- a/params/version.go
+++ b/params/version.go
@@ -23,7 +23,7 @@ import (
const (
VersionMajor = 1 // Major version component of the current release
VersionMinor = 1 // Minor version component of the current release
- VersionPatch = 8 // Patch version component of the current release
+ VersionPatch = 10 // Patch version component of the current release
VersionMeta = "" // Version metadata to append to the version string
)
diff --git a/rpc/client.go b/rpc/client.go
index c2ffa532c..383d51d7c 100644
--- a/rpc/client.go
+++ b/rpc/client.go
@@ -408,12 +408,12 @@ func (c *Client) Notify(ctx context.Context, method string, args ...interface{})
return c.send(ctx, op, msg)
}
-// EthSubscribe registers a subscripion under the "eth" namespace.
+// EthSubscribe registers a subscription under the "eth" namespace.
func (c *Client) EthSubscribe(ctx context.Context, channel interface{}, args ...interface{}) (*ClientSubscription, error) {
return c.Subscribe(ctx, "eth", channel, args...)
}
-// ShhSubscribe registers a subscripion under the "shh" namespace.
+// ShhSubscribe registers a subscription under the "shh" namespace.
// Deprecated: use Subscribe(ctx, "shh", ...).
func (c *Client) ShhSubscribe(ctx context.Context, channel interface{}, args ...interface{}) (*ClientSubscription, error) {
return c.Subscribe(ctx, "shh", channel, args...)
diff --git a/rpc/subscription.go b/rpc/subscription.go
index 942e764e5..d7ba784fc 100644
--- a/rpc/subscription.go
+++ b/rpc/subscription.go
@@ -34,7 +34,7 @@ import (
var (
// ErrNotificationsUnsupported is returned when the connection doesn't support notifications
ErrNotificationsUnsupported = errors.New("notifications not supported")
- // ErrNotificationNotFound is returned when the notification for the given id is not found
+ // ErrSubscriptionNotFound is returned when the notification for the given id is not found
ErrSubscriptionNotFound = errors.New("subscription not found")
)
diff --git a/trie/database.go b/trie/database.go
index b6a3154d4..649af6dbf 100644
--- a/trie/database.go
+++ b/trie/database.go
@@ -722,17 +722,18 @@ func (db *Database) Commit(node common.Hash, report bool, callback func(common.H
batch := db.diskdb.NewBatch()
// Move all of the accumulated preimages into a write batch
+ db.lock.RLock()
if db.preimages != nil {
rawdb.WritePreimages(batch, db.preimages)
// Since we're going to replay trie node writes into the clean cache, flush out
// any batched pre-images before continuing.
if err := batch.Write(); err != nil {
+ db.lock.RUnlock()
return err
}
batch.Reset()
}
// Move the trie itself into the batch, flushing if enough data is accumulated
- db.lock.RLock()
nodes, storage := len(db.dirties), db.dirtiesSize
db.lock.RUnlock()