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 @@

{{
- + + {{ if .Bep2eInfos }} + {{ end }}
{{if .Recaptcha}}
{{end}}
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()