diff --git a/cmd/registrar/common.go b/cmd/checkpoint-admin/common.go
similarity index 89%
rename from cmd/registrar/common.go
rename to cmd/checkpoint-admin/common.go
index 592ab5390c..f57afb817c 100644
--- a/cmd/registrar/common.go
+++ b/cmd/checkpoint-admin/common.go
@@ -27,7 +27,6 @@ import (
"github.com/ethereum/go-ethereum/console"
"github.com/ethereum/go-ethereum/contracts/registrar"
"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"
@@ -35,12 +34,10 @@ import (
// newClient creates a client with specified remote URL.
func newClient(ctx *cli.Context) *ethclient.Client {
- url := ctx.GlobalString(nodeURLFlag.Name)
- client, err := ethclient.Dial(url)
+ client, err := ethclient.Dial(ctx.GlobalString(nodeURLFlag.Name))
if err != nil {
- utils.Fatalf("Failed to setup ethereum client at url %s: %v", url, err)
+ utils.Fatalf("Failed to connect to Ethereum node: %v", err)
}
- log.Info("Setup ethereum client", "URL", url)
return client
}
@@ -48,9 +45,8 @@ func newClient(ctx *cli.Context) *ethclient.Client {
func newRPCClient(url string) *rpc.Client {
client, err := rpc.Dial(url)
if err != nil {
- utils.Fatalf("Failed to setup rpc client at url %s: %v", url, err)
+ utils.Fatalf("Failed to connect to Ethereum node: %v", err)
}
- log.Info("Setup rpc client", "URL", url)
return client
}
@@ -58,9 +54,8 @@ func newRPCClient(url string) *rpc.Client {
// rpc request.
func getContractAddr(client *rpc.Client) common.Address {
var addr string
- err := client.Call(&addr, "les_getCheckpointContractAddress")
- if err != nil {
- utils.Fatalf("Failed to fetch checkpoint contract address, err %v", err)
+ if err := client.Call(&addr, "les_getCheckpointContractAddress"); err != nil {
+ utils.Fatalf("Failed to fetch checkpoint oracle address: %v", err)
}
return common.HexToAddress(addr)
}
@@ -104,7 +99,7 @@ func getCheckpoint(ctx *cli.Context, client *rpc.Client) *params.TrustedCheckpoi
// newContract creates a registrar contract instance with specified
// contract address or the default contracts for mainnet or testnet.
-func newContract(client *rpc.Client) *registrar.Registrar {
+func newContract(client *rpc.Client) (common.Address, *registrar.Registrar) {
addr := getContractAddr(client)
if addr == (common.Address{}) {
utils.Fatalf("No specified registrar contract address")
@@ -113,8 +108,7 @@ func newContract(client *rpc.Client) *registrar.Registrar {
if err != nil {
utils.Fatalf("Failed to setup registrar contract %s: %v", addr, err)
}
- log.Info("Setup registrar contract", "address", addr)
- return contract
+ return addr, contract
}
// promptPassphrase prompts the user for a passphrase.
diff --git a/cmd/checkpoint-admin/exec.go b/cmd/checkpoint-admin/exec.go
new file mode 100644
index 0000000000..457f876503
--- /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 .
+
+package main
+
+import (
+ "bytes"
+ "context"
+ "encoding/binary"
+ "fmt"
+ "math/big"
+ "strings"
+ "time"
+
+ "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/registrar"
+ "github.com/ethereum/go-ethereum/contracts/registrar/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,
+ addressFlag,
+ keyFileFlag,
+ signersFlag,
+ utils.PasswordFileFlag,
+ },
+ Action: utils.MigrateFlags(sign),
+}
+
+var commandPublish = cli.Command{
+ Name: "publish",
+ Usage: "Publish a checkpoint into the oracle",
+ Flags: []cli.Flag{
+ nodeURLFlag,
+ indexFlag,
+ signersFlag,
+ 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.DeployContract(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 *registrar.Registrar
+ )
+ 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(addressFlag.Name) {
+ utils.Fatalf("Please specify oracle address (--address) to sign in offline mode")
+ }
+ address = common.HexToAddress(ctx.String(addressFlag.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.GlobalString(signersFlag.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", "data/validator", 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/registrar/main.go b/cmd/checkpoint-admin/main.go
similarity index 87%
rename from cmd/registrar/main.go
rename to cmd/checkpoint-admin/main.go
index 1b5a1166be..3f26e4e108 100644
--- a/cmd/registrar/main.go
+++ b/cmd/checkpoint-admin/main.go
@@ -14,8 +14,8 @@
// You should have received a copy of the GNU General Public License
// along with go-ethereum. If not, see .
-// registrar is a utility that can be used to query checkpoint information
-// and register stable checkpoint into the contract.
+// checkpoint-admin is a utility that can be used to query checkpoint information
+// and register stable checkpoints into an oracle contract.
package main
import (
@@ -52,22 +52,16 @@ var app *cli.App
func init() {
app = utils.NewApp(gitCommit, gitDate, "ethereum checkpoint helper tool")
app.Commands = []cli.Command{
- commandQueryAdmin,
- commandQueryCheckpoint,
- commandDeployContract,
- commandSignCheckpoint,
- commandRegisterCheckpoint,
+ commandStatus,
+ commandDeploy,
+ commandSign,
+ commandPublish,
}
app.Flags = []cli.Flag{
- indexFlag,
- hashFlag,
addressFlag,
- thresholdFlag,
keyFileFlag,
nodeURLFlag,
clefURLFlag,
- signerFlag,
- signatureFlag,
utils.PasswordFileFlag,
}
cli.CommandHelpTemplate = commandHelperTemplate
@@ -105,12 +99,12 @@ var (
Value: "http://localhost:8550",
Usage: "The rpc endpoint of clef",
}
- signerFlag = cli.StringFlag{
- Name: "signer",
+ signersFlag = cli.StringFlag{
+ Name: "signers",
Usage: "Comma separated accounts of trusted checkpoint signers",
}
- signatureFlag = cli.StringFlag{
- Name: "signature",
+ signaturesFlag = cli.StringFlag{
+ Name: "signatures",
Usage: "Comma separated checkpoint signatures to submit",
}
)
diff --git a/cmd/checkpoint-admin/status.go b/cmd/checkpoint-admin/status.go
new file mode 100644
index 0000000000..c134ec090e
--- /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 .
+
+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/registrar/exec.go b/cmd/registrar/exec.go
deleted file mode 100644
index 4859a07e70..0000000000
--- a/cmd/registrar/exec.go
+++ /dev/null
@@ -1,309 +0,0 @@
-// 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 .
-
-package main
-
-import (
- "bytes"
- "context"
- "encoding/binary"
- "fmt"
- "math/big"
- "sort"
- "strings"
- "time"
-
- "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/registrar"
- "github.com/ethereum/go-ethereum/contracts/registrar/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 commandDeployContract = cli.Command{
- Name: "deploy",
- Usage: "Deploy a registrar contract with specified trusted signers.",
- Flags: []cli.Flag{
- signerFlag,
- thresholdFlag,
- nodeURLFlag,
- keyFileFlag,
- utils.PasswordFileFlag,
- },
- Action: utils.MigrateFlags(deployContract),
-}
-
-var commandSignCheckpoint = cli.Command{
- Name: "sign",
- Usage: "Sign the checkpoint with the specified key",
- Flags: []cli.Flag{
- nodeURLFlag,
- clefURLFlag,
- indexFlag,
- hashFlag,
- addressFlag,
- keyFileFlag,
- signerFlag,
- utils.PasswordFileFlag,
- },
- Action: utils.MigrateFlags(signCheckpoint),
-}
-
-var commandRegisterCheckpoint = cli.Command{
- Name: "register",
- Usage: "Register specified checkpoint into contract",
- Flags: []cli.Flag{
- nodeURLFlag,
- indexFlag,
- signerFlag,
- signatureFlag,
- keyFileFlag,
- utils.PasswordFileFlag,
- },
- Action: utils.MigrateFlags(registerCheckpoint),
-}
-
-// deployContract deploys the checkpoint registrar contract.
-//
-// Note the network where the contract is deployed depends on
-// the network where the connected node is located.
-func deployContract(ctx *cli.Context) error {
- var addrs []common.Address
- signers := strings.Split(ctx.GlobalString(signerFlag.Name), ",")
- for _, account := range signers {
- if trimmed := strings.TrimSpace(account); !common.IsHexAddress(trimmed) {
- utils.Fatalf("Invalid account in --signer: %s", trimmed)
- } else {
- addrs = append(addrs, common.HexToAddress(account))
- }
- }
-
- t := ctx.GlobalInt64(thresholdFlag.Name)
- if t == 0 || int(t) > len(signers) {
- utils.Fatalf("Invalid signature threshold %d", t)
- }
- addr, tx, _, err := contract.DeployContract(bind.NewKeyedTransactor(getKey(ctx).PrivateKey), newClient(ctx), addrs, big.NewInt(int64(params.CheckpointFrequency)),
- big.NewInt(int64(params.CheckpointProcessConfirmations)), big.NewInt(t))
- if err != nil {
- utils.Fatalf("Failed to deploy registrar contract %v", err)
- }
- log.Info("Deployed registrar contract successfully", "address", addr, "tx", tx.Hash())
- return nil
-}
-
-// signCheckpoint creates the signature for specific checkpoint
-// with local key. Only contract admins have the permission to
-// sign checkpoint.
-func signCheckpoint(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
- c *registrar.Registrar
- )
- if !ctx.GlobalIsSet(nodeURLFlag.Name) {
- // Offline mode signing
- offline = true
- if !ctx.GlobalIsSet(hashFlag.Name) {
- utils.Fatalf("Please specify checkpoint hash for offline mode signing")
- }
- chash = common.HexToHash(ctx.GlobalString(hashFlag.Name))
- if !ctx.GlobalIsSet(indexFlag.Name) {
- utils.Fatalf("Please specify checkpoint index for offline mode signing")
- }
- cindex = ctx.GlobalUint64(indexFlag.Name)
- if !ctx.GlobalIsSet(addressFlag.Name) {
- utils.Fatalf("Please specify contract address for offline mode signing")
- }
- address = common.HexToAddress(ctx.GlobalString(addressFlag.Name))
- } else {
- // Interactive mode signing
- node = newRPCClient(ctx.GlobalString(nodeURLFlag.Name))
- checkpoint := getCheckpoint(ctx, node)
-
- chash = checkpoint.Hash()
- cindex = checkpoint.SectionIndex
- address = getContractAddr(node)
-
- reqCtx, cancelFn := context.WithTimeout(context.Background(), 10*time.Second)
- defer cancelFn()
-
- // Check the validity of checkpoint.
- 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")
- }
- c = newContract(node)
- latest, _, h, err := c.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 := c.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())
- }
- switch {
- case ctx.GlobalIsSet(clefURLFlag.Name):
- // Sign checkpoint in clef mode.
- signer = ctx.GlobalString(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", "data/validator", 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
- }
- }
- buf := make([]byte, 8)
- binary.BigEndian.PutUint64(buf, cindex)
- data := append([]byte{0x19, 0x00}, append(address[:], append(buf, chash.Bytes()...)...)...)
- sig, err := crypto.Sign(crypto.Keccak256(data), 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")
- }
- log.Info("Successfully signed checkpoint", "index", cindex, "hash", chash.Hex(), "address", address.Hex(), "signer", signer, "signature", signature)
- return nil
-}
-
-type Signer struct {
- addr common.Address
- sig []byte
-}
-type Signers []Signer
-
-func (s Signers) Len() int { return len(s) }
-func (s Signers) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
-func (s Signers) Less(i, j int) bool { return bytes.Compare(s[i].addr.Bytes(), s[j].addr.Bytes()) < 0 }
-
-// registerCheckpoint registers the specified checkpoint which generated by connected
-// node with a authorised private key.
-func registerCheckpoint(ctx *cli.Context) error {
- var (
- addrs []common.Address
- sigs [][]byte
- signers Signers
- )
- signerStrs := strings.Split(ctx.GlobalString(signerFlag.Name), ",")
- for _, account := range signerStrs {
- if trimmed := strings.TrimSpace(account); !common.IsHexAddress(trimmed) {
- utils.Fatalf("Invalid account in --signer: %s", trimmed)
- } else {
- addrs = append(addrs, common.HexToAddress(account))
- }
- }
- sigStrs := strings.Split(ctx.GlobalString(signatureFlag.Name), ",")
- for _, sig := range sigStrs {
- 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))
- }
- }
- if len(addrs) != len(sigs) {
- utils.Fatalf("The length of signer and corresponding signature mismatch")
- }
- for i := 0; i < len(addrs); i++ {
- signers = append(signers, Signer{addr: addrs[i], sig: sigs[i]})
- }
- sort.Sort(signers)
- sigs = sigs[:0]
- for i := 0; i < len(signers); i++ {
- sigs = append(sigs, signers[i].sig)
- }
-
- reqCtx, cancelFn := context.WithTimeout(context.Background(), 10*time.Second)
- defer cancelFn()
-
- // Retrieve recent header info to protect replay attack.
- node := newRPCClient(ctx.GlobalString(nodeURLFlag.Name))
- head, err := ethclient.NewClient(node).HeaderByNumber(reqCtx, nil)
- if err != nil {
- return err
- }
- num := head.Number.Uint64()
- recent, err := ethclient.NewClient(node).HeaderByNumber(reqCtx, big.NewInt(int64(num-128)))
- if err != nil {
- return err
- }
- var (
- c = newContract(node)
- key = getKey(ctx)
- checkpoint = getCheckpoint(ctx, node)
- )
- tx, err := c.RegisterCheckpoint(key.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", "index", checkpoint.SectionIndex, "hash", checkpoint.Hash(), "signumber", len(signers), "txhash", tx.Hash())
- return nil
-}
diff --git a/cmd/registrar/query.go b/cmd/registrar/query.go
deleted file mode 100644
index 7f085d0852..0000000000
--- a/cmd/registrar/query.go
+++ /dev/null
@@ -1,69 +0,0 @@
-// 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 .
-
-package main
-
-import (
- "fmt"
-
- "github.com/ethereum/go-ethereum/cmd/utils"
- "github.com/ethereum/go-ethereum/common"
- "gopkg.in/urfave/cli.v1"
-)
-
-var commandQueryAdmin = cli.Command{
- Name: "queryadmin",
- Usage: "Fetch the admin list of specified registrar contract",
- Flags: []cli.Flag{
- nodeURLFlag,
- },
- Action: utils.MigrateFlags(queryAdmin),
-}
-
-var commandQueryCheckpoint = cli.Command{
- Name: "querycheckpoint",
- Usage: "Fetch the latest registered checkpoint in the registrar contract",
- Flags: []cli.Flag{
- nodeURLFlag,
- },
- Action: utils.MigrateFlags(queryCheckpoint),
-}
-
-// queryAdmin fetches the admin list of specified registrar contract.
-func queryAdmin(ctx *cli.Context) error {
- contract := newContract(newRPCClient(ctx.GlobalString(nodeURLFlag.Name)))
- admins, err := contract.Contract().GetAllAdmin(nil)
- if err != nil {
- return err
- }
- fmt.Println("Total admin number", len(admins))
- for i, admin := range admins {
- fmt.Printf("Admin %d => %s\n", i+1, admin.Hex())
- }
- return nil
-}
-
-// queryCheckpoint fetches the checkpoint hash with specified index from
-// registrar contract.
-func queryCheckpoint(ctx *cli.Context) error {
- contract := newContract(newRPCClient(ctx.GlobalString(nodeURLFlag.Name)))
- index, checkpoint, height, err := contract.Contract().GetLatestCheckpoint(nil)
- if err != nil {
- return err
- }
- fmt.Printf("Latest checkpoint(registered at height #%d) %d => %s\n", height, index, common.Hash(checkpoint).Hex())
- return nil
-}