forked from ethereum/go-ethereum
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Revert "all: remove notion of trusted checkpoints in the post-merge w…
…orld (ethereum#27147)" This reverts commit 1e556d2.
- Loading branch information
Showing
26 changed files
with
2,401 additions
and
117 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,103 @@ | ||
## Checkpoint-admin | ||
|
||
Checkpoint-admin is a tool for updating checkpoint oracle status. It provides a series of functions including deploying checkpoint oracle contract, signing for new checkpoints, and updating checkpoints in the checkpoint oracle contract. | ||
|
||
### Checkpoint | ||
|
||
In the LES protocol, there is an important concept called checkpoint. In simple terms, whenever a certain number of blocks are generated on the blockchain, a new checkpoint is generated which contains some important information such as | ||
|
||
* Block hash at checkpoint | ||
* Canonical hash trie root at checkpoint | ||
* Bloom trie root at checkpoint | ||
|
||
*For a more detailed introduction to checkpoint, please see the LES [spec](https://github.com/ethereum/devp2p/blob/master/caps/les.md).* | ||
|
||
Using this information, light clients can skip all historical block headers when synchronizing data and start synchronization from this checkpoint. Therefore, as long as the light client can obtain some latest and correct checkpoints, the amount of data and time for synchronization will be greatly reduced. | ||
|
||
However, from a security perspective, the most critical step in a synchronization algorithm based on checkpoints is to determine whether the checkpoint used by the light client is correct. Otherwise, all blockchain data synchronized based on this checkpoint may be wrong. For this we provide two different ways to ensure the correctness of the checkpoint used by the light client. | ||
|
||
#### Hardcoded checkpoint | ||
|
||
There are several hardcoded checkpoints in the [source code](https://github.com/ethereum/go-ethereum/blob/master/params/config.go#L38) of the go-ethereum project. These checkpoints are updated by go-ethereum developers when new versions of software are released. Because light client users trust Geth developers to some extent, hardcoded checkpoints in the code can also be considered correct. | ||
|
||
#### Checkpoint oracle | ||
|
||
Hardcoded checkpoints can solve the problem of verifying the correctness of checkpoints (although this is a more centralized solution). But the pain point of this solution is that developers can only update checkpoints when a new version of software is released. In addition, light client users usually do not keep the Geth version they use always up to date. So hardcoded checkpoints used by users are generally stale. Therefore, it still needs to download a large amount of blockchain data during synchronization. | ||
|
||
Checkpoint oracle is a more flexible solution. In simple terms, this is a smart contract that is deployed on the blockchain. The smart contract records several designated trusted signers. Whenever enough trusted signers have issued their signatures for the same checkpoint, it can be considered that the checkpoint has been authenticated by the signers. Checkpoints authenticated by trusted signers can be considered correct. | ||
|
||
So this way, even without updating the software version, as long as the trusted signers regularly update the checkpoint in oracle on time, the light client can always use the latest and verified checkpoint for data synchronization. | ||
|
||
### Usage | ||
|
||
Checkpoint-admin is a command line tool designed for checkpoint oracle. Users can easily deploy contracts and update checkpoints through this tool. | ||
|
||
#### Install | ||
|
||
```shell | ||
go get github.com/ethereum/go-ethereum/cmd/checkpoint-admin | ||
``` | ||
|
||
#### Deploy | ||
|
||
Deploy checkpoint oracle contract. `--signers` indicates the specified trusted signer, and `--threshold` indicates the minimum number of signatures required by trusted signers to update a checkpoint. | ||
|
||
```shell | ||
checkpoint-admin deploy --rpc <NODE_RPC_ENDPOINT> --clef <CLEF_ENDPOINT> --signer <SIGNER_TO_SIGN_TX> --signers <TRUSTED_SIGNER_LIST> --threshold 1 | ||
``` | ||
|
||
It is worth noting that checkpoint-admin only supports clef as a signer for transactions and plain text(checkpoint). For more clef usage, please see the clef [tutorial](https://geth.ethereum.org/docs/tools/clef/tutorial) . | ||
|
||
#### Sign | ||
|
||
Checkpoint-admin provides two different modes of signing. You can automatically obtain the current stable checkpoint and sign it interactively, and you can also use the information provided by the command line flags to sign checkpoint offline. | ||
|
||
**Interactive mode** | ||
|
||
```shell | ||
checkpoint-admin sign --clef <CLEF_ENDPOINT> --signer <SIGNER_TO_SIGN_CHECKPOINT> --rpc <NODE_RPC_ENDPOINT> | ||
``` | ||
|
||
*It is worth noting that the connected Geth node can be a fullnode or a light client. If it is fullnode, you must enable the LES protocol. E.G. add `--light.serv 50` to the startup command line flags*. | ||
|
||
**Offline mode** | ||
|
||
```shell | ||
checkpoint-admin sign --clef <CLEF_ENDPOINT> --signer <SIGNER_TO_SIGN_CHECKPOINT> --index <CHECKPOINT_INDEX> --hash <CHECKPOINT_HASH> --oracle <CHECKPOINT_ORACLE_ADDRESS> | ||
``` | ||
|
||
*CHECKPOINT_HASH is obtained based on this [calculation method](https://github.com/ethereum/go-ethereum/blob/master/params/config.go#L251).* | ||
|
||
#### Publish | ||
|
||
Collect enough signatures from different trusted signers for the same checkpoint and submit them to oracle to update the "authenticated" checkpoint in the contract. | ||
|
||
```shell | ||
checkpoint-admin publish --clef <CLEF_ENDPOINT> --rpc <NODE_RPC_ENDPOINT> --signer <SIGNER_TO_SIGN_TX> --index <CHECKPOINT_INDEX> --signatures <CHECKPOINT_SIGNATURE_LIST> | ||
``` | ||
|
||
#### Status query | ||
|
||
Check the latest status of checkpoint oracle. | ||
|
||
```shell | ||
checkpoint-admin status --rpc <NODE_RPC_ENDPOINT> | ||
``` | ||
|
||
### Enable checkpoint oracle in your private network | ||
|
||
Currently, only the Ethereum mainnet and the default supported test networks (rinkeby, goerli) activate this feature. If you want to activate this feature in your private network, you can overwrite the relevant checkpoint oracle settings through the configuration file after deploying the oracle contract. | ||
|
||
* Get your node configuration file `geth dumpconfig OTHER_COMMAND_LINE_OPTIONS > config.toml` | ||
* Edit the configuration file and add the following information | ||
|
||
```toml | ||
[Eth.CheckpointOracle] | ||
Address = CHECKPOINT_ORACLE_ADDRESS | ||
Signers = [TRUSTED_SIGNER_1, ..., TRUSTED_SIGNER_N] | ||
Threshold = THRESHOLD | ||
``` | ||
|
||
* Start geth with the modified configuration file | ||
|
||
*In the private network, all fullnodes and light clients need to be started using the same checkpoint oracle settings.* |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,120 @@ | ||
// Copyright 2019 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 ( | ||
"strconv" | ||
|
||
"github.com/ethereum/go-ethereum/accounts" | ||
"github.com/ethereum/go-ethereum/accounts/abi/bind" | ||
"github.com/ethereum/go-ethereum/accounts/external" | ||
"github.com/ethereum/go-ethereum/cmd/utils" | ||
"github.com/ethereum/go-ethereum/common" | ||
"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" | ||
"github.com/urfave/cli/v2" | ||
) | ||
|
||
// newClient creates a client with specified remote URL. | ||
func newClient(ctx *cli.Context) *ethclient.Client { | ||
client, err := ethclient.Dial(ctx.String(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.IsSet(indexFlag.Name) { | ||
var result [3]string | ||
index := uint64(ctx.Int64(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 | ||
} | ||
|
||
// newClefSigner sets up a clef backend and returns a clef transaction signer. | ||
func newClefSigner(ctx *cli.Context) *bind.TransactOpts { | ||
clef, err := external.NewExternalSigner(ctx.String(clefURLFlag.Name)) | ||
if err != nil { | ||
utils.Fatalf("Failed to create clef signer %v", err) | ||
} | ||
return bind.NewClefTransactor(clef, accounts.Account{Address: common.HexToAddress(ctx.String(signerFlag.Name))}) | ||
} |
Oops, something went wrong.