forked from cerc-io/plugeth
all: on-chain oracle checkpoint syncing (#19543)
* all: implement simple checkpoint syncing cmd, les, node: remove callback mechanism cmd, node: remove callback definition les: simplify the registrar les: expose checkpoint rpc services in the light client les, light: don't store untrusted receipt cmd, contracts, les: discard stale checkpoint cmd, contracts/registrar: loose restriction of registeration cmd, contracts: add replay-protection all: off-chain multi-signature contract params: deploy checkpoint contract for rinkeby cmd/registrar: add raw signing mode for registrar cmd/registrar, contracts/registrar, les: fixed messages * cmd/registrar, contracts/registrar: fix lints * accounts/abi/bind, les: address comments * cmd, contracts, les, light, params: minor checkpoint sync cleanups * cmd, eth, les, light: move checkpoint config to config file * cmd, eth, les, params: address comments * eth, les, params: address comments * cmd: polish up the checkpoint admin CLI * cmd, contracts, params: deploy new version contract * cmd/checkpoint-admin: add another flag for clef mode signing * cmd, contracts, les: rename and regen checkpoint oracle with abigen
This commit is contained in:
parent
702f52fb99
commit
f7cdea2bdc
@ -45,8 +45,10 @@ import (
|
|||||||
// This nil assignment ensures compile time that SimulatedBackend implements bind.ContractBackend.
|
// This nil assignment ensures compile time that SimulatedBackend implements bind.ContractBackend.
|
||||||
var _ bind.ContractBackend = (*SimulatedBackend)(nil)
|
var _ bind.ContractBackend = (*SimulatedBackend)(nil)
|
||||||
|
|
||||||
var errBlockNumberUnsupported = errors.New("SimulatedBackend cannot access blocks other than the latest block")
|
var (
|
||||||
var errGasEstimationFailed = errors.New("gas required exceeds allowance or always failing transaction")
|
errBlockNumberUnsupported = errors.New("simulatedBackend cannot access blocks other than the latest block")
|
||||||
|
errGasEstimationFailed = errors.New("gas required exceeds allowance or always failing transaction")
|
||||||
|
)
|
||||||
|
|
||||||
// SimulatedBackend implements bind.ContractBackend, simulating a blockchain in
|
// SimulatedBackend implements bind.ContractBackend, simulating a blockchain in
|
||||||
// the background. Its main purpose is to allow easily testing contract bindings.
|
// the background. Its main purpose is to allow easily testing contract bindings.
|
||||||
@ -63,10 +65,9 @@ type SimulatedBackend struct {
|
|||||||
config *params.ChainConfig
|
config *params.ChainConfig
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewSimulatedBackend creates a new binding backend using a simulated blockchain
|
// NewSimulatedBackendWithDatabase creates a new binding backend based on the given database
|
||||||
// for testing purposes.
|
// and uses a simulated blockchain for testing purposes.
|
||||||
func NewSimulatedBackend(alloc core.GenesisAlloc, gasLimit uint64) *SimulatedBackend {
|
func NewSimulatedBackendWithDatabase(database ethdb.Database, alloc core.GenesisAlloc, gasLimit uint64) *SimulatedBackend {
|
||||||
database := rawdb.NewMemoryDatabase()
|
|
||||||
genesis := core.Genesis{Config: params.AllEthashProtocolChanges, GasLimit: gasLimit, Alloc: alloc}
|
genesis := core.Genesis{Config: params.AllEthashProtocolChanges, GasLimit: gasLimit, Alloc: alloc}
|
||||||
genesis.MustCommit(database)
|
genesis.MustCommit(database)
|
||||||
blockchain, _ := core.NewBlockChain(database, nil, genesis.Config, ethash.NewFaker(), vm.Config{}, nil)
|
blockchain, _ := core.NewBlockChain(database, nil, genesis.Config, ethash.NewFaker(), vm.Config{}, nil)
|
||||||
@ -81,6 +82,12 @@ func NewSimulatedBackend(alloc core.GenesisAlloc, gasLimit uint64) *SimulatedBac
|
|||||||
return backend
|
return backend
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NewSimulatedBackend creates a new binding backend using a simulated blockchain
|
||||||
|
// for testing purposes.
|
||||||
|
func NewSimulatedBackend(alloc core.GenesisAlloc, gasLimit uint64) *SimulatedBackend {
|
||||||
|
return NewSimulatedBackendWithDatabase(rawdb.NewMemoryDatabase(), alloc, gasLimit)
|
||||||
|
}
|
||||||
|
|
||||||
// Commit imports all the pending transactions as a single block and starts a
|
// Commit imports all the pending transactions as a single block and starts a
|
||||||
// fresh new state.
|
// fresh new state.
|
||||||
func (b *SimulatedBackend) Commit() {
|
func (b *SimulatedBackend) Commit() {
|
||||||
@ -424,6 +431,11 @@ func (b *SimulatedBackend) AdjustTime(adjustment time.Duration) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Blockchain returns the underlying blockchain.
|
||||||
|
func (b *SimulatedBackend) Blockchain() *core.BlockChain {
|
||||||
|
return b.blockchain
|
||||||
|
}
|
||||||
|
|
||||||
// callmsg implements core.Message to allow passing it as a transaction simulator.
|
// callmsg implements core.Message to allow passing it as a transaction simulator.
|
||||||
type callmsg struct {
|
type callmsg struct {
|
||||||
ethereum.CallMsg
|
ethereum.CallMsg
|
||||||
|
@ -475,11 +475,12 @@ var bindTests = []struct {
|
|||||||
`
|
`
|
||||||
"github.com/ethereum/go-ethereum/accounts/abi/bind"
|
"github.com/ethereum/go-ethereum/accounts/abi/bind"
|
||||||
"github.com/ethereum/go-ethereum/accounts/abi/bind/backends"
|
"github.com/ethereum/go-ethereum/accounts/abi/bind/backends"
|
||||||
|
"github.com/ethereum/go-ethereum/core"
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
`,
|
`,
|
||||||
`
|
`
|
||||||
// Create a simulator and wrap a non-deployed contract
|
// Create a simulator and wrap a non-deployed contract
|
||||||
sim := backends.NewSimulatedBackend(nil, uint64(10000000000))
|
sim := backends.NewSimulatedBackend(core.GenesisAlloc{}, uint64(10000000000))
|
||||||
|
|
||||||
nonexistent, err := NewNonExistent(common.Address{}, sim)
|
nonexistent, err := NewNonExistent(common.Address{}, sim)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -439,6 +439,18 @@ var (
|
|||||||
}
|
}
|
||||||
}), nil
|
}), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Parse{{.Normalized.Name}} is a log parse operation binding the contract event 0x{{printf "%x" .Original.Id}}.
|
||||||
|
//
|
||||||
|
// Solidity: {{.Original.String}}
|
||||||
|
func (_{{$contract.Type}} *{{$contract.Type}}Filterer) Parse{{.Normalized.Name}}(log types.Log) (*{{$contract.Type}}{{.Normalized.Name}}, error) {
|
||||||
|
event := new({{$contract.Type}}{{.Normalized.Name}})
|
||||||
|
if err := _{{$contract.Type}}.contract.UnpackLog(event, "{{.Original.Name}}", log); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return event, nil
|
||||||
|
}
|
||||||
|
|
||||||
{{end}}
|
{{end}}
|
||||||
{{end}}
|
{{end}}
|
||||||
`
|
`
|
||||||
|
@ -56,7 +56,8 @@ func TestWaitDeployed(t *testing.T) {
|
|||||||
backend := backends.NewSimulatedBackend(
|
backend := backends.NewSimulatedBackend(
|
||||||
core.GenesisAlloc{
|
core.GenesisAlloc{
|
||||||
crypto.PubkeyToAddress(testKey.PublicKey): {Balance: big.NewInt(10000000000)},
|
crypto.PubkeyToAddress(testKey.PublicKey): {Balance: big.NewInt(10000000000)},
|
||||||
}, 10000000,
|
},
|
||||||
|
10000000,
|
||||||
)
|
)
|
||||||
|
|
||||||
// Create the transaction.
|
// Create the transaction.
|
||||||
|
166
cmd/checkpoint-admin/common.go
Normal file
166
cmd/checkpoint-admin/common.go
Normal file
@ -0,0 +1,166 @@
|
|||||||
|
// Copyright 2018 The go-ethereum Authors
|
||||||
|
// This file is part of go-ethereum.
|
||||||
|
//
|
||||||
|
// go-ethereum is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// go-ethereum 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 General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io/ioutil"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/accounts/keystore"
|
||||||
|
"github.com/ethereum/go-ethereum/cmd/utils"
|
||||||
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
"github.com/ethereum/go-ethereum/console"
|
||||||
|
"github.com/ethereum/go-ethereum/contracts/checkpointoracle"
|
||||||
|
"github.com/ethereum/go-ethereum/ethclient"
|
||||||
|
"github.com/ethereum/go-ethereum/params"
|
||||||
|
"github.com/ethereum/go-ethereum/rpc"
|
||||||
|
"gopkg.in/urfave/cli.v1"
|
||||||
|
)
|
||||||
|
|
||||||
|
// newClient creates a client with specified remote URL.
|
||||||
|
func newClient(ctx *cli.Context) *ethclient.Client {
|
||||||
|
client, err := ethclient.Dial(ctx.GlobalString(nodeURLFlag.Name))
|
||||||
|
if err != nil {
|
||||||
|
utils.Fatalf("Failed to connect to Ethereum node: %v", err)
|
||||||
|
}
|
||||||
|
return client
|
||||||
|
}
|
||||||
|
|
||||||
|
// newRPCClient creates a rpc client with specified node URL.
|
||||||
|
func newRPCClient(url string) *rpc.Client {
|
||||||
|
client, err := rpc.Dial(url)
|
||||||
|
if err != nil {
|
||||||
|
utils.Fatalf("Failed to connect to Ethereum node: %v", err)
|
||||||
|
}
|
||||||
|
return client
|
||||||
|
}
|
||||||
|
|
||||||
|
// getContractAddr retrieves the register contract address through
|
||||||
|
// rpc request.
|
||||||
|
func getContractAddr(client *rpc.Client) common.Address {
|
||||||
|
var addr string
|
||||||
|
if err := client.Call(&addr, "les_getCheckpointContractAddress"); err != nil {
|
||||||
|
utils.Fatalf("Failed to fetch checkpoint oracle address: %v", err)
|
||||||
|
}
|
||||||
|
return common.HexToAddress(addr)
|
||||||
|
}
|
||||||
|
|
||||||
|
// getCheckpoint retrieves the specified checkpoint or the latest one
|
||||||
|
// through rpc request.
|
||||||
|
func getCheckpoint(ctx *cli.Context, client *rpc.Client) *params.TrustedCheckpoint {
|
||||||
|
var checkpoint *params.TrustedCheckpoint
|
||||||
|
|
||||||
|
if ctx.GlobalIsSet(indexFlag.Name) {
|
||||||
|
var result [3]string
|
||||||
|
index := uint64(ctx.GlobalInt64(indexFlag.Name))
|
||||||
|
if err := client.Call(&result, "les_getCheckpoint", index); err != nil {
|
||||||
|
utils.Fatalf("Failed to get local checkpoint %v, please ensure the les API is exposed", err)
|
||||||
|
}
|
||||||
|
checkpoint = ¶ms.TrustedCheckpoint{
|
||||||
|
SectionIndex: index,
|
||||||
|
SectionHead: common.HexToHash(result[0]),
|
||||||
|
CHTRoot: common.HexToHash(result[1]),
|
||||||
|
BloomRoot: common.HexToHash(result[2]),
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
var result [4]string
|
||||||
|
err := client.Call(&result, "les_latestCheckpoint")
|
||||||
|
if err != nil {
|
||||||
|
utils.Fatalf("Failed to get local checkpoint %v, please ensure the les API is exposed", err)
|
||||||
|
}
|
||||||
|
index, err := strconv.ParseUint(result[0], 0, 64)
|
||||||
|
if err != nil {
|
||||||
|
utils.Fatalf("Failed to parse checkpoint index %v", err)
|
||||||
|
}
|
||||||
|
checkpoint = ¶ms.TrustedCheckpoint{
|
||||||
|
SectionIndex: index,
|
||||||
|
SectionHead: common.HexToHash(result[1]),
|
||||||
|
CHTRoot: common.HexToHash(result[2]),
|
||||||
|
BloomRoot: common.HexToHash(result[3]),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return checkpoint
|
||||||
|
}
|
||||||
|
|
||||||
|
// newContract creates a registrar contract instance with specified
|
||||||
|
// contract address or the default contracts for mainnet or testnet.
|
||||||
|
func newContract(client *rpc.Client) (common.Address, *checkpointoracle.CheckpointOracle) {
|
||||||
|
addr := getContractAddr(client)
|
||||||
|
if addr == (common.Address{}) {
|
||||||
|
utils.Fatalf("No specified registrar contract address")
|
||||||
|
}
|
||||||
|
contract, err := checkpointoracle.NewCheckpointOracle(addr, ethclient.NewClient(client))
|
||||||
|
if err != nil {
|
||||||
|
utils.Fatalf("Failed to setup registrar contract %s: %v", addr, err)
|
||||||
|
}
|
||||||
|
return addr, contract
|
||||||
|
}
|
||||||
|
|
||||||
|
// promptPassphrase prompts the user for a passphrase.
|
||||||
|
// Set confirmation to true to require the user to confirm the passphrase.
|
||||||
|
func promptPassphrase(confirmation bool) string {
|
||||||
|
passphrase, err := console.Stdin.PromptPassword("Passphrase: ")
|
||||||
|
if err != nil {
|
||||||
|
utils.Fatalf("Failed to read passphrase: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if confirmation {
|
||||||
|
confirm, err := console.Stdin.PromptPassword("Repeat passphrase: ")
|
||||||
|
if err != nil {
|
||||||
|
utils.Fatalf("Failed to read passphrase confirmation: %v", err)
|
||||||
|
}
|
||||||
|
if passphrase != confirm {
|
||||||
|
utils.Fatalf("Passphrases do not match")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return passphrase
|
||||||
|
}
|
||||||
|
|
||||||
|
// getPassphrase obtains a passphrase given by the user. It first checks the
|
||||||
|
// --password command line flag and ultimately prompts the user for a
|
||||||
|
// passphrase.
|
||||||
|
func getPassphrase(ctx *cli.Context) string {
|
||||||
|
passphraseFile := ctx.String(utils.PasswordFileFlag.Name)
|
||||||
|
if passphraseFile != "" {
|
||||||
|
content, err := ioutil.ReadFile(passphraseFile)
|
||||||
|
if err != nil {
|
||||||
|
utils.Fatalf("Failed to read passphrase file '%s': %v",
|
||||||
|
passphraseFile, err)
|
||||||
|
}
|
||||||
|
return strings.TrimRight(string(content), "\r\n")
|
||||||
|
}
|
||||||
|
// Otherwise prompt the user for the passphrase.
|
||||||
|
return promptPassphrase(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
// getKey retrieves the user key through specified key file.
|
||||||
|
func getKey(ctx *cli.Context) *keystore.Key {
|
||||||
|
// Read key from file.
|
||||||
|
keyFile := ctx.GlobalString(keyFileFlag.Name)
|
||||||
|
keyJson, err := ioutil.ReadFile(keyFile)
|
||||||
|
if err != nil {
|
||||||
|
utils.Fatalf("Failed to read the keyfile at '%s': %v", keyFile, err)
|
||||||
|
}
|
||||||
|
// Decrypt key with passphrase.
|
||||||
|
passphrase := getPassphrase(ctx)
|
||||||
|
key, err := keystore.DecryptKey(keyJson, passphrase)
|
||||||
|
if err != nil {
|
||||||
|
utils.Fatalf("Failed to decrypt user key '%s': %v", keyFile, err)
|
||||||
|
}
|
||||||
|
return key
|
||||||
|
}
|
335
cmd/checkpoint-admin/exec.go
Normal file
335
cmd/checkpoint-admin/exec.go
Normal file
@ -0,0 +1,335 @@
|
|||||||
|
// Copyright 2018 The go-ethereum Authors
|
||||||
|
// This file is part of go-ethereum.
|
||||||
|
//
|
||||||
|
// go-ethereum is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// go-ethereum 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 General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"context"
|
||||||
|
"encoding/binary"
|
||||||
|
"fmt"
|
||||||
|
"math/big"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/accounts"
|
||||||
|
"github.com/ethereum/go-ethereum/accounts/abi/bind"
|
||||||
|
"github.com/ethereum/go-ethereum/cmd/utils"
|
||||||
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||||
|
"github.com/ethereum/go-ethereum/contracts/checkpointoracle"
|
||||||
|
"github.com/ethereum/go-ethereum/contracts/checkpointoracle/contract"
|
||||||
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
|
"github.com/ethereum/go-ethereum/ethclient"
|
||||||
|
"github.com/ethereum/go-ethereum/log"
|
||||||
|
"github.com/ethereum/go-ethereum/params"
|
||||||
|
"github.com/ethereum/go-ethereum/rpc"
|
||||||
|
"gopkg.in/urfave/cli.v1"
|
||||||
|
)
|
||||||
|
|
||||||
|
var commandDeploy = cli.Command{
|
||||||
|
Name: "deploy",
|
||||||
|
Usage: "Deploy a new checkpoint oracle contract",
|
||||||
|
Flags: []cli.Flag{
|
||||||
|
nodeURLFlag,
|
||||||
|
clefURLFlag,
|
||||||
|
signersFlag,
|
||||||
|
thresholdFlag,
|
||||||
|
keyFileFlag,
|
||||||
|
utils.PasswordFileFlag,
|
||||||
|
},
|
||||||
|
Action: utils.MigrateFlags(deploy),
|
||||||
|
}
|
||||||
|
|
||||||
|
var commandSign = cli.Command{
|
||||||
|
Name: "sign",
|
||||||
|
Usage: "Sign the checkpoint with the specified key",
|
||||||
|
Flags: []cli.Flag{
|
||||||
|
nodeURLFlag,
|
||||||
|
clefURLFlag,
|
||||||
|
indexFlag,
|
||||||
|
hashFlag,
|
||||||
|
oracleFlag,
|
||||||
|
keyFileFlag,
|
||||||
|
signerFlag,
|
||||||
|
utils.PasswordFileFlag,
|
||||||
|
},
|
||||||
|
Action: utils.MigrateFlags(sign),
|
||||||
|
}
|
||||||
|
|
||||||
|
var commandPublish = cli.Command{
|
||||||
|
Name: "publish",
|
||||||
|
Usage: "Publish a checkpoint into the oracle",
|
||||||
|
Flags: []cli.Flag{
|
||||||
|
nodeURLFlag,
|
||||||
|
indexFlag,
|
||||||
|
signaturesFlag,
|
||||||
|
keyFileFlag,
|
||||||
|
utils.PasswordFileFlag,
|
||||||
|
},
|
||||||
|
Action: utils.MigrateFlags(publish),
|
||||||
|
}
|
||||||
|
|
||||||
|
// deploy deploys the checkpoint registrar contract.
|
||||||
|
//
|
||||||
|
// Note the network where the contract is deployed depends on
|
||||||
|
// the network where the connected node is located.
|
||||||
|
func deploy(ctx *cli.Context) error {
|
||||||
|
// Gather all the addresses that should be permitted to sign
|
||||||
|
var addrs []common.Address
|
||||||
|
for _, account := range strings.Split(ctx.String(signersFlag.Name), ",") {
|
||||||
|
if trimmed := strings.TrimSpace(account); !common.IsHexAddress(trimmed) {
|
||||||
|
utils.Fatalf("Invalid account in --signers: '%s'", trimmed)
|
||||||
|
}
|
||||||
|
addrs = append(addrs, common.HexToAddress(account))
|
||||||
|
}
|
||||||
|
// Retrieve and validate the signing threshold
|
||||||
|
needed := ctx.Int(thresholdFlag.Name)
|
||||||
|
if needed == 0 || needed > len(addrs) {
|
||||||
|
utils.Fatalf("Invalid signature threshold %d", needed)
|
||||||
|
}
|
||||||
|
// Print a summary to ensure the user understands what they're signing
|
||||||
|
fmt.Printf("Deploying new checkpoint oracle:\n\n")
|
||||||
|
for i, addr := range addrs {
|
||||||
|
fmt.Printf("Admin %d => %s\n", i+1, addr.Hex())
|
||||||
|
}
|
||||||
|
fmt.Printf("\nSignatures needed to publish: %d\n", needed)
|
||||||
|
|
||||||
|
// Retrieve the private key, create an abigen transactor and an RPC client
|
||||||
|
transactor := bind.NewKeyedTransactor(getKey(ctx).PrivateKey)
|
||||||
|
client := newClient(ctx)
|
||||||
|
|
||||||
|
// Deploy the checkpoint oracle
|
||||||
|
oracle, tx, _, err := contract.DeployCheckpointOracle(transactor, client, addrs, big.NewInt(int64(params.CheckpointFrequency)),
|
||||||
|
big.NewInt(int64(params.CheckpointProcessConfirmations)), big.NewInt(int64(needed)))
|
||||||
|
if err != nil {
|
||||||
|
utils.Fatalf("Failed to deploy checkpoint oracle %v", err)
|
||||||
|
}
|
||||||
|
log.Info("Deployed checkpoint oracle", "address", oracle, "tx", tx.Hash().Hex())
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// sign creates the signature for specific checkpoint
|
||||||
|
// with local key. Only contract admins have the permission to
|
||||||
|
// sign checkpoint.
|
||||||
|
func sign(ctx *cli.Context) error {
|
||||||
|
var (
|
||||||
|
offline bool // The indicator whether we sign checkpoint by offline.
|
||||||
|
chash common.Hash
|
||||||
|
cindex uint64
|
||||||
|
address common.Address
|
||||||
|
|
||||||
|
node *rpc.Client
|
||||||
|
oracle *checkpointoracle.CheckpointOracle
|
||||||
|
)
|
||||||
|
if !ctx.GlobalIsSet(nodeURLFlag.Name) {
|
||||||
|
// Offline mode signing
|
||||||
|
offline = true
|
||||||
|
if !ctx.IsSet(hashFlag.Name) {
|
||||||
|
utils.Fatalf("Please specify the checkpoint hash (--hash) to sign in offline mode")
|
||||||
|
}
|
||||||
|
chash = common.HexToHash(ctx.String(hashFlag.Name))
|
||||||
|
|
||||||
|
if !ctx.IsSet(indexFlag.Name) {
|
||||||
|
utils.Fatalf("Please specify checkpoint index (--index) to sign in offline mode")
|
||||||
|
}
|
||||||
|
cindex = ctx.Uint64(indexFlag.Name)
|
||||||
|
|
||||||
|
if !ctx.IsSet(oracleFlag.Name) {
|
||||||
|
utils.Fatalf("Please specify oracle address (--oracle) to sign in offline mode")
|
||||||
|
}
|
||||||
|
address = common.HexToAddress(ctx.String(oracleFlag.Name))
|
||||||
|
} else {
|
||||||
|
// Interactive mode signing, retrieve the data from the remote node
|
||||||
|
node = newRPCClient(ctx.GlobalString(nodeURLFlag.Name))
|
||||||
|
|
||||||
|
checkpoint := getCheckpoint(ctx, node)
|
||||||
|
chash = checkpoint.Hash()
|
||||||
|
cindex = checkpoint.SectionIndex
|
||||||
|
address = getContractAddr(node)
|
||||||
|
|
||||||
|
// Check the validity of checkpoint
|
||||||
|
reqCtx, cancelFn := context.WithTimeout(context.Background(), 10*time.Second)
|
||||||
|
defer cancelFn()
|
||||||
|
|
||||||
|
head, err := ethclient.NewClient(node).HeaderByNumber(reqCtx, nil)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
num := head.Number.Uint64()
|
||||||
|
if num < ((cindex+1)*params.CheckpointFrequency + params.CheckpointProcessConfirmations) {
|
||||||
|
utils.Fatalf("Invalid future checkpoint")
|
||||||
|
}
|
||||||
|
_, oracle = newContract(node)
|
||||||
|
latest, _, h, err := oracle.Contract().GetLatestCheckpoint(nil)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if cindex < latest {
|
||||||
|
utils.Fatalf("Checkpoint is too old")
|
||||||
|
}
|
||||||
|
if cindex == latest && (latest != 0 || h.Uint64() != 0) {
|
||||||
|
utils.Fatalf("Stale checkpoint, latest registered %d, given %d", latest, cindex)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var (
|
||||||
|
signature string
|
||||||
|
signer string
|
||||||
|
)
|
||||||
|
// isAdmin checks whether the specified signer is admin.
|
||||||
|
isAdmin := func(addr common.Address) error {
|
||||||
|
signers, err := oracle.Contract().GetAllAdmin(nil)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
for _, s := range signers {
|
||||||
|
if s == addr {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return fmt.Errorf("signer %v is not the admin", addr.Hex())
|
||||||
|
}
|
||||||
|
// Print to the user the data thy are about to sign
|
||||||
|
fmt.Printf("Oracle => %s\n", address.Hex())
|
||||||
|
fmt.Printf("Index %4d => %s\n", cindex, chash.Hex())
|
||||||
|
|
||||||
|
switch {
|
||||||
|
case ctx.GlobalIsSet(clefURLFlag.Name):
|
||||||
|
// Sign checkpoint in clef mode.
|
||||||
|
signer = ctx.String(signerFlag.Name)
|
||||||
|
|
||||||
|
if !offline {
|
||||||
|
if err := isAdmin(common.HexToAddress(signer)); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
clef := newRPCClient(ctx.GlobalString(clefURLFlag.Name))
|
||||||
|
p := make(map[string]string)
|
||||||
|
buf := make([]byte, 8)
|
||||||
|
binary.BigEndian.PutUint64(buf, cindex)
|
||||||
|
p["address"] = address.Hex()
|
||||||
|
p["message"] = hexutil.Encode(append(buf, chash.Bytes()...))
|
||||||
|
if err := clef.Call(&signature, "account_signData", accounts.MimetypeDataWithValidator, signer, p); err != nil {
|
||||||
|
utils.Fatalf("Failed to sign checkpoint, err %v", err)
|
||||||
|
}
|
||||||
|
case ctx.GlobalIsSet(keyFileFlag.Name):
|
||||||
|
// Sign checkpoint in raw private key file mode.
|
||||||
|
key := getKey(ctx)
|
||||||
|
signer = key.Address.Hex()
|
||||||
|
|
||||||
|
if !offline {
|
||||||
|
if err := isAdmin(key.Address); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sig, err := crypto.Sign(sighash(cindex, address, chash), key.PrivateKey)
|
||||||
|
if err != nil {
|
||||||
|
utils.Fatalf("Failed to sign checkpoint, err %v", err)
|
||||||
|
}
|
||||||
|
sig[64] += 27 // Transform V from 0/1 to 27/28 according to the yellow paper
|
||||||
|
signature = common.Bytes2Hex(sig)
|
||||||
|
default:
|
||||||
|
utils.Fatalf("Please specify clef URL or private key file path to sign checkpoint")
|
||||||
|
}
|
||||||
|
fmt.Printf("Signer => %s\n", signer)
|
||||||
|
fmt.Printf("Signature => %s\n", signature)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// sighash calculates the hash of the data to sign for the checkpoint oracle.
|
||||||
|
func sighash(index uint64, oracle common.Address, hash common.Hash) []byte {
|
||||||
|
buf := make([]byte, 8)
|
||||||
|
binary.BigEndian.PutUint64(buf, index)
|
||||||
|
|
||||||
|
data := append([]byte{0x19, 0x00}, append(oracle[:], append(buf, hash[:]...)...)...)
|
||||||
|
return crypto.Keccak256(data)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ecrecover calculates the sender address from a sighash and signature combo.
|
||||||
|
func ecrecover(sighash []byte, sig []byte) common.Address {
|
||||||
|
sig[64] -= 27
|
||||||
|
defer func() { sig[64] += 27 }()
|
||||||
|
|
||||||
|
signer, err := crypto.SigToPub(sighash, sig)
|
||||||
|
if err != nil {
|
||||||
|
utils.Fatalf("Failed to recover sender from signature %x: %v", sig, err)
|
||||||
|
}
|
||||||
|
return crypto.PubkeyToAddress(*signer)
|
||||||
|
}
|
||||||
|
|
||||||
|
// publish registers the specified checkpoint which generated by connected node
|
||||||
|
// with a authorised private key.
|
||||||
|
func publish(ctx *cli.Context) error {
|
||||||
|
// Print the checkpoint oracle's current status to make sure we're interacting
|
||||||
|
// with the correct network and contract.
|
||||||
|
status(ctx)
|
||||||
|
|
||||||
|
// Gather the signatures from the CLI
|
||||||
|
var sigs [][]byte
|
||||||
|
for _, sig := range strings.Split(ctx.String(signaturesFlag.Name), ",") {
|
||||||
|
trimmed := strings.TrimPrefix(strings.TrimSpace(sig), "0x")
|
||||||
|
if len(trimmed) != 130 {
|
||||||
|
utils.Fatalf("Invalid signature in --signature: '%s'", trimmed)
|
||||||
|
} else {
|
||||||
|
sigs = append(sigs, common.Hex2Bytes(trimmed))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Retrieve the checkpoint we want to sign to sort the signatures
|
||||||
|
var (
|
||||||
|
client = newRPCClient(ctx.GlobalString(nodeURLFlag.Name))
|
||||||
|
addr, oracle = newContract(client)
|
||||||
|
checkpoint = getCheckpoint(ctx, client)
|
||||||
|
sighash = sighash(checkpoint.SectionIndex, addr, checkpoint.Hash())
|
||||||
|
)
|
||||||
|
for i := 0; i < len(sigs); i++ {
|
||||||
|
for j := i + 1; j < len(sigs); j++ {
|
||||||
|
signerA := ecrecover(sighash, sigs[i])
|
||||||
|
signerB := ecrecover(sighash, sigs[j])
|
||||||
|
if bytes.Compare(signerA.Bytes(), signerB.Bytes()) > 0 {
|
||||||
|
sigs[i], sigs[j] = sigs[j], sigs[i]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Retrieve recent header info to protect replay attack
|
||||||
|
reqCtx, cancelFn := context.WithTimeout(context.Background(), 10*time.Second)
|
||||||
|
defer cancelFn()
|
||||||
|
|
||||||
|
head, err := ethclient.NewClient(client).HeaderByNumber(reqCtx, nil)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
num := head.Number.Uint64()
|
||||||
|
recent, err := ethclient.NewClient(client).HeaderByNumber(reqCtx, big.NewInt(int64(num-128)))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// Print a summary of the operation that's going to be performed
|
||||||
|
fmt.Printf("Publishing %d => %s:\n\n", checkpoint.SectionIndex, checkpoint.Hash().Hex())
|
||||||
|
for i, sig := range sigs {
|
||||||
|
fmt.Printf("Signer %d => %s\n", i+1, ecrecover(sighash, sig).Hex())
|
||||||
|
}
|
||||||
|
fmt.Println()
|
||||||
|
fmt.Printf("Sentry number => %d\nSentry hash => %s\n", recent.Number, recent.Hash().Hex())
|
||||||
|
|
||||||
|
// Publish the checkpoint into the oracle
|
||||||
|
tx, err := oracle.RegisterCheckpoint(getKey(ctx).PrivateKey, checkpoint.SectionIndex, checkpoint.Hash().Bytes(), recent.Number, recent.Hash(), sigs)
|
||||||
|
if err != nil {
|
||||||
|
utils.Fatalf("Register contract failed %v", err)
|
||||||
|
}
|
||||||
|
log.Info("Successfully registered checkpoint", "tx", tx.Hash().Hex())
|
||||||
|
return nil
|
||||||
|
}
|
124
cmd/checkpoint-admin/main.go
Normal file
124
cmd/checkpoint-admin/main.go
Normal file
@ -0,0 +1,124 @@
|
|||||||
|
// Copyright 2018 The go-ethereum Authors
|
||||||
|
// This file is part of go-ethereum.
|
||||||
|
//
|
||||||
|
// go-ethereum is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// go-ethereum 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 General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
// checkpoint-admin is a utility that can be used to query checkpoint information
|
||||||
|
// and register stable checkpoints into an oracle contract.
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/cmd/utils"
|
||||||
|
"github.com/ethereum/go-ethereum/common/fdlimit"
|
||||||
|
"github.com/ethereum/go-ethereum/log"
|
||||||
|
"gopkg.in/urfave/cli.v1"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
commandHelperTemplate = `{{.Name}}{{if .Subcommands}} command{{end}}{{if .Flags}} [command options]{{end}} [arguments...]
|
||||||
|
{{if .Description}}{{.Description}}
|
||||||
|
{{end}}{{if .Subcommands}}
|
||||||
|
SUBCOMMANDS:
|
||||||
|
{{range .Subcommands}}{{.Name}}{{with .ShortName}}, {{.}}{{end}}{{ "\t" }}{{.Usage}}
|
||||||
|
{{end}}{{end}}{{if .Flags}}
|
||||||
|
OPTIONS:
|
||||||
|
{{range $.Flags}}{{"\t"}}{{.}}
|
||||||
|
{{end}}
|
||||||
|
{{end}}`
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
// Git SHA1 commit hash of the release (set via linker flags)
|
||||||
|
gitCommit = ""
|
||||||
|
gitDate = ""
|
||||||
|
)
|
||||||
|
|
||||||
|
var app *cli.App
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
app = utils.NewApp(gitCommit, gitDate, "ethereum checkpoint helper tool")
|
||||||
|
app.Commands = []cli.Command{
|
||||||
|
commandStatus,
|
||||||
|
commandDeploy,
|
||||||
|
commandSign,
|
||||||
|
commandPublish,
|
||||||
|
}
|
||||||
|
app.Flags = []cli.Flag{
|
||||||
|
oracleFlag,
|
||||||
|
keyFileFlag,
|
||||||
|
nodeURLFlag,
|
||||||
|
clefURLFlag,
|
||||||
|
utils.PasswordFileFlag,
|
||||||
|
}
|
||||||
|
cli.CommandHelpTemplate = commandHelperTemplate
|
||||||
|
}
|
||||||
|
|
||||||
|
// Commonly used command line flags.
|
||||||
|
var (
|
||||||
|
indexFlag = cli.Int64Flag{
|
||||||
|
Name: "index",
|
||||||
|
Usage: "Checkpoint index (query latest from remote node if not specified)",
|
||||||
|
}
|
||||||
|
hashFlag = cli.StringFlag{
|
||||||
|
Name: "hash",
|
||||||
|
Usage: "Checkpoint hash (query latest from remote node if not specified)",
|
||||||
|
}
|
||||||
|
oracleFlag = cli.StringFlag{
|
||||||
|
Name: "oracle",
|
||||||
|
Usage: "Checkpoint oracle address (query from remote node if not specified)",
|
||||||
|
}
|
||||||
|
thresholdFlag = cli.Int64Flag{
|
||||||
|
Name: "threshold",
|
||||||
|
Usage: "Minimal number of signatures required to approve a checkpoint",
|
||||||
|
}
|
||||||
|
keyFileFlag = cli.StringFlag{
|
||||||
|
Name: "keyfile",
|
||||||
|
Usage: "The private key file (keyfile signature is not recommended)",
|
||||||
|
}
|
||||||
|
nodeURLFlag = cli.StringFlag{
|
||||||
|
Name: "rpc",
|
||||||
|
Value: "http://localhost:8545",
|
||||||
|
Usage: "The rpc endpoint of a local or remote geth node",
|
||||||
|
}
|
||||||
|
clefURLFlag = cli.StringFlag{
|
||||||
|
Name: "clef",
|
||||||
|
Value: "http://localhost:8550",
|
||||||
|
Usage: "The rpc endpoint of clef",
|
||||||
|
}
|
||||||
|
signerFlag = cli.StringFlag{
|
||||||
|
Name: "signer",
|
||||||
|
Usage: "Signer address for clef mode signing",
|
||||||
|
}
|
||||||
|
signersFlag = cli.StringFlag{
|
||||||
|
Name: "signers",
|
||||||
|
Usage: "Comma separated accounts of trusted checkpoint signers",
|
||||||
|
}
|
||||||
|
signaturesFlag = cli.StringFlag{
|
||||||
|
Name: "signatures",
|
||||||
|
Usage: "Comma separated checkpoint signatures to submit",
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
log.Root().SetHandler(log.LvlFilterHandler(log.LvlInfo, log.StreamHandler(os.Stderr, log.TerminalFormat(true))))
|
||||||
|
fdlimit.Raise(2048)
|
||||||
|
|
||||||
|
if err := app.Run(os.Args); err != nil {
|
||||||
|
fmt.Fprintln(os.Stderr, err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
}
|
61
cmd/checkpoint-admin/status.go
Normal file
61
cmd/checkpoint-admin/status.go
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
// Copyright 2018 The go-ethereum Authors
|
||||||
|
// This file is part of go-ethereum.
|
||||||
|
//
|
||||||
|
// go-ethereum is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// go-ethereum 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 General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/cmd/utils"
|
||||||
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
"gopkg.in/urfave/cli.v1"
|
||||||
|
)
|
||||||
|
|
||||||
|
var commandStatus = cli.Command{
|
||||||
|
Name: "status",
|
||||||
|
Usage: "Fetches the signers and checkpoint status of the oracle contract",
|
||||||
|
Flags: []cli.Flag{
|
||||||
|
nodeURLFlag,
|
||||||
|
},
|
||||||
|
Action: utils.MigrateFlags(status),
|
||||||
|
}
|
||||||
|
|
||||||
|
// status fetches the admin list of specified registrar contract.
|
||||||
|
func status(ctx *cli.Context) error {
|
||||||
|
// Create a wrapper around the checkpoint oracle contract
|
||||||
|
addr, oracle := newContract(newRPCClient(ctx.GlobalString(nodeURLFlag.Name)))
|
||||||
|
fmt.Printf("Oracle => %s\n", addr.Hex())
|
||||||
|
fmt.Println()
|
||||||
|
|
||||||
|
// Retrieve the list of authorized signers (admins)
|
||||||
|
admins, err := oracle.Contract().GetAllAdmin(nil)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
for i, admin := range admins {
|
||||||
|
fmt.Printf("Admin %d => %s\n", i+1, admin.Hex())
|
||||||
|
}
|
||||||
|
fmt.Println()
|
||||||
|
|
||||||
|
// Retrieve the latest checkpoint
|
||||||
|
index, checkpoint, height, err := oracle.Contract().GetLatestCheckpoint(nil)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
fmt.Printf("Checkpoint (published at #%d) %d => %s\n", height, index, common.Hash(checkpoint).Hex())
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
@ -37,6 +37,7 @@ import (
|
|||||||
"github.com/ethereum/go-ethereum/eth/downloader"
|
"github.com/ethereum/go-ethereum/eth/downloader"
|
||||||
"github.com/ethereum/go-ethereum/ethclient"
|
"github.com/ethereum/go-ethereum/ethclient"
|
||||||
"github.com/ethereum/go-ethereum/internal/debug"
|
"github.com/ethereum/go-ethereum/internal/debug"
|
||||||
|
"github.com/ethereum/go-ethereum/les"
|
||||||
"github.com/ethereum/go-ethereum/log"
|
"github.com/ethereum/go-ethereum/log"
|
||||||
"github.com/ethereum/go-ethereum/metrics"
|
"github.com/ethereum/go-ethereum/metrics"
|
||||||
"github.com/ethereum/go-ethereum/node"
|
"github.com/ethereum/go-ethereum/node"
|
||||||
@ -323,14 +324,33 @@ func startNode(ctx *cli.Context, stack *node.Node) {
|
|||||||
events := make(chan accounts.WalletEvent, 16)
|
events := make(chan accounts.WalletEvent, 16)
|
||||||
stack.AccountManager().Subscribe(events)
|
stack.AccountManager().Subscribe(events)
|
||||||
|
|
||||||
go func() {
|
// Create a client to interact with local geth node.
|
||||||
// Create a chain state reader for self-derivation
|
rpcClient, err := stack.Attach()
|
||||||
rpcClient, err := stack.Attach()
|
if err != nil {
|
||||||
if err != nil {
|
utils.Fatalf("Failed to attach to self: %v", err)
|
||||||
utils.Fatalf("Failed to attach to self: %v", err)
|
}
|
||||||
}
|
ethClient := ethclient.NewClient(rpcClient)
|
||||||
stateReader := ethclient.NewClient(rpcClient)
|
|
||||||
|
|
||||||
|
// Set contract backend for ethereum service if local node
|
||||||
|
// is serving LES requests.
|
||||||
|
if ctx.GlobalInt(utils.LightServFlag.Name) > 0 {
|
||||||
|
var ethService *eth.Ethereum
|
||||||
|
if err := stack.Service(ðService); err != nil {
|
||||||
|
utils.Fatalf("Failed to retrieve ethereum service: %v", err)
|
||||||
|
}
|
||||||
|
ethService.SetContractBackend(ethClient)
|
||||||
|
}
|
||||||
|
// Set contract backend for les service if local node is
|
||||||
|
// running as a light client.
|
||||||
|
if ctx.GlobalString(utils.SyncModeFlag.Name) == "light" {
|
||||||
|
var lesService *les.LightEthereum
|
||||||
|
if err := stack.Service(&lesService); err != nil {
|
||||||
|
utils.Fatalf("Failed to retrieve light ethereum service: %v", err)
|
||||||
|
}
|
||||||
|
lesService.SetContractBackend(ethClient)
|
||||||
|
}
|
||||||
|
|
||||||
|
go func() {
|
||||||
// Open any wallets already attached
|
// Open any wallets already attached
|
||||||
for _, wallet := range stack.AccountManager().Wallets() {
|
for _, wallet := range stack.AccountManager().Wallets() {
|
||||||
if err := wallet.Open(""); err != nil {
|
if err := wallet.Open(""); err != nil {
|
||||||
@ -354,7 +374,7 @@ func startNode(ctx *cli.Context, stack *node.Node) {
|
|||||||
}
|
}
|
||||||
derivationPaths = append(derivationPaths, accounts.DefaultBaseDerivationPath)
|
derivationPaths = append(derivationPaths, accounts.DefaultBaseDerivationPath)
|
||||||
|
|
||||||
event.Wallet.SelfDerive(derivationPaths, stateReader)
|
event.Wallet.SelfDerive(derivationPaths, ethClient)
|
||||||
|
|
||||||
case accounts.WalletDropped:
|
case accounts.WalletDropped:
|
||||||
log.Info("Old wallet dropped", "url", event.Wallet.URL())
|
log.Info("Old wallet dropped", "url", event.Wallet.URL())
|
||||||
@ -383,7 +403,6 @@ func startNode(ctx *cli.Context, stack *node.Node) {
|
|||||||
"age", common.PrettyAge(timestamp))
|
"age", common.PrettyAge(timestamp))
|
||||||
stack.Stop()
|
stack.Stop()
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
|
@ -44,12 +44,13 @@ func (w *wizard) makeGenesis() {
|
|||||||
Difficulty: big.NewInt(524288),
|
Difficulty: big.NewInt(524288),
|
||||||
Alloc: make(core.GenesisAlloc),
|
Alloc: make(core.GenesisAlloc),
|
||||||
Config: ¶ms.ChainConfig{
|
Config: ¶ms.ChainConfig{
|
||||||
HomesteadBlock: big.NewInt(1),
|
HomesteadBlock: big.NewInt(0),
|
||||||
EIP150Block: big.NewInt(2),
|
EIP150Block: big.NewInt(0),
|
||||||
EIP155Block: big.NewInt(3),
|
EIP155Block: big.NewInt(0),
|
||||||
EIP158Block: big.NewInt(3),
|
EIP158Block: big.NewInt(0),
|
||||||
ByzantiumBlock: big.NewInt(4),
|
ByzantiumBlock: big.NewInt(0),
|
||||||
ConstantinopleBlock: big.NewInt(5),
|
ConstantinopleBlock: big.NewInt(0),
|
||||||
|
PetersburgBlock: big.NewInt(0),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
// Figure out which consensus engine to choose
|
// Figure out which consensus engine to choose
|
||||||
@ -191,7 +192,7 @@ func (w *wizard) importGenesis() {
|
|||||||
func (w *wizard) manageGenesis() {
|
func (w *wizard) manageGenesis() {
|
||||||
// Figure out whether to modify or export the genesis
|
// Figure out whether to modify or export the genesis
|
||||||
fmt.Println()
|
fmt.Println()
|
||||||
fmt.Println(" 1. Modify existing fork rules")
|
fmt.Println(" 1. Modify existing configurations")
|
||||||
fmt.Println(" 2. Export genesis configurations")
|
fmt.Println(" 2. Export genesis configurations")
|
||||||
fmt.Println(" 3. Remove genesis configuration")
|
fmt.Println(" 3. Remove genesis configuration")
|
||||||
|
|
||||||
@ -226,7 +227,7 @@ func (w *wizard) manageGenesis() {
|
|||||||
w.conf.Genesis.Config.PetersburgBlock = w.conf.Genesis.Config.ConstantinopleBlock
|
w.conf.Genesis.Config.PetersburgBlock = w.conf.Genesis.Config.ConstantinopleBlock
|
||||||
}
|
}
|
||||||
fmt.Println()
|
fmt.Println()
|
||||||
fmt.Printf("Which block should Constantinople-Fix (remove EIP-1283) come into effect? (default = %v)\n", w.conf.Genesis.Config.PetersburgBlock)
|
fmt.Printf("Which block should Petersburg come into effect? (default = %v)\n", w.conf.Genesis.Config.PetersburgBlock)
|
||||||
w.conf.Genesis.Config.PetersburgBlock = w.readDefaultBigInt(w.conf.Genesis.Config.PetersburgBlock)
|
w.conf.Genesis.Config.PetersburgBlock = w.readDefaultBigInt(w.conf.Genesis.Config.PetersburgBlock)
|
||||||
|
|
||||||
out, _ := json.MarshalIndent(w.conf.Genesis.Config, "", " ")
|
out, _ := json.MarshalIndent(w.conf.Genesis.Config, "", " ")
|
||||||
|
415
contracts/checkpointoracle/contract/oracle.go
Normal file
415
contracts/checkpointoracle/contract/oracle.go
Normal file
@ -0,0 +1,415 @@
|
|||||||
|
// Code generated - DO NOT EDIT.
|
||||||
|
// This file is a generated binding and any manual changes will be lost.
|
||||||
|
|
||||||
|
package contract
|
||||||
|
|
||||||
|
import (
|
||||||
|
"math/big"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
ethereum "github.com/ethereum/go-ethereum"
|
||||||
|
"github.com/ethereum/go-ethereum/accounts/abi"
|
||||||
|
"github.com/ethereum/go-ethereum/accounts/abi/bind"
|
||||||
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
|
"github.com/ethereum/go-ethereum/event"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Reference imports to suppress errors if they are not otherwise used.
|
||||||
|
var (
|
||||||
|
_ = big.NewInt
|
||||||
|
_ = strings.NewReader
|
||||||
|
_ = ethereum.NotFound
|
||||||
|
_ = abi.U256
|
||||||
|
_ = bind.Bind
|
||||||
|
_ = common.Big1
|
||||||
|
_ = types.BloomLookup
|
||||||
|
_ = event.NewSubscription
|
||||||
|
)
|
||||||
|
|
||||||
|
// CheckpointOracleABI is the input ABI used to generate the binding from.
|
||||||
|
const CheckpointOracleABI = "[{\"constant\":true,\"inputs\":[],\"name\":\"GetAllAdmin\",\"outputs\":[{\"name\":\"\",\"type\":\"address[]\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"GetLatestCheckpoint\",\"outputs\":[{\"name\":\"\",\"type\":\"uint64\"},{\"name\":\"\",\"type\":\"bytes32\"},{\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"_recentNumber\",\"type\":\"uint256\"},{\"name\":\"_recentHash\",\"type\":\"bytes32\"},{\"name\":\"_hash\",\"type\":\"bytes32\"},{\"name\":\"_sectionIndex\",\"type\":\"uint64\"},{\"name\":\"v\",\"type\":\"uint8[]\"},{\"name\":\"r\",\"type\":\"bytes32[]\"},{\"name\":\"s\",\"type\":\"bytes32[]\"}],\"name\":\"SetCheckpoint\",\"outputs\":[{\"name\":\"\",\"type\":\"bool\"}],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"name\":\"_adminlist\",\"type\":\"address[]\"},{\"name\":\"_sectionSize\",\"type\":\"uint256\"},{\"name\":\"_processConfirms\",\"type\":\"uint256\"},{\"name\":\"_threshold\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"name\":\"index\",\"type\":\"uint64\"},{\"indexed\":false,\"name\":\"checkpointHash\",\"type\":\"bytes32\"},{\"indexed\":false,\"name\":\"v\",\"type\":\"uint8\"},{\"indexed\":false,\"name\":\"r\",\"type\":\"bytes32\"},{\"indexed\":false,\"name\":\"s\",\"type\":\"bytes32\"}],\"name\":\"NewCheckpointVote\",\"type\":\"event\"}]"
|
||||||
|
|
||||||
|
// CheckpointOracleBin is the compiled bytecode used for deploying new contracts.
|
||||||
|
const CheckpointOracleBin = `0x608060405234801561001057600080fd5b506040516108153803806108158339818101604052608081101561003357600080fd5b81019080805164010000000081111561004b57600080fd5b8201602081018481111561005e57600080fd5b815185602082028301116401000000008211171561007b57600080fd5b505060208201516040830151606090930151919450925060005b84518110156101415760016000808784815181106100af57fe5b60200260200101516001600160a01b03166001600160a01b0316815260200190815260200160002060006101000a81548160ff02191690831515021790555060018582815181106100fc57fe5b60209081029190910181015182546001808201855560009485529290932090920180546001600160a01b0319166001600160a01b039093169290921790915501610095565b50600592909255600655600755506106b78061015e6000396000f3fe608060405234801561001057600080fd5b50600436106100415760003560e01c806345848dfc146100465780634d6a304c1461009e578063d459fc46146100cf575b600080fd5b61004e6102b0565b60408051602080825283518183015283519192839290830191858101910280838360005b8381101561008a578181015183820152602001610072565b505050509050019250505060405180910390f35b6100a661034f565b6040805167ffffffffffffffff9094168452602084019290925282820152519081900360600190f35b61029c600480360360e08110156100e557600080fd5b81359160208101359160408201359167ffffffffffffffff6060820135169181019060a08101608082013564010000000081111561012257600080fd5b82018360208201111561013457600080fd5b8035906020019184602083028401116401000000008311171561015657600080fd5b91908080602002602001604051908101604052809392919081815260200183836020028082843760009201919091525092959493602081019350359150506401000000008111156101a657600080fd5b8201836020820111156101b857600080fd5b803590602001918460208302840111640100000000831117156101da57600080fd5b919080806020026020016040519081016040528093929190818152602001838360200280828437600092019190915250929594936020810193503591505064010000000081111561022a57600080fd5b82018360208201111561023c57600080fd5b8035906020019184602083028401116401000000008311171561025e57600080fd5b91908080602002602001604051908101604052809392919081815260200183836020028082843760009201919091525092955061036a945050505050565b604080519115158252519081900360200190f35b6060806001805490506040519080825280602002602001820160405280156102e2578160200160208202803883390190505b50905060005b60015481101561034957600181815481106102ff57fe5b9060005260206000200160009054906101000a90046001600160a01b031682828151811061032957fe5b6001600160a01b03909216602092830291909101909101526001016102e8565b50905090565b60025460045460035467ffffffffffffffff90921691909192565b3360009081526020819052604081205460ff1661038657600080fd5b8688401461039357600080fd5b82518451146103a157600080fd5b81518451146103af57600080fd5b6006546005548660010167ffffffffffffffff1602014310156103d457506000610677565b60025467ffffffffffffffff90811690861610156103f457506000610677565b60025467ffffffffffffffff8681169116148015610426575067ffffffffffffffff8516151580610426575060035415155b1561043357506000610677565b8561044057506000610677565b60408051601960f81b6020808301919091526000602183018190523060601b60228401526001600160c01b031960c08a901b166036840152603e8084018b905284518085039091018152605e909301909352815191012090805b86518110156106715760006001848984815181106104b457fe5b60200260200101518985815181106104c857fe5b60200260200101518986815181106104dc57fe5b602002602001015160405160008152602001604052604051808581526020018460ff1660ff1681526020018381526020018281526020019450505050506020604051602081039080840390855afa15801561053b573d6000803e3d6000fd5b505060408051601f1901516001600160a01b03811660009081526020819052919091205490925060ff16905061057057600080fd5b826001600160a01b0316816001600160a01b03161161058e57600080fd5b8092508867ffffffffffffffff167fce51ffa16246bcaf0899f6504f473cd0114f430f566cef71ab7e03d3dde42a418b8a85815181106105ca57fe5b60200260200101518a86815181106105de57fe5b60200260200101518a87815181106105f257fe5b6020026020010151604051808581526020018460ff1660ff16815260200183815260200182815260200194505050505060405180910390a260075482600101106106685750505060048790555050436003556002805467ffffffffffffffff191667ffffffffffffffff86161790556001610677565b5060010161049a565b50600080fd5b97965050505050505056fea265627a7a723058207f6a191ce575596a2f1e907c8c0a01003d16b69fb2c4f432d10878e8c0a99a0264736f6c634300050a0032`
|
||||||
|
|
||||||
|
// DeployCheckpointOracle deploys a new Ethereum contract, binding an instance of CheckpointOracle to it.
|
||||||
|
func DeployCheckpointOracle(auth *bind.TransactOpts, backend bind.ContractBackend, _adminlist []common.Address, _sectionSize *big.Int, _processConfirms *big.Int, _threshold *big.Int) (common.Address, *types.Transaction, *CheckpointOracle, error) {
|
||||||
|
parsed, err := abi.JSON(strings.NewReader(CheckpointOracleABI))
|
||||||
|
if err != nil {
|
||||||
|
return common.Address{}, nil, nil, err
|
||||||
|
}
|
||||||
|
address, tx, contract, err := bind.DeployContract(auth, parsed, common.FromHex(CheckpointOracleBin), backend, _adminlist, _sectionSize, _processConfirms, _threshold)
|
||||||
|
if err != nil {
|
||||||
|
return common.Address{}, nil, nil, err
|
||||||
|
}
|
||||||
|
return address, tx, &CheckpointOracle{CheckpointOracleCaller: CheckpointOracleCaller{contract: contract}, CheckpointOracleTransactor: CheckpointOracleTransactor{contract: contract}, CheckpointOracleFilterer: CheckpointOracleFilterer{contract: contract}}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// CheckpointOracle is an auto generated Go binding around an Ethereum contract.
|
||||||
|
type CheckpointOracle struct {
|
||||||
|
CheckpointOracleCaller // Read-only binding to the contract
|
||||||
|
CheckpointOracleTransactor // Write-only binding to the contract
|
||||||
|
CheckpointOracleFilterer // Log filterer for contract events
|
||||||
|
}
|
||||||
|
|
||||||
|
// CheckpointOracleCaller is an auto generated read-only Go binding around an Ethereum contract.
|
||||||
|
type CheckpointOracleCaller struct {
|
||||||
|
contract *bind.BoundContract // Generic contract wrapper for the low level calls
|
||||||
|
}
|
||||||
|
|
||||||
|
// CheckpointOracleTransactor is an auto generated write-only Go binding around an Ethereum contract.
|
||||||
|
type CheckpointOracleTransactor struct {
|
||||||
|
contract *bind.BoundContract // Generic contract wrapper for the low level calls
|
||||||
|
}
|
||||||
|
|
||||||
|
// CheckpointOracleFilterer is an auto generated log filtering Go binding around an Ethereum contract events.
|
||||||
|
type CheckpointOracleFilterer struct {
|
||||||
|
contract *bind.BoundContract // Generic contract wrapper for the low level calls
|
||||||
|
}
|
||||||
|
|
||||||
|
// CheckpointOracleSession is an auto generated Go binding around an Ethereum contract,
|
||||||
|
// with pre-set call and transact options.
|
||||||
|
type CheckpointOracleSession struct {
|
||||||
|
Contract *CheckpointOracle // Generic contract binding to set the session for
|
||||||
|
CallOpts bind.CallOpts // Call options to use throughout this session
|
||||||
|
TransactOpts bind.TransactOpts // Transaction auth options to use throughout this session
|
||||||
|
}
|
||||||
|
|
||||||
|
// CheckpointOracleCallerSession is an auto generated read-only Go binding around an Ethereum contract,
|
||||||
|
// with pre-set call options.
|
||||||
|
type CheckpointOracleCallerSession struct {
|
||||||
|
Contract *CheckpointOracleCaller // Generic contract caller binding to set the session for
|
||||||
|
CallOpts bind.CallOpts // Call options to use throughout this session
|
||||||
|
}
|
||||||
|
|
||||||
|
// CheckpointOracleTransactorSession is an auto generated write-only Go binding around an Ethereum contract,
|
||||||
|
// with pre-set transact options.
|
||||||
|
type CheckpointOracleTransactorSession struct {
|
||||||
|
Contract *CheckpointOracleTransactor // Generic contract transactor binding to set the session for
|
||||||
|
TransactOpts bind.TransactOpts // Transaction auth options to use throughout this session
|
||||||
|
}
|
||||||
|
|
||||||
|
// CheckpointOracleRaw is an auto generated low-level Go binding around an Ethereum contract.
|
||||||
|
type CheckpointOracleRaw struct {
|
||||||
|
Contract *CheckpointOracle // Generic contract binding to access the raw methods on
|
||||||
|
}
|
||||||
|
|
||||||
|
// CheckpointOracleCallerRaw is an auto generated low-level read-only Go binding around an Ethereum contract.
|
||||||
|
type CheckpointOracleCallerRaw struct {
|
||||||
|
Contract *CheckpointOracleCaller // Generic read-only contract binding to access the raw methods on
|
||||||
|
}
|
||||||
|
|
||||||
|
// CheckpointOracleTransactorRaw is an auto generated low-level write-only Go binding around an Ethereum contract.
|
||||||
|
type CheckpointOracleTransactorRaw struct {
|
||||||
|
Contract *CheckpointOracleTransactor // Generic write-only contract binding to access the raw methods on
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewCheckpointOracle creates a new instance of CheckpointOracle, bound to a specific deployed contract.
|
||||||
|
func NewCheckpointOracle(address common.Address, backend bind.ContractBackend) (*CheckpointOracle, error) {
|
||||||
|
contract, err := bindCheckpointOracle(address, backend, backend, backend)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &CheckpointOracle{CheckpointOracleCaller: CheckpointOracleCaller{contract: contract}, CheckpointOracleTransactor: CheckpointOracleTransactor{contract: contract}, CheckpointOracleFilterer: CheckpointOracleFilterer{contract: contract}}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewCheckpointOracleCaller creates a new read-only instance of CheckpointOracle, bound to a specific deployed contract.
|
||||||
|
func NewCheckpointOracleCaller(address common.Address, caller bind.ContractCaller) (*CheckpointOracleCaller, error) {
|
||||||
|
contract, err := bindCheckpointOracle(address, caller, nil, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &CheckpointOracleCaller{contract: contract}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewCheckpointOracleTransactor creates a new write-only instance of CheckpointOracle, bound to a specific deployed contract.
|
||||||
|
func NewCheckpointOracleTransactor(address common.Address, transactor bind.ContractTransactor) (*CheckpointOracleTransactor, error) {
|
||||||
|
contract, err := bindCheckpointOracle(address, nil, transactor, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &CheckpointOracleTransactor{contract: contract}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewCheckpointOracleFilterer creates a new log filterer instance of CheckpointOracle, bound to a specific deployed contract.
|
||||||
|
func NewCheckpointOracleFilterer(address common.Address, filterer bind.ContractFilterer) (*CheckpointOracleFilterer, error) {
|
||||||
|
contract, err := bindCheckpointOracle(address, nil, nil, filterer)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &CheckpointOracleFilterer{contract: contract}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// bindCheckpointOracle binds a generic wrapper to an already deployed contract.
|
||||||
|
func bindCheckpointOracle(address common.Address, caller bind.ContractCaller, transactor bind.ContractTransactor, filterer bind.ContractFilterer) (*bind.BoundContract, error) {
|
||||||
|
parsed, err := abi.JSON(strings.NewReader(CheckpointOracleABI))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return bind.NewBoundContract(address, parsed, caller, transactor, filterer), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Call invokes the (constant) contract method with params as input values and
|
||||||
|
// sets the output to result. The result type might be a single field for simple
|
||||||
|
// returns, a slice of interfaces for anonymous returns and a struct for named
|
||||||
|
// returns.
|
||||||
|
func (_CheckpointOracle *CheckpointOracleRaw) Call(opts *bind.CallOpts, result interface{}, method string, params ...interface{}) error {
|
||||||
|
return _CheckpointOracle.Contract.CheckpointOracleCaller.contract.Call(opts, result, method, params...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Transfer initiates a plain transaction to move funds to the contract, calling
|
||||||
|
// its default method if one is available.
|
||||||
|
func (_CheckpointOracle *CheckpointOracleRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) {
|
||||||
|
return _CheckpointOracle.Contract.CheckpointOracleTransactor.contract.Transfer(opts)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Transact invokes the (paid) contract method with params as input values.
|
||||||
|
func (_CheckpointOracle *CheckpointOracleRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) {
|
||||||
|
return _CheckpointOracle.Contract.CheckpointOracleTransactor.contract.Transact(opts, method, params...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Call invokes the (constant) contract method with params as input values and
|
||||||
|
// sets the output to result. The result type might be a single field for simple
|
||||||
|
// returns, a slice of interfaces for anonymous returns and a struct for named
|
||||||
|
// returns.
|
||||||
|
func (_CheckpointOracle *CheckpointOracleCallerRaw) Call(opts *bind.CallOpts, result interface{}, method string, params ...interface{}) error {
|
||||||
|
return _CheckpointOracle.Contract.contract.Call(opts, result, method, params...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Transfer initiates a plain transaction to move funds to the contract, calling
|
||||||
|
// its default method if one is available.
|
||||||
|
func (_CheckpointOracle *CheckpointOracleTransactorRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) {
|
||||||
|
return _CheckpointOracle.Contract.contract.Transfer(opts)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Transact invokes the (paid) contract method with params as input values.
|
||||||
|
func (_CheckpointOracle *CheckpointOracleTransactorRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) {
|
||||||
|
return _CheckpointOracle.Contract.contract.Transact(opts, method, params...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetAllAdmin is a free data retrieval call binding the contract method 0x45848dfc.
|
||||||
|
//
|
||||||
|
// Solidity: function GetAllAdmin() constant returns(address[])
|
||||||
|
func (_CheckpointOracle *CheckpointOracleCaller) GetAllAdmin(opts *bind.CallOpts) ([]common.Address, error) {
|
||||||
|
var (
|
||||||
|
ret0 = new([]common.Address)
|
||||||
|
)
|
||||||
|
out := ret0
|
||||||
|
err := _CheckpointOracle.contract.Call(opts, out, "GetAllAdmin")
|
||||||
|
return *ret0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetAllAdmin is a free data retrieval call binding the contract method 0x45848dfc.
|
||||||
|
//
|
||||||
|
// Solidity: function GetAllAdmin() constant returns(address[])
|
||||||
|
func (_CheckpointOracle *CheckpointOracleSession) GetAllAdmin() ([]common.Address, error) {
|
||||||
|
return _CheckpointOracle.Contract.GetAllAdmin(&_CheckpointOracle.CallOpts)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetAllAdmin is a free data retrieval call binding the contract method 0x45848dfc.
|
||||||
|
//
|
||||||
|
// Solidity: function GetAllAdmin() constant returns(address[])
|
||||||
|
func (_CheckpointOracle *CheckpointOracleCallerSession) GetAllAdmin() ([]common.Address, error) {
|
||||||
|
return _CheckpointOracle.Contract.GetAllAdmin(&_CheckpointOracle.CallOpts)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetLatestCheckpoint is a free data retrieval call binding the contract method 0x4d6a304c.
|
||||||
|
//
|
||||||
|
// Solidity: function GetLatestCheckpoint() constant returns(uint64, bytes32, uint256)
|
||||||
|
func (_CheckpointOracle *CheckpointOracleCaller) GetLatestCheckpoint(opts *bind.CallOpts) (uint64, [32]byte, *big.Int, error) {
|
||||||
|
var (
|
||||||
|
ret0 = new(uint64)
|
||||||
|
ret1 = new([32]byte)
|
||||||
|
ret2 = new(*big.Int)
|
||||||
|
)
|
||||||
|
out := &[]interface{}{
|
||||||
|
ret0,
|
||||||
|
ret1,
|
||||||
|
ret2,
|
||||||
|
}
|
||||||
|
err := _CheckpointOracle.contract.Call(opts, out, "GetLatestCheckpoint")
|
||||||
|
return *ret0, *ret1, *ret2, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetLatestCheckpoint is a free data retrieval call binding the contract method 0x4d6a304c.
|
||||||
|
//
|
||||||
|
// Solidity: function GetLatestCheckpoint() constant returns(uint64, bytes32, uint256)
|
||||||
|
func (_CheckpointOracle *CheckpointOracleSession) GetLatestCheckpoint() (uint64, [32]byte, *big.Int, error) {
|
||||||
|
return _CheckpointOracle.Contract.GetLatestCheckpoint(&_CheckpointOracle.CallOpts)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetLatestCheckpoint is a free data retrieval call binding the contract method 0x4d6a304c.
|
||||||
|
//
|
||||||
|
// Solidity: function GetLatestCheckpoint() constant returns(uint64, bytes32, uint256)
|
||||||
|
func (_CheckpointOracle *CheckpointOracleCallerSession) GetLatestCheckpoint() (uint64, [32]byte, *big.Int, error) {
|
||||||
|
return _CheckpointOracle.Contract.GetLatestCheckpoint(&_CheckpointOracle.CallOpts)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetCheckpoint is a paid mutator transaction binding the contract method 0xd459fc46.
|
||||||
|
//
|
||||||
|
// Solidity: function SetCheckpoint(uint256 _recentNumber, bytes32 _recentHash, bytes32 _hash, uint64 _sectionIndex, uint8[] v, bytes32[] r, bytes32[] s) returns(bool)
|
||||||
|
func (_CheckpointOracle *CheckpointOracleTransactor) SetCheckpoint(opts *bind.TransactOpts, _recentNumber *big.Int, _recentHash [32]byte, _hash [32]byte, _sectionIndex uint64, v []uint8, r [][32]byte, s [][32]byte) (*types.Transaction, error) {
|
||||||
|
return _CheckpointOracle.contract.Transact(opts, "SetCheckpoint", _recentNumber, _recentHash, _hash, _sectionIndex, v, r, s)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetCheckpoint is a paid mutator transaction binding the contract method 0xd459fc46.
|
||||||
|
//
|
||||||
|
// Solidity: function SetCheckpoint(uint256 _recentNumber, bytes32 _recentHash, bytes32 _hash, uint64 _sectionIndex, uint8[] v, bytes32[] r, bytes32[] s) returns(bool)
|
||||||
|
func (_CheckpointOracle *CheckpointOracleSession) SetCheckpoint(_recentNumber *big.Int, _recentHash [32]byte, _hash [32]byte, _sectionIndex uint64, v []uint8, r [][32]byte, s [][32]byte) (*types.Transaction, error) {
|
||||||
|
return _CheckpointOracle.Contract.SetCheckpoint(&_CheckpointOracle.TransactOpts, _recentNumber, _recentHash, _hash, _sectionIndex, v, r, s)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetCheckpoint is a paid mutator transaction binding the contract method 0xd459fc46.
|
||||||
|
//
|
||||||
|
// Solidity: function SetCheckpoint(uint256 _recentNumber, bytes32 _recentHash, bytes32 _hash, uint64 _sectionIndex, uint8[] v, bytes32[] r, bytes32[] s) returns(bool)
|
||||||
|
func (_CheckpointOracle *CheckpointOracleTransactorSession) SetCheckpoint(_recentNumber *big.Int, _recentHash [32]byte, _hash [32]byte, _sectionIndex uint64, v []uint8, r [][32]byte, s [][32]byte) (*types.Transaction, error) {
|
||||||
|
return _CheckpointOracle.Contract.SetCheckpoint(&_CheckpointOracle.TransactOpts, _recentNumber, _recentHash, _hash, _sectionIndex, v, r, s)
|
||||||
|
}
|
||||||
|
|
||||||
|
// CheckpointOracleNewCheckpointVoteIterator is returned from FilterNewCheckpointVote and is used to iterate over the raw logs and unpacked data for NewCheckpointVote events raised by the CheckpointOracle contract.
|
||||||
|
type CheckpointOracleNewCheckpointVoteIterator struct {
|
||||||
|
Event *CheckpointOracleNewCheckpointVote // Event containing the contract specifics and raw log
|
||||||
|
|
||||||
|
contract *bind.BoundContract // Generic contract to use for unpacking event data
|
||||||
|
event string // Event name to use for unpacking event data
|
||||||
|
|
||||||
|
logs chan types.Log // Log channel receiving the found contract events
|
||||||
|
sub ethereum.Subscription // Subscription for errors, completion and termination
|
||||||
|
done bool // Whether the subscription completed delivering logs
|
||||||
|
fail error // Occurred error to stop iteration
|
||||||
|
}
|
||||||
|
|
||||||
|
// Next advances the iterator to the subsequent event, returning whether there
|
||||||
|
// are any more events found. In case of a retrieval or parsing error, false is
|
||||||
|
// returned and Error() can be queried for the exact failure.
|
||||||
|
func (it *CheckpointOracleNewCheckpointVoteIterator) Next() bool {
|
||||||
|
// If the iterator failed, stop iterating
|
||||||
|
if it.fail != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
// If the iterator completed, deliver directly whatever's available
|
||||||
|
if it.done {
|
||||||
|
select {
|
||||||
|
case log := <-it.logs:
|
||||||
|
it.Event = new(CheckpointOracleNewCheckpointVote)
|
||||||
|
if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil {
|
||||||
|
it.fail = err
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
it.Event.Raw = log
|
||||||
|
return true
|
||||||
|
|
||||||
|
default:
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Iterator still in progress, wait for either a data or an error event
|
||||||
|
select {
|
||||||
|
case log := <-it.logs:
|
||||||
|
it.Event = new(CheckpointOracleNewCheckpointVote)
|
||||||
|
if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil {
|
||||||
|
it.fail = err
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
it.Event.Raw = log
|
||||||
|
return true
|
||||||
|
|
||||||
|
case err := <-it.sub.Err():
|
||||||
|
it.done = true
|
||||||
|
it.fail = err
|
||||||
|
return it.Next()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Error returns any retrieval or parsing error occurred during filtering.
|
||||||
|
func (it *CheckpointOracleNewCheckpointVoteIterator) Error() error {
|
||||||
|
return it.fail
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close terminates the iteration process, releasing any pending underlying
|
||||||
|
// resources.
|
||||||
|
func (it *CheckpointOracleNewCheckpointVoteIterator) Close() error {
|
||||||
|
it.sub.Unsubscribe()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// CheckpointOracleNewCheckpointVote represents a NewCheckpointVote event raised by the CheckpointOracle contract.
|
||||||
|
type CheckpointOracleNewCheckpointVote struct {
|
||||||
|
Index uint64
|
||||||
|
CheckpointHash [32]byte
|
||||||
|
V uint8
|
||||||
|
R [32]byte
|
||||||
|
S [32]byte
|
||||||
|
Raw types.Log // Blockchain specific contextual infos
|
||||||
|
}
|
||||||
|
|
||||||
|
// FilterNewCheckpointVote is a free log retrieval operation binding the contract event 0xce51ffa16246bcaf0899f6504f473cd0114f430f566cef71ab7e03d3dde42a41.
|
||||||
|
//
|
||||||
|
// Solidity: event NewCheckpointVote(uint64 indexed index, bytes32 checkpointHash, uint8 v, bytes32 r, bytes32 s)
|
||||||
|
func (_CheckpointOracle *CheckpointOracleFilterer) FilterNewCheckpointVote(opts *bind.FilterOpts, index []uint64) (*CheckpointOracleNewCheckpointVoteIterator, error) {
|
||||||
|
|
||||||
|
var indexRule []interface{}
|
||||||
|
for _, indexItem := range index {
|
||||||
|
indexRule = append(indexRule, indexItem)
|
||||||
|
}
|
||||||
|
|
||||||
|
logs, sub, err := _CheckpointOracle.contract.FilterLogs(opts, "NewCheckpointVote", indexRule)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &CheckpointOracleNewCheckpointVoteIterator{contract: _CheckpointOracle.contract, event: "NewCheckpointVote", logs: logs, sub: sub}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// WatchNewCheckpointVote is a free log subscription operation binding the contract event 0xce51ffa16246bcaf0899f6504f473cd0114f430f566cef71ab7e03d3dde42a41.
|
||||||
|
//
|
||||||
|
// Solidity: event NewCheckpointVote(uint64 indexed index, bytes32 checkpointHash, uint8 v, bytes32 r, bytes32 s)
|
||||||
|
func (_CheckpointOracle *CheckpointOracleFilterer) WatchNewCheckpointVote(opts *bind.WatchOpts, sink chan<- *CheckpointOracleNewCheckpointVote, index []uint64) (event.Subscription, error) {
|
||||||
|
|
||||||
|
var indexRule []interface{}
|
||||||
|
for _, indexItem := range index {
|
||||||
|
indexRule = append(indexRule, indexItem)
|
||||||
|
}
|
||||||
|
|
||||||
|
logs, sub, err := _CheckpointOracle.contract.WatchLogs(opts, "NewCheckpointVote", indexRule)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return event.NewSubscription(func(quit <-chan struct{}) error {
|
||||||
|
defer sub.Unsubscribe()
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case log := <-logs:
|
||||||
|
// New log arrived, parse the event and forward to the user
|
||||||
|
event := new(CheckpointOracleNewCheckpointVote)
|
||||||
|
if err := _CheckpointOracle.contract.UnpackLog(event, "NewCheckpointVote", log); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
event.Raw = log
|
||||||
|
|
||||||
|
select {
|
||||||
|
case sink <- event:
|
||||||
|
case err := <-sub.Err():
|
||||||
|
return err
|
||||||
|
case <-quit:
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
case err := <-sub.Err():
|
||||||
|
return err
|
||||||
|
case <-quit:
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ParseNewCheckpointVote is a log parse operation binding the contract event 0xce51ffa16246bcaf0899f6504f473cd0114f430f566cef71ab7e03d3dde42a41.
|
||||||
|
//
|
||||||
|
// Solidity: event NewCheckpointVote(uint64 indexed index, bytes32 checkpointHash, uint8 v, bytes32 r, bytes32 s)
|
||||||
|
func (_CheckpointOracle *CheckpointOracleFilterer) ParseNewCheckpointVote(log types.Log) (*CheckpointOracleNewCheckpointVote, error) {
|
||||||
|
event := new(CheckpointOracleNewCheckpointVote)
|
||||||
|
if err := _CheckpointOracle.contract.UnpackLog(event, "NewCheckpointVote", log); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return event, nil
|
||||||
|
}
|
174
contracts/checkpointoracle/contract/oracle.sol
Normal file
174
contracts/checkpointoracle/contract/oracle.sol
Normal file
@ -0,0 +1,174 @@
|
|||||||
|
pragma solidity ^0.5.10;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @title CheckpointOracle
|
||||||
|
* @author Gary Rong<garyrong@ethereum.org>, Martin Swende <martin.swende@ethereum.org>
|
||||||
|
* @dev Implementation of the blockchain checkpoint registrar.
|
||||||
|
*/
|
||||||
|
contract CheckpointOracle {
|
||||||
|
/*
|
||||||
|
Events
|
||||||
|
*/
|
||||||
|
|
||||||
|
// NewCheckpointVote is emitted when a new checkpoint proposal receives a vote.
|
||||||
|
event NewCheckpointVote(uint64 indexed index, bytes32 checkpointHash, uint8 v, bytes32 r, bytes32 s);
|
||||||
|
|
||||||
|
/*
|
||||||
|
Public Functions
|
||||||
|
*/
|
||||||
|
constructor(address[] memory _adminlist, uint _sectionSize, uint _processConfirms, uint _threshold) public {
|
||||||
|
for (uint i = 0; i < _adminlist.length; i++) {
|
||||||
|
admins[_adminlist[i]] = true;
|
||||||
|
adminList.push(_adminlist[i]);
|
||||||
|
}
|
||||||
|
sectionSize = _sectionSize;
|
||||||
|
processConfirms = _processConfirms;
|
||||||
|
threshold = _threshold;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dev Get latest stable checkpoint information.
|
||||||
|
* @return section index
|
||||||
|
* @return checkpoint hash
|
||||||
|
* @return block height associated with checkpoint
|
||||||
|
*/
|
||||||
|
function GetLatestCheckpoint()
|
||||||
|
view
|
||||||
|
public
|
||||||
|
returns(uint64, bytes32, uint) {
|
||||||
|
return (sectionIndex, hash, height);
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetCheckpoint sets a new checkpoint. It accepts a list of signatures
|
||||||
|
// @_recentNumber: a recent blocknumber, for replay protection
|
||||||
|
// @_recentHash : the hash of `_recentNumber`
|
||||||
|
// @_hash : the hash to set at _sectionIndex
|
||||||
|
// @_sectionIndex : the section index to set
|
||||||
|
// @v : the list of v-values
|
||||||
|
// @r : the list or r-values
|
||||||
|
// @s : the list of s-values
|
||||||
|
function SetCheckpoint(
|
||||||
|
uint _recentNumber,
|
||||||
|
bytes32 _recentHash,
|
||||||
|
bytes32 _hash,
|
||||||
|
uint64 _sectionIndex,
|
||||||
|
uint8[] memory v,
|
||||||
|
bytes32[] memory r,
|
||||||
|
bytes32[] memory s)
|
||||||
|
public
|
||||||
|
returns (bool)
|
||||||
|
{
|
||||||
|
// Ensure the sender is authorized.
|
||||||
|
require(admins[msg.sender]);
|
||||||
|
|
||||||
|
// These checks replay protection, so it cannot be replayed on forks,
|
||||||
|
// accidentally or intentionally
|
||||||
|
require(blockhash(_recentNumber) == _recentHash);
|
||||||
|
|
||||||
|
// Ensure the batch of signatures are valid.
|
||||||
|
require(v.length == r.length);
|
||||||
|
require(v.length == s.length);
|
||||||
|
|
||||||
|
// Filter out "future" checkpoint.
|
||||||
|
if (block.number < (_sectionIndex+1)*sectionSize+processConfirms) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// Filter out "old" announcement
|
||||||
|
if (_sectionIndex < sectionIndex) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// Filter out "stale" announcement
|
||||||
|
if (_sectionIndex == sectionIndex && (_sectionIndex != 0 || height != 0)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// Filter out "invalid" announcement
|
||||||
|
if (_hash == ""){
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// EIP 191 style signatures
|
||||||
|
//
|
||||||
|
// Arguments when calculating hash to validate
|
||||||
|
// 1: byte(0x19) - the initial 0x19 byte
|
||||||
|
// 2: byte(0) - the version byte (data with intended validator)
|
||||||
|
// 3: this - the validator address
|
||||||
|
// -- Application specific data
|
||||||
|
// 4 : checkpoint section_index(uint64)
|
||||||
|
// 5 : checkpoint hash (bytes32)
|
||||||
|
// hash = keccak256(checkpoint_index, section_head, cht_root, bloom_root)
|
||||||
|
bytes32 signedHash = keccak256(abi.encodePacked(byte(0x19), byte(0), this, _sectionIndex, _hash));
|
||||||
|
|
||||||
|
address lastVoter = address(0);
|
||||||
|
|
||||||
|
// In order for us not to have to maintain a mapping of who has already
|
||||||
|
// voted, and we don't want to count a vote twice, the signatures must
|
||||||
|
// be submitted in strict ordering.
|
||||||
|
for (uint idx = 0; idx < v.length; idx++){
|
||||||
|
address signer = ecrecover(signedHash, v[idx], r[idx], s[idx]);
|
||||||
|
require(admins[signer]);
|
||||||
|
require(uint256(signer) > uint256(lastVoter));
|
||||||
|
lastVoter = signer;
|
||||||
|
emit NewCheckpointVote(_sectionIndex, _hash, v[idx], r[idx], s[idx]);
|
||||||
|
|
||||||
|
// Sufficient signatures present, update latest checkpoint.
|
||||||
|
if (idx+1 >= threshold){
|
||||||
|
hash = _hash;
|
||||||
|
height = block.number;
|
||||||
|
sectionIndex = _sectionIndex;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// We shouldn't wind up here, reverting un-emits the events
|
||||||
|
revert();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dev Get all admin addresses
|
||||||
|
* @return address list
|
||||||
|
*/
|
||||||
|
function GetAllAdmin()
|
||||||
|
public
|
||||||
|
view
|
||||||
|
returns(address[] memory)
|
||||||
|
{
|
||||||
|
address[] memory ret = new address[](adminList.length);
|
||||||
|
for (uint i = 0; i < adminList.length; i++) {
|
||||||
|
ret[i] = adminList[i];
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Fields
|
||||||
|
*/
|
||||||
|
// A map of admin users who have the permission to update CHT and bloom Trie root
|
||||||
|
mapping(address => bool) admins;
|
||||||
|
|
||||||
|
// A list of admin users so that we can obtain all admin users.
|
||||||
|
address[] adminList;
|
||||||
|
|
||||||
|
// Latest stored section id
|
||||||
|
uint64 sectionIndex;
|
||||||
|
|
||||||
|
// The block height associated with latest registered checkpoint.
|
||||||
|
uint height;
|
||||||
|
|
||||||
|
// The hash of latest registered checkpoint.
|
||||||
|
bytes32 hash;
|
||||||
|
|
||||||
|
// The frequency for creating a checkpoint
|
||||||
|
//
|
||||||
|
// The default value should be the same as the checkpoint size(32768) in the ethereum.
|
||||||
|
uint sectionSize;
|
||||||
|
|
||||||
|
// The number of confirmations needed before a checkpoint can be registered.
|
||||||
|
// We have to make sure the checkpoint registered will not be invalid due to
|
||||||
|
// chain reorg.
|
||||||
|
//
|
||||||
|
// The default value should be the same as the checkpoint process confirmations(256)
|
||||||
|
// in the ethereum.
|
||||||
|
uint processConfirms;
|
||||||
|
|
||||||
|
// The required signatures to finalize a stable checkpoint.
|
||||||
|
uint threshold;
|
||||||
|
}
|
91
contracts/checkpointoracle/oracle.go
Normal file
91
contracts/checkpointoracle/oracle.go
Normal file
@ -0,0 +1,91 @@
|
|||||||
|
// Copyright 2018 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 <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
// Package checkpointoracle is a an on-chain light client checkpoint oracle.
|
||||||
|
package checkpointoracle
|
||||||
|
|
||||||
|
//go:generate abigen --sol contract/oracle.sol --pkg contract --out contract/oracle.go
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/ecdsa"
|
||||||
|
"errors"
|
||||||
|
"math/big"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/accounts/abi/bind"
|
||||||
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
"github.com/ethereum/go-ethereum/contracts/checkpointoracle/contract"
|
||||||
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
// CheckpointOracle is a Go wrapper around an on-chain light client checkpoint oracle.
|
||||||
|
type CheckpointOracle struct {
|
||||||
|
contract *contract.CheckpointOracle
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewCheckpointOracle binds checkpoint contract and returns a registrar instance.
|
||||||
|
func NewCheckpointOracle(contractAddr common.Address, backend bind.ContractBackend) (*CheckpointOracle, error) {
|
||||||
|
c, err := contract.NewCheckpointOracle(contractAddr, backend)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &CheckpointOracle{contract: c}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Contract returns the underlying contract instance.
|
||||||
|
func (oracle *CheckpointOracle) Contract() *contract.CheckpointOracle {
|
||||||
|
return oracle.contract
|
||||||
|
}
|
||||||
|
|
||||||
|
// LookupCheckpointEvents searches checkpoint event for specific section in the
|
||||||
|
// given log batches.
|
||||||
|
func (oracle *CheckpointOracle) LookupCheckpointEvents(blockLogs [][]*types.Log, section uint64, hash common.Hash) []*contract.CheckpointOracleNewCheckpointVote {
|
||||||
|
var votes []*contract.CheckpointOracleNewCheckpointVote
|
||||||
|
|
||||||
|
for _, logs := range blockLogs {
|
||||||
|
for _, log := range logs {
|
||||||
|
event, err := oracle.contract.ParseNewCheckpointVote(*log)
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if event.Index == section && common.Hash(event.CheckpointHash) == hash {
|
||||||
|
votes = append(votes, event)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return votes
|
||||||
|
}
|
||||||
|
|
||||||
|
// RegisterCheckpoint registers the checkpoint with a batch of associated signatures
|
||||||
|
// that are collected off-chain and sorted by lexicographical order.
|
||||||
|
//
|
||||||
|
// Notably all signatures given should be transformed to "ethereum style" which transforms
|
||||||
|
// v from 0/1 to 27/28 according to the yellow paper.
|
||||||
|
func (oracle *CheckpointOracle) RegisterCheckpoint(key *ecdsa.PrivateKey, index uint64, hash []byte, rnum *big.Int, rhash [32]byte, sigs [][]byte) (*types.Transaction, error) {
|
||||||
|
var (
|
||||||
|
r [][32]byte
|
||||||
|
s [][32]byte
|
||||||
|
v []uint8
|
||||||
|
)
|
||||||
|
for i := 0; i < len(sigs); i++ {
|
||||||
|
if len(sigs[i]) != 65 {
|
||||||
|
return nil, errors.New("invalid signature")
|
||||||
|
}
|
||||||
|
r = append(r, common.BytesToHash(sigs[i][:32]))
|
||||||
|
s = append(s, common.BytesToHash(sigs[i][32:64]))
|
||||||
|
v = append(v, sigs[i][64])
|
||||||
|
}
|
||||||
|
return oracle.contract.SetCheckpoint(bind.NewKeyedTransactor(key), rnum, rhash, common.BytesToHash(hash), index, v, r, s)
|
||||||
|
}
|
333
contracts/checkpointoracle/oracle_test.go
Normal file
333
contracts/checkpointoracle/oracle_test.go
Normal file
@ -0,0 +1,333 @@
|
|||||||
|
// Copyright 2018 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 <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
package checkpointoracle
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"crypto/ecdsa"
|
||||||
|
"encoding/binary"
|
||||||
|
"errors"
|
||||||
|
"math/big"
|
||||||
|
"reflect"
|
||||||
|
"sort"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/accounts/abi/bind"
|
||||||
|
"github.com/ethereum/go-ethereum/accounts/abi/bind/backends"
|
||||||
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
"github.com/ethereum/go-ethereum/contracts/checkpointoracle/contract"
|
||||||
|
"github.com/ethereum/go-ethereum/core"
|
||||||
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
|
"github.com/ethereum/go-ethereum/params"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
emptyHash = [32]byte{}
|
||||||
|
|
||||||
|
checkpoint0 = params.TrustedCheckpoint{
|
||||||
|
SectionIndex: 0,
|
||||||
|
SectionHead: common.HexToHash("0x7fa3c32f996c2bfb41a1a65b3d8ea3e0a33a1674cde43678ad6f4235e764d17d"),
|
||||||
|
CHTRoot: common.HexToHash("0x98fc5d3de23a0fecebad236f6655533c157d26a1aedcd0852a514dc1169e6350"),
|
||||||
|
BloomRoot: common.HexToHash("0x99b5adb52b337fe25e74c1c6d3835b896bd638611b3aebddb2317cce27a3f9fa"),
|
||||||
|
}
|
||||||
|
checkpoint1 = params.TrustedCheckpoint{
|
||||||
|
SectionIndex: 1,
|
||||||
|
SectionHead: common.HexToHash("0x2d4dee68102125e59b0cc61b176bd89f0d12b3b91cfaf52ef8c2c82fb920c2d2"),
|
||||||
|
CHTRoot: common.HexToHash("0x7d428008ece3b4c4ef5439f071930aad0bb75108d381308df73beadcd01ded95"),
|
||||||
|
BloomRoot: common.HexToHash("0x652571f7736de17e7bbb427ac881474da684c6988a88bf51b10cca9a2ee148f4"),
|
||||||
|
}
|
||||||
|
checkpoint2 = params.TrustedCheckpoint{
|
||||||
|
SectionIndex: 2,
|
||||||
|
SectionHead: common.HexToHash("0x61c0de578c0115b1dff8ef39aa600588c7c6ecb8a2f102003d7cf4c4146e9291"),
|
||||||
|
CHTRoot: common.HexToHash("0x407a08a407a2bc3838b74ca3eb206903c9c8a186ccf5ef14af07794efff1970b"),
|
||||||
|
BloomRoot: common.HexToHash("0x058b4161f558ce295a92925efc57f34f9210d5a30088d7475c183e0d3e58f5ac"),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
// The block frequency for creating checkpoint(only used in test)
|
||||||
|
sectionSize = big.NewInt(512)
|
||||||
|
|
||||||
|
// The number of confirmations needed to generate a checkpoint(only used in test).
|
||||||
|
processConfirms = big.NewInt(4)
|
||||||
|
)
|
||||||
|
|
||||||
|
// validateOperation executes the operation, watches and delivers all events fired by the backend and ensures the
|
||||||
|
// correctness by assert function.
|
||||||
|
func validateOperation(t *testing.T, c *contract.CheckpointOracle, backend *backends.SimulatedBackend, operation func(),
|
||||||
|
assert func(<-chan *contract.CheckpointOracleNewCheckpointVote) error, opName string) {
|
||||||
|
// Watch all events and deliver them to assert function
|
||||||
|
var (
|
||||||
|
sink = make(chan *contract.CheckpointOracleNewCheckpointVote)
|
||||||
|
sub, _ = c.WatchNewCheckpointVote(nil, sink, nil)
|
||||||
|
)
|
||||||
|
defer func() {
|
||||||
|
// Close all subscribers
|
||||||
|
sub.Unsubscribe()
|
||||||
|
}()
|
||||||
|
operation()
|
||||||
|
|
||||||
|
// flush pending block
|
||||||
|
backend.Commit()
|
||||||
|
if err := assert(sink); err != nil {
|
||||||
|
t.Errorf("operation {%s} failed, err %s", opName, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// validateEvents checks that the correct number of contract events
|
||||||
|
// fired by contract backend.
|
||||||
|
func validateEvents(target int, sink interface{}) (bool, []reflect.Value) {
|
||||||
|
chanval := reflect.ValueOf(sink)
|
||||||
|
chantyp := chanval.Type()
|
||||||
|
if chantyp.Kind() != reflect.Chan || chantyp.ChanDir()&reflect.RecvDir == 0 {
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
count := 0
|
||||||
|
var recv []reflect.Value
|
||||||
|
timeout := time.After(1 * time.Second)
|
||||||
|
cases := []reflect.SelectCase{{Chan: chanval, Dir: reflect.SelectRecv}, {Chan: reflect.ValueOf(timeout), Dir: reflect.SelectRecv}}
|
||||||
|
for {
|
||||||
|
chose, v, _ := reflect.Select(cases)
|
||||||
|
if chose == 1 {
|
||||||
|
// Not enough event received
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
count += 1
|
||||||
|
recv = append(recv, v)
|
||||||
|
if count == target {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
done := time.After(50 * time.Millisecond)
|
||||||
|
cases = cases[:1]
|
||||||
|
cases = append(cases, reflect.SelectCase{Chan: reflect.ValueOf(done), Dir: reflect.SelectRecv})
|
||||||
|
chose, _, _ := reflect.Select(cases)
|
||||||
|
// If chose equal 0, it means receiving redundant events.
|
||||||
|
return chose == 1, recv
|
||||||
|
}
|
||||||
|
|
||||||
|
func signCheckpoint(addr common.Address, privateKey *ecdsa.PrivateKey, index uint64, hash common.Hash) []byte {
|
||||||
|
// EIP 191 style signatures
|
||||||
|
//
|
||||||
|
// Arguments when calculating hash to validate
|
||||||
|
// 1: byte(0x19) - the initial 0x19 byte
|
||||||
|
// 2: byte(0) - the version byte (data with intended validator)
|
||||||
|
// 3: this - the validator address
|
||||||
|
// -- Application specific data
|
||||||
|
// 4 : checkpoint section_index(uint64)
|
||||||
|
// 5 : checkpoint hash (bytes32)
|
||||||
|
// hash = keccak256(checkpoint_index, section_head, cht_root, bloom_root)
|
||||||
|
buf := make([]byte, 8)
|
||||||
|
binary.BigEndian.PutUint64(buf, index)
|
||||||
|
data := append([]byte{0x19, 0x00}, append(addr.Bytes(), append(buf, hash.Bytes()...)...)...)
|
||||||
|
sig, _ := crypto.Sign(crypto.Keccak256(data), privateKey)
|
||||||
|
sig[64] += 27 // Transform V from 0/1 to 27/28 according to the yellow paper
|
||||||
|
return sig
|
||||||
|
}
|
||||||
|
|
||||||
|
// assertSignature verifies whether the recovered signers are equal with expected.
|
||||||
|
func assertSignature(addr common.Address, index uint64, hash [32]byte, r, s [32]byte, v uint8, expect common.Address) bool {
|
||||||
|
buf := make([]byte, 8)
|
||||||
|
binary.BigEndian.PutUint64(buf, index)
|
||||||
|
data := append([]byte{0x19, 0x00}, append(addr.Bytes(), append(buf, hash[:]...)...)...)
|
||||||
|
pubkey, err := crypto.Ecrecover(crypto.Keccak256(data), append(r[:], append(s[:], v-27)...))
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
var signer common.Address
|
||||||
|
copy(signer[:], crypto.Keccak256(pubkey[1:])[12:])
|
||||||
|
return bytes.Equal(signer.Bytes(), expect.Bytes())
|
||||||
|
}
|
||||||
|
|
||||||
|
type Account struct {
|
||||||
|
key *ecdsa.PrivateKey
|
||||||
|
addr common.Address
|
||||||
|
}
|
||||||
|
type Accounts []Account
|
||||||
|
|
||||||
|
func (a Accounts) Len() int { return len(a) }
|
||||||
|
func (a Accounts) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
|
||||||
|
func (a Accounts) Less(i, j int) bool { return bytes.Compare(a[i].addr.Bytes(), a[j].addr.Bytes()) < 0 }
|
||||||
|
|
||||||
|
func TestCheckpointRegister(t *testing.T) {
|
||||||
|
// Initialize test accounts
|
||||||
|
var accounts Accounts
|
||||||
|
for i := 0; i < 3; i++ {
|
||||||
|
key, _ := crypto.GenerateKey()
|
||||||
|
addr := crypto.PubkeyToAddress(key.PublicKey)
|
||||||
|
accounts = append(accounts, Account{key: key, addr: addr})
|
||||||
|
}
|
||||||
|
sort.Sort(accounts)
|
||||||
|
|
||||||
|
// Deploy registrar contract
|
||||||
|
transactOpts := bind.NewKeyedTransactor(accounts[0].key)
|
||||||
|
contractBackend := backends.NewSimulatedBackend(core.GenesisAlloc{accounts[0].addr: {Balance: big.NewInt(1000000000)}, accounts[1].addr: {Balance: big.NewInt(1000000000)}, accounts[2].addr: {Balance: big.NewInt(1000000000)}}, 10000000)
|
||||||
|
// 3 trusted signers, threshold 2
|
||||||
|
contractAddr, _, c, err := contract.DeployCheckpointOracle(transactOpts, contractBackend, []common.Address{accounts[0].addr, accounts[1].addr, accounts[2].addr}, sectionSize, processConfirms, big.NewInt(2))
|
||||||
|
if err != nil {
|
||||||
|
t.Error("Failed to deploy registrar contract", err)
|
||||||
|
}
|
||||||
|
contractBackend.Commit()
|
||||||
|
|
||||||
|
// getRecent returns block height and hash of the head parent.
|
||||||
|
getRecent := func() (*big.Int, common.Hash) {
|
||||||
|
parentNumber := new(big.Int).Sub(contractBackend.Blockchain().CurrentHeader().Number, big.NewInt(1))
|
||||||
|
parentHash := contractBackend.Blockchain().CurrentHeader().ParentHash
|
||||||
|
return parentNumber, parentHash
|
||||||
|
}
|
||||||
|
// collectSig generates specified number signatures.
|
||||||
|
collectSig := func(index uint64, hash common.Hash, n int, unauthorized *ecdsa.PrivateKey) (v []uint8, r [][32]byte, s [][32]byte) {
|
||||||
|
for i := 0; i < n; i++ {
|
||||||
|
sig := signCheckpoint(contractAddr, accounts[i].key, index, hash)
|
||||||
|
if unauthorized != nil {
|
||||||
|
sig = signCheckpoint(contractAddr, unauthorized, index, hash)
|
||||||
|
}
|
||||||
|
r = append(r, common.BytesToHash(sig[:32]))
|
||||||
|
s = append(s, common.BytesToHash(sig[32:64]))
|
||||||
|
v = append(v, sig[64])
|
||||||
|
}
|
||||||
|
return v, r, s
|
||||||
|
}
|
||||||
|
// insertEmptyBlocks inserts a batch of empty blocks to blockchain.
|
||||||
|
insertEmptyBlocks := func(number int) {
|
||||||
|
for i := 0; i < number; i++ {
|
||||||
|
contractBackend.Commit()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// assert checks whether the current contract status is same with
|
||||||
|
// the expected.
|
||||||
|
assert := func(index uint64, hash [32]byte, height *big.Int) error {
|
||||||
|
lindex, lhash, lheight, err := c.GetLatestCheckpoint(nil)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if lindex != index {
|
||||||
|
return errors.New("latest checkpoint index mismatch")
|
||||||
|
}
|
||||||
|
if !bytes.Equal(lhash[:], hash[:]) {
|
||||||
|
return errors.New("latest checkpoint hash mismatch")
|
||||||
|
}
|
||||||
|
if lheight.Cmp(height) != 0 {
|
||||||
|
return errors.New("latest checkpoint height mismatch")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test future checkpoint registration
|
||||||
|
validateOperation(t, c, contractBackend, func() {
|
||||||
|
number, hash := getRecent()
|
||||||
|
v, r, s := collectSig(0, checkpoint0.Hash(), 2, nil)
|
||||||
|
c.SetCheckpoint(transactOpts, number, hash, checkpoint0.Hash(), 0, v, r, s)
|
||||||
|
}, func(events <-chan *contract.CheckpointOracleNewCheckpointVote) error {
|
||||||
|
return assert(0, emptyHash, big.NewInt(0))
|
||||||
|
}, "test future checkpoint registration")
|
||||||
|
|
||||||
|
insertEmptyBlocks(int(sectionSize.Uint64() + processConfirms.Uint64()))
|
||||||
|
|
||||||
|
// Test transaction replay protection
|
||||||
|
validateOperation(t, c, contractBackend, func() {
|
||||||
|
number, hash := getRecent()
|
||||||
|
v, r, s := collectSig(0, checkpoint0.Hash(), 2, nil)
|
||||||
|
hash = common.HexToHash("deadbeef")
|
||||||
|
c.SetCheckpoint(transactOpts, number, hash, checkpoint0.Hash(), 0, v, r, s)
|
||||||
|
}, func(events <-chan *contract.CheckpointOracleNewCheckpointVote) error {
|
||||||
|
return assert(0, emptyHash, big.NewInt(0))
|
||||||
|
}, "test transaction replay protection")
|
||||||
|
|
||||||
|
// Test unauthorized signature checking
|
||||||
|
validateOperation(t, c, contractBackend, func() {
|
||||||
|
number, hash := getRecent()
|
||||||
|
u, _ := crypto.GenerateKey()
|
||||||
|
v, r, s := collectSig(0, checkpoint0.Hash(), 2, u)
|
||||||
|
c.SetCheckpoint(transactOpts, number, hash, checkpoint0.Hash(), 0, v, r, s)
|
||||||
|
}, func(events <-chan *contract.CheckpointOracleNewCheckpointVote) error {
|
||||||
|
return assert(0, emptyHash, big.NewInt(0))
|
||||||
|
}, "test unauthorized signature checking")
|
||||||
|
|
||||||
|
// Test un-multi-signature checkpoint registration
|
||||||
|
validateOperation(t, c, contractBackend, func() {
|
||||||
|
number, hash := getRecent()
|
||||||
|
v, r, s := collectSig(0, checkpoint0.Hash(), 1, nil)
|
||||||
|
c.SetCheckpoint(transactOpts, number, hash, checkpoint0.Hash(), 0, v, r, s)
|
||||||
|
}, func(events <-chan *contract.CheckpointOracleNewCheckpointVote) error {
|
||||||
|
return assert(0, emptyHash, big.NewInt(0))
|
||||||
|
}, "test un-multi-signature checkpoint registration")
|
||||||
|
|
||||||
|
// Test valid checkpoint registration
|
||||||
|
validateOperation(t, c, contractBackend, func() {
|
||||||
|
number, hash := getRecent()
|
||||||
|
v, r, s := collectSig(0, checkpoint0.Hash(), 2, nil)
|
||||||
|
c.SetCheckpoint(transactOpts, number, hash, checkpoint0.Hash(), 0, v, r, s)
|
||||||
|
}, func(events <-chan *contract.CheckpointOracleNewCheckpointVote) error {
|
||||||
|
if valid, recv := validateEvents(2, events); !valid {
|
||||||
|
return errors.New("receive incorrect number of events")
|
||||||
|
} else {
|
||||||
|
for i := 0; i < len(recv); i++ {
|
||||||
|
event := recv[i].Interface().(*contract.CheckpointOracleNewCheckpointVote)
|
||||||
|
if !assertSignature(contractAddr, event.Index, event.CheckpointHash, event.R, event.S, event.V, accounts[i].addr) {
|
||||||
|
return errors.New("recover signer failed")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
number, _ := getRecent()
|
||||||
|
return assert(0, checkpoint0.Hash(), number.Add(number, big.NewInt(1)))
|
||||||
|
}, "test valid checkpoint registration")
|
||||||
|
|
||||||
|
distance := 3*sectionSize.Uint64() + processConfirms.Uint64() - contractBackend.Blockchain().CurrentHeader().Number.Uint64()
|
||||||
|
insertEmptyBlocks(int(distance))
|
||||||
|
|
||||||
|
// Test uncontinuous checkpoint registration
|
||||||
|
validateOperation(t, c, contractBackend, func() {
|
||||||
|
number, hash := getRecent()
|
||||||
|
v, r, s := collectSig(2, checkpoint2.Hash(), 2, nil)
|
||||||
|
c.SetCheckpoint(transactOpts, number, hash, checkpoint2.Hash(), 2, v, r, s)
|
||||||
|
}, func(events <-chan *contract.CheckpointOracleNewCheckpointVote) error {
|
||||||
|
if valid, recv := validateEvents(2, events); !valid {
|
||||||
|
return errors.New("receive incorrect number of events")
|
||||||
|
} else {
|
||||||
|
for i := 0; i < len(recv); i++ {
|
||||||
|
event := recv[i].Interface().(*contract.CheckpointOracleNewCheckpointVote)
|
||||||
|
if !assertSignature(contractAddr, event.Index, event.CheckpointHash, event.R, event.S, event.V, accounts[i].addr) {
|
||||||
|
return errors.New("recover signer failed")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
number, _ := getRecent()
|
||||||
|
return assert(2, checkpoint2.Hash(), number.Add(number, big.NewInt(1)))
|
||||||
|
}, "test uncontinuous checkpoint registration")
|
||||||
|
|
||||||
|
// Test old checkpoint registration
|
||||||
|
validateOperation(t, c, contractBackend, func() {
|
||||||
|
number, hash := getRecent()
|
||||||
|
v, r, s := collectSig(1, checkpoint1.Hash(), 2, nil)
|
||||||
|
c.SetCheckpoint(transactOpts, number, hash, checkpoint1.Hash(), 1, v, r, s)
|
||||||
|
}, func(events <-chan *contract.CheckpointOracleNewCheckpointVote) error {
|
||||||
|
number, _ := getRecent()
|
||||||
|
return assert(2, checkpoint2.Hash(), number)
|
||||||
|
}, "test uncontinuous checkpoint registration")
|
||||||
|
|
||||||
|
// Test stale checkpoint registration
|
||||||
|
validateOperation(t, c, contractBackend, func() {
|
||||||
|
number, hash := getRecent()
|
||||||
|
v, r, s := collectSig(2, checkpoint2.Hash(), 2, nil)
|
||||||
|
c.SetCheckpoint(transactOpts, number, hash, checkpoint2.Hash(), 2, v, r, s)
|
||||||
|
}, func(events <-chan *contract.CheckpointOracleNewCheckpointVote) error {
|
||||||
|
number, _ := getRecent()
|
||||||
|
return assert(2, checkpoint2.Hash(), number.Sub(number, big.NewInt(1)))
|
||||||
|
}, "test stale checkpoint registration")
|
||||||
|
}
|
@ -128,12 +128,13 @@ func (c *ChainIndexer) AddCheckpoint(section uint64, shead common.Hash) {
|
|||||||
c.lock.Lock()
|
c.lock.Lock()
|
||||||
defer c.lock.Unlock()
|
defer c.lock.Unlock()
|
||||||
|
|
||||||
|
// Short circuit if the given checkpoint is below than local's.
|
||||||
|
if c.checkpointSections >= section+1 || section < c.storedSections {
|
||||||
|
return
|
||||||
|
}
|
||||||
c.checkpointSections = section + 1
|
c.checkpointSections = section + 1
|
||||||
c.checkpointHead = shead
|
c.checkpointHead = shead
|
||||||
|
|
||||||
if section < c.storedSections {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
c.setSectionHead(section, shead)
|
c.setSectionHead(section, shead)
|
||||||
c.setValidSections(section + 1)
|
c.setValidSections(section + 1)
|
||||||
}
|
}
|
||||||
|
@ -26,6 +26,7 @@ import (
|
|||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/accounts"
|
"github.com/ethereum/go-ethereum/accounts"
|
||||||
|
"github.com/ethereum/go-ethereum/accounts/abi/bind"
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||||
"github.com/ethereum/go-ethereum/consensus"
|
"github.com/ethereum/go-ethereum/consensus"
|
||||||
@ -57,6 +58,7 @@ type LesServer interface {
|
|||||||
APIs() []rpc.API
|
APIs() []rpc.API
|
||||||
Protocols() []p2p.Protocol
|
Protocols() []p2p.Protocol
|
||||||
SetBloomBitsIndexer(bbIndexer *core.ChainIndexer)
|
SetBloomBitsIndexer(bbIndexer *core.ChainIndexer)
|
||||||
|
SetContractBackend(bind.ContractBackend)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ethereum implements the Ethereum full node service.
|
// Ethereum implements the Ethereum full node service.
|
||||||
@ -99,6 +101,14 @@ func (s *Ethereum) AddLesServer(ls LesServer) {
|
|||||||
ls.SetBloomBitsIndexer(s.bloomIndexer)
|
ls.SetBloomBitsIndexer(s.bloomIndexer)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SetClient sets a rpc client which connecting to our local node.
|
||||||
|
func (s *Ethereum) SetContractBackend(backend bind.ContractBackend) {
|
||||||
|
// Pass the rpc client to les server if it is enabled.
|
||||||
|
if s.lesServer != nil {
|
||||||
|
s.lesServer.SetContractBackend(backend)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// New creates a new Ethereum object (including the
|
// New creates a new Ethereum object (including the
|
||||||
// initialisation of the common Ethereum object)
|
// initialisation of the common Ethereum object)
|
||||||
func New(ctx *node.ServiceContext, config *Config) (*Ethereum, error) {
|
func New(ctx *node.ServiceContext, config *Config) (*Ethereum, error) {
|
||||||
@ -192,7 +202,11 @@ func New(ctx *node.ServiceContext, config *Config) (*Ethereum, error) {
|
|||||||
|
|
||||||
// Permit the downloader to use the trie cache allowance during fast sync
|
// Permit the downloader to use the trie cache allowance during fast sync
|
||||||
cacheLimit := cacheConfig.TrieCleanLimit + cacheConfig.TrieDirtyLimit
|
cacheLimit := cacheConfig.TrieCleanLimit + cacheConfig.TrieDirtyLimit
|
||||||
if eth.protocolManager, err = NewProtocolManager(chainConfig, config.SyncMode, config.NetworkId, eth.eventMux, eth.txPool, eth.engine, eth.blockchain, chainDb, cacheLimit, config.Whitelist); err != nil {
|
checkpoint := config.Checkpoint
|
||||||
|
if checkpoint == nil {
|
||||||
|
checkpoint = params.TrustedCheckpoints[genesisHash]
|
||||||
|
}
|
||||||
|
if eth.protocolManager, err = NewProtocolManager(chainConfig, checkpoint, config.SyncMode, config.NetworkId, eth.eventMux, eth.txPool, eth.engine, eth.blockchain, chainDb, cacheLimit, config.Whitelist); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
eth.miner = miner.New(eth, &config.Miner, chainConfig, eth.EventMux(), eth.engine, eth.isLocalBlock)
|
eth.miner = miner.New(eth, &config.Miner, chainConfig, eth.EventMux(), eth.engine, eth.isLocalBlock)
|
||||||
@ -268,6 +282,11 @@ func (s *Ethereum) APIs() []rpc.API {
|
|||||||
// Append any APIs exposed explicitly by the consensus engine
|
// Append any APIs exposed explicitly by the consensus engine
|
||||||
apis = append(apis, s.engine.APIs(s.BlockChain())...)
|
apis = append(apis, s.engine.APIs(s.BlockChain())...)
|
||||||
|
|
||||||
|
// Append any APIs exposed explicitly by the les server
|
||||||
|
if s.lesServer != nil {
|
||||||
|
apis = append(apis, s.lesServer.APIs()...)
|
||||||
|
}
|
||||||
|
|
||||||
// Append all the local APIs and return
|
// Append all the local APIs and return
|
||||||
return append(apis, []rpc.API{
|
return append(apis, []rpc.API{
|
||||||
{
|
{
|
||||||
|
@ -149,4 +149,10 @@ type Config struct {
|
|||||||
|
|
||||||
// RPCGasCap is the global gas cap for eth-call variants.
|
// RPCGasCap is the global gas cap for eth-call variants.
|
||||||
RPCGasCap *big.Int `toml:",omitempty"`
|
RPCGasCap *big.Int `toml:",omitempty"`
|
||||||
|
|
||||||
|
// Checkpoint is a hardcoded checkpoint which can be nil.
|
||||||
|
Checkpoint *params.TrustedCheckpoint
|
||||||
|
|
||||||
|
// CheckpointOracle is the configuration for checkpoint oracle.
|
||||||
|
CheckpointOracle *params.CheckpointOracleConfig
|
||||||
}
|
}
|
||||||
|
@ -12,6 +12,7 @@ import (
|
|||||||
"github.com/ethereum/go-ethereum/eth/downloader"
|
"github.com/ethereum/go-ethereum/eth/downloader"
|
||||||
"github.com/ethereum/go-ethereum/eth/gasprice"
|
"github.com/ethereum/go-ethereum/eth/gasprice"
|
||||||
"github.com/ethereum/go-ethereum/miner"
|
"github.com/ethereum/go-ethereum/miner"
|
||||||
|
"github.com/ethereum/go-ethereum/params"
|
||||||
)
|
)
|
||||||
|
|
||||||
// MarshalTOML marshals as TOML.
|
// MarshalTOML marshals as TOML.
|
||||||
@ -32,6 +33,7 @@ func (c Config) MarshalTOML() (interface{}, error) {
|
|||||||
SkipBcVersionCheck bool `toml:"-"`
|
SkipBcVersionCheck bool `toml:"-"`
|
||||||
DatabaseHandles int `toml:"-"`
|
DatabaseHandles int `toml:"-"`
|
||||||
DatabaseCache int
|
DatabaseCache int
|
||||||
|
DatabaseFreezer string
|
||||||
TrieCleanCache int
|
TrieCleanCache int
|
||||||
TrieDirtyCache int
|
TrieDirtyCache int
|
||||||
TrieTimeout time.Duration
|
TrieTimeout time.Duration
|
||||||
@ -45,6 +47,8 @@ func (c Config) MarshalTOML() (interface{}, error) {
|
|||||||
EVMInterpreter string
|
EVMInterpreter string
|
||||||
ConstantinopleOverride *big.Int
|
ConstantinopleOverride *big.Int
|
||||||
RPCGasCap *big.Int `toml:",omitempty"`
|
RPCGasCap *big.Int `toml:",omitempty"`
|
||||||
|
Checkpoint *params.TrustedCheckpoint
|
||||||
|
CheckpointOracle *params.CheckpointOracleConfig
|
||||||
}
|
}
|
||||||
var enc Config
|
var enc Config
|
||||||
enc.Genesis = c.Genesis
|
enc.Genesis = c.Genesis
|
||||||
@ -62,6 +66,7 @@ func (c Config) MarshalTOML() (interface{}, error) {
|
|||||||
enc.SkipBcVersionCheck = c.SkipBcVersionCheck
|
enc.SkipBcVersionCheck = c.SkipBcVersionCheck
|
||||||
enc.DatabaseHandles = c.DatabaseHandles
|
enc.DatabaseHandles = c.DatabaseHandles
|
||||||
enc.DatabaseCache = c.DatabaseCache
|
enc.DatabaseCache = c.DatabaseCache
|
||||||
|
enc.DatabaseFreezer = c.DatabaseFreezer
|
||||||
enc.TrieCleanCache = c.TrieCleanCache
|
enc.TrieCleanCache = c.TrieCleanCache
|
||||||
enc.TrieDirtyCache = c.TrieDirtyCache
|
enc.TrieDirtyCache = c.TrieDirtyCache
|
||||||
enc.TrieTimeout = c.TrieTimeout
|
enc.TrieTimeout = c.TrieTimeout
|
||||||
@ -75,6 +80,8 @@ func (c Config) MarshalTOML() (interface{}, error) {
|
|||||||
enc.EVMInterpreter = c.EVMInterpreter
|
enc.EVMInterpreter = c.EVMInterpreter
|
||||||
enc.ConstantinopleOverride = c.ConstantinopleOverride
|
enc.ConstantinopleOverride = c.ConstantinopleOverride
|
||||||
enc.RPCGasCap = c.RPCGasCap
|
enc.RPCGasCap = c.RPCGasCap
|
||||||
|
enc.Checkpoint = c.Checkpoint
|
||||||
|
enc.CheckpointOracle = c.CheckpointOracle
|
||||||
return &enc, nil
|
return &enc, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -96,6 +103,7 @@ func (c *Config) UnmarshalTOML(unmarshal func(interface{}) error) error {
|
|||||||
SkipBcVersionCheck *bool `toml:"-"`
|
SkipBcVersionCheck *bool `toml:"-"`
|
||||||
DatabaseHandles *int `toml:"-"`
|
DatabaseHandles *int `toml:"-"`
|
||||||
DatabaseCache *int
|
DatabaseCache *int
|
||||||
|
DatabaseFreezer *string
|
||||||
TrieCleanCache *int
|
TrieCleanCache *int
|
||||||
TrieDirtyCache *int
|
TrieDirtyCache *int
|
||||||
TrieTimeout *time.Duration
|
TrieTimeout *time.Duration
|
||||||
@ -109,6 +117,8 @@ func (c *Config) UnmarshalTOML(unmarshal func(interface{}) error) error {
|
|||||||
EVMInterpreter *string
|
EVMInterpreter *string
|
||||||
ConstantinopleOverride *big.Int
|
ConstantinopleOverride *big.Int
|
||||||
RPCGasCap *big.Int `toml:",omitempty"`
|
RPCGasCap *big.Int `toml:",omitempty"`
|
||||||
|
Checkpoint *params.TrustedCheckpoint
|
||||||
|
CheckpointOracle *params.CheckpointOracleConfig
|
||||||
}
|
}
|
||||||
var dec Config
|
var dec Config
|
||||||
if err := unmarshal(&dec); err != nil {
|
if err := unmarshal(&dec); err != nil {
|
||||||
@ -159,6 +169,9 @@ func (c *Config) UnmarshalTOML(unmarshal func(interface{}) error) error {
|
|||||||
if dec.DatabaseCache != nil {
|
if dec.DatabaseCache != nil {
|
||||||
c.DatabaseCache = *dec.DatabaseCache
|
c.DatabaseCache = *dec.DatabaseCache
|
||||||
}
|
}
|
||||||
|
if dec.DatabaseFreezer != nil {
|
||||||
|
c.DatabaseFreezer = *dec.DatabaseFreezer
|
||||||
|
}
|
||||||
if dec.TrieCleanCache != nil {
|
if dec.TrieCleanCache != nil {
|
||||||
c.TrieCleanCache = *dec.TrieCleanCache
|
c.TrieCleanCache = *dec.TrieCleanCache
|
||||||
}
|
}
|
||||||
@ -198,5 +211,11 @@ func (c *Config) UnmarshalTOML(unmarshal func(interface{}) error) error {
|
|||||||
if dec.RPCGasCap != nil {
|
if dec.RPCGasCap != nil {
|
||||||
c.RPCGasCap = dec.RPCGasCap
|
c.RPCGasCap = dec.RPCGasCap
|
||||||
}
|
}
|
||||||
|
if dec.Checkpoint != nil {
|
||||||
|
c.Checkpoint = dec.Checkpoint
|
||||||
|
}
|
||||||
|
if dec.CheckpointOracle != nil {
|
||||||
|
c.CheckpointOracle = dec.CheckpointOracle
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -106,7 +106,7 @@ type ProtocolManager struct {
|
|||||||
|
|
||||||
// NewProtocolManager returns a new Ethereum sub protocol manager. The Ethereum sub protocol manages peers capable
|
// NewProtocolManager returns a new Ethereum sub protocol manager. The Ethereum sub protocol manages peers capable
|
||||||
// with the Ethereum network.
|
// with the Ethereum network.
|
||||||
func NewProtocolManager(config *params.ChainConfig, mode downloader.SyncMode, networkID uint64, mux *event.TypeMux, txpool txPool, engine consensus.Engine, blockchain *core.BlockChain, chaindb ethdb.Database, cacheLimit int, whitelist map[uint64]common.Hash) (*ProtocolManager, error) {
|
func NewProtocolManager(config *params.ChainConfig, checkpoint *params.TrustedCheckpoint, mode downloader.SyncMode, networkID uint64, mux *event.TypeMux, txpool txPool, engine consensus.Engine, blockchain *core.BlockChain, chaindb ethdb.Database, cacheLimit int, whitelist map[uint64]common.Hash) (*ProtocolManager, error) {
|
||||||
// Create the protocol manager with the base fields
|
// Create the protocol manager with the base fields
|
||||||
manager := &ProtocolManager{
|
manager := &ProtocolManager{
|
||||||
networkID: networkID,
|
networkID: networkID,
|
||||||
@ -145,7 +145,7 @@ func NewProtocolManager(config *params.ChainConfig, mode downloader.SyncMode, ne
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
// If we have trusted checkpoints, enforce them on the chain
|
// If we have trusted checkpoints, enforce them on the chain
|
||||||
if checkpoint, ok := params.TrustedCheckpoints[blockchain.Genesis().Hash()]; ok {
|
if checkpoint != nil {
|
||||||
manager.checkpointNumber = (checkpoint.SectionIndex+1)*params.CHTFrequency - 1
|
manager.checkpointNumber = (checkpoint.SectionIndex+1)*params.CHTFrequency - 1
|
||||||
manager.checkpointHash = checkpoint.SectionHead
|
manager.checkpointHash = checkpoint.SectionHead
|
||||||
}
|
}
|
||||||
|
@ -499,31 +499,30 @@ func testCheckpointChallenge(t *testing.T, syncmode downloader.SyncMode, checkpo
|
|||||||
|
|
||||||
// Initialize a chain and generate a fake CHT if checkpointing is enabled
|
// Initialize a chain and generate a fake CHT if checkpointing is enabled
|
||||||
var (
|
var (
|
||||||
db = rawdb.NewMemoryDatabase()
|
db = rawdb.NewMemoryDatabase()
|
||||||
config = new(params.ChainConfig)
|
config = new(params.ChainConfig)
|
||||||
genesis = (&core.Genesis{Config: config}).MustCommit(db)
|
|
||||||
)
|
)
|
||||||
|
(&core.Genesis{Config: config}).MustCommit(db) // Commit genesis block
|
||||||
// If checkpointing is enabled, create and inject a fake CHT and the corresponding
|
// If checkpointing is enabled, create and inject a fake CHT and the corresponding
|
||||||
// chllenge response.
|
// chllenge response.
|
||||||
var response *types.Header
|
var response *types.Header
|
||||||
|
var cht *params.TrustedCheckpoint
|
||||||
if checkpoint {
|
if checkpoint {
|
||||||
index := uint64(rand.Intn(500))
|
index := uint64(rand.Intn(500))
|
||||||
number := (index+1)*params.CHTFrequency - 1
|
number := (index+1)*params.CHTFrequency - 1
|
||||||
response = &types.Header{Number: big.NewInt(int64(number)), Extra: []byte("valid")}
|
response = &types.Header{Number: big.NewInt(int64(number)), Extra: []byte("valid")}
|
||||||
|
|
||||||
cht := ¶ms.TrustedCheckpoint{
|
cht = ¶ms.TrustedCheckpoint{
|
||||||
SectionIndex: index,
|
SectionIndex: index,
|
||||||
SectionHead: response.Hash(),
|
SectionHead: response.Hash(),
|
||||||
}
|
}
|
||||||
params.TrustedCheckpoints[genesis.Hash()] = cht
|
|
||||||
defer delete(params.TrustedCheckpoints, genesis.Hash())
|
|
||||||
}
|
}
|
||||||
// Create a checkpoint aware protocol manager
|
// Create a checkpoint aware protocol manager
|
||||||
blockchain, err := core.NewBlockChain(db, nil, config, ethash.NewFaker(), vm.Config{}, nil)
|
blockchain, err := core.NewBlockChain(db, nil, config, ethash.NewFaker(), vm.Config{}, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("failed to create new blockchain: %v", err)
|
t.Fatalf("failed to create new blockchain: %v", err)
|
||||||
}
|
}
|
||||||
pm, err := NewProtocolManager(config, syncmode, DefaultConfig.NetworkId, new(event.TypeMux), new(testTxPool), ethash.NewFaker(), blockchain, db, 1, nil)
|
pm, err := NewProtocolManager(config, cht, syncmode, DefaultConfig.NetworkId, new(event.TypeMux), new(testTxPool), ethash.NewFaker(), blockchain, db, 1, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("failed to start test protocol manager: %v", err)
|
t.Fatalf("failed to start test protocol manager: %v", err)
|
||||||
}
|
}
|
||||||
@ -610,7 +609,7 @@ func testBroadcastBlock(t *testing.T, totalPeers, broadcastExpected int) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("failed to create new blockchain: %v", err)
|
t.Fatalf("failed to create new blockchain: %v", err)
|
||||||
}
|
}
|
||||||
pm, err := NewProtocolManager(config, downloader.FullSync, DefaultConfig.NetworkId, evmux, new(testTxPool), pow, blockchain, db, 1, nil)
|
pm, err := NewProtocolManager(config, nil, downloader.FullSync, DefaultConfig.NetworkId, evmux, new(testTxPool), pow, blockchain, db, 1, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("failed to start test protocol manager: %v", err)
|
t.Fatalf("failed to start test protocol manager: %v", err)
|
||||||
}
|
}
|
||||||
|
@ -66,7 +66,7 @@ func newTestProtocolManager(mode downloader.SyncMode, blocks int, generator func
|
|||||||
if _, err := blockchain.InsertChain(chain); err != nil {
|
if _, err := blockchain.InsertChain(chain); err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
pm, err := NewProtocolManager(gspec.Config, mode, DefaultConfig.NetworkId, evmux, &testTxPool{added: newtx}, engine, blockchain, db, 1, nil)
|
pm, err := NewProtocolManager(gspec.Config, nil, mode, DefaultConfig.NetworkId, evmux, &testTxPool{added: newtx}, engine, blockchain, db, 1, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
@ -32,6 +32,7 @@ var Modules = map[string]string{
|
|||||||
"shh": ShhJs,
|
"shh": ShhJs,
|
||||||
"swarmfs": SwarmfsJs,
|
"swarmfs": SwarmfsJs,
|
||||||
"txpool": TxpoolJs,
|
"txpool": TxpoolJs,
|
||||||
|
"les": LESJs,
|
||||||
}
|
}
|
||||||
|
|
||||||
const ChequebookJs = `
|
const ChequebookJs = `
|
||||||
@ -760,3 +761,28 @@ web3._extend({
|
|||||||
]
|
]
|
||||||
});
|
});
|
||||||
`
|
`
|
||||||
|
|
||||||
|
const LESJs = `
|
||||||
|
web3._extend({
|
||||||
|
property: 'les',
|
||||||
|
methods:
|
||||||
|
[
|
||||||
|
new web3._extend.Method({
|
||||||
|
name: 'getCheckpoint',
|
||||||
|
call: 'les_getCheckpoint',
|
||||||
|
params: 1
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
properties:
|
||||||
|
[
|
||||||
|
new web3._extend.Property({
|
||||||
|
name: 'latestCheckpoint',
|
||||||
|
getter: 'les_latestCheckpoint'
|
||||||
|
}),
|
||||||
|
new web3._extend.Property({
|
||||||
|
name: 'checkpointContractAddress',
|
||||||
|
getter: 'les_getCheckpointContractAddress'
|
||||||
|
}),
|
||||||
|
]
|
||||||
|
});
|
||||||
|
`
|
||||||
|
58
les/api.go
58
les/api.go
@ -34,6 +34,8 @@ var (
|
|||||||
ErrMinCap = errors.New("capacity too small")
|
ErrMinCap = errors.New("capacity too small")
|
||||||
ErrTotalCap = errors.New("total capacity exceeded")
|
ErrTotalCap = errors.New("total capacity exceeded")
|
||||||
ErrUnknownBenchmarkType = errors.New("unknown benchmark type")
|
ErrUnknownBenchmarkType = errors.New("unknown benchmark type")
|
||||||
|
ErrNoCheckpoint = errors.New("no local checkpoint provided")
|
||||||
|
ErrNotActivated = errors.New("checkpoint registrar is not activated")
|
||||||
|
|
||||||
dropCapacityDelay = time.Second // delay applied to decreasing capacity changes
|
dropCapacityDelay = time.Second // delay applied to decreasing capacity changes
|
||||||
)
|
)
|
||||||
@ -470,3 +472,59 @@ func (api *PrivateLightServerAPI) Benchmark(setups []map[string]interface{}, pas
|
|||||||
}
|
}
|
||||||
return result, nil
|
return result, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// PrivateLightAPI provides an API to access the LES light server or light client.
|
||||||
|
type PrivateLightAPI struct {
|
||||||
|
backend *lesCommons
|
||||||
|
reg *checkpointOracle
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewPrivateLightAPI creates a new LES service API.
|
||||||
|
func NewPrivateLightAPI(backend *lesCommons, reg *checkpointOracle) *PrivateLightAPI {
|
||||||
|
return &PrivateLightAPI{
|
||||||
|
backend: backend,
|
||||||
|
reg: reg,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// LatestCheckpoint returns the latest local checkpoint package.
|
||||||
|
//
|
||||||
|
// The checkpoint package consists of 4 strings:
|
||||||
|
// result[0], hex encoded latest section index
|
||||||
|
// result[1], 32 bytes hex encoded latest section head hash
|
||||||
|
// result[2], 32 bytes hex encoded latest section canonical hash trie root hash
|
||||||
|
// result[3], 32 bytes hex encoded latest section bloom trie root hash
|
||||||
|
func (api *PrivateLightAPI) LatestCheckpoint() ([4]string, error) {
|
||||||
|
var res [4]string
|
||||||
|
cp := api.backend.latestLocalCheckpoint()
|
||||||
|
if cp.Empty() {
|
||||||
|
return res, ErrNoCheckpoint
|
||||||
|
}
|
||||||
|
res[0] = hexutil.EncodeUint64(cp.SectionIndex)
|
||||||
|
res[1], res[2], res[3] = cp.SectionHead.Hex(), cp.CHTRoot.Hex(), cp.BloomRoot.Hex()
|
||||||
|
return res, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetLocalCheckpoint returns the specific local checkpoint package.
|
||||||
|
//
|
||||||
|
// The checkpoint package consists of 3 strings:
|
||||||
|
// result[0], 32 bytes hex encoded latest section head hash
|
||||||
|
// result[1], 32 bytes hex encoded latest section canonical hash trie root hash
|
||||||
|
// result[2], 32 bytes hex encoded latest section bloom trie root hash
|
||||||
|
func (api *PrivateLightAPI) GetCheckpoint(index uint64) ([3]string, error) {
|
||||||
|
var res [3]string
|
||||||
|
cp := api.backend.getLocalCheckpoint(index)
|
||||||
|
if cp.Empty() {
|
||||||
|
return res, ErrNoCheckpoint
|
||||||
|
}
|
||||||
|
res[0], res[1], res[2] = cp.SectionHead.Hex(), cp.CHTRoot.Hex(), cp.BloomRoot.Hex()
|
||||||
|
return res, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetCheckpointContractAddress returns the contract contract address in hex format.
|
||||||
|
func (api *PrivateLightAPI) GetCheckpointContractAddress() (string, error) {
|
||||||
|
if api.reg == nil {
|
||||||
|
return "", ErrNotActivated
|
||||||
|
}
|
||||||
|
return api.reg.config.Address.Hex(), nil
|
||||||
|
}
|
||||||
|
@ -23,6 +23,7 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/accounts"
|
"github.com/ethereum/go-ethereum/accounts"
|
||||||
|
"github.com/ethereum/go-ethereum/accounts/abi/bind"
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||||
"github.com/ethereum/go-ethereum/common/mclock"
|
"github.com/ethereum/go-ethereum/common/mclock"
|
||||||
@ -43,14 +44,13 @@ import (
|
|||||||
"github.com/ethereum/go-ethereum/p2p"
|
"github.com/ethereum/go-ethereum/p2p"
|
||||||
"github.com/ethereum/go-ethereum/p2p/discv5"
|
"github.com/ethereum/go-ethereum/p2p/discv5"
|
||||||
"github.com/ethereum/go-ethereum/params"
|
"github.com/ethereum/go-ethereum/params"
|
||||||
rpc "github.com/ethereum/go-ethereum/rpc"
|
"github.com/ethereum/go-ethereum/rpc"
|
||||||
)
|
)
|
||||||
|
|
||||||
type LightEthereum struct {
|
type LightEthereum struct {
|
||||||
lesCommons
|
lesCommons
|
||||||
|
|
||||||
odr *LesOdr
|
odr *LesOdr
|
||||||
relay *LesTxRelay
|
|
||||||
chainConfig *params.ChainConfig
|
chainConfig *params.ChainConfig
|
||||||
// Channel for shutting down the service
|
// Channel for shutting down the service
|
||||||
shutdownChan chan bool
|
shutdownChan chan bool
|
||||||
@ -62,6 +62,7 @@ type LightEthereum struct {
|
|||||||
serverPool *serverPool
|
serverPool *serverPool
|
||||||
reqDist *requestDistributor
|
reqDist *requestDistributor
|
||||||
retriever *retrieveManager
|
retriever *retrieveManager
|
||||||
|
relay *lesTxRelay
|
||||||
|
|
||||||
bloomRequests chan chan *bloombits.Retrieval // Channel receiving bloom data retrieval requests
|
bloomRequests chan chan *bloombits.Retrieval // Channel receiving bloom data retrieval requests
|
||||||
bloomIndexer *core.ChainIndexer
|
bloomIndexer *core.ChainIndexer
|
||||||
@ -116,16 +117,20 @@ func New(ctx *node.ServiceContext, config *eth.Config) (*LightEthereum, error) {
|
|||||||
}
|
}
|
||||||
leth.serverPool = newServerPool(chainDb, quitSync, &leth.wg, trustedNodes)
|
leth.serverPool = newServerPool(chainDb, quitSync, &leth.wg, trustedNodes)
|
||||||
leth.retriever = newRetrieveManager(peers, leth.reqDist, leth.serverPool)
|
leth.retriever = newRetrieveManager(peers, leth.reqDist, leth.serverPool)
|
||||||
leth.relay = NewLesTxRelay(peers, leth.retriever)
|
leth.relay = newLesTxRelay(peers, leth.retriever)
|
||||||
|
|
||||||
leth.odr = NewLesOdr(chainDb, light.DefaultClientIndexerConfig, leth.retriever)
|
leth.odr = NewLesOdr(chainDb, light.DefaultClientIndexerConfig, leth.retriever)
|
||||||
leth.chtIndexer = light.NewChtIndexer(chainDb, leth.odr, params.CHTFrequency, params.HelperTrieConfirmations)
|
leth.chtIndexer = light.NewChtIndexer(chainDb, leth.odr, params.CHTFrequency, params.HelperTrieConfirmations)
|
||||||
leth.bloomTrieIndexer = light.NewBloomTrieIndexer(chainDb, leth.odr, params.BloomBitsBlocksClient, params.BloomTrieFrequency)
|
leth.bloomTrieIndexer = light.NewBloomTrieIndexer(chainDb, leth.odr, params.BloomBitsBlocksClient, params.BloomTrieFrequency)
|
||||||
leth.odr.SetIndexers(leth.chtIndexer, leth.bloomTrieIndexer, leth.bloomIndexer)
|
leth.odr.SetIndexers(leth.chtIndexer, leth.bloomTrieIndexer, leth.bloomIndexer)
|
||||||
|
|
||||||
|
checkpoint := config.Checkpoint
|
||||||
|
if checkpoint == nil {
|
||||||
|
checkpoint = params.TrustedCheckpoints[genesisHash]
|
||||||
|
}
|
||||||
// Note: NewLightChain adds the trusted checkpoint so it needs an ODR with
|
// Note: NewLightChain adds the trusted checkpoint so it needs an ODR with
|
||||||
// indexers already set but not started yet
|
// indexers already set but not started yet
|
||||||
if leth.blockchain, err = light.NewLightChain(leth.odr, leth.chainConfig, leth.engine); err != nil {
|
if leth.blockchain, err = light.NewLightChain(leth.odr, leth.chainConfig, leth.engine, checkpoint); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
// Note: AddChildIndexer starts the update process for the child
|
// Note: AddChildIndexer starts the update process for the child
|
||||||
@ -141,32 +146,6 @@ func New(ctx *node.ServiceContext, config *eth.Config) (*LightEthereum, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
leth.txPool = light.NewTxPool(leth.chainConfig, leth.blockchain, leth.relay)
|
leth.txPool = light.NewTxPool(leth.chainConfig, leth.blockchain, leth.relay)
|
||||||
|
|
||||||
if leth.protocolManager, err = NewProtocolManager(
|
|
||||||
leth.chainConfig,
|
|
||||||
light.DefaultClientIndexerConfig,
|
|
||||||
true,
|
|
||||||
config.NetworkId,
|
|
||||||
leth.eventMux,
|
|
||||||
leth.engine,
|
|
||||||
leth.peers,
|
|
||||||
leth.blockchain,
|
|
||||||
nil,
|
|
||||||
chainDb,
|
|
||||||
leth.odr,
|
|
||||||
leth.relay,
|
|
||||||
leth.serverPool,
|
|
||||||
quitSync,
|
|
||||||
&leth.wg,
|
|
||||||
config.ULC,
|
|
||||||
nil); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if leth.protocolManager.isULCEnabled() {
|
|
||||||
log.Warn("Ultra light client is enabled", "trustedNodes", len(leth.protocolManager.ulc.trustedKeys), "minTrustedFraction", leth.protocolManager.ulc.minTrustedFraction)
|
|
||||||
leth.blockchain.DisableCheckFreq()
|
|
||||||
}
|
|
||||||
leth.ApiBackend = &LesApiBackend{ctx.ExtRPCEnabled(), leth, nil}
|
leth.ApiBackend = &LesApiBackend{ctx.ExtRPCEnabled(), leth, nil}
|
||||||
|
|
||||||
gpoParams := config.GPO
|
gpoParams := config.GPO
|
||||||
@ -174,6 +153,19 @@ func New(ctx *node.ServiceContext, config *eth.Config) (*LightEthereum, error) {
|
|||||||
gpoParams.Default = config.Miner.GasPrice
|
gpoParams.Default = config.Miner.GasPrice
|
||||||
}
|
}
|
||||||
leth.ApiBackend.gpo = gasprice.NewOracle(leth.ApiBackend, gpoParams)
|
leth.ApiBackend.gpo = gasprice.NewOracle(leth.ApiBackend, gpoParams)
|
||||||
|
|
||||||
|
oracle := config.CheckpointOracle
|
||||||
|
if oracle == nil {
|
||||||
|
oracle = params.CheckpointOracles[genesisHash]
|
||||||
|
}
|
||||||
|
registrar := newCheckpointOracle(oracle, leth.getLocalCheckpoint)
|
||||||
|
if leth.protocolManager, err = NewProtocolManager(leth.chainConfig, checkpoint, light.DefaultClientIndexerConfig, config.ULC, true, config.NetworkId, leth.eventMux, leth.peers, leth.blockchain, nil, chainDb, leth.odr, leth.serverPool, registrar, quitSync, &leth.wg, nil); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if leth.protocolManager.isULCEnabled() {
|
||||||
|
log.Warn("Ultra light client is enabled", "trustedNodes", len(leth.protocolManager.ulc.trustedKeys), "minTrustedFraction", leth.protocolManager.ulc.minTrustedFraction)
|
||||||
|
leth.blockchain.DisableCheckFreq()
|
||||||
|
}
|
||||||
return leth, nil
|
return leth, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -234,6 +226,11 @@ func (s *LightEthereum) APIs() []rpc.API {
|
|||||||
Version: "1.0",
|
Version: "1.0",
|
||||||
Service: s.netRPCService,
|
Service: s.netRPCService,
|
||||||
Public: true,
|
Public: true,
|
||||||
|
}, {
|
||||||
|
Namespace: "les",
|
||||||
|
Version: "1.0",
|
||||||
|
Service: NewPrivateLightAPI(&s.lesCommons, s.protocolManager.reg),
|
||||||
|
Public: false,
|
||||||
},
|
},
|
||||||
}...)
|
}...)
|
||||||
}
|
}
|
||||||
@ -288,3 +285,12 @@ func (s *LightEthereum) Stop() error {
|
|||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SetClient sets the rpc client and binds the registrar contract.
|
||||||
|
func (s *LightEthereum) SetContractBackend(backend bind.ContractBackend) {
|
||||||
|
// Short circuit if registrar is nil
|
||||||
|
if s.protocolManager.reg == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
s.protocolManager.reg.start(backend)
|
||||||
|
}
|
||||||
|
158
les/checkpointoracle.go
Normal file
158
les/checkpointoracle.go
Normal file
@ -0,0 +1,158 @@
|
|||||||
|
// Copyright 2019 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 <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
package les
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/binary"
|
||||||
|
"sync/atomic"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/accounts/abi/bind"
|
||||||
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
"github.com/ethereum/go-ethereum/contracts/checkpointoracle"
|
||||||
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
|
"github.com/ethereum/go-ethereum/log"
|
||||||
|
"github.com/ethereum/go-ethereum/params"
|
||||||
|
)
|
||||||
|
|
||||||
|
// checkpointOracle is responsible for offering the latest stable checkpoint
|
||||||
|
// generated and announced by the contract admins on-chain. The checkpoint is
|
||||||
|
// verified by clients locally during the checkpoint syncing.
|
||||||
|
type checkpointOracle struct {
|
||||||
|
config *params.CheckpointOracleConfig
|
||||||
|
contract *checkpointoracle.CheckpointOracle
|
||||||
|
|
||||||
|
// Whether the contract backend is set.
|
||||||
|
running int32
|
||||||
|
|
||||||
|
getLocal func(uint64) params.TrustedCheckpoint // Function used to retrieve local checkpoint
|
||||||
|
syncDoneHook func() // Function used to notify that light syncing has completed.
|
||||||
|
}
|
||||||
|
|
||||||
|
// newCheckpointOracle returns a checkpoint registrar handler.
|
||||||
|
func newCheckpointOracle(config *params.CheckpointOracleConfig, getLocal func(uint64) params.TrustedCheckpoint) *checkpointOracle {
|
||||||
|
if config == nil {
|
||||||
|
log.Info("Checkpoint registrar is not enabled")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if config.Address == (common.Address{}) || uint64(len(config.Signers)) < config.Threshold {
|
||||||
|
log.Warn("Invalid checkpoint registrar config")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
log.Info("Configured checkpoint registrar", "address", config.Address, "signers", len(config.Signers), "threshold", config.Threshold)
|
||||||
|
|
||||||
|
return &checkpointOracle{
|
||||||
|
config: config,
|
||||||
|
getLocal: getLocal,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// start binds the registrar contract and start listening to the
|
||||||
|
// newCheckpointEvent for the server side.
|
||||||
|
func (reg *checkpointOracle) start(backend bind.ContractBackend) {
|
||||||
|
contract, err := checkpointoracle.NewCheckpointOracle(reg.config.Address, backend)
|
||||||
|
if err != nil {
|
||||||
|
log.Error("Oracle contract binding failed", "err", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if !atomic.CompareAndSwapInt32(®.running, 0, 1) {
|
||||||
|
log.Error("Already bound and listening to registrar")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
reg.contract = contract
|
||||||
|
}
|
||||||
|
|
||||||
|
// isRunning returns an indicator whether the registrar is running.
|
||||||
|
func (reg *checkpointOracle) isRunning() bool {
|
||||||
|
return atomic.LoadInt32(®.running) == 1
|
||||||
|
}
|
||||||
|
|
||||||
|
// stableCheckpoint returns the stable checkpoint which was generated by local
|
||||||
|
// indexers and announced by trusted signers.
|
||||||
|
func (reg *checkpointOracle) stableCheckpoint() (*params.TrustedCheckpoint, uint64) {
|
||||||
|
// Retrieve the latest checkpoint from the contract, abort if empty
|
||||||
|
latest, hash, height, err := reg.contract.Contract().GetLatestCheckpoint(nil)
|
||||||
|
if err != nil || (latest == 0 && hash == [32]byte{}) {
|
||||||
|
return nil, 0
|
||||||
|
}
|
||||||
|
local := reg.getLocal(latest)
|
||||||
|
|
||||||
|
// The following scenarios may occur:
|
||||||
|
//
|
||||||
|
// * local node is out of sync so that it doesn't have the
|
||||||
|
// checkpoint which registered in the contract.
|
||||||
|
// * local checkpoint doesn't match with the registered one.
|
||||||
|
//
|
||||||
|
// In both cases, server won't send the **stable** checkpoint
|
||||||
|
// to the client(no worry, client can use hardcoded one instead).
|
||||||
|
if local.HashEqual(common.Hash(hash)) {
|
||||||
|
return &local, height.Uint64()
|
||||||
|
}
|
||||||
|
return nil, 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// verifySigners recovers the signer addresses according to the signature and
|
||||||
|
// checks whether there are enough approvals to finalize the checkpoint.
|
||||||
|
func (reg *checkpointOracle) verifySigners(index uint64, hash [32]byte, signatures [][]byte) (bool, []common.Address) {
|
||||||
|
// Short circuit if the given signatures doesn't reach the threshold.
|
||||||
|
if len(signatures) < int(reg.config.Threshold) {
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
var (
|
||||||
|
signers []common.Address
|
||||||
|
checked = make(map[common.Address]struct{})
|
||||||
|
)
|
||||||
|
for i := 0; i < len(signatures); i++ {
|
||||||
|
if len(signatures[i]) != 65 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
// EIP 191 style signatures
|
||||||
|
//
|
||||||
|
// Arguments when calculating hash to validate
|
||||||
|
// 1: byte(0x19) - the initial 0x19 byte
|
||||||
|
// 2: byte(0) - the version byte (data with intended validator)
|
||||||
|
// 3: this - the validator address
|
||||||
|
// -- Application specific data
|
||||||
|
// 4 : checkpoint section_index (uint64)
|
||||||
|
// 5 : checkpoint hash (bytes32)
|
||||||
|
// hash = keccak256(checkpoint_index, section_head, cht_root, bloom_root)
|
||||||
|
buf := make([]byte, 8)
|
||||||
|
binary.BigEndian.PutUint64(buf, index)
|
||||||
|
data := append([]byte{0x19, 0x00}, append(reg.config.Address.Bytes(), append(buf, hash[:]...)...)...)
|
||||||
|
signatures[i][64] -= 27 // Transform V from 27/28 to 0/1 according to the yellow paper for verification.
|
||||||
|
pubkey, err := crypto.Ecrecover(crypto.Keccak256(data), signatures[i])
|
||||||
|
if err != nil {
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
var signer common.Address
|
||||||
|
copy(signer[:], crypto.Keccak256(pubkey[1:])[12:])
|
||||||
|
if _, exist := checked[signer]; exist {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
for _, s := range reg.config.Signers {
|
||||||
|
if s == signer {
|
||||||
|
signers = append(signers, signer)
|
||||||
|
checked[signer] = struct{}{}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
threshold := reg.config.Threshold
|
||||||
|
if uint64(len(signers)) < threshold {
|
||||||
|
log.Warn("Not enough signers to approve checkpoint", "signers", len(signers), "threshold", threshold)
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
return true, signers
|
||||||
|
}
|
@ -76,24 +76,6 @@ func (c *lesCommons) makeProtocols(versions []uint) []p2p.Protocol {
|
|||||||
|
|
||||||
// nodeInfo retrieves some protocol metadata about the running host node.
|
// nodeInfo retrieves some protocol metadata about the running host node.
|
||||||
func (c *lesCommons) nodeInfo() interface{} {
|
func (c *lesCommons) nodeInfo() interface{} {
|
||||||
var cht params.TrustedCheckpoint
|
|
||||||
sections, _, _ := c.chtIndexer.Sections()
|
|
||||||
sections2, _, _ := c.bloomTrieIndexer.Sections()
|
|
||||||
|
|
||||||
if sections2 < sections {
|
|
||||||
sections = sections2
|
|
||||||
}
|
|
||||||
if sections > 0 {
|
|
||||||
sectionIndex := sections - 1
|
|
||||||
sectionHead := c.bloomTrieIndexer.SectionHead(sectionIndex)
|
|
||||||
cht = params.TrustedCheckpoint{
|
|
||||||
SectionIndex: sectionIndex,
|
|
||||||
SectionHead: sectionHead,
|
|
||||||
CHTRoot: light.GetChtRoot(c.chainDb, sectionIndex, sectionHead),
|
|
||||||
BloomRoot: light.GetBloomTrieRoot(c.chainDb, sectionIndex, sectionHead),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
chain := c.protocolManager.blockchain
|
chain := c.protocolManager.blockchain
|
||||||
head := chain.CurrentHeader()
|
head := chain.CurrentHeader()
|
||||||
hash := head.Hash()
|
hash := head.Hash()
|
||||||
@ -103,6 +85,38 @@ func (c *lesCommons) nodeInfo() interface{} {
|
|||||||
Genesis: chain.Genesis().Hash(),
|
Genesis: chain.Genesis().Hash(),
|
||||||
Config: chain.Config(),
|
Config: chain.Config(),
|
||||||
Head: chain.CurrentHeader().Hash(),
|
Head: chain.CurrentHeader().Hash(),
|
||||||
CHT: cht,
|
CHT: c.latestLocalCheckpoint(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// latestLocalCheckpoint finds the common stored section index and returns a set of
|
||||||
|
// post-processed trie roots (CHT and BloomTrie) associated with
|
||||||
|
// the appropriate section index and head hash as a local checkpoint package.
|
||||||
|
func (c *lesCommons) latestLocalCheckpoint() params.TrustedCheckpoint {
|
||||||
|
sections, _, _ := c.chtIndexer.Sections()
|
||||||
|
sections2, _, _ := c.bloomTrieIndexer.Sections()
|
||||||
|
// Cap the section index if the two sections are not consistent.
|
||||||
|
if sections > sections2 {
|
||||||
|
sections = sections2
|
||||||
|
}
|
||||||
|
if sections == 0 {
|
||||||
|
// No checkpoint information can be provided.
|
||||||
|
return params.TrustedCheckpoint{}
|
||||||
|
}
|
||||||
|
return c.getLocalCheckpoint(sections - 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// getLocalCheckpoint returns a set of post-processed trie roots (CHT and BloomTrie)
|
||||||
|
// associated with the appropriate head hash by specific section index.
|
||||||
|
//
|
||||||
|
// The returned checkpoint is only the checkpoint generated by the local indexers,
|
||||||
|
// not the stable checkpoint registered in the registrar contract.
|
||||||
|
func (c *lesCommons) getLocalCheckpoint(index uint64) params.TrustedCheckpoint {
|
||||||
|
sectionHead := c.chtIndexer.SectionHead(index)
|
||||||
|
return params.TrustedCheckpoint{
|
||||||
|
SectionIndex: index,
|
||||||
|
SectionHead: sectionHead,
|
||||||
|
CHTRoot: light.GetChtRoot(c.chainDb, index, sectionHead),
|
||||||
|
BloomRoot: light.GetBloomTrieRoot(c.chainDb, index, sectionHead),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -27,7 +27,6 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/consensus"
|
|
||||||
"github.com/ethereum/go-ethereum/core"
|
"github.com/ethereum/go-ethereum/core"
|
||||||
"github.com/ethereum/go-ethereum/core/rawdb"
|
"github.com/ethereum/go-ethereum/core/rawdb"
|
||||||
"github.com/ethereum/go-ethereum/core/state"
|
"github.com/ethereum/go-ethereum/core/state"
|
||||||
@ -101,7 +100,7 @@ type ProtocolManager struct {
|
|||||||
networkId uint64 // The identity of network.
|
networkId uint64 // The identity of network.
|
||||||
|
|
||||||
txpool txPool
|
txpool txPool
|
||||||
txrelay *LesTxRelay
|
txrelay *lesTxRelay
|
||||||
blockchain BlockChain
|
blockchain BlockChain
|
||||||
chainDb ethdb.Database
|
chainDb ethdb.Database
|
||||||
odr *LesOdr
|
odr *LesOdr
|
||||||
@ -115,6 +114,8 @@ type ProtocolManager struct {
|
|||||||
fetcher *lightFetcher
|
fetcher *lightFetcher
|
||||||
ulc *ulc
|
ulc *ulc
|
||||||
peers *peerSet
|
peers *peerSet
|
||||||
|
checkpoint *params.TrustedCheckpoint
|
||||||
|
reg *checkpointOracle // If reg == nil, it means the checkpoint registrar is not activated
|
||||||
|
|
||||||
// channels for fetcher, syncer, txsyncLoop
|
// channels for fetcher, syncer, txsyncLoop
|
||||||
newPeerCh chan *peer
|
newPeerCh chan *peer
|
||||||
@ -131,23 +132,7 @@ type ProtocolManager struct {
|
|||||||
|
|
||||||
// NewProtocolManager returns a new ethereum sub protocol manager. The Ethereum sub protocol manages peers capable
|
// NewProtocolManager returns a new ethereum sub protocol manager. The Ethereum sub protocol manages peers capable
|
||||||
// with the ethereum network.
|
// with the ethereum network.
|
||||||
func NewProtocolManager(
|
func NewProtocolManager(chainConfig *params.ChainConfig, checkpoint *params.TrustedCheckpoint, indexerConfig *light.IndexerConfig, ulcConfig *eth.ULCConfig, client bool, networkId uint64, mux *event.TypeMux, peers *peerSet, blockchain BlockChain, txpool txPool, chainDb ethdb.Database, odr *LesOdr, serverPool *serverPool, registrar *checkpointOracle, quitSync chan struct{}, wg *sync.WaitGroup, synced func() bool) (*ProtocolManager, error) {
|
||||||
chainConfig *params.ChainConfig,
|
|
||||||
indexerConfig *light.IndexerConfig,
|
|
||||||
client bool,
|
|
||||||
networkId uint64,
|
|
||||||
mux *event.TypeMux,
|
|
||||||
engine consensus.Engine,
|
|
||||||
peers *peerSet,
|
|
||||||
blockchain BlockChain,
|
|
||||||
txpool txPool,
|
|
||||||
chainDb ethdb.Database,
|
|
||||||
odr *LesOdr,
|
|
||||||
txrelay *LesTxRelay,
|
|
||||||
serverPool *serverPool,
|
|
||||||
quitSync chan struct{},
|
|
||||||
wg *sync.WaitGroup,
|
|
||||||
ulcConfig *eth.ULCConfig, synced func() bool) (*ProtocolManager, error) {
|
|
||||||
// Create the protocol manager with the base fields
|
// Create the protocol manager with the base fields
|
||||||
manager := &ProtocolManager{
|
manager := &ProtocolManager{
|
||||||
client: client,
|
client: client,
|
||||||
@ -159,13 +144,14 @@ func NewProtocolManager(
|
|||||||
odr: odr,
|
odr: odr,
|
||||||
networkId: networkId,
|
networkId: networkId,
|
||||||
txpool: txpool,
|
txpool: txpool,
|
||||||
txrelay: txrelay,
|
|
||||||
serverPool: serverPool,
|
serverPool: serverPool,
|
||||||
|
reg: registrar,
|
||||||
peers: peers,
|
peers: peers,
|
||||||
newPeerCh: make(chan *peer),
|
newPeerCh: make(chan *peer),
|
||||||
quitSync: quitSync,
|
quitSync: quitSync,
|
||||||
wg: wg,
|
wg: wg,
|
||||||
noMorePeers: make(chan struct{}),
|
noMorePeers: make(chan struct{}),
|
||||||
|
checkpoint: checkpoint,
|
||||||
synced: synced,
|
synced: synced,
|
||||||
}
|
}
|
||||||
if odr != nil {
|
if odr != nil {
|
||||||
@ -182,11 +168,11 @@ func NewProtocolManager(
|
|||||||
removePeer = func(id string) {}
|
removePeer = func(id string) {}
|
||||||
}
|
}
|
||||||
if client {
|
if client {
|
||||||
var checkpoint uint64
|
var checkpointNumber uint64
|
||||||
if cht, ok := params.TrustedCheckpoints[blockchain.Genesis().Hash()]; ok {
|
if checkpoint != nil {
|
||||||
checkpoint = (cht.SectionIndex+1)*params.CHTFrequency - 1
|
checkpointNumber = (checkpoint.SectionIndex+1)*params.CHTFrequency - 1
|
||||||
}
|
}
|
||||||
manager.downloader = downloader.New(checkpoint, chainDb, nil, manager.eventMux, nil, blockchain, removePeer)
|
manager.downloader = downloader.New(checkpointNumber, chainDb, nil, manager.eventMux, nil, blockchain, removePeer)
|
||||||
manager.peers.notify((*downloaderPeerNotify)(manager))
|
manager.peers.notify((*downloaderPeerNotify)(manager))
|
||||||
manager.fetcher = newLightFetcher(manager)
|
manager.fetcher = newLightFetcher(manager)
|
||||||
}
|
}
|
||||||
|
@ -259,7 +259,6 @@ func testGetCode(t *testing.T, protocol int) {
|
|||||||
|
|
||||||
var codereqs []*CodeReq
|
var codereqs []*CodeReq
|
||||||
var codes [][]byte
|
var codes [][]byte
|
||||||
|
|
||||||
for i := uint64(0); i <= bc.CurrentBlock().NumberU64(); i++ {
|
for i := uint64(0); i <= bc.CurrentBlock().NumberU64(); i++ {
|
||||||
header := bc.GetHeaderByNumber(i)
|
header := bc.GetHeaderByNumber(i)
|
||||||
req := &CodeReq{
|
req := &CodeReq{
|
||||||
@ -342,11 +341,10 @@ func testGetProofs(t *testing.T, protocol int) {
|
|||||||
var proofreqs []ProofReq
|
var proofreqs []ProofReq
|
||||||
proofsV2 := light.NewNodeSet()
|
proofsV2 := light.NewNodeSet()
|
||||||
|
|
||||||
accounts := []common.Address{testBankAddress, acc1Addr, acc2Addr, {}}
|
accounts := []common.Address{bankAddr, userAddr1, userAddr2, {}}
|
||||||
for i := uint64(0); i <= bc.CurrentBlock().NumberU64(); i++ {
|
for i := uint64(0); i <= bc.CurrentBlock().NumberU64(); i++ {
|
||||||
header := bc.GetHeaderByNumber(i)
|
header := bc.GetHeaderByNumber(i)
|
||||||
root := header.Root
|
trie, _ := trie.New(header.Root, trie.NewDatabase(server.db))
|
||||||
trie, _ := trie.New(root, trie.NewDatabase(server.db))
|
|
||||||
|
|
||||||
for _, acc := range accounts {
|
for _, acc := range accounts {
|
||||||
req := ProofReq{
|
req := ProofReq{
|
||||||
@ -377,7 +375,7 @@ func testGetStaleProof(t *testing.T, protocol int) {
|
|||||||
check := func(number uint64, wantOK bool) {
|
check := func(number uint64, wantOK bool) {
|
||||||
var (
|
var (
|
||||||
header = bc.GetHeaderByNumber(number)
|
header = bc.GetHeaderByNumber(number)
|
||||||
account = crypto.Keccak256(testBankAddress.Bytes())
|
account = crypto.Keccak256(userAddr1.Bytes())
|
||||||
)
|
)
|
||||||
req := &ProofReq{
|
req := &ProofReq{
|
||||||
BHash: header.Hash(),
|
BHash: header.Hash(),
|
||||||
@ -390,7 +388,7 @@ func testGetStaleProof(t *testing.T, protocol int) {
|
|||||||
if wantOK {
|
if wantOK {
|
||||||
proofsV2 := light.NewNodeSet()
|
proofsV2 := light.NewNodeSet()
|
||||||
t, _ := trie.New(header.Root, trie.NewDatabase(server.db))
|
t, _ := trie.New(header.Root, trie.NewDatabase(server.db))
|
||||||
t.Prove(crypto.Keccak256(account), 0, proofsV2)
|
t.Prove(account, 0, proofsV2)
|
||||||
expected = proofsV2.NodeList()
|
expected = proofsV2.NodeList()
|
||||||
}
|
}
|
||||||
if err := expectResponse(server.tPeer.app, ProofsV2Msg, 42, testBufLimit, expected); err != nil {
|
if err := expectResponse(server.tPeer.app, ProofsV2Msg, 42, testBufLimit, expected); err != nil {
|
||||||
@ -496,14 +494,15 @@ func TestGetBloombitsProofs(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestTransactionStatusLes2(t *testing.T) {
|
func TestTransactionStatusLes2(t *testing.T) {
|
||||||
db := rawdb.NewMemoryDatabase()
|
server, tearDown := newServerEnv(t, 0, 2, nil)
|
||||||
pm := newTestProtocolManagerMust(t, false, 0, nil, nil, nil, db, nil)
|
defer tearDown()
|
||||||
chain := pm.blockchain.(*core.BlockChain)
|
|
||||||
|
chain := server.pm.blockchain.(*core.BlockChain)
|
||||||
config := core.DefaultTxPoolConfig
|
config := core.DefaultTxPoolConfig
|
||||||
config.Journal = ""
|
config.Journal = ""
|
||||||
txpool := core.NewTxPool(config, params.TestChainConfig, chain)
|
txpool := core.NewTxPool(config, params.TestChainConfig, chain)
|
||||||
pm.txpool = txpool
|
server.pm.txpool = txpool
|
||||||
peer, _ := newTestPeer(t, "peer", 2, pm, true, 0)
|
peer, _ := newTestPeer(t, "peer", 2, server.pm, true, 0)
|
||||||
defer peer.close()
|
defer peer.close()
|
||||||
|
|
||||||
var reqID uint64
|
var reqID uint64
|
||||||
@ -511,13 +510,13 @@ func TestTransactionStatusLes2(t *testing.T) {
|
|||||||
test := func(tx *types.Transaction, send bool, expStatus light.TxStatus) {
|
test := func(tx *types.Transaction, send bool, expStatus light.TxStatus) {
|
||||||
reqID++
|
reqID++
|
||||||
if send {
|
if send {
|
||||||
cost := peer.GetRequestCost(SendTxV2Msg, 1)
|
cost := server.tPeer.GetRequestCost(SendTxV2Msg, 1)
|
||||||
sendRequest(peer.app, SendTxV2Msg, reqID, cost, types.Transactions{tx})
|
sendRequest(server.tPeer.app, SendTxV2Msg, reqID, cost, types.Transactions{tx})
|
||||||
} else {
|
} else {
|
||||||
cost := peer.GetRequestCost(GetTxStatusMsg, 1)
|
cost := server.tPeer.GetRequestCost(GetTxStatusMsg, 1)
|
||||||
sendRequest(peer.app, GetTxStatusMsg, reqID, cost, []common.Hash{tx.Hash()})
|
sendRequest(server.tPeer.app, GetTxStatusMsg, reqID, cost, []common.Hash{tx.Hash()})
|
||||||
}
|
}
|
||||||
if err := expectResponse(peer.app, TxStatusMsg, reqID, testBufLimit, []light.TxStatus{expStatus}); err != nil {
|
if err := expectResponse(server.tPeer.app, TxStatusMsg, reqID, testBufLimit, []light.TxStatus{expStatus}); err != nil {
|
||||||
t.Errorf("transaction status mismatch")
|
t.Errorf("transaction status mismatch")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -525,16 +524,16 @@ func TestTransactionStatusLes2(t *testing.T) {
|
|||||||
signer := types.HomesteadSigner{}
|
signer := types.HomesteadSigner{}
|
||||||
|
|
||||||
// test error status by sending an underpriced transaction
|
// test error status by sending an underpriced transaction
|
||||||
tx0, _ := types.SignTx(types.NewTransaction(0, acc1Addr, big.NewInt(10000), params.TxGas, nil, nil), signer, testBankKey)
|
tx0, _ := types.SignTx(types.NewTransaction(0, userAddr1, big.NewInt(10000), params.TxGas, nil, nil), signer, bankKey)
|
||||||
test(tx0, true, light.TxStatus{Status: core.TxStatusUnknown, Error: core.ErrUnderpriced.Error()})
|
test(tx0, true, light.TxStatus{Status: core.TxStatusUnknown, Error: core.ErrUnderpriced.Error()})
|
||||||
|
|
||||||
tx1, _ := types.SignTx(types.NewTransaction(0, acc1Addr, big.NewInt(10000), params.TxGas, big.NewInt(100000000000), nil), signer, testBankKey)
|
tx1, _ := types.SignTx(types.NewTransaction(0, userAddr1, big.NewInt(10000), params.TxGas, big.NewInt(100000000000), nil), signer, bankKey)
|
||||||
test(tx1, false, light.TxStatus{Status: core.TxStatusUnknown}) // query before sending, should be unknown
|
test(tx1, false, light.TxStatus{Status: core.TxStatusUnknown}) // query before sending, should be unknown
|
||||||
test(tx1, true, light.TxStatus{Status: core.TxStatusPending}) // send valid processable tx, should return pending
|
test(tx1, true, light.TxStatus{Status: core.TxStatusPending}) // send valid processable tx, should return pending
|
||||||
test(tx1, true, light.TxStatus{Status: core.TxStatusPending}) // adding it again should not return an error
|
test(tx1, true, light.TxStatus{Status: core.TxStatusPending}) // adding it again should not return an error
|
||||||
|
|
||||||
tx2, _ := types.SignTx(types.NewTransaction(1, acc1Addr, big.NewInt(10000), params.TxGas, big.NewInt(100000000000), nil), signer, testBankKey)
|
tx2, _ := types.SignTx(types.NewTransaction(1, userAddr1, big.NewInt(10000), params.TxGas, big.NewInt(100000000000), nil), signer, bankKey)
|
||||||
tx3, _ := types.SignTx(types.NewTransaction(2, acc1Addr, big.NewInt(10000), params.TxGas, big.NewInt(100000000000), nil), signer, testBankKey)
|
tx3, _ := types.SignTx(types.NewTransaction(2, userAddr1, big.NewInt(10000), params.TxGas, big.NewInt(100000000000), nil), signer, bankKey)
|
||||||
// send transactions in the wrong order, tx3 should be queued
|
// send transactions in the wrong order, tx3 should be queued
|
||||||
test(tx3, true, light.TxStatus{Status: core.TxStatusQueued})
|
test(tx3, true, light.TxStatus{Status: core.TxStatusQueued})
|
||||||
test(tx2, true, light.TxStatus{Status: core.TxStatusPending})
|
test(tx2, true, light.TxStatus{Status: core.TxStatusPending})
|
||||||
@ -542,7 +541,7 @@ func TestTransactionStatusLes2(t *testing.T) {
|
|||||||
test(tx3, false, light.TxStatus{Status: core.TxStatusPending})
|
test(tx3, false, light.TxStatus{Status: core.TxStatusPending})
|
||||||
|
|
||||||
// generate and add a block with tx1 and tx2 included
|
// generate and add a block with tx1 and tx2 included
|
||||||
gchain, _ := core.GenerateChain(params.TestChainConfig, chain.GetBlockByNumber(0), ethash.NewFaker(), db, 1, func(i int, block *core.BlockGen) {
|
gchain, _ := core.GenerateChain(params.TestChainConfig, chain.GetBlockByNumber(0), ethash.NewFaker(), server.db, 1, func(i int, block *core.BlockGen) {
|
||||||
block.AddTx(tx1)
|
block.AddTx(tx1)
|
||||||
block.AddTx(tx2)
|
block.AddTx(tx2)
|
||||||
})
|
})
|
||||||
@ -561,12 +560,12 @@ func TestTransactionStatusLes2(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// check if their status is included now
|
// check if their status is included now
|
||||||
block1hash := rawdb.ReadCanonicalHash(db, 1)
|
block1hash := rawdb.ReadCanonicalHash(server.db, 1)
|
||||||
test(tx1, false, light.TxStatus{Status: core.TxStatusIncluded, Lookup: &rawdb.LegacyTxLookupEntry{BlockHash: block1hash, BlockIndex: 1, Index: 0}})
|
test(tx1, false, light.TxStatus{Status: core.TxStatusIncluded, Lookup: &rawdb.LegacyTxLookupEntry{BlockHash: block1hash, BlockIndex: 1, Index: 0}})
|
||||||
test(tx2, false, light.TxStatus{Status: core.TxStatusIncluded, Lookup: &rawdb.LegacyTxLookupEntry{BlockHash: block1hash, BlockIndex: 1, Index: 1}})
|
test(tx2, false, light.TxStatus{Status: core.TxStatusIncluded, Lookup: &rawdb.LegacyTxLookupEntry{BlockHash: block1hash, BlockIndex: 1, Index: 1}})
|
||||||
|
|
||||||
// create a reorg that rolls them back
|
// create a reorg that rolls them back
|
||||||
gchain, _ = core.GenerateChain(params.TestChainConfig, chain.GetBlockByNumber(0), ethash.NewFaker(), db, 2, func(i int, block *core.BlockGen) {})
|
gchain, _ = core.GenerateChain(params.TestChainConfig, chain.GetBlockByNumber(0), ethash.NewFaker(), server.db, 2, func(i int, block *core.BlockGen) {})
|
||||||
if _, err := chain.InsertChain(gchain); err != nil {
|
if _, err := chain.InsertChain(gchain); err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
@ -589,7 +588,7 @@ func TestStopResumeLes3(t *testing.T) {
|
|||||||
db := rawdb.NewMemoryDatabase()
|
db := rawdb.NewMemoryDatabase()
|
||||||
clock := &mclock.Simulated{}
|
clock := &mclock.Simulated{}
|
||||||
testCost := testBufLimit / 10
|
testCost := testBufLimit / 10
|
||||||
pm, err := newTestProtocolManager(false, 0, nil, nil, nil, db, nil, testCost, clock)
|
pm, _, err := newTestProtocolManager(false, 0, nil, nil, nil, db, nil, testCost, clock)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Failed to create protocol manager: %v", err)
|
t.Fatalf("Failed to create protocol manager: %v", err)
|
||||||
}
|
}
|
||||||
|
@ -20,19 +20,22 @@
|
|||||||
package les
|
package les
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"crypto/rand"
|
"crypto/rand"
|
||||||
"math/big"
|
"math/big"
|
||||||
"sync"
|
"sync"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/accounts/abi/bind"
|
||||||
|
"github.com/ethereum/go-ethereum/accounts/abi/bind/backends"
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/common/mclock"
|
"github.com/ethereum/go-ethereum/common/mclock"
|
||||||
"github.com/ethereum/go-ethereum/consensus/ethash"
|
"github.com/ethereum/go-ethereum/consensus/ethash"
|
||||||
|
"github.com/ethereum/go-ethereum/contracts/checkpointoracle/contract"
|
||||||
"github.com/ethereum/go-ethereum/core"
|
"github.com/ethereum/go-ethereum/core"
|
||||||
"github.com/ethereum/go-ethereum/core/rawdb"
|
"github.com/ethereum/go-ethereum/core/rawdb"
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"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/crypto"
|
||||||
"github.com/ethereum/go-ethereum/eth"
|
"github.com/ethereum/go-ethereum/eth"
|
||||||
"github.com/ethereum/go-ethereum/ethdb"
|
"github.com/ethereum/go-ethereum/ethdb"
|
||||||
@ -45,14 +48,14 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
testBankKey, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
|
bankKey, _ = crypto.GenerateKey()
|
||||||
testBankAddress = crypto.PubkeyToAddress(testBankKey.PublicKey)
|
bankAddr = crypto.PubkeyToAddress(bankKey.PublicKey)
|
||||||
testBankFunds = big.NewInt(1000000000000000000)
|
bankFunds = big.NewInt(1000000000000000000)
|
||||||
|
|
||||||
acc1Key, _ = crypto.HexToECDSA("8a1f9a8f95be41cd7ccb6168179afb4504aefe388d1e14474d32c45c72ce7b7a")
|
userKey1, _ = crypto.GenerateKey()
|
||||||
acc2Key, _ = crypto.HexToECDSA("49a7b37aa6f6645917e7b807e9d1c00d4fa71f18343b0d4122a4d2df64dd6fee")
|
userKey2, _ = crypto.GenerateKey()
|
||||||
acc1Addr = crypto.PubkeyToAddress(acc1Key.PublicKey)
|
userAddr1 = crypto.PubkeyToAddress(userKey1.PublicKey)
|
||||||
acc2Addr = crypto.PubkeyToAddress(acc2Key.PublicKey)
|
userAddr2 = crypto.PubkeyToAddress(userKey2.PublicKey)
|
||||||
|
|
||||||
testContractCode = common.Hex2Bytes("606060405260cc8060106000396000f360606040526000357c01000000000000000000000000000000000000000000000000000000009004806360cd2685146041578063c16431b914606b57603f565b005b6055600480803590602001909190505060a9565b6040518082815260200191505060405180910390f35b60886004808035906020019091908035906020019091905050608a565b005b80600060005083606481101560025790900160005b50819055505b5050565b6000600060005082606481101560025790900160005b5054905060c7565b91905056")
|
testContractCode = common.Hex2Bytes("606060405260cc8060106000396000f360606040526000357c01000000000000000000000000000000000000000000000000000000009004806360cd2685146041578063c16431b914606b57603f565b005b6055600480803590602001909190505060a9565b6040518082815260200191505060405180910390f35b60886004808035906020019091908035906020019091905050608a565b005b80600060005083606481101560025790900160005b50819055505b5050565b6000600060005082606481101560025790900160005b5054905060c7565b91905056")
|
||||||
testContractAddr common.Address
|
testContractAddr common.Address
|
||||||
@ -60,8 +63,21 @@ var (
|
|||||||
testContractDeployed = uint64(2)
|
testContractDeployed = uint64(2)
|
||||||
|
|
||||||
testEventEmitterCode = common.Hex2Bytes("60606040523415600e57600080fd5b7f57050ab73f6b9ebdd9f76b8d4997793f48cf956e965ee070551b9ca0bb71584e60405160405180910390a160358060476000396000f3006060604052600080fd00a165627a7a723058203f727efcad8b5811f8cb1fc2620ce5e8c63570d697aef968172de296ea3994140029")
|
testEventEmitterCode = common.Hex2Bytes("60606040523415600e57600080fd5b7f57050ab73f6b9ebdd9f76b8d4997793f48cf956e965ee070551b9ca0bb71584e60405160405180910390a160358060476000396000f3006060604052600080fd00a165627a7a723058203f727efcad8b5811f8cb1fc2620ce5e8c63570d697aef968172de296ea3994140029")
|
||||||
testEventEmitterAddr common.Address
|
|
||||||
|
|
||||||
|
// Checkpoint registrar relative
|
||||||
|
registrarAddr common.Address
|
||||||
|
signerKey, _ = crypto.GenerateKey()
|
||||||
|
signerAddr = crypto.PubkeyToAddress(signerKey.PublicKey)
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
// The block frequency for creating checkpoint(only used in test)
|
||||||
|
sectionSize = big.NewInt(512)
|
||||||
|
|
||||||
|
// The number of confirmations needed to generate a checkpoint(only used in test).
|
||||||
|
processConfirms = big.NewInt(4)
|
||||||
|
|
||||||
|
//
|
||||||
testBufLimit = uint64(1000000)
|
testBufLimit = uint64(1000000)
|
||||||
testBufRecharge = uint64(1000)
|
testBufRecharge = uint64(1000)
|
||||||
)
|
)
|
||||||
@ -81,102 +97,139 @@ contract test {
|
|||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
func testChainGen(i int, block *core.BlockGen) {
|
// prepareTestchain pre-commits specified number customized blocks into chain.
|
||||||
signer := types.HomesteadSigner{}
|
func prepareTestchain(n int, backend *backends.SimulatedBackend) {
|
||||||
|
var (
|
||||||
|
ctx = context.Background()
|
||||||
|
signer = types.HomesteadSigner{}
|
||||||
|
)
|
||||||
|
for i := 0; i < n; i++ {
|
||||||
|
switch i {
|
||||||
|
case 0:
|
||||||
|
// deploy checkpoint contract
|
||||||
|
registrarAddr, _, _, _ = contract.DeployCheckpointOracle(bind.NewKeyedTransactor(bankKey), backend, []common.Address{signerAddr}, sectionSize, processConfirms, big.NewInt(1))
|
||||||
|
// bankUser transfers some ether to user1
|
||||||
|
nonce, _ := backend.PendingNonceAt(ctx, bankAddr)
|
||||||
|
tx, _ := types.SignTx(types.NewTransaction(nonce, userAddr1, big.NewInt(10000), params.TxGas, nil, nil), signer, bankKey)
|
||||||
|
backend.SendTransaction(ctx, tx)
|
||||||
|
case 1:
|
||||||
|
bankNonce, _ := backend.PendingNonceAt(ctx, bankAddr)
|
||||||
|
userNonce1, _ := backend.PendingNonceAt(ctx, userAddr1)
|
||||||
|
|
||||||
switch i {
|
// bankUser transfers more ether to user1
|
||||||
case 0:
|
tx1, _ := types.SignTx(types.NewTransaction(bankNonce, userAddr1, big.NewInt(1000), params.TxGas, nil, nil), signer, bankKey)
|
||||||
// In block 1, the test bank sends account #1 some ether.
|
backend.SendTransaction(ctx, tx1)
|
||||||
tx, _ := types.SignTx(types.NewTransaction(block.TxNonce(testBankAddress), acc1Addr, big.NewInt(10000), params.TxGas, nil, nil), signer, testBankKey)
|
|
||||||
block.AddTx(tx)
|
|
||||||
case 1:
|
|
||||||
// In block 2, the test bank sends some more ether to account #1.
|
|
||||||
// acc1Addr passes it on to account #2.
|
|
||||||
// acc1Addr creates a test contract.
|
|
||||||
// acc1Addr creates a test event.
|
|
||||||
nonce := block.TxNonce(acc1Addr)
|
|
||||||
|
|
||||||
tx1, _ := types.SignTx(types.NewTransaction(block.TxNonce(testBankAddress), acc1Addr, big.NewInt(1000), params.TxGas, nil, nil), signer, testBankKey)
|
// user1 relays ether to user2
|
||||||
tx2, _ := types.SignTx(types.NewTransaction(nonce, acc2Addr, big.NewInt(1000), params.TxGas, nil, nil), signer, acc1Key)
|
tx2, _ := types.SignTx(types.NewTransaction(userNonce1, userAddr2, big.NewInt(1000), params.TxGas, nil, nil), signer, userKey1)
|
||||||
tx3, _ := types.SignTx(types.NewContractCreation(nonce+1, big.NewInt(0), 200000, big.NewInt(0), testContractCode), signer, acc1Key)
|
backend.SendTransaction(ctx, tx2)
|
||||||
testContractAddr = crypto.CreateAddress(acc1Addr, nonce+1)
|
|
||||||
tx4, _ := types.SignTx(types.NewContractCreation(nonce+2, big.NewInt(0), 200000, big.NewInt(0), testEventEmitterCode), signer, acc1Key)
|
// user1 deploys a test contract
|
||||||
testEventEmitterAddr = crypto.CreateAddress(acc1Addr, nonce+2)
|
tx3, _ := types.SignTx(types.NewContractCreation(userNonce1+1, big.NewInt(0), 200000, big.NewInt(0), testContractCode), signer, userKey1)
|
||||||
block.AddTx(tx1)
|
backend.SendTransaction(ctx, tx3)
|
||||||
block.AddTx(tx2)
|
testContractAddr = crypto.CreateAddress(userAddr1, userNonce1+1)
|
||||||
block.AddTx(tx3)
|
|
||||||
block.AddTx(tx4)
|
// user1 deploys a event contract
|
||||||
case 2:
|
tx4, _ := types.SignTx(types.NewContractCreation(userNonce1+2, big.NewInt(0), 200000, big.NewInt(0), testEventEmitterCode), signer, userKey1)
|
||||||
// Block 3 is empty but was mined by account #2.
|
backend.SendTransaction(ctx, tx4)
|
||||||
block.SetCoinbase(acc2Addr)
|
case 2:
|
||||||
block.SetExtra([]byte("yeehaw"))
|
// bankUser transfer some ether to signer
|
||||||
data := common.Hex2Bytes("C16431B900000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000001")
|
bankNonce, _ := backend.PendingNonceAt(ctx, bankAddr)
|
||||||
tx, _ := types.SignTx(types.NewTransaction(block.TxNonce(testBankAddress), testContractAddr, big.NewInt(0), 100000, nil, data), signer, testBankKey)
|
tx1, _ := types.SignTx(types.NewTransaction(bankNonce, signerAddr, big.NewInt(1000000000), params.TxGas, nil, nil), signer, bankKey)
|
||||||
block.AddTx(tx)
|
backend.SendTransaction(ctx, tx1)
|
||||||
case 3:
|
|
||||||
// Block 4 includes blocks 2 and 3 as uncle headers (with modified extra data).
|
// invoke test contract
|
||||||
b2 := block.PrevBlock(1).Header()
|
data := common.Hex2Bytes("C16431B900000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000001")
|
||||||
b2.Extra = []byte("foo")
|
tx2, _ := types.SignTx(types.NewTransaction(bankNonce+1, testContractAddr, big.NewInt(0), 100000, nil, data), signer, bankKey)
|
||||||
block.AddUncle(b2)
|
backend.SendTransaction(ctx, tx2)
|
||||||
b3 := block.PrevBlock(2).Header()
|
case 3:
|
||||||
b3.Extra = []byte("foo")
|
// invoke test contract
|
||||||
block.AddUncle(b3)
|
bankNonce, _ := backend.PendingNonceAt(ctx, bankAddr)
|
||||||
data := common.Hex2Bytes("C16431B900000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000002")
|
data := common.Hex2Bytes("C16431B900000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000002")
|
||||||
tx, _ := types.SignTx(types.NewTransaction(block.TxNonce(testBankAddress), testContractAddr, big.NewInt(0), 100000, nil, data), signer, testBankKey)
|
tx, _ := types.SignTx(types.NewTransaction(bankNonce, testContractAddr, big.NewInt(0), 100000, nil, data), signer, bankKey)
|
||||||
block.AddTx(tx)
|
backend.SendTransaction(ctx, tx)
|
||||||
|
}
|
||||||
|
backend.Commit()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// testIndexers creates a set of indexers with specified params for testing purpose.
|
// testIndexers creates a set of indexers with specified params for testing purpose.
|
||||||
func testIndexers(db ethdb.Database, odr light.OdrBackend, iConfig *light.IndexerConfig) (*core.ChainIndexer, *core.ChainIndexer, *core.ChainIndexer) {
|
func testIndexers(db ethdb.Database, odr light.OdrBackend, config *light.IndexerConfig) []*core.ChainIndexer {
|
||||||
chtIndexer := light.NewChtIndexer(db, odr, iConfig.ChtSize, iConfig.ChtConfirms)
|
var indexers [3]*core.ChainIndexer
|
||||||
bloomIndexer := eth.NewBloomIndexer(db, iConfig.BloomSize, iConfig.BloomConfirms)
|
indexers[0] = light.NewChtIndexer(db, odr, config.ChtSize, config.ChtConfirms)
|
||||||
bloomTrieIndexer := light.NewBloomTrieIndexer(db, odr, iConfig.BloomSize, iConfig.BloomTrieSize)
|
indexers[1] = eth.NewBloomIndexer(db, config.BloomSize, config.BloomConfirms)
|
||||||
bloomIndexer.AddChildIndexer(bloomTrieIndexer)
|
indexers[2] = light.NewBloomTrieIndexer(db, odr, config.BloomSize, config.BloomTrieSize)
|
||||||
return chtIndexer, bloomIndexer, bloomTrieIndexer
|
// make bloomTrieIndexer as a child indexer of bloom indexer.
|
||||||
|
indexers[1].AddChildIndexer(indexers[2])
|
||||||
|
return indexers[:]
|
||||||
}
|
}
|
||||||
|
|
||||||
// newTestProtocolManager creates a new protocol manager for testing purposes,
|
// newTestProtocolManager creates a new protocol manager for testing purposes,
|
||||||
// with the given number of blocks already known, potential notification
|
// with the given number of blocks already known, potential notification
|
||||||
// channels for different events and relative chain indexers array.
|
// channels for different events and relative chain indexers array.
|
||||||
func newTestProtocolManager(lightSync bool, blocks int, generator func(int, *core.BlockGen), odr *LesOdr, peers *peerSet, db ethdb.Database, ulcConfig *eth.ULCConfig, testCost uint64, clock mclock.Clock) (*ProtocolManager, error) {
|
func newTestProtocolManager(lightSync bool, blocks int, odr *LesOdr, indexers []*core.ChainIndexer, peers *peerSet, db ethdb.Database, ulcConfig *eth.ULCConfig, testCost uint64, clock mclock.Clock) (*ProtocolManager, *backends.SimulatedBackend, error) {
|
||||||
var (
|
var (
|
||||||
evmux = new(event.TypeMux)
|
evmux = new(event.TypeMux)
|
||||||
engine = ethash.NewFaker()
|
engine = ethash.NewFaker()
|
||||||
gspec = core.Genesis{
|
gspec = core.Genesis{
|
||||||
Config: params.TestChainConfig,
|
Config: params.AllEthashProtocolChanges,
|
||||||
Alloc: core.GenesisAlloc{testBankAddress: {Balance: testBankFunds}},
|
Alloc: core.GenesisAlloc{bankAddr: {Balance: bankFunds}},
|
||||||
}
|
}
|
||||||
genesis = gspec.MustCommit(db)
|
pool txPool
|
||||||
chain BlockChain
|
chain BlockChain
|
||||||
pool txPool
|
exitCh = make(chan struct{})
|
||||||
)
|
)
|
||||||
|
gspec.MustCommit(db)
|
||||||
if peers == nil {
|
if peers == nil {
|
||||||
peers = newPeerSet()
|
peers = newPeerSet()
|
||||||
}
|
}
|
||||||
|
// create a simulation backend and pre-commit several customized block to the database.
|
||||||
|
simulation := backends.NewSimulatedBackendWithDatabase(db, gspec.Alloc, 100000000)
|
||||||
|
prepareTestchain(blocks, simulation)
|
||||||
|
|
||||||
|
// initialize empty chain for light client or pre-committed chain for server.
|
||||||
if lightSync {
|
if lightSync {
|
||||||
chain, _ = light.NewLightChain(odr, gspec.Config, engine)
|
chain, _ = light.NewLightChain(odr, gspec.Config, engine, nil)
|
||||||
} else {
|
} else {
|
||||||
blockchain, _ := core.NewBlockChain(db, nil, gspec.Config, engine, vm.Config{}, nil)
|
chain = simulation.Blockchain()
|
||||||
gchain, _ := core.GenerateChain(gspec.Config, genesis, ethash.NewFaker(), db, blocks, generator)
|
pool = core.NewTxPool(core.DefaultTxPoolConfig, gspec.Config, simulation.Blockchain())
|
||||||
if _, err := blockchain.InsertChain(gchain); err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
chain = blockchain
|
|
||||||
pool = core.NewTxPool(core.DefaultTxPoolConfig, gspec.Config, blockchain)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Create contract registrar
|
||||||
indexConfig := light.TestServerIndexerConfig
|
indexConfig := light.TestServerIndexerConfig
|
||||||
if lightSync {
|
if lightSync {
|
||||||
indexConfig = light.TestClientIndexerConfig
|
indexConfig = light.TestClientIndexerConfig
|
||||||
}
|
}
|
||||||
pm, err := NewProtocolManager(gspec.Config, indexConfig, lightSync, NetworkId, evmux, engine, peers, chain, pool, db, odr, nil, nil, make(chan struct{}), new(sync.WaitGroup), ulcConfig, func() bool { return true })
|
config := ¶ms.CheckpointOracleConfig{
|
||||||
if err != nil {
|
Address: crypto.CreateAddress(bankAddr, 0),
|
||||||
return nil, err
|
Signers: []common.Address{signerAddr},
|
||||||
|
Threshold: 1,
|
||||||
}
|
}
|
||||||
|
var reg *checkpointOracle
|
||||||
|
if indexers != nil {
|
||||||
|
getLocal := func(index uint64) params.TrustedCheckpoint {
|
||||||
|
chtIndexer := indexers[0]
|
||||||
|
sectionHead := chtIndexer.SectionHead(index)
|
||||||
|
return params.TrustedCheckpoint{
|
||||||
|
SectionIndex: index,
|
||||||
|
SectionHead: sectionHead,
|
||||||
|
CHTRoot: light.GetChtRoot(db, index, sectionHead),
|
||||||
|
BloomRoot: light.GetBloomTrieRoot(db, index, sectionHead),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
reg = newCheckpointOracle(config, getLocal)
|
||||||
|
}
|
||||||
|
pm, err := NewProtocolManager(gspec.Config, nil, indexConfig, ulcConfig, lightSync, NetworkId, evmux, peers, chain, pool, db, odr, nil, reg, exitCh, new(sync.WaitGroup), func() bool { return true })
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
// Registrar initialization could failed if checkpoint contract is not specified.
|
||||||
|
if pm.reg != nil {
|
||||||
|
pm.reg.start(simulation)
|
||||||
|
}
|
||||||
|
// Set up les server stuff.
|
||||||
if !lightSync {
|
if !lightSync {
|
||||||
srv := &LesServer{lesCommons: lesCommons{protocolManager: pm}}
|
srv := &LesServer{lesCommons: lesCommons{protocolManager: pm, chainDb: db}}
|
||||||
pm.server = srv
|
pm.server = srv
|
||||||
pm.servingQueue = newServingQueue(int64(time.Millisecond*10), 1, nil)
|
pm.servingQueue = newServingQueue(int64(time.Millisecond*10), 1, nil)
|
||||||
pm.servingQueue.setThreads(4)
|
pm.servingQueue.setThreads(4)
|
||||||
@ -189,19 +242,19 @@ func newTestProtocolManager(lightSync bool, blocks int, generator func(int, *cor
|
|||||||
srv.fcManager = flowcontrol.NewClientManager(nil, clock)
|
srv.fcManager = flowcontrol.NewClientManager(nil, clock)
|
||||||
}
|
}
|
||||||
pm.Start(1000)
|
pm.Start(1000)
|
||||||
return pm, nil
|
return pm, simulation, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// newTestProtocolManagerMust creates a new protocol manager for testing purposes,
|
// newTestProtocolManagerMust creates a new protocol manager for testing purposes,
|
||||||
// with the given number of blocks already known, potential notification
|
// with the given number of blocks already known, potential notification channels
|
||||||
// channels for different events and relative chain indexers array. In case of an error, the constructor force-
|
// for different events and relative chain indexers array. In case of an error, the
|
||||||
// fails the test.
|
// constructor force-fails the test.
|
||||||
func newTestProtocolManagerMust(t *testing.T, lightSync bool, blocks int, generator func(int, *core.BlockGen), odr *LesOdr, peers *peerSet, db ethdb.Database, ulcConfig *eth.ULCConfig) *ProtocolManager {
|
func newTestProtocolManagerMust(t *testing.T, lightSync bool, blocks int, odr *LesOdr, indexers []*core.ChainIndexer, peers *peerSet, db ethdb.Database, ulcConfig *eth.ULCConfig) (*ProtocolManager, *backends.SimulatedBackend) {
|
||||||
pm, err := newTestProtocolManager(lightSync, blocks, generator, odr, peers, db, ulcConfig, 0, &mclock.System{})
|
pm, backend, err := newTestProtocolManager(lightSync, blocks, odr, indexers, peers, db, ulcConfig, 0, &mclock.System{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Failed to create protocol manager: %v", err)
|
t.Fatalf("Failed to create protocol manager: %v", err)
|
||||||
}
|
}
|
||||||
return pm
|
return pm, backend
|
||||||
}
|
}
|
||||||
|
|
||||||
// testPeer is a simulated peer to allow testing direct network calls.
|
// testPeer is a simulated peer to allow testing direct network calls.
|
||||||
@ -324,11 +377,13 @@ func (p *testPeer) close() {
|
|||||||
|
|
||||||
// TestEntity represents a network entity for testing with necessary auxiliary fields.
|
// TestEntity represents a network entity for testing with necessary auxiliary fields.
|
||||||
type TestEntity struct {
|
type TestEntity struct {
|
||||||
db ethdb.Database
|
db ethdb.Database
|
||||||
rPeer *peer
|
rPeer *peer
|
||||||
tPeer *testPeer
|
tPeer *testPeer
|
||||||
peers *peerSet
|
peers *peerSet
|
||||||
pm *ProtocolManager
|
pm *ProtocolManager
|
||||||
|
backend *backends.SimulatedBackend
|
||||||
|
|
||||||
// Indexers
|
// Indexers
|
||||||
chtIndexer *core.ChainIndexer
|
chtIndexer *core.ChainIndexer
|
||||||
bloomIndexer *core.ChainIndexer
|
bloomIndexer *core.ChainIndexer
|
||||||
@ -338,11 +393,12 @@ type TestEntity struct {
|
|||||||
// newServerEnv creates a server testing environment with a connected test peer for testing purpose.
|
// newServerEnv creates a server testing environment with a connected test peer for testing purpose.
|
||||||
func newServerEnv(t *testing.T, blocks int, protocol int, waitIndexers func(*core.ChainIndexer, *core.ChainIndexer, *core.ChainIndexer)) (*TestEntity, func()) {
|
func newServerEnv(t *testing.T, blocks int, protocol int, waitIndexers func(*core.ChainIndexer, *core.ChainIndexer, *core.ChainIndexer)) (*TestEntity, func()) {
|
||||||
db := rawdb.NewMemoryDatabase()
|
db := rawdb.NewMemoryDatabase()
|
||||||
cIndexer, bIndexer, btIndexer := testIndexers(db, nil, light.TestServerIndexerConfig)
|
indexers := testIndexers(db, nil, light.TestServerIndexerConfig)
|
||||||
|
|
||||||
pm := newTestProtocolManagerMust(t, false, blocks, testChainGen, nil, nil, db, nil)
|
pm, b := newTestProtocolManagerMust(t, false, blocks, nil, indexers, nil, db, nil)
|
||||||
peer, _ := newTestPeer(t, "peer", protocol, pm, true, 0)
|
peer, _ := newTestPeer(t, "peer", protocol, pm, true, 0)
|
||||||
|
|
||||||
|
cIndexer, bIndexer, btIndexer := indexers[0], indexers[1], indexers[2]
|
||||||
cIndexer.Start(pm.blockchain.(*core.BlockChain))
|
cIndexer.Start(pm.blockchain.(*core.BlockChain))
|
||||||
bIndexer.Start(pm.blockchain.(*core.BlockChain))
|
bIndexer.Start(pm.blockchain.(*core.BlockChain))
|
||||||
|
|
||||||
@ -355,6 +411,7 @@ func newServerEnv(t *testing.T, blocks int, protocol int, waitIndexers func(*cor
|
|||||||
db: db,
|
db: db,
|
||||||
tPeer: peer,
|
tPeer: peer,
|
||||||
pm: pm,
|
pm: pm,
|
||||||
|
backend: b,
|
||||||
chtIndexer: cIndexer,
|
chtIndexer: cIndexer,
|
||||||
bloomIndexer: bIndexer,
|
bloomIndexer: bIndexer,
|
||||||
bloomTrieIndexer: btIndexer,
|
bloomTrieIndexer: btIndexer,
|
||||||
@ -376,12 +433,16 @@ func newClientServerEnv(t *testing.T, blocks int, protocol int, waitIndexers fun
|
|||||||
rm := newRetrieveManager(lPeers, dist, nil)
|
rm := newRetrieveManager(lPeers, dist, nil)
|
||||||
odr := NewLesOdr(ldb, light.TestClientIndexerConfig, rm)
|
odr := NewLesOdr(ldb, light.TestClientIndexerConfig, rm)
|
||||||
|
|
||||||
cIndexer, bIndexer, btIndexer := testIndexers(db, nil, light.TestServerIndexerConfig)
|
indexers := testIndexers(db, nil, light.TestServerIndexerConfig)
|
||||||
lcIndexer, lbIndexer, lbtIndexer := testIndexers(ldb, odr, light.TestClientIndexerConfig)
|
lIndexers := testIndexers(ldb, odr, light.TestClientIndexerConfig)
|
||||||
|
|
||||||
|
cIndexer, bIndexer, btIndexer := indexers[0], indexers[1], indexers[2]
|
||||||
|
lcIndexer, lbIndexer, lbtIndexer := lIndexers[0], lIndexers[1], lIndexers[2]
|
||||||
|
|
||||||
odr.SetIndexers(lcIndexer, lbtIndexer, lbIndexer)
|
odr.SetIndexers(lcIndexer, lbtIndexer, lbIndexer)
|
||||||
|
|
||||||
pm := newTestProtocolManagerMust(t, false, blocks, testChainGen, nil, peers, db, nil)
|
pm, b := newTestProtocolManagerMust(t, false, blocks, nil, indexers, peers, db, nil)
|
||||||
lpm := newTestProtocolManagerMust(t, true, 0, nil, odr, lPeers, ldb, nil)
|
lpm, lb := newTestProtocolManagerMust(t, true, 0, odr, lIndexers, lPeers, ldb, nil)
|
||||||
|
|
||||||
startIndexers := func(clientMode bool, pm *ProtocolManager) {
|
startIndexers := func(clientMode bool, pm *ProtocolManager) {
|
||||||
if clientMode {
|
if clientMode {
|
||||||
@ -421,6 +482,7 @@ func newClientServerEnv(t *testing.T, blocks int, protocol int, waitIndexers fun
|
|||||||
pm: pm,
|
pm: pm,
|
||||||
rPeer: peer,
|
rPeer: peer,
|
||||||
peers: peers,
|
peers: peers,
|
||||||
|
backend: b,
|
||||||
chtIndexer: cIndexer,
|
chtIndexer: cIndexer,
|
||||||
bloomIndexer: bIndexer,
|
bloomIndexer: bIndexer,
|
||||||
bloomTrieIndexer: btIndexer,
|
bloomTrieIndexer: btIndexer,
|
||||||
@ -429,6 +491,7 @@ func newClientServerEnv(t *testing.T, blocks int, protocol int, waitIndexers fun
|
|||||||
pm: lpm,
|
pm: lpm,
|
||||||
rPeer: lPeer,
|
rPeer: lPeer,
|
||||||
peers: lPeers,
|
peers: lPeers,
|
||||||
|
backend: lb,
|
||||||
chtIndexer: lcIndexer,
|
chtIndexer: lcIndexer,
|
||||||
bloomIndexer: lbIndexer,
|
bloomIndexer: lbIndexer,
|
||||||
bloomTrieIndexer: lbtIndexer,
|
bloomTrieIndexer: lbtIndexer,
|
||||||
|
@ -166,11 +166,13 @@ func (r *ReceiptsRequest) Validate(db ethdb.Database, msg *Msg) error {
|
|||||||
receipt := receipts[0]
|
receipt := receipts[0]
|
||||||
|
|
||||||
// Retrieve our stored header and validate receipt content against it
|
// Retrieve our stored header and validate receipt content against it
|
||||||
header := rawdb.ReadHeader(db, r.Hash, r.Number)
|
if r.Header == nil {
|
||||||
if header == nil {
|
r.Header = rawdb.ReadHeader(db, r.Hash, r.Number)
|
||||||
|
}
|
||||||
|
if r.Header == nil {
|
||||||
return errHeaderUnavailable
|
return errHeaderUnavailable
|
||||||
}
|
}
|
||||||
if header.ReceiptHash != types.DeriveSha(receipt) {
|
if r.Header.ReceiptHash != types.DeriveSha(receipt) {
|
||||||
return errReceiptHashMismatch
|
return errReceiptHashMismatch
|
||||||
}
|
}
|
||||||
// Validations passed, store and return
|
// Validations passed, store and return
|
||||||
@ -323,7 +325,11 @@ func (r *ChtRequest) CanSend(peer *peer) bool {
|
|||||||
peer.lock.RLock()
|
peer.lock.RLock()
|
||||||
defer peer.lock.RUnlock()
|
defer peer.lock.RUnlock()
|
||||||
|
|
||||||
return peer.headInfo.Number >= r.Config.ChtConfirms && r.ChtNum <= (peer.headInfo.Number-r.Config.ChtConfirms)/r.Config.ChtSize
|
if r.Untrusted {
|
||||||
|
return peer.headInfo.Number >= r.BlockNum && peer.id == r.PeerId
|
||||||
|
} else {
|
||||||
|
return peer.headInfo.Number >= r.Config.ChtConfirms && r.ChtNum <= (peer.headInfo.Number-r.Config.ChtConfirms)/r.Config.ChtSize
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Request sends an ODR request to the LES network (implementation of LesOdrRequest)
|
// Request sends an ODR request to the LES network (implementation of LesOdrRequest)
|
||||||
@ -364,32 +370,37 @@ func (r *ChtRequest) Validate(db ethdb.Database, msg *Msg) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Verify the CHT
|
// Verify the CHT
|
||||||
var encNumber [8]byte
|
// Note: For untrusted CHT request, there is no proof response but
|
||||||
binary.BigEndian.PutUint64(encNumber[:], r.BlockNum)
|
// header data.
|
||||||
|
|
||||||
reads := &readTraceDB{db: nodeSet}
|
|
||||||
value, _, err := trie.VerifyProof(r.ChtRoot, encNumber[:], reads)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("merkle proof verification failed: %v", err)
|
|
||||||
}
|
|
||||||
if len(reads.reads) != nodeSet.KeyCount() {
|
|
||||||
return errUselessNodes
|
|
||||||
}
|
|
||||||
|
|
||||||
var node light.ChtNode
|
var node light.ChtNode
|
||||||
if err := rlp.DecodeBytes(value, &node); err != nil {
|
if !r.Untrusted {
|
||||||
return err
|
var encNumber [8]byte
|
||||||
}
|
binary.BigEndian.PutUint64(encNumber[:], r.BlockNum)
|
||||||
if node.Hash != header.Hash() {
|
|
||||||
return errCHTHashMismatch
|
reads := &readTraceDB{db: nodeSet}
|
||||||
}
|
value, _, err := trie.VerifyProof(r.ChtRoot, encNumber[:], reads)
|
||||||
if r.BlockNum != header.Number.Uint64() {
|
if err != nil {
|
||||||
return errCHTNumberMismatch
|
return fmt.Errorf("merkle proof verification failed: %v", err)
|
||||||
|
}
|
||||||
|
if len(reads.reads) != nodeSet.KeyCount() {
|
||||||
|
return errUselessNodes
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := rlp.DecodeBytes(value, &node); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if node.Hash != header.Hash() {
|
||||||
|
return errCHTHashMismatch
|
||||||
|
}
|
||||||
|
if r.BlockNum != header.Number.Uint64() {
|
||||||
|
return errCHTNumberMismatch
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// Verifications passed, store and return
|
// Verifications passed, store and return
|
||||||
r.Header = header
|
r.Header = header
|
||||||
r.Proof = nodeSet
|
r.Proof = nodeSet
|
||||||
r.Td = node.Td
|
r.Td = node.Td // For untrusted request, td here is nil, todo improve the les/2 protocol
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -78,7 +78,7 @@ func TestOdrAccountsLes2(t *testing.T) { testOdr(t, 2, 1, true, odrAccounts) }
|
|||||||
|
|
||||||
func odrAccounts(ctx context.Context, db ethdb.Database, config *params.ChainConfig, bc *core.BlockChain, lc *light.LightChain, bhash common.Hash) []byte {
|
func odrAccounts(ctx context.Context, db ethdb.Database, config *params.ChainConfig, bc *core.BlockChain, lc *light.LightChain, bhash common.Hash) []byte {
|
||||||
dummyAddr := common.HexToAddress("1234567812345678123456781234567812345678")
|
dummyAddr := common.HexToAddress("1234567812345678123456781234567812345678")
|
||||||
acc := []common.Address{testBankAddress, acc1Addr, acc2Addr, dummyAddr}
|
acc := []common.Address{bankAddr, userAddr1, userAddr2, dummyAddr}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
res []byte
|
res []byte
|
||||||
@ -121,7 +121,7 @@ func odrContractCall(ctx context.Context, db ethdb.Database, config *params.Chai
|
|||||||
statedb, err := state.New(header.Root, state.NewDatabase(db))
|
statedb, err := state.New(header.Root, state.NewDatabase(db))
|
||||||
|
|
||||||
if err == nil {
|
if err == nil {
|
||||||
from := statedb.GetOrNewStateObject(testBankAddress)
|
from := statedb.GetOrNewStateObject(bankAddr)
|
||||||
from.SetBalance(math.MaxBig256)
|
from.SetBalance(math.MaxBig256)
|
||||||
|
|
||||||
msg := callmsg{types.NewMessage(from.Address(), &testContractAddr, 0, new(big.Int), 100000, new(big.Int), data, false)}
|
msg := callmsg{types.NewMessage(from.Address(), &testContractAddr, 0, new(big.Int), 100000, new(big.Int), data, false)}
|
||||||
@ -137,8 +137,8 @@ func odrContractCall(ctx context.Context, db ethdb.Database, config *params.Chai
|
|||||||
} else {
|
} else {
|
||||||
header := lc.GetHeaderByHash(bhash)
|
header := lc.GetHeaderByHash(bhash)
|
||||||
state := light.NewState(ctx, header, lc.Odr())
|
state := light.NewState(ctx, header, lc.Odr())
|
||||||
state.SetBalance(testBankAddress, math.MaxBig256)
|
state.SetBalance(bankAddr, math.MaxBig256)
|
||||||
msg := callmsg{types.NewMessage(testBankAddress, &testContractAddr, 0, new(big.Int), 100000, new(big.Int), data, false)}
|
msg := callmsg{types.NewMessage(bankAddr, &testContractAddr, 0, new(big.Int), 100000, new(big.Int), data, false)}
|
||||||
context := core.NewEVMContext(msg, header, lc, nil)
|
context := core.NewEVMContext(msg, header, lc, nil)
|
||||||
vmenv := vm.NewEVM(context, state, config, vm.Config{})
|
vmenv := vm.NewEVM(context, state, config, vm.Config{})
|
||||||
gp := new(core.GasPool).AddGas(math.MaxUint64)
|
gp := new(core.GasPool).AddGas(math.MaxUint64)
|
||||||
|
27
les/peer.go
27
les/peer.go
@ -33,6 +33,7 @@ import (
|
|||||||
"github.com/ethereum/go-ethereum/les/flowcontrol"
|
"github.com/ethereum/go-ethereum/les/flowcontrol"
|
||||||
"github.com/ethereum/go-ethereum/light"
|
"github.com/ethereum/go-ethereum/light"
|
||||||
"github.com/ethereum/go-ethereum/p2p"
|
"github.com/ethereum/go-ethereum/p2p"
|
||||||
|
"github.com/ethereum/go-ethereum/params"
|
||||||
"github.com/ethereum/go-ethereum/rlp"
|
"github.com/ethereum/go-ethereum/rlp"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -79,6 +80,10 @@ type peer struct {
|
|||||||
|
|
||||||
announceType uint64
|
announceType uint64
|
||||||
|
|
||||||
|
// Checkpoint relative fields
|
||||||
|
checkpoint params.TrustedCheckpoint
|
||||||
|
checkpointNumber uint64
|
||||||
|
|
||||||
id string
|
id string
|
||||||
|
|
||||||
headInfo *announceData
|
headInfo *announceData
|
||||||
@ -575,6 +580,14 @@ func (p *peer) Handshake(td *big.Int, head common.Hash, headNum uint64, genesis
|
|||||||
send = send.add("flowControl/MRC", costList)
|
send = send.add("flowControl/MRC", costList)
|
||||||
p.fcCosts = costList.decode(ProtocolLengths[uint(p.version)])
|
p.fcCosts = costList.decode(ProtocolLengths[uint(p.version)])
|
||||||
p.fcParams = server.defParams
|
p.fcParams = server.defParams
|
||||||
|
|
||||||
|
if server.protocolManager != nil && server.protocolManager.reg != nil && server.protocolManager.reg.isRunning() {
|
||||||
|
cp, height := server.protocolManager.reg.stableCheckpoint()
|
||||||
|
if cp != nil {
|
||||||
|
send = send.add("checkpoint/value", cp)
|
||||||
|
send = send.add("checkpoint/registerHeight", height)
|
||||||
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
//on client node
|
//on client node
|
||||||
p.announceType = announceTypeSimple
|
p.announceType = announceTypeSimple
|
||||||
@ -658,20 +671,24 @@ func (p *peer) Handshake(td *big.Int, head common.Hash, headNum uint64, genesis
|
|||||||
return errResp(ErrUselessPeer, "peer cannot serve requests")
|
return errResp(ErrUselessPeer, "peer cannot serve requests")
|
||||||
}
|
}
|
||||||
|
|
||||||
var params flowcontrol.ServerParams
|
var sParams flowcontrol.ServerParams
|
||||||
if err := recv.get("flowControl/BL", ¶ms.BufLimit); err != nil {
|
if err := recv.get("flowControl/BL", &sParams.BufLimit); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err := recv.get("flowControl/MRR", ¶ms.MinRecharge); err != nil {
|
if err := recv.get("flowControl/MRR", &sParams.MinRecharge); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
var MRC RequestCostList
|
var MRC RequestCostList
|
||||||
if err := recv.get("flowControl/MRC", &MRC); err != nil {
|
if err := recv.get("flowControl/MRC", &MRC); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
p.fcParams = params
|
p.fcParams = sParams
|
||||||
p.fcServer = flowcontrol.NewServerNode(params, &mclock.System{})
|
p.fcServer = flowcontrol.NewServerNode(sParams, &mclock.System{})
|
||||||
p.fcCosts = MRC.decode(ProtocolLengths[uint(p.version)])
|
p.fcCosts = MRC.decode(ProtocolLengths[uint(p.version)])
|
||||||
|
|
||||||
|
recv.get("checkpoint/value", &p.checkpoint)
|
||||||
|
recv.get("checkpoint/registerHeight", &p.checkpointNumber)
|
||||||
|
|
||||||
if !p.isOnlyAnnounce {
|
if !p.isOnlyAnnounce {
|
||||||
for msgCode := range reqAvgTimeCost {
|
for msgCode := range reqAvgTimeCost {
|
||||||
if p.fcCosts[msgCode] == nil {
|
if p.fcCosts[msgCode] == nil {
|
||||||
|
@ -28,7 +28,7 @@ import (
|
|||||||
"github.com/ethereum/go-ethereum/light"
|
"github.com/ethereum/go-ethereum/light"
|
||||||
)
|
)
|
||||||
|
|
||||||
var testBankSecureTrieKey = secAddr(testBankAddress)
|
var testBankSecureTrieKey = secAddr(bankAddr)
|
||||||
|
|
||||||
func secAddr(addr common.Address) []byte {
|
func secAddr(addr common.Address) []byte {
|
||||||
return crypto.Keccak256(addr[:])
|
return crypto.Keccak256(addr[:])
|
||||||
|
@ -21,6 +21,7 @@ import (
|
|||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/accounts/abi/bind"
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/common/mclock"
|
"github.com/ethereum/go-ethereum/common/mclock"
|
||||||
"github.com/ethereum/go-ethereum/core"
|
"github.com/ethereum/go-ethereum/core"
|
||||||
@ -72,68 +73,38 @@ type LesServer struct {
|
|||||||
priorityClientPool *priorityClientPool
|
priorityClientPool *priorityClientPool
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewLesServer(eth *eth.Ethereum, config *eth.Config) (*LesServer, error) {
|
func NewLesServer(e *eth.Ethereum, config *eth.Config) (*LesServer, error) {
|
||||||
var csvLogger *csvlogger.Logger
|
var csvLogger *csvlogger.Logger
|
||||||
if logFileName != "" {
|
if logFileName != "" {
|
||||||
csvLogger = csvlogger.NewLogger(logFileName, time.Second*10, "event, peerId")
|
csvLogger = csvlogger.NewLogger(logFileName, time.Second*10, "event, peerId")
|
||||||
}
|
}
|
||||||
|
|
||||||
quitSync := make(chan struct{})
|
|
||||||
pm, err := NewProtocolManager(
|
|
||||||
eth.BlockChain().Config(),
|
|
||||||
light.DefaultServerIndexerConfig,
|
|
||||||
false,
|
|
||||||
config.NetworkId,
|
|
||||||
eth.EventMux(),
|
|
||||||
eth.Engine(),
|
|
||||||
newPeerSet(),
|
|
||||||
eth.BlockChain(),
|
|
||||||
eth.TxPool(),
|
|
||||||
eth.ChainDb(),
|
|
||||||
nil,
|
|
||||||
nil,
|
|
||||||
nil,
|
|
||||||
quitSync,
|
|
||||||
new(sync.WaitGroup),
|
|
||||||
config.ULC,
|
|
||||||
eth.Synced)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if logProtocolHandler {
|
|
||||||
pm.logger = csvLogger
|
|
||||||
}
|
|
||||||
requestLogger := csvLogger
|
requestLogger := csvLogger
|
||||||
if !logRequestServing {
|
if !logRequestServing {
|
||||||
requestLogger = nil
|
requestLogger = nil
|
||||||
}
|
}
|
||||||
pm.servingQueue = newServingQueue(int64(time.Millisecond*10), float64(config.LightServ)/100, requestLogger)
|
|
||||||
|
|
||||||
lesTopics := make([]discv5.Topic, len(AdvertiseProtocolVersions))
|
lesTopics := make([]discv5.Topic, len(AdvertiseProtocolVersions))
|
||||||
for i, pv := range AdvertiseProtocolVersions {
|
for i, pv := range AdvertiseProtocolVersions {
|
||||||
lesTopics[i] = lesTopic(eth.BlockChain().Genesis().Hash(), pv)
|
lesTopics[i] = lesTopic(e.BlockChain().Genesis().Hash(), pv)
|
||||||
}
|
}
|
||||||
|
quitSync := make(chan struct{})
|
||||||
srv := &LesServer{
|
srv := &LesServer{
|
||||||
lesCommons: lesCommons{
|
lesCommons: lesCommons{
|
||||||
config: config,
|
config: config,
|
||||||
chainDb: eth.ChainDb(),
|
|
||||||
iConfig: light.DefaultServerIndexerConfig,
|
iConfig: light.DefaultServerIndexerConfig,
|
||||||
chtIndexer: light.NewChtIndexer(eth.ChainDb(), nil, params.CHTFrequency, params.HelperTrieProcessConfirmations),
|
chainDb: e.ChainDb(),
|
||||||
bloomTrieIndexer: light.NewBloomTrieIndexer(eth.ChainDb(), nil, params.BloomBitsBlocks, params.BloomTrieFrequency),
|
chtIndexer: light.NewChtIndexer(e.ChainDb(), nil, params.CHTFrequency, params.HelperTrieProcessConfirmations),
|
||||||
protocolManager: pm,
|
bloomTrieIndexer: light.NewBloomTrieIndexer(e.ChainDb(), nil, params.BloomBitsBlocks, params.BloomTrieFrequency),
|
||||||
},
|
},
|
||||||
archiveMode: eth.ArchiveMode(),
|
archiveMode: e.ArchiveMode(),
|
||||||
quitSync: quitSync,
|
quitSync: quitSync,
|
||||||
lesTopics: lesTopics,
|
lesTopics: lesTopics,
|
||||||
onlyAnnounce: config.OnlyAnnounce,
|
onlyAnnounce: config.OnlyAnnounce,
|
||||||
csvLogger: csvLogger,
|
csvLogger: csvLogger,
|
||||||
logTotalCap: requestLogger.NewChannel("totalCapacity", 0.01),
|
logTotalCap: requestLogger.NewChannel("totalCapacity", 0.01),
|
||||||
}
|
}
|
||||||
srv.costTracker, srv.minCapacity = newCostTracker(eth.ChainDb(), config, requestLogger)
|
srv.costTracker, srv.minCapacity = newCostTracker(e.ChainDb(), config, requestLogger)
|
||||||
|
|
||||||
logger := log.New()
|
logger := log.New()
|
||||||
pm.server = srv
|
|
||||||
srv.thcNormal = config.LightServ * 4 / 100
|
srv.thcNormal = config.LightServ * 4 / 100
|
||||||
if srv.thcNormal < 4 {
|
if srv.thcNormal < 4 {
|
||||||
srv.thcNormal = 4
|
srv.thcNormal = 4
|
||||||
@ -141,22 +112,31 @@ func NewLesServer(eth *eth.Ethereum, config *eth.Config) (*LesServer, error) {
|
|||||||
srv.thcBlockProcessing = config.LightServ/100 + 1
|
srv.thcBlockProcessing = config.LightServ/100 + 1
|
||||||
srv.fcManager = flowcontrol.NewClientManager(nil, &mclock.System{})
|
srv.fcManager = flowcontrol.NewClientManager(nil, &mclock.System{})
|
||||||
|
|
||||||
chtSectionCount, _, _ := srv.chtIndexer.Sections()
|
checkpoint := srv.latestLocalCheckpoint()
|
||||||
if chtSectionCount != 0 {
|
if !checkpoint.Empty() {
|
||||||
chtLastSection := chtSectionCount - 1
|
logger.Info("Loaded latest checkpoint", "section", checkpoint.SectionIndex, "head", checkpoint.SectionHead,
|
||||||
chtSectionHead := srv.chtIndexer.SectionHead(chtLastSection)
|
"chtroot", checkpoint.CHTRoot, "bloomroot", checkpoint.BloomRoot)
|
||||||
chtRoot := light.GetChtRoot(pm.chainDb, chtLastSection, chtSectionHead)
|
|
||||||
logger.Info("Loaded CHT", "section", chtLastSection, "head", chtSectionHead, "root", chtRoot)
|
|
||||||
}
|
|
||||||
bloomTrieSectionCount, _, _ := srv.bloomTrieIndexer.Sections()
|
|
||||||
if bloomTrieSectionCount != 0 {
|
|
||||||
bloomTrieLastSection := bloomTrieSectionCount - 1
|
|
||||||
bloomTrieSectionHead := srv.bloomTrieIndexer.SectionHead(bloomTrieLastSection)
|
|
||||||
bloomTrieRoot := light.GetBloomTrieRoot(pm.chainDb, bloomTrieLastSection, bloomTrieSectionHead)
|
|
||||||
logger.Info("Loaded bloom trie", "section", bloomTrieLastSection, "head", bloomTrieSectionHead, "root", bloomTrieRoot)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
srv.chtIndexer.Start(eth.BlockChain())
|
srv.chtIndexer.Start(e.BlockChain())
|
||||||
|
|
||||||
|
oracle := config.CheckpointOracle
|
||||||
|
if oracle == nil {
|
||||||
|
oracle = params.CheckpointOracles[e.BlockChain().Genesis().Hash()]
|
||||||
|
}
|
||||||
|
registrar := newCheckpointOracle(oracle, srv.getLocalCheckpoint)
|
||||||
|
// TODO(rjl493456442) Checkpoint is useless for les server, separate handler for client and server.
|
||||||
|
pm, err := NewProtocolManager(e.BlockChain().Config(), nil, light.DefaultServerIndexerConfig, config.ULC, false, config.NetworkId, e.EventMux(), newPeerSet(), e.BlockChain(), e.TxPool(), e.ChainDb(), nil, nil, registrar, quitSync, new(sync.WaitGroup), e.Synced)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
srv.protocolManager = pm
|
||||||
|
if logProtocolHandler {
|
||||||
|
pm.logger = csvLogger
|
||||||
|
}
|
||||||
|
pm.servingQueue = newServingQueue(int64(time.Millisecond*10), float64(config.LightServ)/100, requestLogger)
|
||||||
|
pm.server = srv
|
||||||
|
|
||||||
return srv, nil
|
return srv, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -168,6 +148,12 @@ func (s *LesServer) APIs() []rpc.API {
|
|||||||
Service: NewPrivateLightServerAPI(s),
|
Service: NewPrivateLightServerAPI(s),
|
||||||
Public: false,
|
Public: false,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
Namespace: "les",
|
||||||
|
Version: "1.0",
|
||||||
|
Service: NewPrivateLightAPI(&s.lesCommons, s.protocolManager.reg),
|
||||||
|
Public: false,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -292,6 +278,13 @@ func (s *LesServer) SetBloomBitsIndexer(bloomIndexer *core.ChainIndexer) {
|
|||||||
bloomIndexer.AddChildIndexer(s.bloomTrieIndexer)
|
bloomIndexer.AddChildIndexer(s.bloomTrieIndexer)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SetClient sets the rpc client and starts running checkpoint contract if it is not yet watched.
|
||||||
|
func (s *LesServer) SetContractBackend(backend bind.ContractBackend) {
|
||||||
|
if s.protocolManager.reg != nil {
|
||||||
|
s.protocolManager.reg.start(backend)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Stop stops the LES service
|
// Stop stops the LES service
|
||||||
func (s *LesServer) Stop() {
|
func (s *LesServer) Stop() {
|
||||||
s.fcManager.Stop()
|
s.fcManager.Stop()
|
||||||
|
157
les/sync.go
157
les/sync.go
@ -18,11 +18,29 @@ package les
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"errors"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/core/rawdb"
|
"github.com/ethereum/go-ethereum/core/rawdb"
|
||||||
"github.com/ethereum/go-ethereum/eth/downloader"
|
"github.com/ethereum/go-ethereum/eth/downloader"
|
||||||
"github.com/ethereum/go-ethereum/light"
|
"github.com/ethereum/go-ethereum/light"
|
||||||
|
"github.com/ethereum/go-ethereum/log"
|
||||||
|
)
|
||||||
|
|
||||||
|
var errInvalidCheckpoint = errors.New("invalid advertised checkpoint")
|
||||||
|
|
||||||
|
const (
|
||||||
|
// lightSync starts syncing from the current highest block.
|
||||||
|
// If the chain is empty, syncing the entire header chain.
|
||||||
|
lightSync = iota
|
||||||
|
|
||||||
|
// legacyCheckpointSync starts syncing from a hardcoded checkpoint.
|
||||||
|
legacyCheckpointSync
|
||||||
|
|
||||||
|
// checkpointSync starts syncing from a checkpoint signed by trusted
|
||||||
|
// signer or hardcoded checkpoint for compatibility.
|
||||||
|
checkpointSync
|
||||||
)
|
)
|
||||||
|
|
||||||
// syncer is responsible for periodically synchronising with the network, both
|
// syncer is responsible for periodically synchronising with the network, both
|
||||||
@ -54,26 +72,141 @@ func (pm *ProtocolManager) syncer() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (pm *ProtocolManager) needToSync(peerHead blockInfo) bool {
|
// validateCheckpoint verifies the advertised checkpoint by peer is valid or not.
|
||||||
head := pm.blockchain.CurrentHeader()
|
//
|
||||||
currentTd := rawdb.ReadTd(pm.chainDb, head.Hash(), head.Number.Uint64())
|
// Each network has several hard-coded checkpoint signer addresses. Only the
|
||||||
return currentTd != nil && peerHead.Td.Cmp(currentTd) > 0
|
// checkpoint issued by the specified signer is considered valid.
|
||||||
|
//
|
||||||
|
// In addition to the checkpoint registered in the registrar contract, there are
|
||||||
|
// several legacy hardcoded checkpoints in our codebase. These checkpoints are
|
||||||
|
// also considered as valid.
|
||||||
|
func (pm *ProtocolManager) validateCheckpoint(peer *peer) error {
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), time.Second*10)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
// Fetch the block header corresponding to the checkpoint registration.
|
||||||
|
cp := peer.checkpoint
|
||||||
|
header, err := light.GetUntrustedHeaderByNumber(ctx, pm.odr, peer.checkpointNumber, peer.id)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// Fetch block logs associated with the block header.
|
||||||
|
logs, err := light.GetUntrustedBlockLogs(ctx, pm.odr, header)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
events := pm.reg.contract.LookupCheckpointEvents(logs, cp.SectionIndex, cp.Hash())
|
||||||
|
if len(events) == 0 {
|
||||||
|
return errInvalidCheckpoint
|
||||||
|
}
|
||||||
|
var (
|
||||||
|
index = events[0].Index
|
||||||
|
hash = events[0].CheckpointHash
|
||||||
|
signatures [][]byte
|
||||||
|
)
|
||||||
|
for _, event := range events {
|
||||||
|
signatures = append(signatures, append(event.R[:], append(event.S[:], event.V)...))
|
||||||
|
}
|
||||||
|
valid, signers := pm.reg.verifySigners(index, hash, signatures)
|
||||||
|
if !valid {
|
||||||
|
return errInvalidCheckpoint
|
||||||
|
}
|
||||||
|
log.Warn("Verified advertised checkpoint", "peer", peer.id, "signers", len(signers))
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// synchronise tries to sync up our local block chain with a remote peer.
|
// synchronise tries to sync up our local chain with a remote peer.
|
||||||
func (pm *ProtocolManager) synchronise(peer *peer) {
|
func (pm *ProtocolManager) synchronise(peer *peer) {
|
||||||
// Short circuit if no peers are available
|
// Short circuit if the peer is nil.
|
||||||
if peer == nil {
|
if peer == nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Make sure the peer's TD is higher than our own.
|
// Make sure the peer's TD is higher than our own.
|
||||||
if !pm.needToSync(peer.headBlockInfo()) {
|
latest := pm.blockchain.CurrentHeader()
|
||||||
|
currentTd := rawdb.ReadTd(pm.chainDb, latest.Hash(), latest.Number.Uint64())
|
||||||
|
if currentTd != nil && peer.headBlockInfo().Td.Cmp(currentTd) < 0 {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
// Recap the checkpoint.
|
||||||
|
//
|
||||||
|
// The light client may be connected to several different versions of the server.
|
||||||
|
// (1) Old version server which can not provide stable checkpoint in the handshake packet.
|
||||||
|
// => Use hardcoded checkpoint or empty checkpoint
|
||||||
|
// (2) New version server but simple checkpoint syncing is not enabled(e.g. mainnet, new testnet or private network)
|
||||||
|
// => Use hardcoded checkpoint or empty checkpoint
|
||||||
|
// (3) New version server but the provided stable checkpoint is even lower than the hardcoded one.
|
||||||
|
// => Use hardcoded checkpoint
|
||||||
|
// (4) New version server with valid and higher stable checkpoint
|
||||||
|
// => Use provided checkpoint
|
||||||
|
var checkpoint = &peer.checkpoint
|
||||||
|
var hardcoded bool
|
||||||
|
if pm.checkpoint != nil && pm.checkpoint.SectionIndex >= peer.checkpoint.SectionIndex {
|
||||||
|
checkpoint = pm.checkpoint // Use the hardcoded one.
|
||||||
|
hardcoded = true
|
||||||
|
}
|
||||||
|
// Determine whether we should run checkpoint syncing or normal light syncing.
|
||||||
|
//
|
||||||
|
// Here has four situations that we will disable the checkpoint syncing:
|
||||||
|
//
|
||||||
|
// 1. The checkpoint is empty
|
||||||
|
// 2. The latest head block of the local chain is above the checkpoint.
|
||||||
|
// 3. The checkpoint is hardcoded(recap with local hardcoded checkpoint)
|
||||||
|
// 4. For some networks the checkpoint syncing is not activated.
|
||||||
|
mode := checkpointSync
|
||||||
|
switch {
|
||||||
|
case checkpoint.Empty():
|
||||||
|
mode = lightSync
|
||||||
|
log.Debug("Disable checkpoint syncing", "reason", "empty checkpoint")
|
||||||
|
case latest.Number.Uint64() >= (checkpoint.SectionIndex+1)*pm.iConfig.ChtSize-1:
|
||||||
|
mode = lightSync
|
||||||
|
log.Debug("Disable checkpoint syncing", "reason", "local chain beyond the checkpoint")
|
||||||
|
case hardcoded:
|
||||||
|
mode = legacyCheckpointSync
|
||||||
|
log.Debug("Disable checkpoint syncing", "reason", "checkpoint is hardcoded")
|
||||||
|
case pm.reg == nil || !pm.reg.isRunning():
|
||||||
|
mode = legacyCheckpointSync
|
||||||
|
log.Debug("Disable checkpoint syncing", "reason", "checkpoint syncing is not activated")
|
||||||
|
}
|
||||||
|
// Notify testing framework if syncing has completed(for testing purpose).
|
||||||
|
defer func() {
|
||||||
|
if pm.reg != nil && pm.reg.syncDoneHook != nil {
|
||||||
|
pm.reg.syncDoneHook()
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
start := time.Now()
|
||||||
|
if mode == checkpointSync || mode == legacyCheckpointSync {
|
||||||
|
// Validate the advertised checkpoint
|
||||||
|
if mode == legacyCheckpointSync {
|
||||||
|
checkpoint = pm.checkpoint
|
||||||
|
} else if mode == checkpointSync {
|
||||||
|
if err := pm.validateCheckpoint(peer); err != nil {
|
||||||
|
log.Debug("Failed to validate checkpoint", "reason", err)
|
||||||
|
pm.removePeer(peer.id)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
pm.blockchain.(*light.LightChain).AddTrustedCheckpoint(checkpoint)
|
||||||
|
}
|
||||||
|
log.Debug("Checkpoint syncing start", "peer", peer.id, "checkpoint", checkpoint.SectionIndex)
|
||||||
|
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), time.Second*5)
|
// Fetch the start point block header.
|
||||||
defer cancel()
|
//
|
||||||
pm.blockchain.(*light.LightChain).SyncCht(ctx)
|
// For the ethash consensus engine, the start header is the block header
|
||||||
pm.downloader.Synchronise(peer.id, peer.Head(), peer.Td(), downloader.LightSync)
|
// of the checkpoint.
|
||||||
|
//
|
||||||
|
// For the clique consensus engine, the start header is the block header
|
||||||
|
// of the latest epoch covered by checkpoint.
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), time.Second*5)
|
||||||
|
defer cancel()
|
||||||
|
if !checkpoint.Empty() && !pm.blockchain.(*light.LightChain).SyncCheckpoint(ctx, checkpoint) {
|
||||||
|
log.Debug("Sync checkpoint failed")
|
||||||
|
pm.removePeer(peer.id)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Fetch the remaining block headers based on the current chain header.
|
||||||
|
if err := pm.downloader.Synchronise(peer.id, peer.Head(), peer.Td(), downloader.LightSync); err != nil {
|
||||||
|
log.Debug("Synchronise failed", "reason", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
log.Debug("Synchronise finished", "elapsed", common.PrettyDuration(time.Since(start)))
|
||||||
}
|
}
|
||||||
|
133
les/sync_test.go
Normal file
133
les/sync_test.go
Normal file
@ -0,0 +1,133 @@
|
|||||||
|
// Copyright 2018 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 <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
package les
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"math/big"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/core"
|
||||||
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
|
"github.com/ethereum/go-ethereum/light"
|
||||||
|
"github.com/ethereum/go-ethereum/params"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Test light syncing which will download all headers from genesis.
|
||||||
|
func TestLightSyncingLes2(t *testing.T) { testCheckpointSyncing(t, 2, 0) }
|
||||||
|
func TestLightSyncingLes3(t *testing.T) { testCheckpointSyncing(t, 3, 0) }
|
||||||
|
|
||||||
|
// Test legacy checkpoint syncing which will download tail headers
|
||||||
|
// based on a hardcoded checkpoint.
|
||||||
|
func TestLegacyCheckpointSyncingLes2(t *testing.T) { testCheckpointSyncing(t, 2, 1) }
|
||||||
|
func TestLegacyCheckpointSyncingLes3(t *testing.T) { testCheckpointSyncing(t, 3, 1) }
|
||||||
|
|
||||||
|
// Test checkpoint syncing which will download tail headers based
|
||||||
|
// on a verified checkpoint.
|
||||||
|
func TestCheckpointSyncingLes2(t *testing.T) { testCheckpointSyncing(t, 2, 2) }
|
||||||
|
func TestCheckpointSyncingLes3(t *testing.T) { testCheckpointSyncing(t, 3, 2) }
|
||||||
|
|
||||||
|
func testCheckpointSyncing(t *testing.T, protocol int, syncMode int) {
|
||||||
|
config := light.TestServerIndexerConfig
|
||||||
|
|
||||||
|
waitIndexers := func(cIndexer, bIndexer, btIndexer *core.ChainIndexer) {
|
||||||
|
for {
|
||||||
|
cs, _, _ := cIndexer.Sections()
|
||||||
|
bts, _, _ := btIndexer.Sections()
|
||||||
|
if cs >= 1 && bts >= 1 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
time.Sleep(10 * time.Millisecond)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Generate 512+4 blocks (totally 1 CHT sections)
|
||||||
|
server, client, tearDown := newClientServerEnv(t, int(config.ChtSize+config.ChtConfirms), protocol, waitIndexers, false)
|
||||||
|
defer tearDown()
|
||||||
|
|
||||||
|
expected := config.ChtSize + config.ChtConfirms
|
||||||
|
|
||||||
|
// Checkpoint syncing or legacy checkpoint syncing.
|
||||||
|
if syncMode == 1 || syncMode == 2 {
|
||||||
|
// Assemble checkpoint 0
|
||||||
|
s, _, head := server.chtIndexer.Sections()
|
||||||
|
cp := ¶ms.TrustedCheckpoint{
|
||||||
|
SectionIndex: 0,
|
||||||
|
SectionHead: head,
|
||||||
|
CHTRoot: light.GetChtRoot(server.db, s-1, head),
|
||||||
|
BloomRoot: light.GetBloomTrieRoot(server.db, s-1, head),
|
||||||
|
}
|
||||||
|
if syncMode == 1 {
|
||||||
|
// Register the assembled checkpoint as hardcoded one.
|
||||||
|
client.pm.checkpoint = cp
|
||||||
|
client.pm.blockchain.(*light.LightChain).AddTrustedCheckpoint(cp)
|
||||||
|
} else {
|
||||||
|
// Register the assembled checkpoint into oracle.
|
||||||
|
header := server.backend.Blockchain().CurrentHeader()
|
||||||
|
|
||||||
|
data := append([]byte{0x19, 0x00}, append(registrarAddr.Bytes(), append([]byte{0, 0, 0, 0, 0, 0, 0, 0}, cp.Hash().Bytes()...)...)...)
|
||||||
|
sig, _ := crypto.Sign(crypto.Keccak256(data), signerKey)
|
||||||
|
sig[64] += 27 // Transform V from 0/1 to 27/28 according to the yellow paper
|
||||||
|
if _, err := server.pm.reg.contract.RegisterCheckpoint(signerKey, cp.SectionIndex, cp.Hash().Bytes(), new(big.Int).Sub(header.Number, big.NewInt(1)), header.ParentHash, [][]byte{sig}); err != nil {
|
||||||
|
t.Error("register checkpoint failed", err)
|
||||||
|
}
|
||||||
|
server.backend.Commit()
|
||||||
|
|
||||||
|
// Wait for the checkpoint registration
|
||||||
|
for {
|
||||||
|
_, hash, _, err := server.pm.reg.contract.Contract().GetLatestCheckpoint(nil)
|
||||||
|
if err != nil || hash == [32]byte{} {
|
||||||
|
time.Sleep(100 * time.Millisecond)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
expected += 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
done := make(chan error)
|
||||||
|
client.pm.reg.syncDoneHook = func() {
|
||||||
|
header := client.pm.blockchain.CurrentHeader()
|
||||||
|
if header.Number.Uint64() == expected {
|
||||||
|
done <- nil
|
||||||
|
} else {
|
||||||
|
done <- fmt.Errorf("blockchain length mismatch, want %d, got %d", expected, header.Number)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create connected peer pair.
|
||||||
|
peer, err1, lPeer, err2 := newTestPeerPair("peer", protocol, server.pm, client.pm)
|
||||||
|
select {
|
||||||
|
case <-time.After(time.Millisecond * 100):
|
||||||
|
case err := <-err1:
|
||||||
|
t.Fatalf("peer 1 handshake error: %v", err)
|
||||||
|
case err := <-err2:
|
||||||
|
t.Fatalf("peer 2 handshake error: %v", err)
|
||||||
|
}
|
||||||
|
server.rPeer, client.rPeer = peer, lPeer
|
||||||
|
|
||||||
|
select {
|
||||||
|
case err := <-done:
|
||||||
|
if err != nil {
|
||||||
|
t.Error("sync failed", err)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
case <-time.NewTimer(10 * time.Second).C:
|
||||||
|
t.Error("checkpoint syncing timeout")
|
||||||
|
}
|
||||||
|
}
|
@ -30,7 +30,7 @@ type ltrInfo struct {
|
|||||||
sentTo map[*peer]struct{}
|
sentTo map[*peer]struct{}
|
||||||
}
|
}
|
||||||
|
|
||||||
type LesTxRelay struct {
|
type lesTxRelay struct {
|
||||||
txSent map[common.Hash]*ltrInfo
|
txSent map[common.Hash]*ltrInfo
|
||||||
txPending map[common.Hash]struct{}
|
txPending map[common.Hash]struct{}
|
||||||
ps *peerSet
|
ps *peerSet
|
||||||
@ -42,8 +42,8 @@ type LesTxRelay struct {
|
|||||||
retriever *retrieveManager
|
retriever *retrieveManager
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewLesTxRelay(ps *peerSet, retriever *retrieveManager) *LesTxRelay {
|
func newLesTxRelay(ps *peerSet, retriever *retrieveManager) *lesTxRelay {
|
||||||
r := &LesTxRelay{
|
r := &lesTxRelay{
|
||||||
txSent: make(map[common.Hash]*ltrInfo),
|
txSent: make(map[common.Hash]*ltrInfo),
|
||||||
txPending: make(map[common.Hash]struct{}),
|
txPending: make(map[common.Hash]struct{}),
|
||||||
ps: ps,
|
ps: ps,
|
||||||
@ -54,18 +54,18 @@ func NewLesTxRelay(ps *peerSet, retriever *retrieveManager) *LesTxRelay {
|
|||||||
return r
|
return r
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *LesTxRelay) Stop() {
|
func (self *lesTxRelay) Stop() {
|
||||||
close(self.stop)
|
close(self.stop)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *LesTxRelay) registerPeer(p *peer) {
|
func (self *lesTxRelay) registerPeer(p *peer) {
|
||||||
self.lock.Lock()
|
self.lock.Lock()
|
||||||
defer self.lock.Unlock()
|
defer self.lock.Unlock()
|
||||||
|
|
||||||
self.peerList = self.ps.AllPeers()
|
self.peerList = self.ps.AllPeers()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *LesTxRelay) unregisterPeer(p *peer) {
|
func (self *lesTxRelay) unregisterPeer(p *peer) {
|
||||||
self.lock.Lock()
|
self.lock.Lock()
|
||||||
defer self.lock.Unlock()
|
defer self.lock.Unlock()
|
||||||
|
|
||||||
@ -74,7 +74,7 @@ func (self *LesTxRelay) unregisterPeer(p *peer) {
|
|||||||
|
|
||||||
// send sends a list of transactions to at most a given number of peers at
|
// send sends a list of transactions to at most a given number of peers at
|
||||||
// once, never resending any particular transaction to the same peer twice
|
// once, never resending any particular transaction to the same peer twice
|
||||||
func (self *LesTxRelay) send(txs types.Transactions, count int) {
|
func (self *lesTxRelay) send(txs types.Transactions, count int) {
|
||||||
sendTo := make(map[*peer]types.Transactions)
|
sendTo := make(map[*peer]types.Transactions)
|
||||||
|
|
||||||
self.peerStartPos++ // rotate the starting position of the peer list
|
self.peerStartPos++ // rotate the starting position of the peer list
|
||||||
@ -143,14 +143,14 @@ func (self *LesTxRelay) send(txs types.Transactions, count int) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *LesTxRelay) Send(txs types.Transactions) {
|
func (self *lesTxRelay) Send(txs types.Transactions) {
|
||||||
self.lock.Lock()
|
self.lock.Lock()
|
||||||
defer self.lock.Unlock()
|
defer self.lock.Unlock()
|
||||||
|
|
||||||
self.send(txs, 3)
|
self.send(txs, 3)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *LesTxRelay) NewHead(head common.Hash, mined []common.Hash, rollback []common.Hash) {
|
func (self *lesTxRelay) NewHead(head common.Hash, mined []common.Hash, rollback []common.Hash) {
|
||||||
self.lock.Lock()
|
self.lock.Lock()
|
||||||
defer self.lock.Unlock()
|
defer self.lock.Unlock()
|
||||||
|
|
||||||
@ -173,7 +173,7 @@ func (self *LesTxRelay) NewHead(head common.Hash, mined []common.Hash, rollback
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *LesTxRelay) Discard(hashes []common.Hash) {
|
func (self *lesTxRelay) Discard(hashes []common.Hash) {
|
||||||
self.lock.Lock()
|
self.lock.Lock()
|
||||||
defer self.lock.Unlock()
|
defer self.lock.Unlock()
|
||||||
|
|
||||||
|
@ -26,7 +26,6 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common/mclock"
|
"github.com/ethereum/go-ethereum/common/mclock"
|
||||||
"github.com/ethereum/go-ethereum/core"
|
|
||||||
"github.com/ethereum/go-ethereum/core/rawdb"
|
"github.com/ethereum/go-ethereum/core/rawdb"
|
||||||
"github.com/ethereum/go-ethereum/crypto"
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
"github.com/ethereum/go-ethereum/eth"
|
"github.com/ethereum/go-ethereum/eth"
|
||||||
@ -36,7 +35,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func TestULCSyncWithOnePeer(t *testing.T) {
|
func TestULCSyncWithOnePeer(t *testing.T) {
|
||||||
f := newFullPeerPair(t, 1, 4, testChainGen)
|
f := newFullPeerPair(t, 1, 4)
|
||||||
ulcConfig := ð.ULCConfig{
|
ulcConfig := ð.ULCConfig{
|
||||||
MinTrustedFraction: 100,
|
MinTrustedFraction: 100,
|
||||||
TrustedServers: []string{f.Node.String()},
|
TrustedServers: []string{f.Node.String()},
|
||||||
@ -63,7 +62,7 @@ func TestULCSyncWithOnePeer(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestULCReceiveAnnounce(t *testing.T) {
|
func TestULCReceiveAnnounce(t *testing.T) {
|
||||||
f := newFullPeerPair(t, 1, 4, testChainGen)
|
f := newFullPeerPair(t, 1, 4)
|
||||||
ulcConfig := ð.ULCConfig{
|
ulcConfig := ð.ULCConfig{
|
||||||
MinTrustedFraction: 100,
|
MinTrustedFraction: 100,
|
||||||
TrustedServers: []string{f.Node.String()},
|
TrustedServers: []string{f.Node.String()},
|
||||||
@ -100,8 +99,8 @@ func TestULCReceiveAnnounce(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestULCShouldNotSyncWithTwoPeersOneHaveEmptyChain(t *testing.T) {
|
func TestULCShouldNotSyncWithTwoPeersOneHaveEmptyChain(t *testing.T) {
|
||||||
f1 := newFullPeerPair(t, 1, 4, testChainGen)
|
f1 := newFullPeerPair(t, 1, 4)
|
||||||
f2 := newFullPeerPair(t, 2, 0, nil)
|
f2 := newFullPeerPair(t, 2, 0)
|
||||||
ulcConf := &ulc{minTrustedFraction: 100, trustedKeys: make(map[string]struct{})}
|
ulcConf := &ulc{minTrustedFraction: 100, trustedKeys: make(map[string]struct{})}
|
||||||
ulcConf.trustedKeys[f1.Node.ID().String()] = struct{}{}
|
ulcConf.trustedKeys[f1.Node.ID().String()] = struct{}{}
|
||||||
ulcConf.trustedKeys[f2.Node.ID().String()] = struct{}{}
|
ulcConf.trustedKeys[f2.Node.ID().String()] = struct{}{}
|
||||||
@ -131,9 +130,9 @@ func TestULCShouldNotSyncWithTwoPeersOneHaveEmptyChain(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestULCShouldNotSyncWithThreePeersOneHaveEmptyChain(t *testing.T) {
|
func TestULCShouldNotSyncWithThreePeersOneHaveEmptyChain(t *testing.T) {
|
||||||
f1 := newFullPeerPair(t, 1, 3, testChainGen)
|
f1 := newFullPeerPair(t, 1, 3)
|
||||||
f2 := newFullPeerPair(t, 2, 4, testChainGen)
|
f2 := newFullPeerPair(t, 2, 4)
|
||||||
f3 := newFullPeerPair(t, 3, 0, nil)
|
f3 := newFullPeerPair(t, 3, 0)
|
||||||
|
|
||||||
ulcConfig := ð.ULCConfig{
|
ulcConfig := ð.ULCConfig{
|
||||||
MinTrustedFraction: 60,
|
MinTrustedFraction: 60,
|
||||||
@ -211,10 +210,10 @@ func connectPeers(full, light pairPeer, version int) (*peer, *peer, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// newFullPeerPair creates node with full sync mode
|
// newFullPeerPair creates node with full sync mode
|
||||||
func newFullPeerPair(t *testing.T, index int, numberOfblocks int, chainGen func(int, *core.BlockGen)) pairPeer {
|
func newFullPeerPair(t *testing.T, index int, numberOfblocks int) pairPeer {
|
||||||
db := rawdb.NewMemoryDatabase()
|
db := rawdb.NewMemoryDatabase()
|
||||||
|
|
||||||
pmFull := newTestProtocolManagerMust(t, false, numberOfblocks, chainGen, nil, nil, db, nil)
|
pmFull, _ := newTestProtocolManagerMust(t, false, numberOfblocks, nil, nil, nil, db, nil)
|
||||||
|
|
||||||
peerPairFull := pairPeer{
|
peerPairFull := pairPeer{
|
||||||
Name: "full node",
|
Name: "full node",
|
||||||
@ -238,7 +237,7 @@ func newLightPeer(t *testing.T, ulcConfig *eth.ULCConfig) pairPeer {
|
|||||||
|
|
||||||
odr := NewLesOdr(ldb, light.DefaultClientIndexerConfig, rm)
|
odr := NewLesOdr(ldb, light.DefaultClientIndexerConfig, rm)
|
||||||
|
|
||||||
pmLight := newTestProtocolManagerMust(t, true, 0, nil, odr, peers, ldb, ulcConfig)
|
pmLight, _ := newTestProtocolManagerMust(t, true, 0, odr, nil, peers, ldb, ulcConfig)
|
||||||
peerPairLight := pairPeer{
|
peerPairLight := pairPeer{
|
||||||
Name: "ulc node",
|
Name: "ulc node",
|
||||||
PM: pmLight,
|
PM: pmLight,
|
||||||
|
@ -77,7 +77,7 @@ type LightChain struct {
|
|||||||
// NewLightChain returns a fully initialised light chain using information
|
// NewLightChain returns a fully initialised light chain using information
|
||||||
// available in the database. It initialises the default Ethereum header
|
// available in the database. It initialises the default Ethereum header
|
||||||
// validator.
|
// validator.
|
||||||
func NewLightChain(odr OdrBackend, config *params.ChainConfig, engine consensus.Engine) (*LightChain, error) {
|
func NewLightChain(odr OdrBackend, config *params.ChainConfig, engine consensus.Engine, checkpoint *params.TrustedCheckpoint) (*LightChain, error) {
|
||||||
bodyCache, _ := lru.New(bodyCacheLimit)
|
bodyCache, _ := lru.New(bodyCacheLimit)
|
||||||
bodyRLPCache, _ := lru.New(bodyCacheLimit)
|
bodyRLPCache, _ := lru.New(bodyCacheLimit)
|
||||||
blockCache, _ := lru.New(blockCacheLimit)
|
blockCache, _ := lru.New(blockCacheLimit)
|
||||||
@ -101,8 +101,8 @@ func NewLightChain(odr OdrBackend, config *params.ChainConfig, engine consensus.
|
|||||||
if bc.genesisBlock == nil {
|
if bc.genesisBlock == nil {
|
||||||
return nil, core.ErrNoGenesis
|
return nil, core.ErrNoGenesis
|
||||||
}
|
}
|
||||||
if cp, ok := params.TrustedCheckpoints[bc.genesisBlock.Hash()]; ok {
|
if checkpoint != nil {
|
||||||
bc.addTrustedCheckpoint(cp)
|
bc.AddTrustedCheckpoint(checkpoint)
|
||||||
}
|
}
|
||||||
if err := bc.loadLastState(); err != nil {
|
if err := bc.loadLastState(); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -118,8 +118,8 @@ func NewLightChain(odr OdrBackend, config *params.ChainConfig, engine consensus.
|
|||||||
return bc, nil
|
return bc, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// addTrustedCheckpoint adds a trusted checkpoint to the blockchain
|
// AddTrustedCheckpoint adds a trusted checkpoint to the blockchain
|
||||||
func (lc *LightChain) addTrustedCheckpoint(cp *params.TrustedCheckpoint) {
|
func (lc *LightChain) AddTrustedCheckpoint(cp *params.TrustedCheckpoint) {
|
||||||
if lc.odr.ChtIndexer() != nil {
|
if lc.odr.ChtIndexer() != nil {
|
||||||
StoreChtRoot(lc.chainDb, cp.SectionIndex, cp.SectionHead, cp.CHTRoot)
|
StoreChtRoot(lc.chainDb, cp.SectionIndex, cp.SectionHead, cp.CHTRoot)
|
||||||
lc.odr.ChtIndexer().AddCheckpoint(cp.SectionIndex, cp.SectionHead)
|
lc.odr.ChtIndexer().AddCheckpoint(cp.SectionIndex, cp.SectionHead)
|
||||||
@ -131,7 +131,7 @@ func (lc *LightChain) addTrustedCheckpoint(cp *params.TrustedCheckpoint) {
|
|||||||
if lc.odr.BloomIndexer() != nil {
|
if lc.odr.BloomIndexer() != nil {
|
||||||
lc.odr.BloomIndexer().AddCheckpoint(cp.SectionIndex, cp.SectionHead)
|
lc.odr.BloomIndexer().AddCheckpoint(cp.SectionIndex, cp.SectionHead)
|
||||||
}
|
}
|
||||||
log.Info("Added trusted checkpoint", "chain", cp.Name, "block", (cp.SectionIndex+1)*lc.indexerConfig.ChtSize-1, "hash", cp.SectionHead)
|
log.Info("Added trusted checkpoint", "block", (cp.SectionIndex+1)*lc.indexerConfig.ChtSize-1, "hash", cp.SectionHead)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (lc *LightChain) getProcInterrupt() bool {
|
func (lc *LightChain) getProcInterrupt() bool {
|
||||||
@ -462,21 +462,21 @@ func (lc *LightChain) GetHeaderByNumberOdr(ctx context.Context, number uint64) (
|
|||||||
// Config retrieves the header chain's chain configuration.
|
// Config retrieves the header chain's chain configuration.
|
||||||
func (lc *LightChain) Config() *params.ChainConfig { return lc.hc.Config() }
|
func (lc *LightChain) Config() *params.ChainConfig { return lc.hc.Config() }
|
||||||
|
|
||||||
func (lc *LightChain) SyncCht(ctx context.Context) bool {
|
// SyncCheckpoint fetches the checkpoint point block header according to
|
||||||
// If we don't have a CHT indexer, abort
|
// the checkpoint provided by the remote peer.
|
||||||
if lc.odr.ChtIndexer() == nil {
|
//
|
||||||
return false
|
// Note if we are running the clique, fetches the last epoch snapshot header
|
||||||
}
|
// which covered by checkpoint.
|
||||||
// Ensure the remote CHT head is ahead of us
|
func (lc *LightChain) SyncCheckpoint(ctx context.Context, checkpoint *params.TrustedCheckpoint) bool {
|
||||||
|
// Ensure the remote checkpoint head is ahead of us
|
||||||
head := lc.CurrentHeader().Number.Uint64()
|
head := lc.CurrentHeader().Number.Uint64()
|
||||||
sections, _, _ := lc.odr.ChtIndexer().Sections()
|
|
||||||
|
|
||||||
latest := sections*lc.indexerConfig.ChtSize - 1
|
latest := (checkpoint.SectionIndex+1)*lc.indexerConfig.ChtSize - 1
|
||||||
if clique := lc.hc.Config().Clique; clique != nil {
|
if clique := lc.hc.Config().Clique; clique != nil {
|
||||||
latest -= latest % clique.Epoch // epoch snapshot for clique
|
latest -= latest % clique.Epoch // epoch snapshot for clique
|
||||||
}
|
}
|
||||||
if head >= latest {
|
if head >= latest {
|
||||||
return false
|
return true
|
||||||
}
|
}
|
||||||
// Retrieve the latest useful header and update to it
|
// Retrieve the latest useful header and update to it
|
||||||
if header, err := GetHeaderByNumber(ctx, lc.odr, latest); header != nil && err == nil {
|
if header, err := GetHeaderByNumber(ctx, lc.odr, latest); header != nil && err == nil {
|
||||||
|
@ -55,7 +55,7 @@ func newCanonical(n int) (ethdb.Database, *LightChain, error) {
|
|||||||
db := rawdb.NewMemoryDatabase()
|
db := rawdb.NewMemoryDatabase()
|
||||||
gspec := core.Genesis{Config: params.TestChainConfig}
|
gspec := core.Genesis{Config: params.TestChainConfig}
|
||||||
genesis := gspec.MustCommit(db)
|
genesis := gspec.MustCommit(db)
|
||||||
blockchain, _ := NewLightChain(&dummyOdr{db: db, indexerConfig: TestClientIndexerConfig}, gspec.Config, ethash.NewFaker())
|
blockchain, _ := NewLightChain(&dummyOdr{db: db, indexerConfig: TestClientIndexerConfig}, gspec.Config, ethash.NewFaker(), nil)
|
||||||
|
|
||||||
// Create and inject the requested chain
|
// Create and inject the requested chain
|
||||||
if n == 0 {
|
if n == 0 {
|
||||||
@ -75,7 +75,7 @@ func newTestLightChain() *LightChain {
|
|||||||
Config: params.TestChainConfig,
|
Config: params.TestChainConfig,
|
||||||
}
|
}
|
||||||
gspec.MustCommit(db)
|
gspec.MustCommit(db)
|
||||||
lc, err := NewLightChain(&dummyOdr{db: db}, gspec.Config, ethash.NewFullFaker())
|
lc, err := NewLightChain(&dummyOdr{db: db}, gspec.Config, ethash.NewFullFaker(), nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
@ -344,7 +344,7 @@ func TestReorgBadHeaderHashes(t *testing.T) {
|
|||||||
defer func() { delete(core.BadHashes, headers[3].Hash()) }()
|
defer func() { delete(core.BadHashes, headers[3].Hash()) }()
|
||||||
|
|
||||||
// Create a new LightChain and check that it rolled back the state.
|
// Create a new LightChain and check that it rolled back the state.
|
||||||
ncm, err := NewLightChain(&dummyOdr{db: bc.chainDb}, params.TestChainConfig, ethash.NewFaker())
|
ncm, err := NewLightChain(&dummyOdr{db: bc.chainDb}, params.TestChainConfig, ethash.NewFaker(), nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("failed to create new chain manager: %v", err)
|
t.Fatalf("failed to create new chain manager: %v", err)
|
||||||
}
|
}
|
||||||
|
22
light/odr.go
22
light/odr.go
@ -122,19 +122,25 @@ func (req *BlockRequest) StoreResult(db ethdb.Database) {
|
|||||||
// ReceiptsRequest is the ODR request type for retrieving block bodies
|
// ReceiptsRequest is the ODR request type for retrieving block bodies
|
||||||
type ReceiptsRequest struct {
|
type ReceiptsRequest struct {
|
||||||
OdrRequest
|
OdrRequest
|
||||||
Hash common.Hash
|
Untrusted bool // Indicator whether the result retrieved is trusted or not
|
||||||
Number uint64
|
Hash common.Hash
|
||||||
Receipts types.Receipts
|
Number uint64
|
||||||
|
Header *types.Header
|
||||||
|
Receipts types.Receipts
|
||||||
}
|
}
|
||||||
|
|
||||||
// StoreResult stores the retrieved data in local database
|
// StoreResult stores the retrieved data in local database
|
||||||
func (req *ReceiptsRequest) StoreResult(db ethdb.Database) {
|
func (req *ReceiptsRequest) StoreResult(db ethdb.Database) {
|
||||||
rawdb.WriteReceipts(db, req.Hash, req.Number, req.Receipts)
|
if !req.Untrusted {
|
||||||
|
rawdb.WriteReceipts(db, req.Hash, req.Number, req.Receipts)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ChtRequest is the ODR request type for state/storage trie entries
|
// ChtRequest is the ODR request type for state/storage trie entries
|
||||||
type ChtRequest struct {
|
type ChtRequest struct {
|
||||||
OdrRequest
|
OdrRequest
|
||||||
|
Untrusted bool // Indicator whether the result retrieved is trusted or not
|
||||||
|
PeerId string // The specified peer id from which to retrieve data.
|
||||||
Config *IndexerConfig
|
Config *IndexerConfig
|
||||||
ChtNum, BlockNum uint64
|
ChtNum, BlockNum uint64
|
||||||
ChtRoot common.Hash
|
ChtRoot common.Hash
|
||||||
@ -147,9 +153,11 @@ type ChtRequest struct {
|
|||||||
func (req *ChtRequest) StoreResult(db ethdb.Database) {
|
func (req *ChtRequest) StoreResult(db ethdb.Database) {
|
||||||
hash, num := req.Header.Hash(), req.Header.Number.Uint64()
|
hash, num := req.Header.Hash(), req.Header.Number.Uint64()
|
||||||
|
|
||||||
rawdb.WriteHeader(db, req.Header)
|
if !req.Untrusted {
|
||||||
rawdb.WriteTd(db, hash, num, req.Td)
|
rawdb.WriteHeader(db, req.Header)
|
||||||
rawdb.WriteCanonicalHash(db, hash, num)
|
rawdb.WriteTd(db, hash, num, req.Td)
|
||||||
|
rawdb.WriteCanonicalHash(db, hash, num)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// BloomRequest is the ODR request type for retrieving bloom filters from a CHT structure
|
// BloomRequest is the ODR request type for retrieving bloom filters from a CHT structure
|
||||||
|
@ -264,7 +264,7 @@ func testChainOdr(t *testing.T, protocol int, fn odrTestFn) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
odr := &testOdr{sdb: sdb, ldb: ldb, indexerConfig: TestClientIndexerConfig}
|
odr := &testOdr{sdb: sdb, ldb: ldb, indexerConfig: TestClientIndexerConfig}
|
||||||
lightchain, err := NewLightChain(odr, params.TestChainConfig, ethash.NewFullFaker())
|
lightchain, err := NewLightChain(odr, params.TestChainConfig, ethash.NewFullFaker(), nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
@ -69,6 +69,16 @@ func GetHeaderByNumber(ctx context.Context, odr OdrBackend, number uint64) (*typ
|
|||||||
return r.Header, nil
|
return r.Header, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetUntrustedHeaderByNumber fetches specified block header without correctness checking.
|
||||||
|
// Note this function should only be used in light client checkpoint syncing.
|
||||||
|
func GetUntrustedHeaderByNumber(ctx context.Context, odr OdrBackend, number uint64, peerId string) (*types.Header, error) {
|
||||||
|
r := &ChtRequest{BlockNum: number, ChtNum: number / odr.IndexerConfig().ChtSize, Untrusted: true, PeerId: peerId, Config: odr.IndexerConfig()}
|
||||||
|
if err := odr.Retrieve(ctx, r); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return r.Header, nil
|
||||||
|
}
|
||||||
|
|
||||||
func GetCanonicalHash(ctx context.Context, odr OdrBackend, number uint64) (common.Hash, error) {
|
func GetCanonicalHash(ctx context.Context, odr OdrBackend, number uint64) (common.Hash, error) {
|
||||||
hash := rawdb.ReadCanonicalHash(odr.Database(), number)
|
hash := rawdb.ReadCanonicalHash(odr.Database(), number)
|
||||||
if (hash != common.Hash{}) {
|
if (hash != common.Hash{}) {
|
||||||
@ -169,6 +179,30 @@ func GetBlockLogs(ctx context.Context, odr OdrBackend, hash common.Hash, number
|
|||||||
return logs, nil
|
return logs, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetUntrustedBlockLogs retrieves the logs generated by the transactions included in a
|
||||||
|
// block. The retrieved logs are regarded as untrusted and will not be stored in the
|
||||||
|
// database. This function should only be used in light client checkpoint syncing.
|
||||||
|
func GetUntrustedBlockLogs(ctx context.Context, odr OdrBackend, header *types.Header) ([][]*types.Log, error) {
|
||||||
|
// Retrieve the potentially incomplete receipts from disk or network
|
||||||
|
hash, number := header.Hash(), header.Number.Uint64()
|
||||||
|
receipts := rawdb.ReadRawReceipts(odr.Database(), hash, number)
|
||||||
|
if receipts == nil {
|
||||||
|
r := &ReceiptsRequest{Hash: hash, Number: number, Header: header, Untrusted: true}
|
||||||
|
if err := odr.Retrieve(ctx, r); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
receipts = r.Receipts
|
||||||
|
// Untrusted receipts won't be stored in the database. Therefore
|
||||||
|
// derived fields computation is unnecessary.
|
||||||
|
}
|
||||||
|
// Return the logs without deriving any computed fields on the receipts
|
||||||
|
logs := make([][]*types.Log, len(receipts))
|
||||||
|
for i, receipt := range receipts {
|
||||||
|
logs[i] = receipt.Logs
|
||||||
|
}
|
||||||
|
return logs, nil
|
||||||
|
}
|
||||||
|
|
||||||
// GetBloomBits retrieves a batch of compressed bloomBits vectors belonging to the given bit index and section indexes
|
// GetBloomBits retrieves a batch of compressed bloomBits vectors belonging to the given bit index and section indexes
|
||||||
func GetBloomBits(ctx context.Context, odr OdrBackend, bitIdx uint, sectionIdxList []uint64) ([][]byte, error) {
|
func GetBloomBits(ctx context.Context, odr OdrBackend, bitIdx uint, sectionIdxList []uint64) ([][]byte, error) {
|
||||||
var (
|
var (
|
||||||
|
@ -100,7 +100,7 @@ func TestTxPool(t *testing.T) {
|
|||||||
discard: make(chan int, 1),
|
discard: make(chan int, 1),
|
||||||
mined: make(chan int, 1),
|
mined: make(chan int, 1),
|
||||||
}
|
}
|
||||||
lightchain, _ := NewLightChain(odr, params.TestChainConfig, ethash.NewFullFaker())
|
lightchain, _ := NewLightChain(odr, params.TestChainConfig, ethash.NewFullFaker(), nil)
|
||||||
txPermanent = 50
|
txPermanent = 50
|
||||||
pool := NewTxPool(params.TestChainConfig, lightchain, relay)
|
pool := NewTxPool(params.TestChainConfig, lightchain, relay)
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second)
|
ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second)
|
||||||
|
@ -247,7 +247,6 @@ func (n *Node) Start() error {
|
|||||||
n.services = services
|
n.services = services
|
||||||
n.server = running
|
n.server = running
|
||||||
n.stop = make(chan struct{})
|
n.stop = make(chan struct{})
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -169,7 +169,7 @@ type MsgPipeRW struct {
|
|||||||
closed *int32
|
closed *int32
|
||||||
}
|
}
|
||||||
|
|
||||||
// WriteMsg sends a messsage on the pipe.
|
// WriteMsg sends a message on the pipe.
|
||||||
// It blocks until the receiver has consumed the message payload.
|
// It blocks until the receiver has consumed the message payload.
|
||||||
func (p *MsgPipeRW) WriteMsg(msg Msg) error {
|
func (p *MsgPipeRW) WriteMsg(msg Msg) error {
|
||||||
if atomic.LoadInt32(p.closed) == 0 {
|
if atomic.LoadInt32(p.closed) == 0 {
|
||||||
|
@ -17,10 +17,12 @@
|
|||||||
package params
|
package params
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/binary"
|
||||||
"fmt"
|
"fmt"
|
||||||
"math/big"
|
"math/big"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Genesis hashes to enforce below configs on.
|
// Genesis hashes to enforce below configs on.
|
||||||
@ -40,6 +42,12 @@ var TrustedCheckpoints = map[common.Hash]*TrustedCheckpoint{
|
|||||||
GoerliGenesisHash: GoerliTrustedCheckpoint,
|
GoerliGenesisHash: GoerliTrustedCheckpoint,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CheckpointOracles associates each known checkpoint oracles with the genesis hash of
|
||||||
|
// the chain it belongs to.
|
||||||
|
var CheckpointOracles = map[common.Hash]*CheckpointOracleConfig{
|
||||||
|
RinkebyGenesisHash: RinkebyCheckpointOracle,
|
||||||
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
// MainnetChainConfig is the chain parameters to run a node on the main network.
|
// MainnetChainConfig is the chain parameters to run a node on the main network.
|
||||||
MainnetChainConfig = &ChainConfig{
|
MainnetChainConfig = &ChainConfig{
|
||||||
@ -59,7 +67,6 @@ var (
|
|||||||
|
|
||||||
// MainnetTrustedCheckpoint contains the light client trusted checkpoint for the main network.
|
// MainnetTrustedCheckpoint contains the light client trusted checkpoint for the main network.
|
||||||
MainnetTrustedCheckpoint = &TrustedCheckpoint{
|
MainnetTrustedCheckpoint = &TrustedCheckpoint{
|
||||||
Name: "mainnet",
|
|
||||||
SectionIndex: 227,
|
SectionIndex: 227,
|
||||||
SectionHead: common.HexToHash("0xa2e0b25d72c2fc6e35a7f853cdacb193b4b4f95c606accf7f8fa8415283582c7"),
|
SectionHead: common.HexToHash("0xa2e0b25d72c2fc6e35a7f853cdacb193b4b4f95c606accf7f8fa8415283582c7"),
|
||||||
CHTRoot: common.HexToHash("0xf69bdd4053b95b61a27b106a0e86103d791edd8574950dc96aa351ab9b9f1aa0"),
|
CHTRoot: common.HexToHash("0xf69bdd4053b95b61a27b106a0e86103d791edd8574950dc96aa351ab9b9f1aa0"),
|
||||||
@ -84,7 +91,6 @@ var (
|
|||||||
|
|
||||||
// TestnetTrustedCheckpoint contains the light client trusted checkpoint for the Ropsten test network.
|
// TestnetTrustedCheckpoint contains the light client trusted checkpoint for the Ropsten test network.
|
||||||
TestnetTrustedCheckpoint = &TrustedCheckpoint{
|
TestnetTrustedCheckpoint = &TrustedCheckpoint{
|
||||||
Name: "testnet",
|
|
||||||
SectionIndex: 161,
|
SectionIndex: 161,
|
||||||
SectionHead: common.HexToHash("0x5378afa734e1feafb34bcca1534c4d96952b754579b96a4afb23d5301ecececc"),
|
SectionHead: common.HexToHash("0x5378afa734e1feafb34bcca1534c4d96952b754579b96a4afb23d5301ecececc"),
|
||||||
CHTRoot: common.HexToHash("0x1cf2b071e7443a62914362486b613ff30f60cea0d9c268ed8c545f876a3ee60c"),
|
CHTRoot: common.HexToHash("0x1cf2b071e7443a62914362486b613ff30f60cea0d9c268ed8c545f876a3ee60c"),
|
||||||
@ -112,13 +118,24 @@ var (
|
|||||||
|
|
||||||
// RinkebyTrustedCheckpoint contains the light client trusted checkpoint for the Rinkeby test network.
|
// RinkebyTrustedCheckpoint contains the light client trusted checkpoint for the Rinkeby test network.
|
||||||
RinkebyTrustedCheckpoint = &TrustedCheckpoint{
|
RinkebyTrustedCheckpoint = &TrustedCheckpoint{
|
||||||
Name: "rinkeby",
|
|
||||||
SectionIndex: 125,
|
SectionIndex: 125,
|
||||||
SectionHead: common.HexToHash("0x8a738386f6bb34add15846f8f49c4c519a2f32519096e792b9f43bcb407c831c"),
|
SectionHead: common.HexToHash("0x8a738386f6bb34add15846f8f49c4c519a2f32519096e792b9f43bcb407c831c"),
|
||||||
CHTRoot: common.HexToHash("0xa1e5720a9bad4dce794f129e4ac6744398197b652868011486a6f89c8ec84a75"),
|
CHTRoot: common.HexToHash("0xa1e5720a9bad4dce794f129e4ac6744398197b652868011486a6f89c8ec84a75"),
|
||||||
BloomRoot: common.HexToHash("0xa3048fe8b7e30f77f11bc755a88478363d7d3e71c2bdfe4e8ab9e269cd804ba2"),
|
BloomRoot: common.HexToHash("0xa3048fe8b7e30f77f11bc755a88478363d7d3e71c2bdfe4e8ab9e269cd804ba2"),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// RinkebyCheckpointOracle contains a set of configs for the Rinkeby test network oracle.
|
||||||
|
RinkebyCheckpointOracle = &CheckpointOracleConfig{
|
||||||
|
Address: common.HexToAddress("0xebe8eFA441B9302A0d7eaECc277c09d20D684540"),
|
||||||
|
Signers: []common.Address{
|
||||||
|
common.HexToAddress("0xd9c9cd5f6779558b6e0ed4e6acf6b1947e7fa1f3"), // Peter
|
||||||
|
common.HexToAddress("0x78d1ad571a1a09d60d9bbf25894b44e4c8859595"), // Martin
|
||||||
|
common.HexToAddress("0x286834935f4A8Cfb4FF4C77D5770C2775aE2b0E7"), // Zsolt
|
||||||
|
common.HexToAddress("0xb86e2B0Ab5A4B1373e40c51A7C712c70Ba2f9f8E"), // Gary
|
||||||
|
},
|
||||||
|
Threshold: 2,
|
||||||
|
}
|
||||||
|
|
||||||
// GoerliChainConfig contains the chain parameters to run a node on the Görli test network.
|
// GoerliChainConfig contains the chain parameters to run a node on the Görli test network.
|
||||||
GoerliChainConfig = &ChainConfig{
|
GoerliChainConfig = &ChainConfig{
|
||||||
ChainID: big.NewInt(5),
|
ChainID: big.NewInt(5),
|
||||||
@ -139,7 +156,6 @@ var (
|
|||||||
|
|
||||||
// GoerliTrustedCheckpoint contains the light client trusted checkpoint for the Görli test network.
|
// GoerliTrustedCheckpoint contains the light client trusted checkpoint for the Görli test network.
|
||||||
GoerliTrustedCheckpoint = &TrustedCheckpoint{
|
GoerliTrustedCheckpoint = &TrustedCheckpoint{
|
||||||
Name: "goerli",
|
|
||||||
SectionIndex: 9,
|
SectionIndex: 9,
|
||||||
SectionHead: common.HexToHash("0x8e223d827391eee53b07cb8ee057dbfa11c93e0b45352188c783affd7840a921"),
|
SectionHead: common.HexToHash("0x8e223d827391eee53b07cb8ee057dbfa11c93e0b45352188c783affd7840a921"),
|
||||||
CHTRoot: common.HexToHash("0xe0a817ac69b36c1e437c5b0cff9e764853f5115702b5f66d451b665d6afb7e78"),
|
CHTRoot: common.HexToHash("0xe0a817ac69b36c1e437c5b0cff9e764853f5115702b5f66d451b665d6afb7e78"),
|
||||||
@ -169,13 +185,43 @@ var (
|
|||||||
// used to start light syncing from this checkpoint and avoid downloading the
|
// used to start light syncing from this checkpoint and avoid downloading the
|
||||||
// entire header chain while still being able to securely access old headers/logs.
|
// entire header chain while still being able to securely access old headers/logs.
|
||||||
type TrustedCheckpoint struct {
|
type TrustedCheckpoint struct {
|
||||||
Name string `json:"-"`
|
|
||||||
SectionIndex uint64 `json:"sectionIndex"`
|
SectionIndex uint64 `json:"sectionIndex"`
|
||||||
SectionHead common.Hash `json:"sectionHead"`
|
SectionHead common.Hash `json:"sectionHead"`
|
||||||
CHTRoot common.Hash `json:"chtRoot"`
|
CHTRoot common.Hash `json:"chtRoot"`
|
||||||
BloomRoot common.Hash `json:"bloomRoot"`
|
BloomRoot common.Hash `json:"bloomRoot"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// HashEqual returns an indicator comparing the itself hash with given one.
|
||||||
|
func (c *TrustedCheckpoint) HashEqual(hash common.Hash) bool {
|
||||||
|
if c.Empty() {
|
||||||
|
return hash == common.Hash{}
|
||||||
|
}
|
||||||
|
return c.Hash() == hash
|
||||||
|
}
|
||||||
|
|
||||||
|
// Hash returns the hash of checkpoint's four key fields(index, sectionHead, chtRoot and bloomTrieRoot).
|
||||||
|
func (c *TrustedCheckpoint) Hash() common.Hash {
|
||||||
|
buf := make([]byte, 8+3*common.HashLength)
|
||||||
|
binary.BigEndian.PutUint64(buf, c.SectionIndex)
|
||||||
|
copy(buf[8:], c.SectionHead.Bytes())
|
||||||
|
copy(buf[8+common.HashLength:], c.CHTRoot.Bytes())
|
||||||
|
copy(buf[8+2*common.HashLength:], c.BloomRoot.Bytes())
|
||||||
|
return crypto.Keccak256Hash(buf)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Empty returns an indicator whether the checkpoint is regarded as empty.
|
||||||
|
func (c *TrustedCheckpoint) Empty() bool {
|
||||||
|
return c.SectionHead == (common.Hash{}) || c.CHTRoot == (common.Hash{}) || c.BloomRoot == (common.Hash{})
|
||||||
|
}
|
||||||
|
|
||||||
|
// CheckpointOracleConfig represents a set of checkpoint contract(which acts as an oracle)
|
||||||
|
// config which used for light client checkpoint syncing.
|
||||||
|
type CheckpointOracleConfig struct {
|
||||||
|
Address common.Address `json:"address"`
|
||||||
|
Signers []common.Address `json:"signers"`
|
||||||
|
Threshold uint64 `json:"threshold"`
|
||||||
|
}
|
||||||
|
|
||||||
// ChainConfig is the core config which determines the blockchain settings.
|
// ChainConfig is the core config which determines the blockchain settings.
|
||||||
//
|
//
|
||||||
// ChainConfig is stored in the database on a per block basis. This means
|
// ChainConfig is stored in the database on a per block basis. This means
|
||||||
|
@ -47,6 +47,12 @@ const (
|
|||||||
// is generated
|
// is generated
|
||||||
HelperTrieProcessConfirmations = 256
|
HelperTrieProcessConfirmations = 256
|
||||||
|
|
||||||
|
// CheckpointFrequency is the block frequency for creating checkpoint
|
||||||
|
CheckpointFrequency = 32768
|
||||||
|
|
||||||
|
// CheckpointProcessConfirmations is the number before a checkpoint is generated
|
||||||
|
CheckpointProcessConfirmations = 256
|
||||||
|
|
||||||
// ImmutabilityThreshold is the number of blocks after which a chain segment is
|
// ImmutabilityThreshold is the number of blocks after which a chain segment is
|
||||||
// considered immutable (i.e. soft finality). It is used by the downloader as a
|
// considered immutable (i.e. soft finality). It is used by the downloader as a
|
||||||
// hard limit against deep ancestors, by the blockchain against deep reorgs, by
|
// hard limit against deep ancestors, by the blockchain against deep reorgs, by
|
||||||
|
Loading…
Reference in New Issue
Block a user