aboutsummaryrefslogtreecommitdiffstats
path: root/cmd
diff options
context:
space:
mode:
authorgary rong <garyrong0905@gmail.com>2019-06-28 15:34:02 +0800
committerPéter Szilágyi <peterke@gmail.com>2019-06-28 15:34:02 +0800
commitf7cdea2bdcd7ff3cec99731cb912cde0b233d6c9 (patch)
treeb463c8dd42547edceb778d946927d2c363303324 /cmd
parent702f52fb99d60b4b6bab05799c14dafdd8648854 (diff)
downloadgo-tangerine-f7cdea2bdcd7ff3cec99731cb912cde0b233d6c9.tar
go-tangerine-f7cdea2bdcd7ff3cec99731cb912cde0b233d6c9.tar.gz
go-tangerine-f7cdea2bdcd7ff3cec99731cb912cde0b233d6c9.tar.bz2
go-tangerine-f7cdea2bdcd7ff3cec99731cb912cde0b233d6c9.tar.lz
go-tangerine-f7cdea2bdcd7ff3cec99731cb912cde0b233d6c9.tar.xz
go-tangerine-f7cdea2bdcd7ff3cec99731cb912cde0b233d6c9.tar.zst
go-tangerine-f7cdea2bdcd7ff3cec99731cb912cde0b233d6c9.zip
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
Diffstat (limited to 'cmd')
-rw-r--r--cmd/checkpoint-admin/common.go166
-rw-r--r--cmd/checkpoint-admin/exec.go335
-rw-r--r--cmd/checkpoint-admin/main.go124
-rw-r--r--cmd/checkpoint-admin/status.go61
-rw-r--r--cmd/geth/main.go35
-rw-r--r--cmd/puppeth/wizard_genesis.go17
6 files changed, 722 insertions, 16 deletions
diff --git a/cmd/checkpoint-admin/common.go b/cmd/checkpoint-admin/common.go
new file mode 100644
index 000000000..1f4a34a41
--- /dev/null
+++ b/cmd/checkpoint-admin/common.go
@@ -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
+}
diff --git a/cmd/checkpoint-admin/exec.go b/cmd/checkpoint-admin/exec.go
new file mode 100644
index 000000000..02c4f35cc
--- /dev/null
+++ b/cmd/checkpoint-admin/exec.go
@@ -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
+}
diff --git a/cmd/checkpoint-admin/main.go b/cmd/checkpoint-admin/main.go
new file mode 100644
index 000000000..39eae6878
--- /dev/null
+++ b/cmd/checkpoint-admin/main.go
@@ -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)
+ }
+}
diff --git a/cmd/checkpoint-admin/status.go b/cmd/checkpoint-admin/status.go
new file mode 100644
index 000000000..c134ec090
--- /dev/null
+++ b/cmd/checkpoint-admin/status.go
@@ -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
+}
diff --git a/cmd/geth/main.go b/cmd/geth/main.go
index 00809e2e1..7e94da1f5 100644
--- a/cmd/geth/main.go
+++ b/cmd/geth/main.go
@@ -37,6 +37,7 @@ import (
"github.com/ethereum/go-ethereum/eth/downloader"
"github.com/ethereum/go-ethereum/ethclient"
"github.com/ethereum/go-ethereum/internal/debug"
+ "github.com/ethereum/go-ethereum/les"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/metrics"
"github.com/ethereum/go-ethereum/node"
@@ -323,14 +324,33 @@ func startNode(ctx *cli.Context, stack *node.Node) {
events := make(chan accounts.WalletEvent, 16)
stack.AccountManager().Subscribe(events)
- go func() {
- // Create a chain state reader for self-derivation
- rpcClient, err := stack.Attach()
- if err != nil {
- utils.Fatalf("Failed to attach to self: %v", err)
+ // Create a client to interact with local geth node.
+ rpcClient, err := stack.Attach()
+ if err != nil {
+ utils.Fatalf("Failed to attach to self: %v", err)
+ }
+ ethClient := 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(&ethService); 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)
}
- stateReader := ethclient.NewClient(rpcClient)
+ lesService.SetContractBackend(ethClient)
+ }
+ go func() {
// Open any wallets already attached
for _, wallet := range stack.AccountManager().Wallets() {
if err := wallet.Open(""); err != nil {
@@ -354,7 +374,7 @@ func startNode(ctx *cli.Context, stack *node.Node) {
}
derivationPaths = append(derivationPaths, accounts.DefaultBaseDerivationPath)
- event.Wallet.SelfDerive(derivationPaths, stateReader)
+ event.Wallet.SelfDerive(derivationPaths, ethClient)
case accounts.WalletDropped:
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))
stack.Stop()
}
-
}
}()
}
diff --git a/cmd/puppeth/wizard_genesis.go b/cmd/puppeth/wizard_genesis.go
index 6aed09f14..499f320f6 100644
--- a/cmd/puppeth/wizard_genesis.go
+++ b/cmd/puppeth/wizard_genesis.go
@@ -44,12 +44,13 @@ func (w *wizard) makeGenesis() {
Difficulty: big.NewInt(524288),
Alloc: make(core.GenesisAlloc),
Config: &params.ChainConfig{
- HomesteadBlock: big.NewInt(1),
- EIP150Block: big.NewInt(2),
- EIP155Block: big.NewInt(3),
- EIP158Block: big.NewInt(3),
- ByzantiumBlock: big.NewInt(4),
- ConstantinopleBlock: big.NewInt(5),
+ HomesteadBlock: big.NewInt(0),
+ EIP150Block: big.NewInt(0),
+ EIP155Block: big.NewInt(0),
+ EIP158Block: big.NewInt(0),
+ ByzantiumBlock: big.NewInt(0),
+ ConstantinopleBlock: big.NewInt(0),
+ PetersburgBlock: big.NewInt(0),
},
}
// Figure out which consensus engine to choose
@@ -191,7 +192,7 @@ func (w *wizard) importGenesis() {
func (w *wizard) manageGenesis() {
// Figure out whether to modify or export the genesis
fmt.Println()
- fmt.Println(" 1. Modify existing fork rules")
+ fmt.Println(" 1. Modify existing configurations")
fmt.Println(" 2. Export genesis configurations")
fmt.Println(" 3. Remove genesis configuration")
@@ -226,7 +227,7 @@ func (w *wizard) manageGenesis() {
w.conf.Genesis.Config.PetersburgBlock = w.conf.Genesis.Config.ConstantinopleBlock
}
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)
out, _ := json.MarshalIndent(w.conf.Genesis.Config, "", " ")