Skip to content

Commit

Permalink
all: on-chain oracle checkpoint syncing (#19543)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
rjl493456442 authored and karalabe committed Jun 28, 2019
1 parent 702f52f commit f7cdea2
Show file tree
Hide file tree
Showing 49 changed files with 2,859 additions and 381 deletions.
24 changes: 18 additions & 6 deletions accounts/abi/bind/backends/simulated.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,10 @@ import (
// This nil assignment ensures compile time that SimulatedBackend implements bind.ContractBackend.
var _ bind.ContractBackend = (*SimulatedBackend)(nil)

var errBlockNumberUnsupported = errors.New("SimulatedBackend cannot access blocks other than the latest block")
var errGasEstimationFailed = errors.New("gas required exceeds allowance or always failing transaction")
var (
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
// the background. Its main purpose is to allow easily testing contract bindings.
Expand All @@ -63,10 +65,9 @@ type SimulatedBackend struct {
config *params.ChainConfig
}

// NewSimulatedBackend creates a new binding backend using a simulated blockchain
// for testing purposes.
func NewSimulatedBackend(alloc core.GenesisAlloc, gasLimit uint64) *SimulatedBackend {
database := rawdb.NewMemoryDatabase()
// NewSimulatedBackendWithDatabase creates a new binding backend based on the given database
// and uses a simulated blockchain for testing purposes.
func NewSimulatedBackendWithDatabase(database ethdb.Database, alloc core.GenesisAlloc, gasLimit uint64) *SimulatedBackend {
genesis := core.Genesis{Config: params.AllEthashProtocolChanges, GasLimit: gasLimit, Alloc: alloc}
genesis.MustCommit(database)
blockchain, _ := core.NewBlockChain(database, nil, genesis.Config, ethash.NewFaker(), vm.Config{}, nil)
Expand All @@ -81,6 +82,12 @@ func NewSimulatedBackend(alloc core.GenesisAlloc, gasLimit uint64) *SimulatedBac
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
// fresh new state.
func (b *SimulatedBackend) Commit() {
Expand Down Expand Up @@ -424,6 +431,11 @@ func (b *SimulatedBackend) AdjustTime(adjustment time.Duration) error {
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.
type callmsg struct {
ethereum.CallMsg
Expand Down
3 changes: 2 additions & 1 deletion accounts/abi/bind/bind_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -475,11 +475,12 @@ var bindTests = []struct {
`
"github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/accounts/abi/bind/backends"
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/common"
`,
`
// 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)
if err != nil {
Expand Down
12 changes: 12 additions & 0 deletions accounts/abi/bind/template.go
Original file line number Diff line number Diff line change
Expand Up @@ -439,6 +439,18 @@ var (
}
}), 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}}
`
Expand Down
3 changes: 2 additions & 1 deletion accounts/abi/bind/util_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,8 @@ func TestWaitDeployed(t *testing.T) {
backend := backends.NewSimulatedBackend(
core.GenesisAlloc{
crypto.PubkeyToAddress(testKey.PublicKey): {Balance: big.NewInt(10000000000)},
}, 10000000,
},
10000000,
)

// Create the transaction.
Expand Down
166 changes: 166 additions & 0 deletions cmd/checkpoint-admin/common.go
Original file line number Diff line number Diff line change
@@ -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 = &params.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 = &params.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
}
Loading

0 comments on commit f7cdea2

Please sign in to comment.