mirror of
https://github.com/tendermint/tendermint.git
synced 2025-12-23 06:15:19 +00:00
light: rename lite2 to light & remove lite (#4946)
This PR removes lite & renames lite2 to light throughout the repo Signed-off-by: Marko Baricevic <marbar3778@yahoo.com> Closes: #4944
This commit is contained in:
@@ -20,6 +20,7 @@ Friendly reminder, we have a [bug bounty program](https://hackerone.com/tendermi
|
||||
- sr25519: type `PrivKeySr25519` is now `PrivKey`
|
||||
- sr25519: type `PubKeySr25519` is now `PubKey`
|
||||
- multisig: type `PubKeyMultisigThreshold` is now `PubKey`
|
||||
- [light] \#4946 Rename `lite2` pkg to `light`, the lite cmd has also been renamed to `light`. Remove `lite` implementation.
|
||||
|
||||
- Apps
|
||||
|
||||
|
||||
@@ -14,17 +14,17 @@ import (
|
||||
|
||||
"github.com/tendermint/tendermint/libs/log"
|
||||
tmos "github.com/tendermint/tendermint/libs/os"
|
||||
lite "github.com/tendermint/tendermint/lite2"
|
||||
lproxy "github.com/tendermint/tendermint/lite2/proxy"
|
||||
lrpc "github.com/tendermint/tendermint/lite2/rpc"
|
||||
dbs "github.com/tendermint/tendermint/lite2/store/db"
|
||||
"github.com/tendermint/tendermint/light"
|
||||
lproxy "github.com/tendermint/tendermint/light/proxy"
|
||||
lrpc "github.com/tendermint/tendermint/light/rpc"
|
||||
dbs "github.com/tendermint/tendermint/light/store/db"
|
||||
rpchttp "github.com/tendermint/tendermint/rpc/client/http"
|
||||
rpcserver "github.com/tendermint/tendermint/rpc/jsonrpc/server"
|
||||
)
|
||||
|
||||
// LiteCmd represents the base command when called without any subcommands
|
||||
var LiteCmd = &cobra.Command{
|
||||
Use: "lite [chainID]",
|
||||
// LightCmd represents the base command when called without any subcommands
|
||||
var LightCmd = &cobra.Command{
|
||||
Use: "light [chainID]",
|
||||
Short: "Run a light client proxy server, verifying Tendermint rpc",
|
||||
Long: `Run a light client proxy server, verifying Tendermint rpc.
|
||||
|
||||
@@ -36,16 +36,16 @@ Example:
|
||||
|
||||
start a fresh instance:
|
||||
|
||||
lite cosmoshub-3 -p http://52.57.29.196:26657 -w http://public-seed-node.cosmoshub.certus.one:26657
|
||||
light cosmoshub-3 -p http://52.57.29.196:26657 -w http://public-seed-node.cosmoshub.certus.one:26657
|
||||
--height 962118 --hash 28B97BE9F6DE51AC69F70E0B7BFD7E5C9CD1A595B7DC31AFF27C50D4948020CD
|
||||
|
||||
continue from latest state:
|
||||
|
||||
lite cosmoshub-3 -p http://52.57.29.196:26657 -w http://public-seed-node.cosmoshub.certus.one:26657
|
||||
light cosmoshub-3 -p http://52.57.29.196:26657 -w http://public-seed-node.cosmoshub.certus.one:26657
|
||||
`,
|
||||
RunE: runProxy,
|
||||
Args: cobra.ExactArgs(1),
|
||||
Example: `lite cosmoshub-3 -p http://52.57.29.196:26657 -w http://public-seed-node.cosmoshub.certus.one:26657
|
||||
Example: `light cosmoshub-3 -p http://52.57.29.196:26657 -w http://public-seed-node.cosmoshub.certus.one:26657
|
||||
--height 962118 --hash 28B97BE9F6DE51AC69F70E0B7BFD7E5C9CD1A595B7DC31AFF27C50D4948020CD`,
|
||||
}
|
||||
|
||||
@@ -65,23 +65,23 @@ var (
|
||||
)
|
||||
|
||||
func init() {
|
||||
LiteCmd.Flags().StringVar(&listenAddr, "laddr", "tcp://localhost:8888",
|
||||
LightCmd.Flags().StringVar(&listenAddr, "laddr", "tcp://localhost:8888",
|
||||
"Serve the proxy on the given address")
|
||||
LiteCmd.Flags().StringVarP(&primaryAddr, "primary", "p", "",
|
||||
LightCmd.Flags().StringVarP(&primaryAddr, "primary", "p", "",
|
||||
"Connect to a Tendermint node at this address")
|
||||
LiteCmd.Flags().StringVarP(&witnessAddrsJoined, "witnesses", "w", "",
|
||||
LightCmd.Flags().StringVarP(&witnessAddrsJoined, "witnesses", "w", "",
|
||||
"Tendermint nodes to cross-check the primary node, comma-separated")
|
||||
LiteCmd.Flags().StringVar(&home, "home-dir", ".tendermint-lite", "Specify the home directory")
|
||||
LiteCmd.Flags().IntVar(
|
||||
LightCmd.Flags().StringVar(&home, "home-dir", ".tendermint-light", "Specify the home directory")
|
||||
LightCmd.Flags().IntVar(
|
||||
&maxOpenConnections,
|
||||
"max-open-connections",
|
||||
900,
|
||||
"Maximum number of simultaneous connections (including WebSocket).")
|
||||
LiteCmd.Flags().DurationVar(&trustingPeriod, "trusting-period", 168*time.Hour,
|
||||
LightCmd.Flags().DurationVar(&trustingPeriod, "trusting-period", 168*time.Hour,
|
||||
"Trusting period. Should be significantly less than the unbonding period")
|
||||
LiteCmd.Flags().Int64Var(&trustedHeight, "height", 1, "Trusted header's height")
|
||||
LiteCmd.Flags().BytesHexVar(&trustedHash, "hash", []byte{}, "Trusted header's hash")
|
||||
LiteCmd.Flags().BoolVar(&verbose, "verbose", false, "Verbose output")
|
||||
LightCmd.Flags().Int64Var(&trustedHeight, "height", 1, "Trusted header's height")
|
||||
LightCmd.Flags().BytesHexVar(&trustedHash, "hash", []byte{}, "Trusted header's hash")
|
||||
LightCmd.Flags().BoolVar(&verbose, "verbose", false, "Verbose output")
|
||||
}
|
||||
|
||||
func runProxy(cmd *cobra.Command, args []string) error {
|
||||
@@ -100,16 +100,16 @@ func runProxy(cmd *cobra.Command, args []string) error {
|
||||
|
||||
witnessesAddrs := strings.Split(witnessAddrsJoined, ",")
|
||||
|
||||
db, err := dbm.NewGoLevelDB("lite-client-db", home)
|
||||
db, err := dbm.NewGoLevelDB("light-client-db", home)
|
||||
if err != nil {
|
||||
return fmt.Errorf("can't create a db: %w", err)
|
||||
}
|
||||
|
||||
var c *lite.Client
|
||||
var c *light.Client
|
||||
if trustedHeight > 0 && len(trustedHash) > 0 { // fresh installation
|
||||
c, err = lite.NewHTTPClient(
|
||||
c, err = light.NewHTTPClient(
|
||||
chainID,
|
||||
lite.TrustOptions{
|
||||
light.TrustOptions{
|
||||
Period: trustingPeriod,
|
||||
Height: trustedHeight,
|
||||
Hash: trustedHash,
|
||||
@@ -117,16 +117,16 @@ func runProxy(cmd *cobra.Command, args []string) error {
|
||||
primaryAddr,
|
||||
witnessesAddrs,
|
||||
dbs.New(db, chainID),
|
||||
lite.Logger(logger),
|
||||
light.Logger(logger),
|
||||
)
|
||||
} else { // continue from latest state
|
||||
c, err = lite.NewHTTPClientFromTrustedStore(
|
||||
c, err = light.NewHTTPClientFromTrustedStore(
|
||||
chainID,
|
||||
trustingPeriod,
|
||||
primaryAddr,
|
||||
witnessesAddrs,
|
||||
dbs.New(db, chainID),
|
||||
lite.Logger(logger),
|
||||
light.Logger(logger),
|
||||
)
|
||||
}
|
||||
if err != nil {
|
||||
|
||||
@@ -17,7 +17,7 @@ func main() {
|
||||
cmd.GenValidatorCmd,
|
||||
cmd.InitFilesCmd,
|
||||
cmd.ProbeUpnpCmd,
|
||||
cmd.LiteCmd,
|
||||
cmd.LightCmd,
|
||||
cmd.ReplayCmd,
|
||||
cmd.ReplayConsoleCmd,
|
||||
cmd.ResetAllCmd,
|
||||
|
||||
@@ -316,7 +316,7 @@ field can be left empty. Tendermint core will take care of updating the
|
||||
validator set. Note the change in voting power must be strictly less than 1/3
|
||||
per block if you want a light client to be able to prove the transition
|
||||
externally. See the [light client
|
||||
docs](https://godoc.org/github.com/tendermint/tendermint/lite#hdr-How_We_Track_Validators)
|
||||
docs](https://godoc.org/github.com/tendermint/tendermint/light#hdr-How_We_Track_Validators)
|
||||
for details on how it tracks validators.
|
||||
|
||||
In go:
|
||||
|
||||
@@ -288,7 +288,7 @@ the application's `Query` method.
|
||||
|
||||
Applications are free to provide their own APIs. But by using Tendermint Core
|
||||
as a proxy, clients (including [light client
|
||||
package](https://godoc.org/github.com/tendermint/tendermint/lite2)) can leverage
|
||||
package](https://godoc.org/github.com/tendermint/tendermint/light)) can leverage
|
||||
the unified API across different applications. Plus they won't have to call the
|
||||
otherwise separate Tendermint Core API for additional proofs.
|
||||
|
||||
|
||||
@@ -430,7 +430,7 @@ the application's `Query` method.
|
||||
|
||||
Applications are free to provide their own APIs. But by using Tendermint Core
|
||||
as a proxy, clients (including [light client
|
||||
package](https://godoc.org/github.com/tendermint/tendermint/lite)) can leverage
|
||||
package](https://godoc.org/github.com/tendermint/tendermint/light)) can leverage
|
||||
the unified API across different applications. Plus they won't have to call the
|
||||
otherwise separate Tendermint Core API for additional proofs.
|
||||
|
||||
|
||||
@@ -410,7 +410,7 @@ the application's `Query` method.
|
||||
|
||||
Applications are free to provide their own APIs. But by using Tendermint Core
|
||||
as a proxy, clients (including [light client
|
||||
package](https://godoc.org/github.com/tendermint/tendermint/lite)) can leverage
|
||||
package](https://godoc.org/github.com/tendermint/tendermint/light)) can leverage
|
||||
the unified API across different applications. Plus they won't have to call the
|
||||
otherwise separate Tendermint Core API for additional proofs.
|
||||
|
||||
|
||||
@@ -361,7 +361,7 @@ Note after the block H, Tendermint creates something we call a "proof block"
|
||||
(only if the application hash changed) H+1. The reason for this is to support
|
||||
proofs. If you have a transaction in block H that changes the state to X, the
|
||||
new application hash will only be included in block H+1. If after your
|
||||
transaction is committed, you want to get a lite-client proof for the new state
|
||||
transaction is committed, you want to get a light-client proof for the new state
|
||||
(X), you need the new block to be committed in order to do that because the new
|
||||
block has the new application hash for the state X. That's why we make a new
|
||||
(empty) block if the application hash changes. Otherwise, you won't be able to
|
||||
|
||||
@@ -8,8 +8,8 @@ Light clients are an important part of the complete blockchain system for most
|
||||
applications. Tendermint provides unique speed and security properties for
|
||||
light client applications.
|
||||
|
||||
See our [lite
|
||||
package](https://pkg.go.dev/github.com/tendermint/tendermint/lite2?tab=doc).
|
||||
See our [light
|
||||
package](https://pkg.go.dev/github.com/tendermint/tendermint/light?tab=doc).
|
||||
|
||||
## Overview
|
||||
|
||||
@@ -33,7 +33,7 @@ proofs](https://github.com/tendermint/spec/blob/953523c3cb99fdb8c8f7a2d21e3a9909
|
||||
|
||||
## Where to obtain trusted height & hash?
|
||||
|
||||
https://pkg.go.dev/github.com/tendermint/tendermint/lite2?tab=doc#TrustOptions
|
||||
https://pkg.go.dev/github.com/tendermint/tendermint/light?tab=doc#TrustOptions
|
||||
|
||||
One way to obtain semi-trusted hash & height is to query multiple full nodes
|
||||
and compare their hashes:
|
||||
@@ -48,16 +48,16 @@ $ curl -s https://233.123.0.140:26657:26657/commit | jq "{height: .result.signed
|
||||
|
||||
## HTTP proxy
|
||||
|
||||
Tendermint comes with a built-in `tendermint lite` command, which can be used
|
||||
Tendermint comes with a built-in `tendermint light` command, which can be used
|
||||
to run a light client proxy server, verifying Tendermint rpc. All calls that
|
||||
can be tracked back to a block header by a proof will be verified before
|
||||
passing them back to the caller. Other than that, it will present the same
|
||||
interface as a full Tendermint node.
|
||||
|
||||
```sh
|
||||
$ tendermint lite supernova -p tcp://233.123.0.140:26657 \
|
||||
$ tendermint light supernova -p tcp://233.123.0.140:26657 \
|
||||
-w tcp://179.63.29.15:26657,tcp://144.165.223.135:26657 \
|
||||
--height=10 --hash=37E9A6DD3FA25E83B22C18835401E8E56088D0D7ABC6FD99FCDC920DD76C1C57
|
||||
```
|
||||
|
||||
For additional options, run `tendermint lite --help`.
|
||||
For additional options, run `tendermint light --help`.
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package lite
|
||||
package light
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
@@ -10,8 +10,8 @@ import (
|
||||
|
||||
"github.com/tendermint/tendermint/libs/log"
|
||||
tmmath "github.com/tendermint/tendermint/libs/math"
|
||||
"github.com/tendermint/tendermint/lite2/provider"
|
||||
"github.com/tendermint/tendermint/lite2/store"
|
||||
"github.com/tendermint/tendermint/light/provider"
|
||||
"github.com/tendermint/tendermint/light/store"
|
||||
"github.com/tendermint/tendermint/types"
|
||||
)
|
||||
|
||||
@@ -68,7 +68,7 @@ func SkippingVerification(trustLevel tmmath.Fraction) Option {
|
||||
// that the light client stores. When Prune() is run, all headers (along with
|
||||
// the associated validator sets) that are earlier than the h amount of headers
|
||||
// will be removed from the store. Default: 1000. A pruning size of 0 will not
|
||||
// prune the lite client at all.
|
||||
// prune the light client at all.
|
||||
func PruningSize(h uint16) Option {
|
||||
return func(c *Client) {
|
||||
c.pruningSize = h
|
||||
@@ -120,7 +120,7 @@ type Client struct {
|
||||
maxRetryAttempts uint16 // see MaxRetryAttempts option
|
||||
maxClockDrift time.Duration
|
||||
|
||||
// Mutex for locking during changes of the lite clients providers
|
||||
// Mutex for locking during changes of the light clients providers
|
||||
providerMutex sync.Mutex
|
||||
// Primary provider of new headers.
|
||||
primary provider.Provider
|
||||
@@ -1,4 +1,4 @@
|
||||
package lite_test
|
||||
package light_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
@@ -7,10 +7,10 @@ import (
|
||||
dbm "github.com/tendermint/tm-db"
|
||||
|
||||
"github.com/tendermint/tendermint/libs/log"
|
||||
lite "github.com/tendermint/tendermint/lite2"
|
||||
"github.com/tendermint/tendermint/lite2/provider"
|
||||
mockp "github.com/tendermint/tendermint/lite2/provider/mock"
|
||||
dbs "github.com/tendermint/tendermint/lite2/store/db"
|
||||
"github.com/tendermint/tendermint/light"
|
||||
"github.com/tendermint/tendermint/light/provider"
|
||||
mockp "github.com/tendermint/tendermint/light/provider/mock"
|
||||
dbs "github.com/tendermint/tendermint/light/store/db"
|
||||
)
|
||||
|
||||
// NOTE: block is produced every minute. Make sure the verification time
|
||||
@@ -26,9 +26,9 @@ var (
|
||||
)
|
||||
|
||||
func BenchmarkSequence(b *testing.B) {
|
||||
c, err := lite.NewClient(
|
||||
c, err := light.NewClient(
|
||||
chainID,
|
||||
lite.TrustOptions{
|
||||
light.TrustOptions{
|
||||
Period: 24 * time.Hour,
|
||||
Height: 1,
|
||||
Hash: genesisHeader.Hash(),
|
||||
@@ -36,8 +36,8 @@ func BenchmarkSequence(b *testing.B) {
|
||||
benchmarkFullNode,
|
||||
[]provider.Provider{benchmarkFullNode},
|
||||
dbs.New(dbm.NewMemDB(), chainID),
|
||||
lite.Logger(log.TestingLogger()),
|
||||
lite.SequentialVerification(),
|
||||
light.Logger(log.TestingLogger()),
|
||||
light.SequentialVerification(),
|
||||
)
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
@@ -53,9 +53,9 @@ func BenchmarkSequence(b *testing.B) {
|
||||
}
|
||||
|
||||
func BenchmarkBisection(b *testing.B) {
|
||||
c, err := lite.NewClient(
|
||||
c, err := light.NewClient(
|
||||
chainID,
|
||||
lite.TrustOptions{
|
||||
light.TrustOptions{
|
||||
Period: 24 * time.Hour,
|
||||
Height: 1,
|
||||
Hash: genesisHeader.Hash(),
|
||||
@@ -63,7 +63,7 @@ func BenchmarkBisection(b *testing.B) {
|
||||
benchmarkFullNode,
|
||||
[]provider.Provider{benchmarkFullNode},
|
||||
dbs.New(dbm.NewMemDB(), chainID),
|
||||
lite.Logger(log.TestingLogger()),
|
||||
light.Logger(log.TestingLogger()),
|
||||
)
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
@@ -80,9 +80,9 @@ func BenchmarkBisection(b *testing.B) {
|
||||
|
||||
func BenchmarkBackwards(b *testing.B) {
|
||||
trustedHeader, _ := benchmarkFullNode.SignedHeader(0)
|
||||
c, err := lite.NewClient(
|
||||
c, err := light.NewClient(
|
||||
chainID,
|
||||
lite.TrustOptions{
|
||||
light.TrustOptions{
|
||||
Period: 24 * time.Hour,
|
||||
Height: trustedHeader.Height,
|
||||
Hash: trustedHeader.Hash(),
|
||||
@@ -90,7 +90,7 @@ func BenchmarkBackwards(b *testing.B) {
|
||||
benchmarkFullNode,
|
||||
[]provider.Provider{benchmarkFullNode},
|
||||
dbs.New(dbm.NewMemDB(), chainID),
|
||||
lite.Logger(log.TestingLogger()),
|
||||
light.Logger(log.TestingLogger()),
|
||||
)
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
@@ -1,4 +1,4 @@
|
||||
package lite_test
|
||||
package light_test
|
||||
|
||||
import (
|
||||
"sync"
|
||||
@@ -11,10 +11,10 @@ import (
|
||||
dbm "github.com/tendermint/tm-db"
|
||||
|
||||
"github.com/tendermint/tendermint/libs/log"
|
||||
lite "github.com/tendermint/tendermint/lite2"
|
||||
"github.com/tendermint/tendermint/lite2/provider"
|
||||
mockp "github.com/tendermint/tendermint/lite2/provider/mock"
|
||||
dbs "github.com/tendermint/tendermint/lite2/store/db"
|
||||
"github.com/tendermint/tendermint/light"
|
||||
"github.com/tendermint/tendermint/light/provider"
|
||||
mockp "github.com/tendermint/tendermint/light/provider/mock"
|
||||
dbs "github.com/tendermint/tendermint/light/store/db"
|
||||
"github.com/tendermint/tendermint/types"
|
||||
)
|
||||
|
||||
@@ -35,7 +35,7 @@ var (
|
||||
h3 = keys.GenSignedHeaderLastBlockID(chainID, 3, bTime.Add(1*time.Hour), nil, vals, vals,
|
||||
hash("app_hash"), hash("cons_hash"), hash("results_hash"), 0, len(keys), types.BlockID{Hash: h2.Hash()})
|
||||
trustPeriod = 4 * time.Hour
|
||||
trustOptions = lite.TrustOptions{
|
||||
trustOptions = light.TrustOptions{
|
||||
Period: 4 * time.Hour,
|
||||
Height: 1,
|
||||
Hash: h1.Hash(),
|
||||
@@ -141,7 +141,7 @@ func TestClient_SequentialVerification(t *testing.T) {
|
||||
for _, tc := range testCases {
|
||||
tc := tc
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
c, err := lite.NewClient(
|
||||
c, err := light.NewClient(
|
||||
chainID,
|
||||
trustOptions,
|
||||
mockp.New(
|
||||
@@ -155,7 +155,7 @@ func TestClient_SequentialVerification(t *testing.T) {
|
||||
tc.vals,
|
||||
)},
|
||||
dbs.New(dbm.NewMemDB(), chainID),
|
||||
lite.SequentialVerification(),
|
||||
light.SequentialVerification(),
|
||||
)
|
||||
|
||||
if tc.initErr {
|
||||
@@ -264,7 +264,7 @@ func TestClient_SkippingVerification(t *testing.T) {
|
||||
for _, tc := range testCases {
|
||||
tc := tc
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
c, err := lite.NewClient(
|
||||
c, err := light.NewClient(
|
||||
chainID,
|
||||
trustOptions,
|
||||
mockp.New(
|
||||
@@ -278,7 +278,7 @@ func TestClient_SkippingVerification(t *testing.T) {
|
||||
tc.vals,
|
||||
)},
|
||||
dbs.New(dbm.NewMemDB(), chainID),
|
||||
lite.SkippingVerification(lite.DefaultTrustLevel),
|
||||
light.SkippingVerification(light.DefaultTrustLevel),
|
||||
)
|
||||
if tc.initErr {
|
||||
require.Error(t, err)
|
||||
@@ -301,9 +301,9 @@ func TestClient_SkippingVerification(t *testing.T) {
|
||||
veryLargeFullNode := mockp.New(GenMockNode(chainID, 100, 3, 1, bTime))
|
||||
h1, err := veryLargeFullNode.SignedHeader(90)
|
||||
require.NoError(t, err)
|
||||
c, err := lite.NewClient(
|
||||
c, err := light.NewClient(
|
||||
chainID,
|
||||
lite.TrustOptions{
|
||||
light.TrustOptions{
|
||||
Period: 4 * time.Hour,
|
||||
Height: 90,
|
||||
Hash: h1.Hash(),
|
||||
@@ -311,7 +311,7 @@ func TestClient_SkippingVerification(t *testing.T) {
|
||||
veryLargeFullNode,
|
||||
[]provider.Provider{veryLargeFullNode},
|
||||
dbs.New(dbm.NewMemDB(), chainID),
|
||||
lite.SkippingVerification(lite.DefaultTrustLevel),
|
||||
light.SkippingVerification(light.DefaultTrustLevel),
|
||||
)
|
||||
require.NoError(t, err)
|
||||
h, err := c.Update(bTime.Add(100 * time.Minute))
|
||||
@@ -322,13 +322,13 @@ func TestClient_SkippingVerification(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestClient_Cleanup(t *testing.T) {
|
||||
c, err := lite.NewClient(
|
||||
c, err := light.NewClient(
|
||||
chainID,
|
||||
trustOptions,
|
||||
fullNode,
|
||||
[]provider.Provider{fullNode},
|
||||
dbs.New(dbm.NewMemDB(), chainID),
|
||||
lite.Logger(log.TestingLogger()),
|
||||
light.Logger(log.TestingLogger()),
|
||||
)
|
||||
require.NoError(t, err)
|
||||
_, err = c.TrustedHeader(1)
|
||||
@@ -355,13 +355,13 @@ func TestClientRestoresTrustedHeaderAfterStartup1(t *testing.T) {
|
||||
err := trustedStore.SaveSignedHeaderAndValidatorSet(h1, vals)
|
||||
require.NoError(t, err)
|
||||
|
||||
c, err := lite.NewClient(
|
||||
c, err := light.NewClient(
|
||||
chainID,
|
||||
trustOptions,
|
||||
fullNode,
|
||||
[]provider.Provider{fullNode},
|
||||
trustedStore,
|
||||
lite.Logger(log.TestingLogger()),
|
||||
light.Logger(log.TestingLogger()),
|
||||
)
|
||||
require.NoError(t, err)
|
||||
|
||||
@@ -397,9 +397,9 @@ func TestClientRestoresTrustedHeaderAfterStartup1(t *testing.T) {
|
||||
valSet,
|
||||
)
|
||||
|
||||
c, err := lite.NewClient(
|
||||
c, err := light.NewClient(
|
||||
chainID,
|
||||
lite.TrustOptions{
|
||||
light.TrustOptions{
|
||||
Period: 4 * time.Hour,
|
||||
Height: 1,
|
||||
Hash: header1.Hash(),
|
||||
@@ -407,7 +407,7 @@ func TestClientRestoresTrustedHeaderAfterStartup1(t *testing.T) {
|
||||
primary,
|
||||
[]provider.Provider{primary},
|
||||
trustedStore,
|
||||
lite.Logger(log.TestingLogger()),
|
||||
light.Logger(log.TestingLogger()),
|
||||
)
|
||||
require.NoError(t, err)
|
||||
|
||||
@@ -434,9 +434,9 @@ func TestClientRestoresTrustedHeaderAfterStartup2(t *testing.T) {
|
||||
err := trustedStore.SaveSignedHeaderAndValidatorSet(h1, vals)
|
||||
require.NoError(t, err)
|
||||
|
||||
c, err := lite.NewClient(
|
||||
c, err := light.NewClient(
|
||||
chainID,
|
||||
lite.TrustOptions{
|
||||
light.TrustOptions{
|
||||
Period: 4 * time.Hour,
|
||||
Height: 2,
|
||||
Hash: h2.Hash(),
|
||||
@@ -444,7 +444,7 @@ func TestClientRestoresTrustedHeaderAfterStartup2(t *testing.T) {
|
||||
fullNode,
|
||||
[]provider.Provider{fullNode},
|
||||
trustedStore,
|
||||
lite.Logger(log.TestingLogger()),
|
||||
light.Logger(log.TestingLogger()),
|
||||
)
|
||||
require.NoError(t, err)
|
||||
|
||||
@@ -485,9 +485,9 @@ func TestClientRestoresTrustedHeaderAfterStartup2(t *testing.T) {
|
||||
valSet,
|
||||
)
|
||||
|
||||
c, err := lite.NewClient(
|
||||
c, err := light.NewClient(
|
||||
chainID,
|
||||
lite.TrustOptions{
|
||||
light.TrustOptions{
|
||||
Period: 4 * time.Hour,
|
||||
Height: 2,
|
||||
Hash: diffHeader2.Hash(),
|
||||
@@ -495,7 +495,7 @@ func TestClientRestoresTrustedHeaderAfterStartup2(t *testing.T) {
|
||||
primary,
|
||||
[]provider.Provider{primary},
|
||||
trustedStore,
|
||||
lite.Logger(log.TestingLogger()),
|
||||
light.Logger(log.TestingLogger()),
|
||||
)
|
||||
require.NoError(t, err)
|
||||
|
||||
@@ -523,13 +523,13 @@ func TestClientRestoresTrustedHeaderAfterStartup3(t *testing.T) {
|
||||
err = trustedStore.SaveSignedHeaderAndValidatorSet(h2, vals)
|
||||
require.NoError(t, err)
|
||||
|
||||
c, err := lite.NewClient(
|
||||
c, err := light.NewClient(
|
||||
chainID,
|
||||
trustOptions,
|
||||
fullNode,
|
||||
[]provider.Provider{fullNode},
|
||||
trustedStore,
|
||||
lite.Logger(log.TestingLogger()),
|
||||
light.Logger(log.TestingLogger()),
|
||||
)
|
||||
require.NoError(t, err)
|
||||
|
||||
@@ -580,9 +580,9 @@ func TestClientRestoresTrustedHeaderAfterStartup3(t *testing.T) {
|
||||
valSet,
|
||||
)
|
||||
|
||||
c, err := lite.NewClient(
|
||||
c, err := light.NewClient(
|
||||
chainID,
|
||||
lite.TrustOptions{
|
||||
light.TrustOptions{
|
||||
Period: 4 * time.Hour,
|
||||
Height: 1,
|
||||
Hash: header1.Hash(),
|
||||
@@ -590,7 +590,7 @@ func TestClientRestoresTrustedHeaderAfterStartup3(t *testing.T) {
|
||||
primary,
|
||||
[]provider.Provider{primary},
|
||||
trustedStore,
|
||||
lite.Logger(log.TestingLogger()),
|
||||
light.Logger(log.TestingLogger()),
|
||||
)
|
||||
require.NoError(t, err)
|
||||
|
||||
@@ -619,13 +619,13 @@ func TestClientRestoresTrustedHeaderAfterStartup3(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestClient_Update(t *testing.T) {
|
||||
c, err := lite.NewClient(
|
||||
c, err := light.NewClient(
|
||||
chainID,
|
||||
trustOptions,
|
||||
fullNode,
|
||||
[]provider.Provider{fullNode},
|
||||
dbs.New(dbm.NewMemDB(), chainID),
|
||||
lite.Logger(log.TestingLogger()),
|
||||
light.Logger(log.TestingLogger()),
|
||||
)
|
||||
require.NoError(t, err)
|
||||
|
||||
@@ -644,13 +644,13 @@ func TestClient_Update(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestClient_Concurrency(t *testing.T) {
|
||||
c, err := lite.NewClient(
|
||||
c, err := light.NewClient(
|
||||
chainID,
|
||||
trustOptions,
|
||||
fullNode,
|
||||
[]provider.Provider{fullNode},
|
||||
dbs.New(dbm.NewMemDB(), chainID),
|
||||
lite.Logger(log.TestingLogger()),
|
||||
light.Logger(log.TestingLogger()),
|
||||
)
|
||||
require.NoError(t, err)
|
||||
|
||||
@@ -688,14 +688,14 @@ func TestClient_Concurrency(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestClientReplacesPrimaryWithWitnessIfPrimaryIsUnavailable(t *testing.T) {
|
||||
c, err := lite.NewClient(
|
||||
c, err := light.NewClient(
|
||||
chainID,
|
||||
trustOptions,
|
||||
deadNode,
|
||||
[]provider.Provider{fullNode, fullNode},
|
||||
dbs.New(dbm.NewMemDB(), chainID),
|
||||
lite.Logger(log.TestingLogger()),
|
||||
lite.MaxRetryAttempts(1),
|
||||
light.Logger(log.TestingLogger()),
|
||||
light.MaxRetryAttempts(1),
|
||||
)
|
||||
|
||||
require.NoError(t, err)
|
||||
@@ -709,9 +709,9 @@ func TestClientReplacesPrimaryWithWitnessIfPrimaryIsUnavailable(t *testing.T) {
|
||||
func TestClient_BackwardsVerification(t *testing.T) {
|
||||
{
|
||||
trustHeader, _ := largeFullNode.SignedHeader(6)
|
||||
c, err := lite.NewClient(
|
||||
c, err := light.NewClient(
|
||||
chainID,
|
||||
lite.TrustOptions{
|
||||
light.TrustOptions{
|
||||
Period: 4 * time.Minute,
|
||||
Height: trustHeader.Height,
|
||||
Hash: trustHeader.Hash(),
|
||||
@@ -719,7 +719,7 @@ func TestClient_BackwardsVerification(t *testing.T) {
|
||||
largeFullNode,
|
||||
[]provider.Provider{largeFullNode},
|
||||
dbs.New(dbm.NewMemDB(), chainID),
|
||||
lite.Logger(log.TestingLogger()),
|
||||
light.Logger(log.TestingLogger()),
|
||||
)
|
||||
require.NoError(t, err)
|
||||
|
||||
@@ -794,9 +794,9 @@ func TestClient_BackwardsVerification(t *testing.T) {
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
c, err := lite.NewClient(
|
||||
c, err := light.NewClient(
|
||||
chainID,
|
||||
lite.TrustOptions{
|
||||
light.TrustOptions{
|
||||
Period: 1 * time.Hour,
|
||||
Height: 3,
|
||||
Hash: h3.Hash(),
|
||||
@@ -804,7 +804,7 @@ func TestClient_BackwardsVerification(t *testing.T) {
|
||||
tc.provider,
|
||||
[]provider.Provider{tc.provider},
|
||||
dbs.New(dbm.NewMemDB(), chainID),
|
||||
lite.Logger(log.TestingLogger()),
|
||||
light.Logger(log.TestingLogger()),
|
||||
)
|
||||
require.NoError(t, err)
|
||||
|
||||
@@ -820,7 +820,7 @@ func TestClient_NewClientFromTrustedStore(t *testing.T) {
|
||||
err := db.SaveSignedHeaderAndValidatorSet(h1, vals)
|
||||
require.NoError(t, err)
|
||||
|
||||
c, err := lite.NewClientFromTrustedStore(
|
||||
c, err := light.NewClientFromTrustedStore(
|
||||
chainID,
|
||||
trustPeriod,
|
||||
deadNode,
|
||||
@@ -844,14 +844,14 @@ func TestClient_NewClientFromTrustedStore(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestNewClientErrorsIfAllWitnessesUnavailable(t *testing.T) {
|
||||
_, err := lite.NewClient(
|
||||
_, err := light.NewClient(
|
||||
chainID,
|
||||
trustOptions,
|
||||
fullNode,
|
||||
[]provider.Provider{deadNode, deadNode},
|
||||
dbs.New(dbm.NewMemDB(), chainID),
|
||||
lite.Logger(log.TestingLogger()),
|
||||
lite.MaxRetryAttempts(1),
|
||||
light.Logger(log.TestingLogger()),
|
||||
light.MaxRetryAttempts(1),
|
||||
)
|
||||
if assert.Error(t, err) {
|
||||
assert.Contains(t, err.Error(), "awaiting response from all witnesses exceeded dropout time")
|
||||
@@ -887,14 +887,14 @@ func TestClientRemovesWitnessIfItSendsUsIncorrectHeader(t *testing.T) {
|
||||
},
|
||||
)
|
||||
|
||||
c, err := lite.NewClient(
|
||||
c, err := light.NewClient(
|
||||
chainID,
|
||||
trustOptions,
|
||||
fullNode,
|
||||
[]provider.Provider{badProvider1, badProvider2},
|
||||
dbs.New(dbm.NewMemDB(), chainID),
|
||||
lite.Logger(log.TestingLogger()),
|
||||
lite.MaxRetryAttempts(1),
|
||||
light.Logger(log.TestingLogger()),
|
||||
light.MaxRetryAttempts(1),
|
||||
)
|
||||
// witness should have behaved properly -> no error
|
||||
require.NoError(t, err)
|
||||
@@ -914,13 +914,13 @@ func TestClientRemovesWitnessIfItSendsUsIncorrectHeader(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestClientTrustedValidatorSet(t *testing.T) {
|
||||
c, err := lite.NewClient(
|
||||
c, err := light.NewClient(
|
||||
chainID,
|
||||
trustOptions,
|
||||
fullNode,
|
||||
[]provider.Provider{fullNode},
|
||||
dbs.New(dbm.NewMemDB(), chainID),
|
||||
lite.Logger(log.TestingLogger()),
|
||||
light.Logger(log.TestingLogger()),
|
||||
)
|
||||
|
||||
require.NoError(t, err)
|
||||
@@ -951,14 +951,14 @@ func TestClientReportsConflictingHeadersEvidence(t *testing.T) {
|
||||
},
|
||||
)
|
||||
|
||||
c, err := lite.NewClient(
|
||||
c, err := light.NewClient(
|
||||
chainID,
|
||||
trustOptions,
|
||||
fullNode,
|
||||
[]provider.Provider{fullNode2},
|
||||
dbs.New(dbm.NewMemDB(), chainID),
|
||||
lite.Logger(log.TestingLogger()),
|
||||
lite.MaxRetryAttempts(1),
|
||||
light.Logger(log.TestingLogger()),
|
||||
light.MaxRetryAttempts(1),
|
||||
)
|
||||
require.NoError(t, err)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
Package lite provides a light client implementation.
|
||||
package light provides a light client implementation.
|
||||
|
||||
The concept of light clients was introduced in the Bitcoin white paper. It
|
||||
describes a watcher of distributed consensus process that only validates the
|
||||
@@ -14,7 +14,7 @@ chain's state machine).
|
||||
|
||||
In a network that is expected to reliably punish validators for misbehavior by
|
||||
slashing bonded stake and where the validator set changes infrequently, clients
|
||||
can take advantage of this assumption to safely synchronize a lite client
|
||||
can take advantage of this assumption to safely synchronize a light client
|
||||
without downloading the intervening headers.
|
||||
|
||||
Light clients (and full nodes) operating in the Proof Of Stake context need a
|
||||
@@ -41,15 +41,15 @@ precommit-vote signatures to prove its validity (> 2/3 of the voting power)
|
||||
given the validator set responsible for signing that header.
|
||||
|
||||
The hash of the next validator set is included and signed in the SignedHeader.
|
||||
This lets the lite client keep track of arbitrary changes to the validator set,
|
||||
This lets the light client keep track of arbitrary changes to the validator set,
|
||||
as every change to the validator set must be approved by inclusion in the
|
||||
header and signed in the commit.
|
||||
|
||||
In the worst case, with every block changing the validators around completely,
|
||||
a lite client can sync up with every block header to verify each validator set
|
||||
a light client can sync up with every block header to verify each validator set
|
||||
change on the chain. In practice, most applications will not have frequent
|
||||
drastic updates to the validator set, so the logic defined in this package for
|
||||
lite client syncing is optimized to use intelligent bisection.
|
||||
light client syncing is optimized to use intelligent bisection.
|
||||
|
||||
# What this package provides
|
||||
|
||||
@@ -63,7 +63,7 @@ This package provides three major things:
|
||||
|
||||
Example usage:
|
||||
|
||||
db, err := dbm.NewGoLevelDB("lite-client-db", dbDir)
|
||||
db, err := dbm.NewGoLevelDB("light-client-db", dbDir)
|
||||
if err != nil {
|
||||
// handle error
|
||||
}
|
||||
@@ -124,4 +124,4 @@ Or see
|
||||
https://github.com/tendermint/spec/tree/master/spec/consensus/light-client
|
||||
for the full spec
|
||||
*/
|
||||
package lite
|
||||
package light
|
||||
@@ -1,10 +1,10 @@
|
||||
package lite
|
||||
package light
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/tendermint/tendermint/lite2/provider"
|
||||
"github.com/tendermint/tendermint/light/provider"
|
||||
"github.com/tendermint/tendermint/types"
|
||||
)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package lite_test
|
||||
package light_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
@@ -11,10 +11,10 @@ import (
|
||||
dbm "github.com/tendermint/tm-db"
|
||||
|
||||
"github.com/tendermint/tendermint/abci/example/kvstore"
|
||||
lite "github.com/tendermint/tendermint/lite2"
|
||||
"github.com/tendermint/tendermint/lite2/provider"
|
||||
httpp "github.com/tendermint/tendermint/lite2/provider/http"
|
||||
dbs "github.com/tendermint/tendermint/lite2/store/db"
|
||||
"github.com/tendermint/tendermint/light"
|
||||
"github.com/tendermint/tendermint/light/provider"
|
||||
httpp "github.com/tendermint/tendermint/light/provider/http"
|
||||
dbs "github.com/tendermint/tendermint/light/store/db"
|
||||
rpctest "github.com/tendermint/tendermint/rpc/test"
|
||||
)
|
||||
|
||||
@@ -23,7 +23,7 @@ func ExampleClient_Update() {
|
||||
// give Tendermint time to generate some blocks
|
||||
time.Sleep(5 * time.Second)
|
||||
|
||||
dbDir, err := ioutil.TempDir("", "lite-client-example")
|
||||
dbDir, err := ioutil.TempDir("", "light-client-example")
|
||||
if err != nil {
|
||||
stdlog.Fatal(err)
|
||||
}
|
||||
@@ -44,14 +44,14 @@ func ExampleClient_Update() {
|
||||
stdlog.Fatal(err)
|
||||
}
|
||||
|
||||
db, err := dbm.NewGoLevelDB("lite-client-db", dbDir)
|
||||
db, err := dbm.NewGoLevelDB("light-client-db", dbDir)
|
||||
if err != nil {
|
||||
stdlog.Fatal(err)
|
||||
}
|
||||
|
||||
c, err := lite.NewClient(
|
||||
c, err := light.NewClient(
|
||||
chainID,
|
||||
lite.TrustOptions{
|
||||
light.TrustOptions{
|
||||
Period: 504 * time.Hour, // 21 days
|
||||
Height: 2,
|
||||
Hash: header.Hash(),
|
||||
@@ -92,7 +92,7 @@ func ExampleClient_VerifyHeaderAtHeight() {
|
||||
// give Tendermint time to generate some blocks
|
||||
time.Sleep(5 * time.Second)
|
||||
|
||||
dbDir, err := ioutil.TempDir("", "lite-client-example")
|
||||
dbDir, err := ioutil.TempDir("", "light-client-example")
|
||||
if err != nil {
|
||||
stdlog.Fatal(err)
|
||||
}
|
||||
@@ -113,14 +113,14 @@ func ExampleClient_VerifyHeaderAtHeight() {
|
||||
stdlog.Fatal(err)
|
||||
}
|
||||
|
||||
db, err := dbm.NewGoLevelDB("lite-client-db", dbDir)
|
||||
db, err := dbm.NewGoLevelDB("light-client-db", dbDir)
|
||||
if err != nil {
|
||||
stdlog.Fatal(err)
|
||||
}
|
||||
|
||||
c, err := lite.NewClient(
|
||||
c, err := light.NewClient(
|
||||
chainID,
|
||||
lite.TrustOptions{
|
||||
light.TrustOptions{
|
||||
Period: 504 * time.Hour, // 21 days
|
||||
Height: 2,
|
||||
Hash: header.Hash(),
|
||||
@@ -1,4 +1,4 @@
|
||||
package lite_test
|
||||
package light_test
|
||||
|
||||
import (
|
||||
"time"
|
||||
@@ -6,7 +6,7 @@ import (
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"github.com/tendermint/tendermint/lite2/provider"
|
||||
"github.com/tendermint/tendermint/light/provider"
|
||||
rpcclient "github.com/tendermint/tendermint/rpc/client"
|
||||
rpchttp "github.com/tendermint/tendermint/rpc/client/http"
|
||||
"github.com/tendermint/tendermint/types"
|
||||
@@ -9,8 +9,8 @@ import (
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/tendermint/tendermint/abci/example/kvstore"
|
||||
"github.com/tendermint/tendermint/lite2/provider"
|
||||
litehttp "github.com/tendermint/tendermint/lite2/provider/http"
|
||||
"github.com/tendermint/tendermint/light/provider"
|
||||
lighthttp "github.com/tendermint/tendermint/light/provider/http"
|
||||
rpcclient "github.com/tendermint/tendermint/rpc/client"
|
||||
rpchttp "github.com/tendermint/tendermint/rpc/client/http"
|
||||
rpctest "github.com/tendermint/tendermint/rpc/test"
|
||||
@@ -18,15 +18,15 @@ import (
|
||||
)
|
||||
|
||||
func TestNewProvider(t *testing.T) {
|
||||
c, err := litehttp.New("chain-test", "192.168.0.1:26657")
|
||||
c, err := lighthttp.New("chain-test", "192.168.0.1:26657")
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, fmt.Sprintf("%s", c), "http{http://192.168.0.1:26657}")
|
||||
|
||||
c, err = litehttp.New("chain-test", "http://153.200.0.1:26657")
|
||||
c, err = lighthttp.New("chain-test", "http://153.200.0.1:26657")
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, fmt.Sprintf("%s", c), "http{http://153.200.0.1:26657}")
|
||||
|
||||
c, err = litehttp.New("chain-test", "153.200.0.1")
|
||||
c, err = lighthttp.New("chain-test", "153.200.0.1")
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, fmt.Sprintf("%s", c), "http{http://153.200.0.1}")
|
||||
}
|
||||
@@ -56,7 +56,7 @@ func TestProvider(t *testing.T) {
|
||||
c, err := rpchttp.New(rpcAddr, "/websocket")
|
||||
require.Nil(t, err)
|
||||
|
||||
p := litehttp.NewWithClient(chainID, c)
|
||||
p := lighthttp.NewWithClient(chainID, c)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, p)
|
||||
|
||||
@@ -3,7 +3,7 @@ package mock
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"github.com/tendermint/tendermint/lite2/provider"
|
||||
"github.com/tendermint/tendermint/light/provider"
|
||||
"github.com/tendermint/tendermint/types"
|
||||
)
|
||||
|
||||
@@ -4,7 +4,7 @@ import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/tendermint/tendermint/lite2/provider"
|
||||
"github.com/tendermint/tendermint/light/provider"
|
||||
"github.com/tendermint/tendermint/types"
|
||||
)
|
||||
|
||||
@@ -4,7 +4,7 @@ import (
|
||||
"github.com/tendermint/tendermint/types"
|
||||
)
|
||||
|
||||
// Provider provides information for the lite client to sync (verification
|
||||
// Provider provides information for the light client to sync (verification
|
||||
// happens in the client).
|
||||
type Provider interface {
|
||||
// ChainID returns the blockchain ID.
|
||||
@@ -10,7 +10,7 @@ import (
|
||||
|
||||
"github.com/tendermint/tendermint/libs/log"
|
||||
tmpubsub "github.com/tendermint/tendermint/libs/pubsub"
|
||||
lrpc "github.com/tendermint/tendermint/lite2/rpc"
|
||||
lrpc "github.com/tendermint/tendermint/light/rpc"
|
||||
ctypes "github.com/tendermint/tendermint/rpc/core/types"
|
||||
rpcserver "github.com/tendermint/tendermint/rpc/jsonrpc/server"
|
||||
)
|
||||
@@ -2,7 +2,7 @@ package proxy
|
||||
|
||||
import (
|
||||
"github.com/tendermint/tendermint/libs/bytes"
|
||||
lrpc "github.com/tendermint/tendermint/lite2/rpc"
|
||||
lrpc "github.com/tendermint/tendermint/light/rpc"
|
||||
ctypes "github.com/tendermint/tendermint/rpc/core/types"
|
||||
rpcserver "github.com/tendermint/tendermint/rpc/jsonrpc/server"
|
||||
rpctypes "github.com/tendermint/tendermint/rpc/jsonrpc/types"
|
||||
@@ -11,7 +11,7 @@ import (
|
||||
"github.com/tendermint/tendermint/crypto/merkle"
|
||||
tmbytes "github.com/tendermint/tendermint/libs/bytes"
|
||||
service "github.com/tendermint/tendermint/libs/service"
|
||||
lite "github.com/tendermint/tendermint/lite2"
|
||||
light "github.com/tendermint/tendermint/light"
|
||||
rpcclient "github.com/tendermint/tendermint/rpc/client"
|
||||
ctypes "github.com/tendermint/tendermint/rpc/core/types"
|
||||
rpctypes "github.com/tendermint/tendermint/rpc/jsonrpc/types"
|
||||
@@ -20,20 +20,20 @@ import (
|
||||
|
||||
var errNegOrZeroHeight = errors.New("negative or zero height")
|
||||
|
||||
// Client is an RPC client, which uses lite#Client to verify data (if it can be
|
||||
// Client is an RPC client, which uses light#Client to verify data (if it can be
|
||||
// proved!).
|
||||
type Client struct {
|
||||
service.BaseService
|
||||
|
||||
next rpcclient.Client
|
||||
lc *lite.Client
|
||||
lc *light.Client
|
||||
prt *merkle.ProofRuntime
|
||||
}
|
||||
|
||||
var _ rpcclient.Client = (*Client)(nil)
|
||||
|
||||
// NewClient returns a new client.
|
||||
func NewClient(next rpcclient.Client, lc *lite.Client) *Client {
|
||||
func NewClient(next rpcclient.Client, lc *light.Client) *Client {
|
||||
c := &Client{
|
||||
next: next,
|
||||
lc: lc,
|
||||
@@ -92,7 +92,7 @@ func (c *Client) ABCIQueryWithOptions(path string, data tmbytes.HexBytes,
|
||||
|
||||
// Update the light client if we're behind.
|
||||
// NOTE: AppHash for height H is in header H+1.
|
||||
h, err := c.updateLiteClientIfNeededTo(resp.Height + 1)
|
||||
h, err := c.updateLightClientIfNeededTo(resp.Height + 1)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -171,7 +171,7 @@ func (c *Client) ConsensusParams(height *int64) (*ctypes.ResultConsensusParams,
|
||||
}
|
||||
|
||||
// Update the light client if we're behind.
|
||||
h, err := c.updateLiteClientIfNeededTo(res.BlockHeight)
|
||||
h, err := c.updateLightClientIfNeededTo(res.BlockHeight)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -210,7 +210,7 @@ func (c *Client) BlockchainInfo(minHeight, maxHeight int64) (*ctypes.ResultBlock
|
||||
// Update the light client if we're behind.
|
||||
if len(res.BlockMetas) > 0 {
|
||||
lastHeight := res.BlockMetas[len(res.BlockMetas)-1].Header.Height
|
||||
if _, err := c.updateLiteClientIfNeededTo(lastHeight); err != nil {
|
||||
if _, err := c.updateLightClientIfNeededTo(lastHeight); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
@@ -254,7 +254,7 @@ func (c *Client) Block(height *int64) (*ctypes.ResultBlock, error) {
|
||||
}
|
||||
|
||||
// Update the light client if we're behind.
|
||||
h, err := c.updateLiteClientIfNeededTo(res.Block.Height)
|
||||
h, err := c.updateLightClientIfNeededTo(res.Block.Height)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -288,7 +288,7 @@ func (c *Client) BlockByHash(hash []byte) (*ctypes.ResultBlock, error) {
|
||||
}
|
||||
|
||||
// Update the light client if we're behind.
|
||||
h, err := c.updateLiteClientIfNeededTo(res.Block.Height)
|
||||
h, err := c.updateLightClientIfNeededTo(res.Block.Height)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -329,7 +329,7 @@ func (c *Client) BlockResults(height *int64) (*ctypes.ResultBlockResults, error)
|
||||
}
|
||||
|
||||
// Update the light client if we're behind.
|
||||
trustedHeader, err := c.updateLiteClientIfNeededTo(h + 1)
|
||||
trustedHeader, err := c.updateLightClientIfNeededTo(h + 1)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -359,7 +359,7 @@ func (c *Client) Commit(height *int64) (*ctypes.ResultCommit, error) {
|
||||
}
|
||||
|
||||
// Update the light client if we're behind.
|
||||
h, err := c.updateLiteClientIfNeededTo(res.Height)
|
||||
h, err := c.updateLightClientIfNeededTo(res.Height)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -387,7 +387,7 @@ func (c *Client) Tx(hash []byte, prove bool) (*ctypes.ResultTx, error) {
|
||||
}
|
||||
|
||||
// Update the light client if we're behind.
|
||||
h, err := c.updateLiteClientIfNeededTo(res.Height)
|
||||
h, err := c.updateLightClientIfNeededTo(res.Height)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -417,7 +417,7 @@ func (c *Client) Validators(height *int64, page, perPage int) (*ctypes.ResultVal
|
||||
}
|
||||
|
||||
// Update the light client if we're behind.
|
||||
h, err := c.updateLiteClientIfNeededTo(res.BlockHeight)
|
||||
h, err := c.updateLightClientIfNeededTo(res.BlockHeight)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -450,7 +450,7 @@ func (c *Client) UnsubscribeAll(ctx context.Context, subscriber string) error {
|
||||
return c.next.UnsubscribeAll(ctx, subscriber)
|
||||
}
|
||||
|
||||
func (c *Client) updateLiteClientIfNeededTo(height int64) (*types.SignedHeader, error) {
|
||||
func (c *Client) updateLightClientIfNeededTo(height int64) (*types.SignedHeader, error) {
|
||||
h, err := c.lc.VerifyHeaderAtHeight(height, time.Now())
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to update light client to %d: %w", height, err)
|
||||
@@ -11,8 +11,6 @@ package rpc
|
||||
|
||||
// "github.com/tendermint/tendermint/abci/example/kvstore"
|
||||
// "github.com/tendermint/tendermint/crypto/merkle"
|
||||
// "github.com/tendermint/tendermint/lite"
|
||||
// certclient "github.com/tendermint/tendermint/lite/client"
|
||||
// nm "github.com/tendermint/tendermint/node"
|
||||
// "github.com/tendermint/tendermint/rpc/client"
|
||||
// rpctest "github.com/tendermint/tendermint/rpc/test"
|
||||
@@ -1,15 +1,15 @@
|
||||
package lite
|
||||
package light
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/tendermint/tendermint/lite2/provider"
|
||||
"github.com/tendermint/tendermint/lite2/provider/http"
|
||||
"github.com/tendermint/tendermint/lite2/store"
|
||||
"github.com/tendermint/tendermint/light/provider"
|
||||
"github.com/tendermint/tendermint/light/provider/http"
|
||||
"github.com/tendermint/tendermint/light/store"
|
||||
)
|
||||
|
||||
// NewHTTPClient initiates an instance of a lite client using HTTP addresses
|
||||
// for both the primary provider and witnesses of the lite client. A trusted
|
||||
// NewHTTPClient initiates an instance of a light client using HTTP addresses
|
||||
// for both the primary provider and witnesses of the light client. A trusted
|
||||
// header and hash must be passed to initialize the client.
|
||||
//
|
||||
// See all Option(s) for the additional configuration.
|
||||
@@ -36,7 +36,7 @@ func NewHTTPClient(
|
||||
options...)
|
||||
}
|
||||
|
||||
// NewHTTPClientFromTrustedStore initiates an instance of a lite client using
|
||||
// NewHTTPClientFromTrustedStore initiates an instance of a light client using
|
||||
// HTTP addresses for both the primary provider and witnesses and uses a
|
||||
// trusted store as the root of trust.
|
||||
//
|
||||
@@ -11,7 +11,7 @@ import (
|
||||
dbm "github.com/tendermint/tm-db"
|
||||
|
||||
cryptoAmino "github.com/tendermint/tendermint/crypto/encoding/amino"
|
||||
"github.com/tendermint/tendermint/lite2/store"
|
||||
"github.com/tendermint/tendermint/light/store"
|
||||
"github.com/tendermint/tendermint/types"
|
||||
)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package lite
|
||||
package light
|
||||
|
||||
import (
|
||||
"errors"
|
||||
@@ -1,4 +1,4 @@
|
||||
package lite
|
||||
package light
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
@@ -1,4 +1,4 @@
|
||||
package lite_test
|
||||
package light_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
@@ -8,7 +8,7 @@ import (
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
tmmath "github.com/tendermint/tendermint/libs/math"
|
||||
lite "github.com/tendermint/tendermint/lite2"
|
||||
"github.com/tendermint/tendermint/light"
|
||||
"github.com/tendermint/tendermint/types"
|
||||
)
|
||||
|
||||
@@ -117,7 +117,7 @@ func TestVerifyAdjacentHeaders(t *testing.T) {
|
||||
vals,
|
||||
3 * time.Hour,
|
||||
bTime.Add(2 * time.Hour),
|
||||
lite.ErrInvalidHeader{Reason: types.ErrNotEnoughVotingPowerSigned{Got: 50, Needed: 93}},
|
||||
light.ErrInvalidHeader{Reason: types.ErrNotEnoughVotingPowerSigned{Got: 50, Needed: 93}},
|
||||
"",
|
||||
},
|
||||
// vals does not match with what we have -> error
|
||||
@@ -155,7 +155,7 @@ func TestVerifyAdjacentHeaders(t *testing.T) {
|
||||
for i, tc := range testCases {
|
||||
tc := tc
|
||||
t.Run(fmt.Sprintf("#%d", i), func(t *testing.T) {
|
||||
err := lite.VerifyAdjacent(chainID, header, tc.newHeader, tc.newVals, tc.trustingPeriod, tc.now, maxClockDrift)
|
||||
err := light.VerifyAdjacent(chainID, header, tc.newHeader, tc.newVals, tc.trustingPeriod, tc.now, maxClockDrift)
|
||||
switch {
|
||||
case tc.expErr != nil && assert.Error(t, err):
|
||||
assert.Equal(t, tc.expErr, err)
|
||||
@@ -231,7 +231,7 @@ func TestVerifyNonAdjacentHeaders(t *testing.T) {
|
||||
vals,
|
||||
3 * time.Hour,
|
||||
bTime.Add(2 * time.Hour),
|
||||
lite.ErrInvalidHeader{types.ErrNotEnoughVotingPowerSigned{Got: 50, Needed: 93}},
|
||||
light.ErrInvalidHeader{types.ErrNotEnoughVotingPowerSigned{Got: 50, Needed: 93}},
|
||||
"",
|
||||
},
|
||||
// 3/3 new vals signed, 2/3 old vals present -> no error
|
||||
@@ -261,7 +261,7 @@ func TestVerifyNonAdjacentHeaders(t *testing.T) {
|
||||
lessThanOneThirdVals,
|
||||
3 * time.Hour,
|
||||
bTime.Add(2 * time.Hour),
|
||||
lite.ErrNewValSetCantBeTrusted{types.ErrNotEnoughVotingPowerSigned{Got: 20, Needed: 46}},
|
||||
light.ErrNewValSetCantBeTrusted{types.ErrNotEnoughVotingPowerSigned{Got: 20, Needed: 46}},
|
||||
"",
|
||||
},
|
||||
}
|
||||
@@ -269,9 +269,9 @@ func TestVerifyNonAdjacentHeaders(t *testing.T) {
|
||||
for i, tc := range testCases {
|
||||
tc := tc
|
||||
t.Run(fmt.Sprintf("#%d", i), func(t *testing.T) {
|
||||
err := lite.VerifyNonAdjacent(chainID, header, vals, tc.newHeader, tc.newVals, tc.trustingPeriod,
|
||||
err := light.VerifyNonAdjacent(chainID, header, vals, tc.newHeader, tc.newVals, tc.trustingPeriod,
|
||||
tc.now, maxClockDrift,
|
||||
lite.DefaultTrustLevel)
|
||||
light.DefaultTrustLevel)
|
||||
|
||||
switch {
|
||||
case tc.expErr != nil && assert.Error(t, err):
|
||||
@@ -300,7 +300,7 @@ func TestVerifyReturnsErrorIfTrustLevelIsInvalid(t *testing.T) {
|
||||
hash("app_hash"), hash("cons_hash"), hash("results_hash"), 0, len(keys))
|
||||
)
|
||||
|
||||
err := lite.Verify(chainID, header, vals, header, vals, 2*time.Hour, time.Now(), maxClockDrift,
|
||||
err := light.Verify(chainID, header, vals, header, vals, 2*time.Hour, time.Now(), maxClockDrift,
|
||||
tmmath.Fraction{Numerator: 2, Denominator: 1})
|
||||
assert.Error(t, err)
|
||||
}
|
||||
@@ -327,7 +327,7 @@ func TestValidateTrustLevel(t *testing.T) {
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
err := lite.ValidateTrustLevel(tc.lvl)
|
||||
err := light.ValidateTrustLevel(tc.lvl)
|
||||
if !tc.valid {
|
||||
assert.Error(t, err)
|
||||
} else {
|
||||
@@ -1,79 +0,0 @@
|
||||
package lite
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
|
||||
lerr "github.com/tendermint/tendermint/lite/errors"
|
||||
"github.com/tendermint/tendermint/types"
|
||||
)
|
||||
|
||||
var _ Verifier = (*BaseVerifier)(nil)
|
||||
|
||||
// BaseVerifier lets us check the validity of SignedHeaders at height or
|
||||
// later, requiring sufficient votes (> 2/3) from the given valset.
|
||||
// To verify blocks produced by a blockchain with mutable validator sets,
|
||||
// use the DynamicVerifier.
|
||||
// TODO: Handle unbonding time.
|
||||
type BaseVerifier struct {
|
||||
chainID string
|
||||
height int64
|
||||
valset *types.ValidatorSet
|
||||
}
|
||||
|
||||
// NewBaseVerifier returns a new Verifier initialized with a validator set at
|
||||
// some height.
|
||||
func NewBaseVerifier(chainID string, height int64, valset *types.ValidatorSet) *BaseVerifier {
|
||||
if valset.IsNilOrEmpty() {
|
||||
panic("NewBaseVerifier requires a valid valset")
|
||||
}
|
||||
return &BaseVerifier{
|
||||
chainID: chainID,
|
||||
height: height,
|
||||
valset: valset,
|
||||
}
|
||||
}
|
||||
|
||||
// Implements Verifier.
|
||||
func (bv *BaseVerifier) ChainID() string {
|
||||
return bv.chainID
|
||||
}
|
||||
|
||||
// Implements Verifier.
|
||||
func (bv *BaseVerifier) Verify(signedHeader types.SignedHeader) error {
|
||||
|
||||
// We can't verify commits for a different chain.
|
||||
if signedHeader.ChainID != bv.chainID {
|
||||
return errors.Errorf("BaseVerifier chainID is %v, cannot verify chainID %v",
|
||||
bv.chainID, signedHeader.ChainID)
|
||||
}
|
||||
|
||||
// We can't verify commits older than bv.height.
|
||||
if signedHeader.Height < bv.height {
|
||||
return errors.Errorf("BaseVerifier height is %v, cannot verify height %v",
|
||||
bv.height, signedHeader.Height)
|
||||
}
|
||||
|
||||
// We can't verify with the wrong validator set.
|
||||
if !bytes.Equal(signedHeader.ValidatorsHash,
|
||||
bv.valset.Hash()) {
|
||||
return lerr.ErrUnexpectedValidators(signedHeader.ValidatorsHash, bv.valset.Hash())
|
||||
}
|
||||
|
||||
// Do basic sanity checks.
|
||||
err := signedHeader.ValidateBasic(bv.chainID)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "in verify")
|
||||
}
|
||||
|
||||
// Check commit signatures.
|
||||
err = bv.valset.VerifyCommit(
|
||||
bv.chainID, signedHeader.Commit.BlockID,
|
||||
signedHeader.Height, signedHeader.Commit)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "in verify")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -1,66 +0,0 @@
|
||||
package lite
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
"github.com/tendermint/tendermint/crypto/tmhash"
|
||||
lerr "github.com/tendermint/tendermint/lite/errors"
|
||||
"github.com/tendermint/tendermint/types"
|
||||
)
|
||||
|
||||
func TestBaseCert(t *testing.T) {
|
||||
// TODO: Requires proposer address to be set in header.
|
||||
t.SkipNow()
|
||||
|
||||
assert := assert.New(t)
|
||||
|
||||
keys := genPrivKeys(4)
|
||||
// 20, 30, 40, 50 - the first 3 don't have 2/3, the last 3 do!
|
||||
vals := keys.ToValidators(20, 10)
|
||||
// and a Verifier based on our known set
|
||||
chainID := "test-static"
|
||||
cert := NewBaseVerifier(chainID, 2, vals)
|
||||
|
||||
cases := []struct {
|
||||
keys privKeys
|
||||
vals *types.ValidatorSet
|
||||
height int64
|
||||
first, last int // who actually signs
|
||||
proper bool // true -> expect no error
|
||||
changed bool // true -> expect validator change error
|
||||
}{
|
||||
// height regression
|
||||
{keys, vals, 1, 0, len(keys), false, false},
|
||||
// perfect, signed by everyone
|
||||
{keys, vals, 2, 0, len(keys), true, false},
|
||||
// skip little guy is okay
|
||||
{keys, vals, 3, 1, len(keys), true, false},
|
||||
// but not the big guy
|
||||
{keys, vals, 4, 0, len(keys) - 1, false, false},
|
||||
// Changing the power a little bit breaks the static validator.
|
||||
// The sigs are enough, but the validator hash is unknown.
|
||||
{keys, keys.ToValidators(20, 11), 5, 0, len(keys), false, true},
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
sh := tc.keys.GenSignedHeader(
|
||||
chainID, tc.height, nil, tc.vals, tc.vals,
|
||||
tmhash.Sum([]byte("foo")),
|
||||
tmhash.Sum([]byte("params")),
|
||||
tmhash.Sum([]byte("results")),
|
||||
tc.first, tc.last,
|
||||
)
|
||||
|
||||
err := cert.Verify(sh)
|
||||
if tc.proper {
|
||||
assert.Nil(err, "%+v", err)
|
||||
} else {
|
||||
assert.NotNil(err)
|
||||
if tc.changed {
|
||||
assert.True(lerr.IsErrUnexpectedValidators(err), "%+v", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,139 +0,0 @@
|
||||
/*
|
||||
Package client defines a provider that uses a rpchttp
|
||||
to get information, which is used to get new headers
|
||||
and validators directly from a Tendermint client.
|
||||
*/
|
||||
package client
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
log "github.com/tendermint/tendermint/libs/log"
|
||||
"github.com/tendermint/tendermint/lite"
|
||||
lerr "github.com/tendermint/tendermint/lite/errors"
|
||||
rpcclient "github.com/tendermint/tendermint/rpc/client"
|
||||
rpchttp "github.com/tendermint/tendermint/rpc/client/http"
|
||||
ctypes "github.com/tendermint/tendermint/rpc/core/types"
|
||||
"github.com/tendermint/tendermint/types"
|
||||
)
|
||||
|
||||
// SignStatusClient combines a SignClient and StatusClient.
|
||||
type SignStatusClient interface {
|
||||
rpcclient.SignClient
|
||||
rpcclient.StatusClient
|
||||
}
|
||||
|
||||
type provider struct {
|
||||
logger log.Logger
|
||||
chainID string
|
||||
client SignStatusClient
|
||||
}
|
||||
|
||||
// NewProvider implements Provider (but not PersistentProvider).
|
||||
func NewProvider(chainID string, client SignStatusClient) lite.Provider {
|
||||
return &provider{
|
||||
logger: log.NewNopLogger(),
|
||||
chainID: chainID,
|
||||
client: client,
|
||||
}
|
||||
}
|
||||
|
||||
// NewHTTPProvider can connect to a tendermint json-rpc endpoint
|
||||
// at the given url, and uses that as a read-only provider.
|
||||
func NewHTTPProvider(chainID, remote string) (lite.Provider, error) {
|
||||
httpClient, err := rpchttp.New(remote, "/websocket")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return NewProvider(chainID, httpClient), nil
|
||||
}
|
||||
|
||||
// Implements Provider.
|
||||
func (p *provider) SetLogger(logger log.Logger) {
|
||||
logger = logger.With("module", "lite/client")
|
||||
p.logger = logger
|
||||
}
|
||||
|
||||
// StatusClient returns the internal client as a StatusClient
|
||||
func (p *provider) StatusClient() rpcclient.StatusClient {
|
||||
return p.client
|
||||
}
|
||||
|
||||
// LatestFullCommit implements Provider.
|
||||
func (p *provider) LatestFullCommit(chainID string, minHeight, maxHeight int64) (fc lite.FullCommit, err error) {
|
||||
if chainID != p.chainID {
|
||||
err = fmt.Errorf("expected chainID %s, got %s", p.chainID, chainID)
|
||||
return
|
||||
}
|
||||
if maxHeight != 0 && maxHeight < minHeight {
|
||||
err = fmt.Errorf("need maxHeight == 0 or minHeight <= maxHeight, got min %v and max %v",
|
||||
minHeight, maxHeight)
|
||||
return
|
||||
}
|
||||
commit, err := p.fetchLatestCommit(minHeight, maxHeight)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
fc, err = p.fillFullCommit(commit.SignedHeader)
|
||||
return
|
||||
}
|
||||
|
||||
// fetchLatestCommit fetches the latest commit from the client.
|
||||
func (p *provider) fetchLatestCommit(minHeight int64, maxHeight int64) (*ctypes.ResultCommit, error) {
|
||||
status, err := p.client.Status()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if status.SyncInfo.LatestBlockHeight < minHeight {
|
||||
err = fmt.Errorf("provider is at %v but require minHeight=%v",
|
||||
status.SyncInfo.LatestBlockHeight, minHeight)
|
||||
return nil, err
|
||||
}
|
||||
if maxHeight == 0 {
|
||||
maxHeight = status.SyncInfo.LatestBlockHeight
|
||||
} else if status.SyncInfo.LatestBlockHeight < maxHeight {
|
||||
maxHeight = status.SyncInfo.LatestBlockHeight
|
||||
}
|
||||
return p.client.Commit(&maxHeight)
|
||||
}
|
||||
|
||||
// Implements Provider.
|
||||
func (p *provider) ValidatorSet(chainID string, height int64) (valset *types.ValidatorSet, err error) {
|
||||
return p.getValidatorSet(chainID, height)
|
||||
}
|
||||
|
||||
func (p *provider) getValidatorSet(chainID string, height int64) (valset *types.ValidatorSet, err error) {
|
||||
if chainID != p.chainID {
|
||||
err = fmt.Errorf("expected chainID %s, got %s", p.chainID, chainID)
|
||||
return
|
||||
}
|
||||
if height < 1 {
|
||||
err = fmt.Errorf("expected height >= 1, got height %v", height)
|
||||
return
|
||||
}
|
||||
res, err := p.client.Validators(&height, 0, 0)
|
||||
if err != nil {
|
||||
// TODO pass through other types of errors.
|
||||
return nil, lerr.ErrUnknownValidators(chainID, height)
|
||||
}
|
||||
valset = types.NewValidatorSet(res.Validators)
|
||||
return
|
||||
}
|
||||
|
||||
// This does no validation.
|
||||
func (p *provider) fillFullCommit(signedHeader types.SignedHeader) (fc lite.FullCommit, err error) {
|
||||
|
||||
// Get the validators.
|
||||
valset, err := p.getValidatorSet(signedHeader.ChainID, signedHeader.Height)
|
||||
if err != nil {
|
||||
return lite.FullCommit{}, err
|
||||
}
|
||||
|
||||
// Get the next validators.
|
||||
nextValset, err := p.getValidatorSet(signedHeader.ChainID, signedHeader.Height+1)
|
||||
if err != nil {
|
||||
return lite.FullCommit{}, err
|
||||
}
|
||||
|
||||
return lite.NewFullCommit(signedHeader, valset, nextValset), nil
|
||||
}
|
||||
@@ -1,62 +0,0 @@
|
||||
package client
|
||||
|
||||
import (
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/tendermint/tendermint/abci/example/kvstore"
|
||||
rpcclient "github.com/tendermint/tendermint/rpc/client"
|
||||
rpctest "github.com/tendermint/tendermint/rpc/test"
|
||||
"github.com/tendermint/tendermint/types"
|
||||
)
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
app := kvstore.NewApplication()
|
||||
node := rpctest.StartTendermint(app)
|
||||
|
||||
code := m.Run()
|
||||
|
||||
rpctest.StopTendermint(node)
|
||||
os.Exit(code)
|
||||
}
|
||||
|
||||
func TestProvider(t *testing.T) {
|
||||
assert, require := assert.New(t), require.New(t)
|
||||
|
||||
cfg := rpctest.GetConfig()
|
||||
defer os.RemoveAll(cfg.RootDir)
|
||||
rpcAddr := cfg.RPC.ListenAddress
|
||||
genDoc, err := types.GenesisDocFromFile(cfg.GenesisFile())
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
chainID := genDoc.ChainID
|
||||
t.Log("chainID:", chainID)
|
||||
p, err := NewHTTPProvider(chainID, rpcAddr)
|
||||
require.Nil(err)
|
||||
require.NotNil(p)
|
||||
|
||||
// let it produce some blocks
|
||||
err = rpcclient.WaitForHeight(p.(*provider).client, 6, nil)
|
||||
require.Nil(err)
|
||||
|
||||
// let's get the highest block
|
||||
fc, err := p.LatestFullCommit(chainID, 1, 1<<63-1)
|
||||
|
||||
require.Nil(err, "%+v", err)
|
||||
sh := fc.Height()
|
||||
assert.True(sh < 5000)
|
||||
|
||||
// let's check this is valid somehow
|
||||
assert.Nil(fc.ValidateFull(chainID))
|
||||
|
||||
// historical queries now work :)
|
||||
lower := sh - 5
|
||||
fc, err = p.LatestFullCommit(chainID, lower, lower)
|
||||
assert.Nil(err, "%+v", err)
|
||||
assert.Equal(lower, fc.Height())
|
||||
|
||||
}
|
||||
@@ -1,87 +0,0 @@
|
||||
package lite
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/tendermint/tendermint/types"
|
||||
)
|
||||
|
||||
// FullCommit contains a SignedHeader (the block header and a commit that signs it),
|
||||
// the validator set which signed the commit, and the next validator set. The
|
||||
// next validator set (which is proven from the block header) allows us to
|
||||
// revert to block-by-block updating of lite Verifier's latest validator set,
|
||||
// even in the face of arbitrarily large power changes.
|
||||
type FullCommit struct {
|
||||
SignedHeader types.SignedHeader `json:"signed_header"`
|
||||
Validators *types.ValidatorSet `json:"validator_set"`
|
||||
NextValidators *types.ValidatorSet `json:"next_validator_set"`
|
||||
}
|
||||
|
||||
// NewFullCommit returns a new FullCommit.
|
||||
func NewFullCommit(signedHeader types.SignedHeader, valset, nextValset *types.ValidatorSet) FullCommit {
|
||||
return FullCommit{
|
||||
SignedHeader: signedHeader,
|
||||
Validators: valset,
|
||||
NextValidators: nextValset,
|
||||
}
|
||||
}
|
||||
|
||||
// Validate the components and check for consistency.
|
||||
// This also checks to make sure that Validators actually
|
||||
// signed the SignedHeader.Commit.
|
||||
// If > 2/3 did not sign the Commit from fc.Validators, it
|
||||
// is not a valid commit!
|
||||
func (fc FullCommit) ValidateFull(chainID string) error {
|
||||
// Ensure that Validators exists and matches the header.
|
||||
if fc.Validators.Size() == 0 {
|
||||
return errors.New("need FullCommit.Validators")
|
||||
}
|
||||
if !bytes.Equal(
|
||||
fc.SignedHeader.ValidatorsHash,
|
||||
fc.Validators.Hash()) {
|
||||
return fmt.Errorf("header has vhash %X but valset hash is %X",
|
||||
fc.SignedHeader.ValidatorsHash,
|
||||
fc.Validators.Hash(),
|
||||
)
|
||||
}
|
||||
// Ensure that NextValidators exists and matches the header.
|
||||
if fc.NextValidators.Size() == 0 {
|
||||
return errors.New("need FullCommit.NextValidators")
|
||||
}
|
||||
if !bytes.Equal(
|
||||
fc.SignedHeader.NextValidatorsHash,
|
||||
fc.NextValidators.Hash()) {
|
||||
return fmt.Errorf("header has next vhash %X but next valset hash is %X",
|
||||
fc.SignedHeader.NextValidatorsHash,
|
||||
fc.NextValidators.Hash(),
|
||||
)
|
||||
}
|
||||
// Validate the header.
|
||||
err := fc.SignedHeader.ValidateBasic(chainID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// Validate the signatures on the commit.
|
||||
hdr, cmt := fc.SignedHeader.Header, fc.SignedHeader.Commit
|
||||
return fc.Validators.VerifyCommit(
|
||||
hdr.ChainID, cmt.BlockID,
|
||||
hdr.Height, cmt)
|
||||
}
|
||||
|
||||
// Height returns the height of the header.
|
||||
func (fc FullCommit) Height() int64 {
|
||||
if fc.SignedHeader.Header == nil {
|
||||
panic("should not happen")
|
||||
}
|
||||
return fc.SignedHeader.Height
|
||||
}
|
||||
|
||||
// ChainID returns the chainID of the header.
|
||||
func (fc FullCommit) ChainID() string {
|
||||
if fc.SignedHeader.Header == nil {
|
||||
panic("should not happen")
|
||||
}
|
||||
return fc.SignedHeader.ChainID
|
||||
}
|
||||
@@ -1,285 +0,0 @@
|
||||
package lite
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"regexp"
|
||||
"strconv"
|
||||
|
||||
amino "github.com/tendermint/go-amino"
|
||||
dbm "github.com/tendermint/tm-db"
|
||||
|
||||
cryptoamino "github.com/tendermint/tendermint/crypto/encoding/amino"
|
||||
log "github.com/tendermint/tendermint/libs/log"
|
||||
lerr "github.com/tendermint/tendermint/lite/errors"
|
||||
"github.com/tendermint/tendermint/types"
|
||||
)
|
||||
|
||||
var _ PersistentProvider = (*DBProvider)(nil)
|
||||
|
||||
// DBProvider stores commits and validator sets in a DB.
|
||||
type DBProvider struct {
|
||||
logger log.Logger
|
||||
label string
|
||||
db dbm.DB
|
||||
cdc *amino.Codec
|
||||
limit int
|
||||
}
|
||||
|
||||
func NewDBProvider(label string, db dbm.DB) *DBProvider {
|
||||
|
||||
// NOTE: when debugging, this type of construction might be useful.
|
||||
//db = dbm.NewDebugDB("db provider "+tmrand.Str(4), db)
|
||||
|
||||
cdc := amino.NewCodec()
|
||||
cryptoamino.RegisterAmino(cdc)
|
||||
dbp := &DBProvider{
|
||||
logger: log.NewNopLogger(),
|
||||
label: label,
|
||||
db: db,
|
||||
cdc: cdc,
|
||||
}
|
||||
return dbp
|
||||
}
|
||||
|
||||
func (dbp *DBProvider) SetLogger(logger log.Logger) {
|
||||
dbp.logger = logger.With("label", dbp.label)
|
||||
}
|
||||
|
||||
func (dbp *DBProvider) SetLimit(limit int) *DBProvider {
|
||||
dbp.limit = limit
|
||||
return dbp
|
||||
}
|
||||
|
||||
// Implements PersistentProvider.
|
||||
func (dbp *DBProvider) SaveFullCommit(fc FullCommit) error {
|
||||
|
||||
dbp.logger.Info("DBProvider.SaveFullCommit()...", "fc", fc)
|
||||
batch := dbp.db.NewBatch()
|
||||
defer batch.Close()
|
||||
|
||||
// Save the fc.validators.
|
||||
// We might be overwriting what we already have, but
|
||||
// it makes the logic easier for now.
|
||||
vsKey := validatorSetKey(fc.ChainID(), fc.Height())
|
||||
vsBz, err := dbp.cdc.MarshalBinaryLengthPrefixed(fc.Validators)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
batch.Set(vsKey, vsBz)
|
||||
|
||||
// Save the fc.NextValidators.
|
||||
nvsKey := validatorSetKey(fc.ChainID(), fc.Height()+1)
|
||||
nvsBz, err := dbp.cdc.MarshalBinaryLengthPrefixed(fc.NextValidators)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
batch.Set(nvsKey, nvsBz)
|
||||
|
||||
// Save the fc.SignedHeader
|
||||
shKey := signedHeaderKey(fc.ChainID(), fc.Height())
|
||||
shBz, err := dbp.cdc.MarshalBinaryLengthPrefixed(fc.SignedHeader)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
batch.Set(shKey, shBz)
|
||||
|
||||
// And write sync.
|
||||
batch.WriteSync()
|
||||
|
||||
// Garbage collect.
|
||||
// TODO: optimize later.
|
||||
if dbp.limit > 0 {
|
||||
dbp.deleteAfterN(fc.ChainID(), dbp.limit)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Implements Provider.
|
||||
func (dbp *DBProvider) LatestFullCommit(chainID string, minHeight, maxHeight int64) (
|
||||
FullCommit, error) {
|
||||
|
||||
dbp.logger.Info("DBProvider.LatestFullCommit()...",
|
||||
"chainID", chainID, "minHeight", minHeight, "maxHeight", maxHeight)
|
||||
|
||||
if minHeight <= 0 {
|
||||
minHeight = 1
|
||||
}
|
||||
if maxHeight == 0 {
|
||||
maxHeight = 1<<63 - 1
|
||||
}
|
||||
|
||||
itr, err := dbp.db.ReverseIterator(
|
||||
signedHeaderKey(chainID, minHeight),
|
||||
append(signedHeaderKey(chainID, maxHeight), byte(0x00)),
|
||||
)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
defer itr.Close()
|
||||
|
||||
for itr.Valid() {
|
||||
key := itr.Key()
|
||||
_, _, ok := parseSignedHeaderKey(key)
|
||||
if !ok {
|
||||
// Skip over other keys.
|
||||
itr.Next()
|
||||
continue
|
||||
} else {
|
||||
// Found the latest full commit signed header.
|
||||
shBz := itr.Value()
|
||||
sh := types.SignedHeader{}
|
||||
err := dbp.cdc.UnmarshalBinaryLengthPrefixed(shBz, &sh)
|
||||
if err != nil {
|
||||
return FullCommit{}, err
|
||||
}
|
||||
|
||||
lfc, err := dbp.fillFullCommit(sh)
|
||||
if err == nil {
|
||||
dbp.logger.Info("DBProvider.LatestFullCommit() found latest.", "height", lfc.Height())
|
||||
return lfc, nil
|
||||
}
|
||||
|
||||
dbp.logger.Error("DBProvider.LatestFullCommit() got error", "lfc", lfc)
|
||||
dbp.logger.Error(fmt.Sprintf("%+v", err))
|
||||
return lfc, err
|
||||
|
||||
}
|
||||
}
|
||||
return FullCommit{}, lerr.ErrCommitNotFound()
|
||||
}
|
||||
|
||||
func (dbp *DBProvider) ValidatorSet(chainID string, height int64) (valset *types.ValidatorSet, err error) {
|
||||
return dbp.getValidatorSet(chainID, height)
|
||||
}
|
||||
|
||||
func (dbp *DBProvider) getValidatorSet(chainID string, height int64) (valset *types.ValidatorSet, err error) {
|
||||
vsBz, err := dbp.db.Get(validatorSetKey(chainID, height))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(vsBz) == 0 {
|
||||
err = lerr.ErrUnknownValidators(chainID, height)
|
||||
return
|
||||
}
|
||||
err = dbp.cdc.UnmarshalBinaryLengthPrefixed(vsBz, &valset)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// To test deep equality. This makes it easier to test for e.g. valset
|
||||
// equivalence using assert.Equal (tests for deep equality) in our tests,
|
||||
// which also tests for unexported/private field equivalence.
|
||||
valset.TotalVotingPower()
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (dbp *DBProvider) fillFullCommit(sh types.SignedHeader) (FullCommit, error) {
|
||||
var chainID = sh.ChainID
|
||||
var height = sh.Height
|
||||
var valset, nextValset *types.ValidatorSet
|
||||
// Load the validator set.
|
||||
valset, err := dbp.getValidatorSet(chainID, height)
|
||||
if err != nil {
|
||||
return FullCommit{}, err
|
||||
}
|
||||
// Load the next validator set.
|
||||
nextValset, err = dbp.getValidatorSet(chainID, height+1)
|
||||
if err != nil {
|
||||
return FullCommit{}, err
|
||||
}
|
||||
// Return filled FullCommit.
|
||||
return FullCommit{
|
||||
SignedHeader: sh,
|
||||
Validators: valset,
|
||||
NextValidators: nextValset,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (dbp *DBProvider) deleteAfterN(chainID string, after int) error {
|
||||
|
||||
dbp.logger.Info("DBProvider.deleteAfterN()...", "chainID", chainID, "after", after)
|
||||
|
||||
itr, err := dbp.db.ReverseIterator(
|
||||
signedHeaderKey(chainID, 1),
|
||||
append(signedHeaderKey(chainID, 1<<63-1), byte(0x00)),
|
||||
)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
defer itr.Close()
|
||||
|
||||
var lastHeight int64 = 1<<63 - 1
|
||||
var numSeen = 0
|
||||
var numDeleted = 0
|
||||
|
||||
for itr.Valid() {
|
||||
key := itr.Key()
|
||||
_, height, ok := parseChainKeyPrefix(key)
|
||||
if !ok {
|
||||
return fmt.Errorf("unexpected key %v", key)
|
||||
}
|
||||
|
||||
if height < lastHeight {
|
||||
lastHeight = height
|
||||
numSeen++
|
||||
}
|
||||
if numSeen > after {
|
||||
dbp.db.Delete(key)
|
||||
numDeleted++
|
||||
}
|
||||
|
||||
itr.Next()
|
||||
}
|
||||
|
||||
dbp.logger.Info(fmt.Sprintf("DBProvider.deleteAfterN() deleted %v items", numDeleted))
|
||||
return nil
|
||||
}
|
||||
|
||||
//----------------------------------------
|
||||
// key encoding
|
||||
|
||||
func signedHeaderKey(chainID string, height int64) []byte {
|
||||
return []byte(fmt.Sprintf("%s/%010d/sh", chainID, height))
|
||||
}
|
||||
|
||||
func validatorSetKey(chainID string, height int64) []byte {
|
||||
return []byte(fmt.Sprintf("%s/%010d/vs", chainID, height))
|
||||
}
|
||||
|
||||
//----------------------------------------
|
||||
// key parsing
|
||||
|
||||
var keyPattern = regexp.MustCompile(`^([^/]+)/([0-9]*)/(.*)$`)
|
||||
|
||||
func parseKey(key []byte) (chainID string, height int64, part string, ok bool) {
|
||||
submatch := keyPattern.FindSubmatch(key)
|
||||
if submatch == nil {
|
||||
return "", 0, "", false
|
||||
}
|
||||
chainID = string(submatch[1])
|
||||
heightStr := string(submatch[2])
|
||||
heightInt, err := strconv.Atoi(heightStr)
|
||||
if err != nil {
|
||||
return "", 0, "", false
|
||||
}
|
||||
height = int64(heightInt)
|
||||
part = string(submatch[3])
|
||||
ok = true // good!
|
||||
return
|
||||
}
|
||||
|
||||
func parseSignedHeaderKey(key []byte) (chainID string, height int64, ok bool) {
|
||||
var part string
|
||||
chainID, height, part, ok = parseKey(key)
|
||||
if part != "sh" {
|
||||
return "", 0, false
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func parseChainKeyPrefix(key []byte) (chainID string, height int64, ok bool) {
|
||||
chainID, height, _, ok = parseKey(key)
|
||||
return
|
||||
}
|
||||
133
lite/doc.go
133
lite/doc.go
@@ -1,133 +0,0 @@
|
||||
/*
|
||||
Package lite is deprecated and will be removed in v0.34!
|
||||
|
||||
Package lite allows you to securely validate headers without a full node.
|
||||
|
||||
This library pulls together all the crypto and algorithms, so given a
|
||||
relatively recent (< unbonding period) known validator set, one can get
|
||||
indisputable proof that data is in the chain (current state) or detect if the
|
||||
node is lying to the client.
|
||||
|
||||
Tendermint RPC exposes a lot of info, but a malicious node could return any
|
||||
data it wants to queries, or even to block headers, even making up fake
|
||||
signatures from non-existent validators to justify it. This is a lot of logic
|
||||
to get right, to be contained in a small, easy to use library, that does this
|
||||
for you, so you can just build nice applications.
|
||||
|
||||
We design for clients who have no strong trust relationship with any Tendermint
|
||||
node, just the blockchain and validator set as a whole.
|
||||
|
||||
SignedHeader
|
||||
|
||||
SignedHeader is a block header along with a commit -- enough validator
|
||||
precommit-vote signatures to prove its validity (> 2/3 of the voting power)
|
||||
given the validator set responsible for signing that header. A FullCommit is a
|
||||
SignedHeader along with the current and next validator sets.
|
||||
|
||||
The hash of the next validator set is included and signed in the SignedHeader.
|
||||
This lets the lite client keep track of arbitrary changes to the validator set,
|
||||
as every change to the validator set must be approved by inclusion in the
|
||||
header and signed in the commit.
|
||||
|
||||
In the worst case, with every block changing the validators around completely,
|
||||
a lite client can sync up with every block header to verify each validator set
|
||||
change on the chain. In practice, most applications will not have frequent
|
||||
drastic updates to the validator set, so the logic defined in this package for
|
||||
lite client syncing is optimized to use intelligent bisection and
|
||||
block-skipping for efficient sourcing and verification of these data structures
|
||||
and updates to the validator set (see the DynamicVerifier for more
|
||||
information).
|
||||
|
||||
The FullCommit is also declared in this package as a convenience structure,
|
||||
which includes the SignedHeader along with the full current and next
|
||||
ValidatorSets.
|
||||
|
||||
Verifier
|
||||
|
||||
A Verifier validates a new SignedHeader given the currently known state. There
|
||||
are two different types of Verifiers provided.
|
||||
|
||||
BaseVerifier - given a validator set and a height, this Verifier verifies
|
||||
that > 2/3 of the voting power of the given validator set had signed the
|
||||
SignedHeader, and that the SignedHeader was to be signed by the exact given
|
||||
validator set, and that the height of the commit is at least height (or
|
||||
greater).
|
||||
|
||||
DynamicVerifier - this Verifier implements an auto-update and persistence
|
||||
strategy to verify any SignedHeader of the blockchain.
|
||||
|
||||
Provider and PersistentProvider
|
||||
|
||||
A Provider allows us to store and retrieve the FullCommits.
|
||||
|
||||
type Provider interface {
|
||||
// LatestFullCommit returns the latest commit with
|
||||
// minHeight <= height <= maxHeight.
|
||||
// If maxHeight is zero, returns the latest where
|
||||
// minHeight <= height.
|
||||
LatestFullCommit(chainID string, minHeight, maxHeight int64) (FullCommit, error)
|
||||
}
|
||||
|
||||
* client.NewHTTPProvider - query Tendermint rpc.
|
||||
|
||||
A PersistentProvider is a Provider that also allows for saving state. This is
|
||||
used by the DynamicVerifier for persistence.
|
||||
|
||||
type PersistentProvider interface {
|
||||
Provider
|
||||
|
||||
// SaveFullCommit saves a FullCommit (without verification).
|
||||
SaveFullCommit(fc FullCommit) error
|
||||
}
|
||||
|
||||
* DBProvider - persistence provider for use with any libs/DB.
|
||||
|
||||
* MultiProvider - combine multiple providers.
|
||||
|
||||
The suggested use for local light clients is client.NewHTTPProvider(...) for
|
||||
getting new data (Source), and NewMultiProvider(NewDBProvider("label",
|
||||
dbm.NewMemDB()), NewDBProvider("label", db.NewFileDB(...))) to store confirmed
|
||||
full commits (Trusted)
|
||||
|
||||
|
||||
How We Track Validators
|
||||
|
||||
Unless you want to blindly trust the node you talk with, you need to trace
|
||||
every response back to a hash in a block header and validate the commit
|
||||
signatures of that block header match the proper validator set. If there is a
|
||||
static validator set, you store it locally upon initialization of the client,
|
||||
and check against that every time.
|
||||
|
||||
If the validator set for the blockchain is dynamic, verifying block commits is
|
||||
a bit more involved -- if there is a block at height H with a known (trusted)
|
||||
validator set V, and another block at height H' (H' > H) with validator set V'
|
||||
!= V, then we want a way to safely update it.
|
||||
|
||||
First, we get the new (unconfirmed) validator set V' and verify that H' is
|
||||
internally consistent and properly signed by this V'. Assuming it is a valid
|
||||
block, we check that at least 2/3 of the validators in V also signed it,
|
||||
meaning it would also be valid under our old assumptions. Then, we accept H'
|
||||
and V' as valid and trusted and use that to validate for heights X > H' until a
|
||||
more recent and updated validator set is found.
|
||||
|
||||
If we cannot update directly from H -> H' because there was too much change to
|
||||
the validator set, then we can look for some Hm (H < Hm < H') with a validator
|
||||
set Vm. Then we try to update H -> Hm and then Hm -> H' in two steps. If one
|
||||
of these steps doesn't work, then we continue bisecting, until we eventually
|
||||
have to externally validate the validator set changes at every block.
|
||||
|
||||
Since we never trust any server in this protocol, only the signatures
|
||||
themselves, it doesn't matter if the seed comes from a (possibly malicious)
|
||||
node or a (possibly malicious) user. We can accept it or reject it based only
|
||||
on our trusted validator set and cryptographic proofs. This makes it extremely
|
||||
important to verify that you have the proper validator set when initializing
|
||||
the client, as that is the root of all trust.
|
||||
|
||||
The software currently assumes that the unbonding period is infinite in
|
||||
duration. If the DynamicVerifier hasn't been updated in a while, you should
|
||||
manually verify the block headers using other sources.
|
||||
|
||||
TODO: Update the software to handle cases around the unbonding period.
|
||||
|
||||
*/
|
||||
package lite
|
||||
@@ -1,275 +0,0 @@
|
||||
package lite
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"sync"
|
||||
|
||||
log "github.com/tendermint/tendermint/libs/log"
|
||||
lerr "github.com/tendermint/tendermint/lite/errors"
|
||||
"github.com/tendermint/tendermint/types"
|
||||
)
|
||||
|
||||
const sizeOfPendingMap = 1024
|
||||
|
||||
var _ Verifier = (*DynamicVerifier)(nil)
|
||||
|
||||
// DynamicVerifier implements an auto-updating Verifier. It uses a
|
||||
// "source" provider to obtain the needed FullCommits to securely sync with
|
||||
// validator set changes. It stores properly validated data on the
|
||||
// "trusted" local system.
|
||||
// TODO: make this single threaded and create a new
|
||||
// ConcurrentDynamicVerifier that wraps it with concurrency.
|
||||
// see https://github.com/tendermint/tendermint/issues/3170
|
||||
type DynamicVerifier struct {
|
||||
chainID string
|
||||
logger log.Logger
|
||||
|
||||
// Already validated, stored locally
|
||||
trusted PersistentProvider
|
||||
|
||||
// New info, like a node rpc, or other import method.
|
||||
source Provider
|
||||
|
||||
// pending map to synchronize concurrent verification requests
|
||||
mtx sync.Mutex
|
||||
pendingVerifications map[int64]chan struct{}
|
||||
}
|
||||
|
||||
// NewDynamicVerifier returns a new DynamicVerifier. It uses the
|
||||
// trusted provider to store validated data and the source provider to
|
||||
// obtain missing data (e.g. FullCommits).
|
||||
//
|
||||
// The trusted provider should be a DBProvider.
|
||||
// The source provider should be a client.HTTPProvider.
|
||||
func NewDynamicVerifier(chainID string, trusted PersistentProvider, source Provider) *DynamicVerifier {
|
||||
return &DynamicVerifier{
|
||||
logger: log.NewNopLogger(),
|
||||
chainID: chainID,
|
||||
trusted: trusted,
|
||||
source: source,
|
||||
pendingVerifications: make(map[int64]chan struct{}, sizeOfPendingMap),
|
||||
}
|
||||
}
|
||||
|
||||
func (dv *DynamicVerifier) SetLogger(logger log.Logger) {
|
||||
logger = logger.With("module", "lite")
|
||||
dv.logger = logger
|
||||
dv.trusted.SetLogger(logger)
|
||||
dv.source.SetLogger(logger)
|
||||
}
|
||||
|
||||
// Implements Verifier.
|
||||
func (dv *DynamicVerifier) ChainID() string {
|
||||
return dv.chainID
|
||||
}
|
||||
|
||||
// Implements Verifier.
|
||||
//
|
||||
// If the validators have changed since the last known time, it looks to
|
||||
// dv.trusted and dv.source to prove the new validators. On success, it will
|
||||
// try to store the SignedHeader in dv.trusted if the next
|
||||
// validator can be sourced.
|
||||
func (dv *DynamicVerifier) Verify(shdr types.SignedHeader) error {
|
||||
|
||||
// Performs synchronization for multi-threads verification at the same height.
|
||||
dv.mtx.Lock()
|
||||
if pending := dv.pendingVerifications[shdr.Height]; pending != nil {
|
||||
dv.mtx.Unlock()
|
||||
<-pending // pending is chan struct{}
|
||||
} else {
|
||||
pending := make(chan struct{})
|
||||
dv.pendingVerifications[shdr.Height] = pending
|
||||
defer func() {
|
||||
close(pending)
|
||||
dv.mtx.Lock()
|
||||
delete(dv.pendingVerifications, shdr.Height)
|
||||
dv.mtx.Unlock()
|
||||
}()
|
||||
dv.mtx.Unlock()
|
||||
}
|
||||
|
||||
//Get the exact trusted commit for h, and if it is
|
||||
// equal to shdr, then it's already trusted, so
|
||||
// just return nil.
|
||||
trustedFCSameHeight, err := dv.trusted.LatestFullCommit(dv.chainID, shdr.Height, shdr.Height)
|
||||
if err == nil {
|
||||
// If loading trust commit successfully, and trust commit equal to shdr, then don't verify it,
|
||||
// just return nil.
|
||||
if bytes.Equal(trustedFCSameHeight.SignedHeader.Hash(), shdr.Hash()) {
|
||||
dv.logger.Info(fmt.Sprintf("Load full commit at height %d from cache, there is not need to verify.", shdr.Height))
|
||||
return nil
|
||||
}
|
||||
} else if !lerr.IsErrCommitNotFound(err) {
|
||||
// Return error if it is not CommitNotFound error
|
||||
dv.logger.Info(fmt.Sprintf("Encountered unknown error in loading full commit at height %d.", shdr.Height))
|
||||
return err
|
||||
}
|
||||
|
||||
// Get the latest known full commit <= h-1 from our trusted providers.
|
||||
// The full commit at h-1 contains the valset to sign for h.
|
||||
prevHeight := shdr.Height - 1
|
||||
trustedFC, err := dv.trusted.LatestFullCommit(dv.chainID, 1, prevHeight)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// sync up to the prevHeight and assert our latest NextValidatorSet
|
||||
// is the ValidatorSet for the SignedHeader
|
||||
if trustedFC.Height() == prevHeight {
|
||||
// Return error if valset doesn't match.
|
||||
if !bytes.Equal(
|
||||
trustedFC.NextValidators.Hash(),
|
||||
shdr.Header.ValidatorsHash) {
|
||||
return lerr.ErrUnexpectedValidators(
|
||||
trustedFC.NextValidators.Hash(),
|
||||
shdr.Header.ValidatorsHash)
|
||||
}
|
||||
} else {
|
||||
// If valset doesn't match, try to update
|
||||
if !bytes.Equal(
|
||||
trustedFC.NextValidators.Hash(),
|
||||
shdr.Header.ValidatorsHash) {
|
||||
// ... update.
|
||||
trustedFC, err = dv.updateToHeight(prevHeight)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// Return error if valset _still_ doesn't match.
|
||||
if !bytes.Equal(trustedFC.NextValidators.Hash(),
|
||||
shdr.Header.ValidatorsHash) {
|
||||
return lerr.ErrUnexpectedValidators(
|
||||
trustedFC.NextValidators.Hash(),
|
||||
shdr.Header.ValidatorsHash)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Verify the signed header using the matching valset.
|
||||
cert := NewBaseVerifier(dv.chainID, trustedFC.Height()+1, trustedFC.NextValidators)
|
||||
err = cert.Verify(shdr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// By now, the SignedHeader is fully validated and we're synced up to
|
||||
// SignedHeader.Height - 1. To sync to SignedHeader.Height, we need
|
||||
// the validator set at SignedHeader.Height + 1 so we can verify the
|
||||
// SignedHeader.NextValidatorSet.
|
||||
// TODO: is the ValidateFull below mostly redundant with the BaseVerifier.Verify above?
|
||||
// See https://github.com/tendermint/tendermint/issues/3174.
|
||||
|
||||
// Get the next validator set.
|
||||
nextValset, err := dv.source.ValidatorSet(dv.chainID, shdr.Height+1)
|
||||
if lerr.IsErrUnknownValidators(err) {
|
||||
// Ignore this error.
|
||||
return nil
|
||||
} else if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Create filled FullCommit.
|
||||
nfc := FullCommit{
|
||||
SignedHeader: shdr,
|
||||
Validators: trustedFC.NextValidators,
|
||||
NextValidators: nextValset,
|
||||
}
|
||||
// Validate the full commit. This checks the cryptographic
|
||||
// signatures of Commit against Validators.
|
||||
if err := nfc.ValidateFull(dv.chainID); err != nil {
|
||||
return err
|
||||
}
|
||||
// Trust it.
|
||||
return dv.trusted.SaveFullCommit(nfc)
|
||||
}
|
||||
|
||||
// verifyAndSave will verify if this is a valid source full commit given the
|
||||
// best match trusted full commit, and if good, persist to dv.trusted.
|
||||
// Returns ErrNotEnoughVotingPowerSigned when >2/3 of trustedFC did not sign sourceFC.
|
||||
// Panics if trustedFC.Height() >= sourceFC.Height().
|
||||
func (dv *DynamicVerifier) verifyAndSave(trustedFC, sourceFC FullCommit) error {
|
||||
if trustedFC.Height() >= sourceFC.Height() {
|
||||
panic("should not happen")
|
||||
}
|
||||
err := trustedFC.NextValidators.VerifyFutureCommit(
|
||||
sourceFC.Validators,
|
||||
dv.chainID, sourceFC.SignedHeader.Commit.BlockID,
|
||||
sourceFC.SignedHeader.Height, sourceFC.SignedHeader.Commit,
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return dv.trusted.SaveFullCommit(sourceFC)
|
||||
}
|
||||
|
||||
// updateToHeight will use divide-and-conquer to find a path to h.
|
||||
// Returns nil error iff we successfully verify and persist a full commit
|
||||
// for height h, using repeated applications of bisection if necessary.
|
||||
//
|
||||
// Returns ErrCommitNotFound if source provider doesn't have the commit for h.
|
||||
func (dv *DynamicVerifier) updateToHeight(h int64) (FullCommit, error) {
|
||||
|
||||
// Fetch latest full commit from source.
|
||||
sourceFC, err := dv.source.LatestFullCommit(dv.chainID, h, h)
|
||||
if err != nil {
|
||||
return FullCommit{}, err
|
||||
}
|
||||
|
||||
// If sourceFC.Height() != h, we can't do it.
|
||||
if sourceFC.Height() != h {
|
||||
return FullCommit{}, lerr.ErrCommitNotFound()
|
||||
}
|
||||
|
||||
// Validate the full commit. This checks the cryptographic
|
||||
// signatures of Commit against Validators.
|
||||
if err := sourceFC.ValidateFull(dv.chainID); err != nil {
|
||||
return FullCommit{}, err
|
||||
}
|
||||
|
||||
// Verify latest FullCommit against trusted FullCommits
|
||||
FOR_LOOP:
|
||||
for {
|
||||
// Fetch latest full commit from trusted.
|
||||
trustedFC, err := dv.trusted.LatestFullCommit(dv.chainID, 1, h)
|
||||
if err != nil {
|
||||
return FullCommit{}, err
|
||||
}
|
||||
// We have nothing to do.
|
||||
if trustedFC.Height() == h {
|
||||
return trustedFC, nil
|
||||
}
|
||||
|
||||
// Try to update to full commit with checks.
|
||||
err = dv.verifyAndSave(trustedFC, sourceFC)
|
||||
if err == nil {
|
||||
// All good!
|
||||
return sourceFC, nil
|
||||
}
|
||||
|
||||
// Handle special case when err is ErrNotEnoughVotingPowerSigned.
|
||||
if types.IsErrNotEnoughVotingPowerSigned(err) {
|
||||
// Divide and conquer.
|
||||
start, end := trustedFC.Height(), sourceFC.Height()
|
||||
if !(start < end) {
|
||||
panic("should not happen")
|
||||
}
|
||||
mid := (start + end) / 2
|
||||
_, err = dv.updateToHeight(mid)
|
||||
if err != nil {
|
||||
return FullCommit{}, err
|
||||
}
|
||||
// If we made it to mid, we retry.
|
||||
continue FOR_LOOP
|
||||
}
|
||||
return FullCommit{}, err
|
||||
}
|
||||
}
|
||||
|
||||
func (dv *DynamicVerifier) LastTrustedHeight() int64 {
|
||||
fc, err := dv.trusted.LatestFullCommit(dv.chainID, 1, 1<<63-1)
|
||||
if err != nil {
|
||||
panic("should not happen")
|
||||
}
|
||||
return fc.Height()
|
||||
}
|
||||
@@ -1,299 +0,0 @@
|
||||
package lite
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sync"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
dbm "github.com/tendermint/tm-db"
|
||||
|
||||
"github.com/tendermint/tendermint/crypto/tmhash"
|
||||
log "github.com/tendermint/tendermint/libs/log"
|
||||
"github.com/tendermint/tendermint/types"
|
||||
)
|
||||
|
||||
const testChainID = "inquiry-test"
|
||||
|
||||
func TestInquirerValidPath(t *testing.T) {
|
||||
assert, require := assert.New(t), require.New(t)
|
||||
trust := NewDBProvider("trust", dbm.NewMemDB())
|
||||
source := NewDBProvider("source", dbm.NewMemDB())
|
||||
|
||||
// Set up the validators to generate test blocks.
|
||||
var vote int64 = 10
|
||||
keys := genPrivKeys(5)
|
||||
nkeys := keys.Extend(1)
|
||||
|
||||
// Construct a bunch of commits, each with one more height than the last.
|
||||
chainID := testChainID
|
||||
consHash := []byte("params")
|
||||
resHash := []byte("results")
|
||||
count := 50
|
||||
fcz := make([]FullCommit, count)
|
||||
for i := 0; i < count; i++ {
|
||||
vals := keys.ToValidators(vote, 0)
|
||||
nextVals := nkeys.ToValidators(vote, 0)
|
||||
h := int64(1 + i)
|
||||
appHash := []byte(fmt.Sprintf("h=%d", h))
|
||||
fcz[i] = keys.GenFullCommit(
|
||||
chainID, h, nil,
|
||||
vals, nextVals,
|
||||
appHash, consHash, resHash, 0, len(keys))
|
||||
// Extend the keys by 1 each time.
|
||||
keys = nkeys
|
||||
nkeys = nkeys.Extend(1)
|
||||
}
|
||||
|
||||
// Initialize a Verifier with the initial state.
|
||||
err := trust.SaveFullCommit(fcz[0])
|
||||
require.Nil(err)
|
||||
cert := NewDynamicVerifier(chainID, trust, source)
|
||||
cert.SetLogger(log.TestingLogger())
|
||||
|
||||
// This should fail validation:
|
||||
sh := fcz[count-1].SignedHeader
|
||||
err = cert.Verify(sh)
|
||||
require.NotNil(err)
|
||||
|
||||
// Adding a few commits in the middle should be insufficient.
|
||||
for i := 10; i < 13; i++ {
|
||||
err := source.SaveFullCommit(fcz[i])
|
||||
require.Nil(err)
|
||||
}
|
||||
err = cert.Verify(sh)
|
||||
assert.NotNil(err)
|
||||
|
||||
// With more info, we succeed.
|
||||
for i := 0; i < count; i++ {
|
||||
err := source.SaveFullCommit(fcz[i])
|
||||
require.Nil(err)
|
||||
}
|
||||
|
||||
// TODO: Requires proposer address to be set in header.
|
||||
// err = cert.Verify(sh)
|
||||
// assert.Nil(err, "%+v", err)
|
||||
}
|
||||
|
||||
func TestDynamicVerify(t *testing.T) {
|
||||
trust := NewDBProvider("trust", dbm.NewMemDB())
|
||||
source := NewDBProvider("source", dbm.NewMemDB())
|
||||
|
||||
// 10 commits with one valset, 1 to change,
|
||||
// 10 commits with the next one
|
||||
n1, n2 := 10, 10
|
||||
nCommits := n1 + n2 + 1
|
||||
maxHeight := int64(nCommits)
|
||||
fcz := make([]FullCommit, nCommits)
|
||||
|
||||
// gen the 2 val sets
|
||||
chainID := "dynamic-verifier"
|
||||
power := int64(10)
|
||||
keys1 := genPrivKeys(5)
|
||||
vals1 := keys1.ToValidators(power, 0)
|
||||
keys2 := genPrivKeys(5)
|
||||
vals2 := keys2.ToValidators(power, 0)
|
||||
|
||||
// make some commits with the first
|
||||
for i := 0; i < n1; i++ {
|
||||
fcz[i] = makeFullCommit(int64(i), keys1, vals1, vals1, chainID)
|
||||
}
|
||||
|
||||
// update the val set
|
||||
fcz[n1] = makeFullCommit(int64(n1), keys1, vals1, vals2, chainID)
|
||||
|
||||
// make some commits with the new one
|
||||
for i := n1 + 1; i < nCommits; i++ {
|
||||
fcz[i] = makeFullCommit(int64(i), keys2, vals2, vals2, chainID)
|
||||
}
|
||||
|
||||
// Save everything in the source
|
||||
for _, fc := range fcz {
|
||||
source.SaveFullCommit(fc)
|
||||
}
|
||||
|
||||
// Initialize a Verifier with the initial state.
|
||||
err := trust.SaveFullCommit(fcz[0])
|
||||
require.Nil(t, err)
|
||||
ver := NewDynamicVerifier(chainID, trust, source)
|
||||
ver.SetLogger(log.TestingLogger())
|
||||
|
||||
// fetch the latest from the source
|
||||
_, err = source.LatestFullCommit(chainID, 1, maxHeight)
|
||||
require.NoError(t, err)
|
||||
|
||||
// TODO: Requires proposer address to be set in header.
|
||||
// try to update to the latest
|
||||
// err = ver.Verify(latestFC.SignedHeader)
|
||||
// require.NoError(t, err)
|
||||
}
|
||||
|
||||
func makeFullCommit(height int64, keys privKeys, vals, nextVals *types.ValidatorSet, chainID string) FullCommit {
|
||||
height++
|
||||
|
||||
consHash := tmhash.Sum([]byte("special-params"))
|
||||
appHash := tmhash.Sum([]byte(fmt.Sprintf("h=%d", height)))
|
||||
resHash := tmhash.Sum([]byte(fmt.Sprintf("res=%d", height)))
|
||||
|
||||
return keys.GenFullCommit(
|
||||
chainID, height, nil,
|
||||
vals, nextVals,
|
||||
appHash, consHash, resHash, 0, len(keys),
|
||||
)
|
||||
}
|
||||
|
||||
func TestInquirerVerifyHistorical(t *testing.T) {
|
||||
assert, require := assert.New(t), require.New(t)
|
||||
trust := NewDBProvider("trust", dbm.NewMemDB())
|
||||
source := NewDBProvider("source", dbm.NewMemDB())
|
||||
|
||||
// Set up the validators to generate test blocks.
|
||||
var vote int64 = 10
|
||||
keys := genPrivKeys(5)
|
||||
nkeys := keys.Extend(1)
|
||||
|
||||
// Construct a bunch of commits, each with one more height than the last.
|
||||
chainID := testChainID
|
||||
count := 10
|
||||
consHash := []byte("special-params")
|
||||
fcz := make([]FullCommit, count)
|
||||
for i := 0; i < count; i++ {
|
||||
vals := keys.ToValidators(vote, 0)
|
||||
nextVals := nkeys.ToValidators(vote, 0)
|
||||
h := int64(1 + i)
|
||||
appHash := []byte(fmt.Sprintf("h=%d", h))
|
||||
resHash := []byte(fmt.Sprintf("res=%d", h))
|
||||
fcz[i] = keys.GenFullCommit(
|
||||
chainID, h, nil,
|
||||
vals, nextVals,
|
||||
appHash, consHash, resHash, 0, len(keys))
|
||||
// Extend the keys by 1 each time.
|
||||
keys = nkeys
|
||||
nkeys = nkeys.Extend(1)
|
||||
}
|
||||
|
||||
// Initialize a Verifier with the initial state.
|
||||
err := trust.SaveFullCommit(fcz[0])
|
||||
require.Nil(err)
|
||||
cert := NewDynamicVerifier(chainID, trust, source)
|
||||
cert.SetLogger(log.TestingLogger())
|
||||
|
||||
// Store a few full commits as trust.
|
||||
for _, i := range []int{2, 5} {
|
||||
trust.SaveFullCommit(fcz[i])
|
||||
}
|
||||
|
||||
// See if we can jump forward using trusted full commits.
|
||||
// Souce doesn't have fcz[9] so cert.LastTrustedHeight wont' change.
|
||||
err = source.SaveFullCommit(fcz[7])
|
||||
require.Nil(err, "%+v", err)
|
||||
|
||||
// TODO: Requires proposer address to be set in header.
|
||||
// sh := fcz[8].SignedHeader
|
||||
// err = cert.Verify(sh)
|
||||
// require.Nil(err, "%+v", err)
|
||||
// assert.Equal(fcz[7].Height(), cert.LastTrustedHeight())
|
||||
|
||||
commit, err := trust.LatestFullCommit(chainID, fcz[8].Height(), fcz[8].Height())
|
||||
require.NotNil(err, "%+v", err)
|
||||
assert.Equal(commit, (FullCommit{}))
|
||||
|
||||
// With fcz[9] Verify will update last trusted height.
|
||||
err = source.SaveFullCommit(fcz[9])
|
||||
require.Nil(err, "%+v", err)
|
||||
|
||||
// TODO: Requires proposer address to be set in header.
|
||||
// sh = fcz[8].SignedHeader
|
||||
// err = cert.Verify(sh)
|
||||
// require.Nil(err, "%+v", err)
|
||||
// assert.Equal(fcz[8].Height(), cert.LastTrustedHeight())
|
||||
|
||||
// TODO: Requires proposer address to be set in header.
|
||||
// commit, err = trust.LatestFullCommit(chainID, fcz[8].Height(), fcz[8].Height())
|
||||
// require.Nil(err, "%+v", err)
|
||||
// assert.Equal(commit.Height(), fcz[8].Height())
|
||||
|
||||
// Add access to all full commits via untrusted source.
|
||||
for i := 0; i < count; i++ {
|
||||
err := source.SaveFullCommit(fcz[i])
|
||||
require.Nil(err)
|
||||
}
|
||||
|
||||
// TODO: Requires proposer address to be set in header.
|
||||
// Try to check an unknown seed in the past.
|
||||
// sh = fcz[3].SignedHeader
|
||||
// err = cert.Verify(sh)
|
||||
// require.Nil(err, "%+v", err)
|
||||
// assert.Equal(fcz[8].Height(), cert.LastTrustedHeight())
|
||||
|
||||
// TODO: Requires proposer address to be set in header.
|
||||
// Jump all the way forward again.
|
||||
// sh = fcz[count-1].SignedHeader
|
||||
// err = cert.Verify(sh)
|
||||
// require.Nil(err, "%+v", err)
|
||||
// assert.Equal(fcz[9].Height(), cert.LastTrustedHeight())
|
||||
}
|
||||
|
||||
func TestConcurrencyInquirerVerify(t *testing.T) {
|
||||
_, require := assert.New(t), require.New(t)
|
||||
trust := NewDBProvider("trust", dbm.NewMemDB()).SetLimit(10)
|
||||
source := NewDBProvider("source", dbm.NewMemDB())
|
||||
|
||||
// Set up the validators to generate test blocks.
|
||||
var vote int64 = 10
|
||||
keys := genPrivKeys(5)
|
||||
nkeys := keys.Extend(1)
|
||||
|
||||
// Construct a bunch of commits, each with one more height than the last.
|
||||
chainID := testChainID
|
||||
count := 10
|
||||
consHash := []byte("special-params")
|
||||
fcz := make([]FullCommit, count)
|
||||
for i := 0; i < count; i++ {
|
||||
vals := keys.ToValidators(vote, 0)
|
||||
nextVals := nkeys.ToValidators(vote, 0)
|
||||
h := int64(1 + i)
|
||||
appHash := []byte(fmt.Sprintf("h=%d", h))
|
||||
resHash := []byte(fmt.Sprintf("res=%d", h))
|
||||
fcz[i] = keys.GenFullCommit(
|
||||
chainID, h, nil,
|
||||
vals, nextVals,
|
||||
appHash, consHash, resHash, 0, len(keys))
|
||||
// Extend the keys by 1 each time.
|
||||
keys = nkeys
|
||||
nkeys = nkeys.Extend(1)
|
||||
}
|
||||
|
||||
// Initialize a Verifier with the initial state.
|
||||
err := trust.SaveFullCommit(fcz[0])
|
||||
require.Nil(err)
|
||||
cert := NewDynamicVerifier(chainID, trust, source)
|
||||
cert.SetLogger(log.TestingLogger())
|
||||
|
||||
err = source.SaveFullCommit(fcz[7])
|
||||
require.Nil(err, "%+v", err)
|
||||
err = source.SaveFullCommit(fcz[8])
|
||||
require.Nil(err, "%+v", err)
|
||||
sh := fcz[8].SignedHeader
|
||||
|
||||
var wg sync.WaitGroup
|
||||
count = 100
|
||||
errList := make([]error, count)
|
||||
|
||||
for i := 0; i < count; i++ {
|
||||
wg.Add(1)
|
||||
go func(index int) {
|
||||
errList[index] = cert.Verify(sh)
|
||||
defer wg.Done()
|
||||
}(i)
|
||||
}
|
||||
|
||||
wg.Wait()
|
||||
|
||||
// TODO: Requires proposer address to be set in header.
|
||||
// for _, err := range errList {
|
||||
// require.Nil(err)
|
||||
// }
|
||||
}
|
||||
@@ -1,99 +0,0 @@
|
||||
package errors
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
//----------------------------------------
|
||||
// Error types
|
||||
|
||||
type errCommitNotFound struct{}
|
||||
|
||||
func (e errCommitNotFound) Error() string {
|
||||
return "Commit not found by provider"
|
||||
}
|
||||
|
||||
type errUnexpectedValidators struct {
|
||||
got []byte
|
||||
want []byte
|
||||
}
|
||||
|
||||
func (e errUnexpectedValidators) Error() string {
|
||||
return fmt.Sprintf("Validator set is different. Got %X want %X",
|
||||
e.got, e.want)
|
||||
}
|
||||
|
||||
type errUnknownValidators struct {
|
||||
chainID string
|
||||
height int64
|
||||
}
|
||||
|
||||
func (e errUnknownValidators) Error() string {
|
||||
return fmt.Sprintf("Validators are unknown or missing for chain %s and height %d",
|
||||
e.chainID, e.height)
|
||||
}
|
||||
|
||||
type errEmptyTree struct{}
|
||||
|
||||
func (e errEmptyTree) Error() string {
|
||||
return "Tree is empty"
|
||||
}
|
||||
|
||||
//----------------------------------------
|
||||
// Methods for above error types
|
||||
|
||||
//-----------------
|
||||
// ErrCommitNotFound
|
||||
|
||||
// ErrCommitNotFound indicates that a the requested commit was not found.
|
||||
func ErrCommitNotFound() error {
|
||||
return errors.Wrap(errCommitNotFound{}, "")
|
||||
}
|
||||
|
||||
func IsErrCommitNotFound(err error) bool {
|
||||
_, ok := errors.Cause(err).(errCommitNotFound)
|
||||
return ok
|
||||
}
|
||||
|
||||
//-----------------
|
||||
// ErrUnexpectedValidators
|
||||
|
||||
// ErrUnexpectedValidators indicates a validator set mismatch.
|
||||
func ErrUnexpectedValidators(got, want []byte) error {
|
||||
return errors.Wrap(errUnexpectedValidators{
|
||||
got: got,
|
||||
want: want,
|
||||
}, "")
|
||||
}
|
||||
|
||||
func IsErrUnexpectedValidators(err error) bool {
|
||||
_, ok := errors.Cause(err).(errUnexpectedValidators)
|
||||
return ok
|
||||
}
|
||||
|
||||
//-----------------
|
||||
// ErrUnknownValidators
|
||||
|
||||
// ErrUnknownValidators indicates that some validator set was missing or unknown.
|
||||
func ErrUnknownValidators(chainID string, height int64) error {
|
||||
return errors.Wrap(errUnknownValidators{chainID, height}, "")
|
||||
}
|
||||
|
||||
func IsErrUnknownValidators(err error) bool {
|
||||
_, ok := errors.Cause(err).(errUnknownValidators)
|
||||
return ok
|
||||
}
|
||||
|
||||
//-----------------
|
||||
// ErrEmptyTree
|
||||
|
||||
func ErrEmptyTree() error {
|
||||
return errors.Wrap(errEmptyTree{}, "")
|
||||
}
|
||||
|
||||
func IsErrEmptyTree(err error) bool {
|
||||
_, ok := errors.Cause(err).(errEmptyTree)
|
||||
return ok
|
||||
}
|
||||
159
lite/helpers.go
159
lite/helpers.go
@@ -1,159 +0,0 @@
|
||||
package lite
|
||||
|
||||
import (
|
||||
"github.com/tendermint/tendermint/crypto"
|
||||
"github.com/tendermint/tendermint/crypto/ed25519"
|
||||
"github.com/tendermint/tendermint/crypto/secp256k1"
|
||||
|
||||
"github.com/tendermint/tendermint/types"
|
||||
tmtime "github.com/tendermint/tendermint/types/time"
|
||||
)
|
||||
|
||||
// PrivKeys is a helper type for testing.
|
||||
//
|
||||
// It lets us simulate signing with many keys. The main use case is to create
|
||||
// a set, and call GenSignedHeader to get properly signed header for testing.
|
||||
//
|
||||
// You can set different weights of validators each time you call ToValidators,
|
||||
// and can optionally extend the validator set later with Extend.
|
||||
type privKeys []crypto.PrivKey
|
||||
|
||||
// genPrivKeys produces an array of private keys to generate commits.
|
||||
func genPrivKeys(n int) privKeys {
|
||||
res := make(privKeys, n)
|
||||
for i := range res {
|
||||
res[i] = ed25519.GenPrivKey()
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
// Change replaces the key at index i.
|
||||
func (pkz privKeys) Change(i int) privKeys {
|
||||
res := make(privKeys, len(pkz))
|
||||
copy(res, pkz)
|
||||
res[i] = ed25519.GenPrivKey()
|
||||
return res
|
||||
}
|
||||
|
||||
// Extend adds n more keys (to remove, just take a slice).
|
||||
func (pkz privKeys) Extend(n int) privKeys {
|
||||
extra := genPrivKeys(n)
|
||||
return append(pkz, extra...)
|
||||
}
|
||||
|
||||
// GenSecpPrivKeys produces an array of secp256k1 private keys to generate commits.
|
||||
func genSecpPrivKeys(n int) privKeys {
|
||||
res := make(privKeys, n)
|
||||
for i := range res {
|
||||
res[i] = secp256k1.GenPrivKey()
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
// ExtendSecp adds n more secp256k1 keys (to remove, just take a slice).
|
||||
func (pkz privKeys) ExtendSecp(n int) privKeys {
|
||||
extra := genSecpPrivKeys(n)
|
||||
return append(pkz, extra...)
|
||||
}
|
||||
|
||||
// ToValidators produces a valset from the set of keys.
|
||||
// The first key has weight `init` and it increases by `inc` every step
|
||||
// so we can have all the same weight, or a simple linear distribution
|
||||
// (should be enough for testing).
|
||||
func (pkz privKeys) ToValidators(init, inc int64) *types.ValidatorSet {
|
||||
res := make([]*types.Validator, len(pkz))
|
||||
for i, k := range pkz {
|
||||
res[i] = types.NewValidator(k.PubKey(), init+int64(i)*inc)
|
||||
}
|
||||
return types.NewValidatorSet(res)
|
||||
}
|
||||
|
||||
// signHeader properly signs the header with all keys from first to last exclusive.
|
||||
func (pkz privKeys) signHeader(header *types.Header, first, last int) *types.Commit {
|
||||
commitSigs := make([]types.CommitSig, len(pkz))
|
||||
for i := 0; i < len(pkz); i++ {
|
||||
commitSigs[i] = types.NewCommitSigAbsent()
|
||||
}
|
||||
|
||||
// We need this list to keep the ordering.
|
||||
vset := pkz.ToValidators(1, 0)
|
||||
|
||||
blockID := types.BlockID{
|
||||
Hash: header.Hash(),
|
||||
PartsHeader: types.PartSetHeader{Total: 1, Hash: crypto.CRandBytes(32)},
|
||||
}
|
||||
|
||||
// Fill in the votes we want.
|
||||
for i := first; i < last && i < len(pkz); i++ {
|
||||
vote := makeVote(header, vset, pkz[i], blockID)
|
||||
commitSigs[vote.ValidatorIndex] = vote.CommitSig()
|
||||
}
|
||||
|
||||
return types.NewCommit(header.Height, 1, blockID, commitSigs)
|
||||
}
|
||||
|
||||
func makeVote(header *types.Header, valset *types.ValidatorSet, key crypto.PrivKey, blockID types.BlockID) *types.Vote {
|
||||
addr := key.PubKey().Address()
|
||||
idx, _ := valset.GetByAddress(addr)
|
||||
vote := &types.Vote{
|
||||
ValidatorAddress: addr,
|
||||
ValidatorIndex: idx,
|
||||
Height: header.Height,
|
||||
Round: 1,
|
||||
Timestamp: tmtime.Now(),
|
||||
Type: types.PrecommitType,
|
||||
BlockID: blockID,
|
||||
}
|
||||
// Sign it
|
||||
signBytes := vote.SignBytes(header.ChainID)
|
||||
// TODO Consider reworking makeVote API to return an error
|
||||
sig, err := key.Sign(signBytes)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
vote.Signature = sig
|
||||
|
||||
return vote
|
||||
}
|
||||
|
||||
func genHeader(chainID string, height int64, txs types.Txs,
|
||||
valset, nextValset *types.ValidatorSet, appHash, consHash, resHash []byte) *types.Header {
|
||||
|
||||
return &types.Header{
|
||||
ChainID: chainID,
|
||||
Height: height,
|
||||
Time: tmtime.Now(),
|
||||
// LastBlockID
|
||||
// LastCommitHash
|
||||
ValidatorsHash: valset.Hash(),
|
||||
NextValidatorsHash: nextValset.Hash(),
|
||||
DataHash: txs.Hash(),
|
||||
AppHash: appHash,
|
||||
ConsensusHash: consHash,
|
||||
LastResultsHash: resHash,
|
||||
}
|
||||
}
|
||||
|
||||
// GenSignedHeader calls genHeader and signHeader and combines them into a SignedHeader.
|
||||
func (pkz privKeys) GenSignedHeader(chainID string, height int64, txs types.Txs,
|
||||
valset, nextValset *types.ValidatorSet, appHash, consHash, resHash []byte, first, last int) types.SignedHeader {
|
||||
|
||||
header := genHeader(chainID, height, txs, valset, nextValset, appHash, consHash, resHash)
|
||||
check := types.SignedHeader{
|
||||
Header: header,
|
||||
Commit: pkz.signHeader(header, first, last),
|
||||
}
|
||||
return check
|
||||
}
|
||||
|
||||
// GenFullCommit calls genHeader and signHeader and combines them into a FullCommit.
|
||||
func (pkz privKeys) GenFullCommit(chainID string, height int64, txs types.Txs,
|
||||
valset, nextValset *types.ValidatorSet, appHash, consHash, resHash []byte, first, last int) FullCommit {
|
||||
|
||||
header := genHeader(chainID, height, txs, valset, nextValset, appHash, consHash, resHash)
|
||||
commit := types.SignedHeader{
|
||||
Header: header,
|
||||
Commit: pkz.signHeader(header, first, last),
|
||||
}
|
||||
return NewFullCommit(commit, valset, nextValset)
|
||||
}
|
||||
@@ -1,85 +0,0 @@
|
||||
package lite
|
||||
|
||||
import (
|
||||
log "github.com/tendermint/tendermint/libs/log"
|
||||
lerr "github.com/tendermint/tendermint/lite/errors"
|
||||
"github.com/tendermint/tendermint/types"
|
||||
)
|
||||
|
||||
var _ PersistentProvider = (*multiProvider)(nil)
|
||||
|
||||
// multiProvider allows you to place one or more caches in front of a source
|
||||
// Provider. It runs through them in order until a match is found.
|
||||
type multiProvider struct {
|
||||
logger log.Logger
|
||||
providers []PersistentProvider
|
||||
}
|
||||
|
||||
// NewMultiProvider returns a new provider which wraps multiple other providers.
|
||||
func NewMultiProvider(providers ...PersistentProvider) PersistentProvider {
|
||||
return &multiProvider{
|
||||
logger: log.NewNopLogger(),
|
||||
providers: providers,
|
||||
}
|
||||
}
|
||||
|
||||
// SetLogger sets logger on self and all subproviders.
|
||||
func (mc *multiProvider) SetLogger(logger log.Logger) {
|
||||
mc.logger = logger
|
||||
for _, p := range mc.providers {
|
||||
p.SetLogger(logger)
|
||||
}
|
||||
}
|
||||
|
||||
// SaveFullCommit saves on all providers, and aborts on the first error.
|
||||
func (mc *multiProvider) SaveFullCommit(fc FullCommit) (err error) {
|
||||
for _, p := range mc.providers {
|
||||
err = p.SaveFullCommit(fc)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// LatestFullCommit loads the latest from all providers and provides
|
||||
// the latest FullCommit that satisfies the conditions.
|
||||
// Returns the first error encountered.
|
||||
func (mc *multiProvider) LatestFullCommit(chainID string, minHeight, maxHeight int64) (fc FullCommit, err error) {
|
||||
for _, p := range mc.providers {
|
||||
var commit FullCommit
|
||||
commit, err = p.LatestFullCommit(chainID, minHeight, maxHeight)
|
||||
if lerr.IsErrCommitNotFound(err) {
|
||||
err = nil
|
||||
continue
|
||||
} else if err != nil {
|
||||
return
|
||||
}
|
||||
if fc == (FullCommit{}) {
|
||||
fc = commit
|
||||
} else if commit.Height() > fc.Height() {
|
||||
fc = commit
|
||||
}
|
||||
if fc.Height() == maxHeight {
|
||||
return
|
||||
}
|
||||
}
|
||||
if fc == (FullCommit{}) {
|
||||
err = lerr.ErrCommitNotFound()
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// ValidatorSet returns validator set at height as provided by the first
|
||||
// provider which has it, or an error otherwise.
|
||||
func (mc *multiProvider) ValidatorSet(chainID string, height int64) (valset *types.ValidatorSet, err error) {
|
||||
for _, p := range mc.providers {
|
||||
valset, err = p.ValidatorSet(chainID, height)
|
||||
if err == nil {
|
||||
// TODO Log unexpected types of errors.
|
||||
return valset, nil
|
||||
}
|
||||
}
|
||||
return nil, lerr.ErrUnknownValidators(chainID, height)
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
package lite
|
||||
|
||||
import (
|
||||
"github.com/tendermint/tendermint/libs/log"
|
||||
"github.com/tendermint/tendermint/types"
|
||||
)
|
||||
|
||||
// Provider provides information for the lite client to sync validators.
|
||||
// Examples: MemProvider, files.Provider, client.Provider, CacheProvider.
|
||||
type Provider interface {
|
||||
|
||||
// LatestFullCommit returns the latest commit with minHeight <= height <=
|
||||
// maxHeight.
|
||||
// If maxHeight is zero, returns the latest where minHeight <= height.
|
||||
LatestFullCommit(chainID string, minHeight, maxHeight int64) (FullCommit, error)
|
||||
|
||||
// Get the valset that corresponds to chainID and height and return.
|
||||
// Height must be >= 1.
|
||||
ValidatorSet(chainID string, height int64) (*types.ValidatorSet, error)
|
||||
|
||||
// Set a logger.
|
||||
SetLogger(logger log.Logger)
|
||||
}
|
||||
|
||||
// A provider that can also persist new information.
|
||||
// Examples: MemProvider, files.Provider, CacheProvider.
|
||||
type PersistentProvider interface {
|
||||
Provider
|
||||
|
||||
// SaveFullCommit saves a FullCommit (without verification).
|
||||
SaveFullCommit(fc FullCommit) error
|
||||
}
|
||||
@@ -1,141 +0,0 @@
|
||||
package lite
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
dbm "github.com/tendermint/tm-db"
|
||||
|
||||
log "github.com/tendermint/tendermint/libs/log"
|
||||
lerr "github.com/tendermint/tendermint/lite/errors"
|
||||
"github.com/tendermint/tendermint/types"
|
||||
)
|
||||
|
||||
// missingProvider doesn't store anything, always a miss.
|
||||
// Designed as a mock for testing.
|
||||
type missingProvider struct{}
|
||||
|
||||
// NewMissingProvider returns a provider which does not store anything and always misses.
|
||||
func NewMissingProvider() PersistentProvider {
|
||||
return missingProvider{}
|
||||
}
|
||||
|
||||
func (missingProvider) SaveFullCommit(FullCommit) error { return nil }
|
||||
func (missingProvider) LatestFullCommit(chainID string, minHeight, maxHeight int64) (FullCommit, error) {
|
||||
return FullCommit{}, lerr.ErrCommitNotFound()
|
||||
}
|
||||
func (missingProvider) ValidatorSet(chainID string, height int64) (*types.ValidatorSet, error) {
|
||||
return nil, errors.New("missing validator set")
|
||||
}
|
||||
func (missingProvider) SetLogger(_ log.Logger) {}
|
||||
|
||||
func TestMemProvider(t *testing.T) {
|
||||
p := NewDBProvider("mem", dbm.NewMemDB())
|
||||
checkProvider(t, p, "test-mem", "empty")
|
||||
}
|
||||
|
||||
func TestMultiProvider(t *testing.T) {
|
||||
p := NewMultiProvider(
|
||||
NewMissingProvider(),
|
||||
NewDBProvider("mem", dbm.NewMemDB()),
|
||||
NewMissingProvider(),
|
||||
)
|
||||
checkProvider(t, p, "test-cache", "kjfhekfhkewhgit")
|
||||
}
|
||||
|
||||
func checkProvider(t *testing.T, p PersistentProvider, chainID, app string) {
|
||||
assert, require := assert.New(t), require.New(t)
|
||||
appHash := []byte(app)
|
||||
keys := genPrivKeys(5)
|
||||
count := 10
|
||||
|
||||
// Make a bunch of full commits.
|
||||
fcz := make([]FullCommit, count)
|
||||
for i := 0; i < count; i++ {
|
||||
vals := keys.ToValidators(10, int64(count/2))
|
||||
h := int64(20 + 10*i)
|
||||
fcz[i] = keys.GenFullCommit(chainID, h, nil, vals, vals, appHash, []byte("params"), []byte("results"), 0, 5)
|
||||
}
|
||||
|
||||
// Check that provider is initially empty.
|
||||
fc, err := p.LatestFullCommit(chainID, 1, 1<<63-1)
|
||||
require.NotNil(err)
|
||||
assert.True(lerr.IsErrCommitNotFound(err))
|
||||
|
||||
// Save all full commits to the provider.
|
||||
for _, fc := range fcz {
|
||||
err = p.SaveFullCommit(fc)
|
||||
require.Nil(err)
|
||||
// Make sure we can get it back.
|
||||
fc2, err := p.LatestFullCommit(chainID, fc.Height(), fc.Height())
|
||||
assert.Nil(err)
|
||||
assert.Equal(fc.SignedHeader, fc2.SignedHeader)
|
||||
assert.Equal(fc.Validators, fc2.Validators)
|
||||
assert.Equal(fc.NextValidators, fc2.NextValidators)
|
||||
}
|
||||
|
||||
// Make sure we get the last hash if we overstep.
|
||||
fc, err = p.LatestFullCommit(chainID, 1, 5000)
|
||||
if assert.Nil(err) {
|
||||
assert.Equal(fcz[count-1].Height(), fc.Height())
|
||||
assert.Equal(fcz[count-1], fc)
|
||||
}
|
||||
|
||||
// ... and middle ones as well.
|
||||
fc, err = p.LatestFullCommit(chainID, 1, 47)
|
||||
if assert.Nil(err) {
|
||||
// we only step by 10, so 40 must be the one below this
|
||||
assert.EqualValues(40, fc.Height())
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// This will make a get height, and if it is good, set the data as well.
|
||||
func checkLatestFullCommit(t *testing.T, p PersistentProvider, chainID string, ask, expect int64) {
|
||||
fc, err := p.LatestFullCommit(chainID, 1, ask)
|
||||
require.Nil(t, err)
|
||||
if assert.Equal(t, expect, fc.Height()) {
|
||||
err = p.SaveFullCommit(fc)
|
||||
require.Nil(t, err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMultiLatestFullCommit(t *testing.T) {
|
||||
require := require.New(t)
|
||||
|
||||
// We will write data to the second level of the cache (p2), and see what
|
||||
// gets cached/stored in.
|
||||
p := NewDBProvider("mem1", dbm.NewMemDB())
|
||||
p2 := NewDBProvider("mem2", dbm.NewMemDB())
|
||||
cp := NewMultiProvider(p, p2)
|
||||
|
||||
chainID := "cache-best-height"
|
||||
appHash := []byte("01234567")
|
||||
keys := genPrivKeys(5)
|
||||
count := 10
|
||||
|
||||
// Set a bunch of full commits.
|
||||
for i := 0; i < count; i++ {
|
||||
vals := keys.ToValidators(10, int64(count/2))
|
||||
h := int64(10 * (i + 1))
|
||||
fc := keys.GenFullCommit(chainID, h, nil, vals, vals, appHash, []byte("params"), []byte("results"), 0, 5)
|
||||
err := p2.SaveFullCommit(fc)
|
||||
require.NoError(err)
|
||||
}
|
||||
|
||||
// Get a few heights from the cache and set them proper.
|
||||
checkLatestFullCommit(t, cp, chainID, 57, 50)
|
||||
checkLatestFullCommit(t, cp, chainID, 33, 30)
|
||||
|
||||
// make sure they are set in p as well (but nothing else)
|
||||
checkLatestFullCommit(t, p, chainID, 44, 30)
|
||||
checkLatestFullCommit(t, p, chainID, 50, 50)
|
||||
checkLatestFullCommit(t, p, chainID, 99, 50)
|
||||
|
||||
// now, query the cache for a higher value
|
||||
checkLatestFullCommit(t, p2, chainID, 99, 90)
|
||||
checkLatestFullCommit(t, cp, chainID, 99, 90)
|
||||
}
|
||||
@@ -1,48 +0,0 @@
|
||||
package proxy
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
|
||||
"github.com/tendermint/tendermint/types"
|
||||
)
|
||||
|
||||
func ValidateBlockMeta(meta *types.BlockMeta, sh types.SignedHeader) error {
|
||||
if meta == nil {
|
||||
return errors.New("expecting a non-nil BlockMeta")
|
||||
}
|
||||
// TODO: check the BlockID??
|
||||
return ValidateHeader(&meta.Header, sh)
|
||||
}
|
||||
|
||||
func ValidateBlock(meta *types.Block, sh types.SignedHeader) error {
|
||||
if meta == nil {
|
||||
return errors.New("expecting a non-nil Block")
|
||||
}
|
||||
err := ValidateHeader(&meta.Header, sh)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !bytes.Equal(meta.Data.Hash(), meta.Header.DataHash) {
|
||||
return errors.New("data hash doesn't match header")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func ValidateHeader(head *types.Header, sh types.SignedHeader) error {
|
||||
if head == nil {
|
||||
return errors.New("expecting a non-nil Header")
|
||||
}
|
||||
if sh.Header == nil {
|
||||
return errors.New("unexpected empty SignedHeader")
|
||||
}
|
||||
// Make sure they are for the same height (obvious fail).
|
||||
if head.Height != sh.Height {
|
||||
return errors.New("header heights mismatched")
|
||||
}
|
||||
// Check if they are equal by using hashes.
|
||||
if !bytes.Equal(head.Hash(), sh.Hash()) {
|
||||
return errors.New("headers don't match")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
package proxy
|
||||
|
||||
import (
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
type errNoData struct{}
|
||||
|
||||
func (e errNoData) Error() string {
|
||||
return "No data returned for query"
|
||||
}
|
||||
|
||||
// IsErrNoData checks whether an error is due to a query returning empty data
|
||||
func IsErrNoData(err error) bool {
|
||||
_, ok := errors.Cause(err).(errNoData)
|
||||
return ok
|
||||
}
|
||||
|
||||
func ErrNoData() error {
|
||||
return errors.Wrap(errNoData{}, "")
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
package proxy
|
||||
|
||||
import (
|
||||
"github.com/tendermint/tendermint/crypto/merkle"
|
||||
)
|
||||
|
||||
func defaultProofRuntime() *merkle.ProofRuntime {
|
||||
prt := merkle.NewProofRuntime()
|
||||
prt.RegisterOpDecoder(
|
||||
merkle.ProofOpSimpleValue,
|
||||
merkle.SimpleValueOpDecoder,
|
||||
)
|
||||
return prt
|
||||
}
|
||||
@@ -1,187 +0,0 @@
|
||||
package proxy
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
|
||||
amino "github.com/tendermint/go-amino"
|
||||
|
||||
"github.com/tendermint/tendermint/libs/bytes"
|
||||
"github.com/tendermint/tendermint/libs/log"
|
||||
rpcclient "github.com/tendermint/tendermint/rpc/client"
|
||||
ctypes "github.com/tendermint/tendermint/rpc/core/types"
|
||||
rpcserver "github.com/tendermint/tendermint/rpc/jsonrpc/server"
|
||||
rpctypes "github.com/tendermint/tendermint/rpc/jsonrpc/types"
|
||||
"github.com/tendermint/tendermint/types"
|
||||
)
|
||||
|
||||
const (
|
||||
wsEndpoint = "/websocket"
|
||||
)
|
||||
|
||||
// StartProxy will start the websocket manager on the client,
|
||||
// set up the rpc routes to proxy via the given client,
|
||||
// and start up an http/rpc server on the location given by bind (eg. :1234)
|
||||
// NOTE: This function blocks - you may want to call it in a go-routine.
|
||||
func StartProxy(c rpcclient.Client, listenAddr string, logger log.Logger, maxOpenConnections int) error {
|
||||
err := c.Start()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
cdc := amino.NewCodec()
|
||||
ctypes.RegisterAmino(cdc)
|
||||
r := RPCRoutes(c)
|
||||
|
||||
// build the handler...
|
||||
mux := http.NewServeMux()
|
||||
rpcserver.RegisterRPCFuncs(mux, r, cdc, logger)
|
||||
|
||||
unsubscribeFromAllEvents := func(remoteAddr string) {
|
||||
if err := c.UnsubscribeAll(context.Background(), remoteAddr); err != nil {
|
||||
logger.Error("Failed to unsubscribe from events", "err", err)
|
||||
}
|
||||
}
|
||||
wm := rpcserver.NewWebsocketManager(r, cdc, rpcserver.OnDisconnect(unsubscribeFromAllEvents))
|
||||
wm.SetLogger(logger)
|
||||
// core.SetLogger(logger)
|
||||
mux.HandleFunc(wsEndpoint, wm.WebsocketHandler)
|
||||
|
||||
config := rpcserver.DefaultConfig()
|
||||
config.MaxOpenConnections = maxOpenConnections
|
||||
l, err := rpcserver.Listen(listenAddr, config)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return rpcserver.Serve(l, mux, logger, config)
|
||||
}
|
||||
|
||||
// RPCRoutes just routes everything to the given client, as if it were
|
||||
// a tendermint fullnode.
|
||||
//
|
||||
// if we want security, the client must implement it as a secure client
|
||||
func RPCRoutes(c rpcclient.Client) map[string]*rpcserver.RPCFunc {
|
||||
return map[string]*rpcserver.RPCFunc{
|
||||
// Subscribe/unsubscribe are reserved for websocket events.
|
||||
"subscribe": rpcserver.NewWSRPCFunc(c.(Wrapper).SubscribeWS, "query"),
|
||||
"unsubscribe": rpcserver.NewWSRPCFunc(c.(Wrapper).UnsubscribeWS, "query"),
|
||||
"unsubscribe_all": rpcserver.NewWSRPCFunc(c.(Wrapper).UnsubscribeAllWS, ""),
|
||||
|
||||
// info API
|
||||
"status": rpcserver.NewRPCFunc(makeStatusFunc(c), ""),
|
||||
"blockchain": rpcserver.NewRPCFunc(makeBlockchainInfoFunc(c), "minHeight,maxHeight"),
|
||||
"genesis": rpcserver.NewRPCFunc(makeGenesisFunc(c), ""),
|
||||
"block": rpcserver.NewRPCFunc(makeBlockFunc(c), "height"),
|
||||
"block_by_hash": rpcserver.NewRPCFunc(makeBlockByHashFunc(c), "hash"),
|
||||
"commit": rpcserver.NewRPCFunc(makeCommitFunc(c), "height"),
|
||||
"tx": rpcserver.NewRPCFunc(makeTxFunc(c), "hash,prove"),
|
||||
"validators": rpcserver.NewRPCFunc(makeValidatorsFunc(c), "height"),
|
||||
|
||||
// broadcast API
|
||||
"broadcast_tx_commit": rpcserver.NewRPCFunc(makeBroadcastTxCommitFunc(c), "tx"),
|
||||
"broadcast_tx_sync": rpcserver.NewRPCFunc(makeBroadcastTxSyncFunc(c), "tx"),
|
||||
"broadcast_tx_async": rpcserver.NewRPCFunc(makeBroadcastTxAsyncFunc(c), "tx"),
|
||||
|
||||
// abci API
|
||||
"abci_query": rpcserver.NewRPCFunc(makeABCIQueryFunc(c), "path,data"),
|
||||
"abci_info": rpcserver.NewRPCFunc(makeABCIInfoFunc(c), ""),
|
||||
}
|
||||
}
|
||||
|
||||
func makeStatusFunc(c rpcclient.StatusClient) func(ctx *rpctypes.Context) (*ctypes.ResultStatus, error) {
|
||||
return func(ctx *rpctypes.Context) (*ctypes.ResultStatus, error) {
|
||||
return c.Status()
|
||||
}
|
||||
}
|
||||
|
||||
func makeBlockchainInfoFunc(c rpcclient.Client) func(
|
||||
ctx *rpctypes.Context,
|
||||
minHeight,
|
||||
maxHeight int64,
|
||||
) (*ctypes.ResultBlockchainInfo, error) {
|
||||
return func(ctx *rpctypes.Context, minHeight, maxHeight int64) (*ctypes.ResultBlockchainInfo, error) {
|
||||
return c.BlockchainInfo(minHeight, maxHeight)
|
||||
}
|
||||
}
|
||||
|
||||
func makeGenesisFunc(c rpcclient.Client) func(ctx *rpctypes.Context) (*ctypes.ResultGenesis, error) {
|
||||
return func(ctx *rpctypes.Context) (*ctypes.ResultGenesis, error) {
|
||||
return c.Genesis()
|
||||
}
|
||||
}
|
||||
|
||||
func makeBlockFunc(c rpcclient.Client) func(ctx *rpctypes.Context, height *int64) (*ctypes.ResultBlock, error) {
|
||||
return func(ctx *rpctypes.Context, height *int64) (*ctypes.ResultBlock, error) {
|
||||
return c.Block(height)
|
||||
}
|
||||
}
|
||||
|
||||
func makeBlockByHashFunc(c rpcclient.Client) func(ctx *rpctypes.Context, hash []byte) (*ctypes.ResultBlock, error) {
|
||||
return func(ctx *rpctypes.Context, hash []byte) (*ctypes.ResultBlock, error) {
|
||||
return c.BlockByHash(hash)
|
||||
}
|
||||
}
|
||||
|
||||
func makeCommitFunc(c rpcclient.Client) func(ctx *rpctypes.Context, height *int64) (*ctypes.ResultCommit, error) {
|
||||
return func(ctx *rpctypes.Context, height *int64) (*ctypes.ResultCommit, error) {
|
||||
return c.Commit(height)
|
||||
}
|
||||
}
|
||||
|
||||
func makeTxFunc(c rpcclient.Client) func(ctx *rpctypes.Context, hash []byte, prove bool) (*ctypes.ResultTx, error) {
|
||||
return func(ctx *rpctypes.Context, hash []byte, prove bool) (*ctypes.ResultTx, error) {
|
||||
return c.Tx(hash, prove)
|
||||
}
|
||||
}
|
||||
|
||||
func makeValidatorsFunc(c rpcclient.Client) func(
|
||||
ctx *rpctypes.Context,
|
||||
height *int64,
|
||||
) (*ctypes.ResultValidators, error) {
|
||||
return func(ctx *rpctypes.Context, height *int64) (*ctypes.ResultValidators, error) {
|
||||
return c.Validators(height, 0, 0)
|
||||
}
|
||||
}
|
||||
|
||||
func makeBroadcastTxCommitFunc(c rpcclient.Client) func(
|
||||
ctx *rpctypes.Context,
|
||||
tx types.Tx,
|
||||
) (*ctypes.ResultBroadcastTxCommit, error) {
|
||||
return func(ctx *rpctypes.Context, tx types.Tx) (*ctypes.ResultBroadcastTxCommit, error) {
|
||||
return c.BroadcastTxCommit(tx)
|
||||
}
|
||||
}
|
||||
|
||||
func makeBroadcastTxSyncFunc(c rpcclient.Client) func(
|
||||
ctx *rpctypes.Context,
|
||||
tx types.Tx,
|
||||
) (*ctypes.ResultBroadcastTx, error) {
|
||||
return func(ctx *rpctypes.Context, tx types.Tx) (*ctypes.ResultBroadcastTx, error) {
|
||||
return c.BroadcastTxSync(tx)
|
||||
}
|
||||
}
|
||||
|
||||
func makeBroadcastTxAsyncFunc(c rpcclient.Client) func(
|
||||
ctx *rpctypes.Context,
|
||||
tx types.Tx,
|
||||
) (*ctypes.ResultBroadcastTx, error) {
|
||||
return func(ctx *rpctypes.Context, tx types.Tx) (*ctypes.ResultBroadcastTx, error) {
|
||||
return c.BroadcastTxAsync(tx)
|
||||
}
|
||||
}
|
||||
|
||||
func makeABCIQueryFunc(c rpcclient.Client) func(
|
||||
ctx *rpctypes.Context,
|
||||
path string,
|
||||
data bytes.HexBytes,
|
||||
) (*ctypes.ResultABCIQuery, error) {
|
||||
return func(ctx *rpctypes.Context, path string, data bytes.HexBytes) (*ctypes.ResultABCIQuery, error) {
|
||||
return c.ABCIQuery(path, data)
|
||||
}
|
||||
}
|
||||
|
||||
func makeABCIInfoFunc(c rpcclient.Client) func(ctx *rpctypes.Context) (*ctypes.ResultABCIInfo, error) {
|
||||
return func(ctx *rpctypes.Context) (*ctypes.ResultABCIInfo, error) {
|
||||
return c.ABCIInfo()
|
||||
}
|
||||
}
|
||||
@@ -1,148 +0,0 @@
|
||||
package proxy
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
|
||||
"github.com/tendermint/tendermint/crypto/merkle"
|
||||
"github.com/tendermint/tendermint/libs/bytes"
|
||||
"github.com/tendermint/tendermint/lite"
|
||||
lerr "github.com/tendermint/tendermint/lite/errors"
|
||||
rpcclient "github.com/tendermint/tendermint/rpc/client"
|
||||
ctypes "github.com/tendermint/tendermint/rpc/core/types"
|
||||
"github.com/tendermint/tendermint/types"
|
||||
)
|
||||
|
||||
// GetWithProof will query the key on the given node, and verify it has
|
||||
// a valid proof, as defined by the Verifier.
|
||||
//
|
||||
// If there is any error in checking, returns an error.
|
||||
func GetWithProof(prt *merkle.ProofRuntime, key []byte, reqHeight int64, node rpcclient.Client,
|
||||
cert lite.Verifier) (
|
||||
val bytes.HexBytes, height int64, proof *merkle.Proof, err error) {
|
||||
|
||||
if reqHeight < 0 {
|
||||
err = errors.New("height cannot be negative")
|
||||
return
|
||||
}
|
||||
|
||||
res, err := GetWithProofOptions(prt, "/key", key,
|
||||
rpcclient.ABCIQueryOptions{Height: reqHeight, Prove: true},
|
||||
node, cert)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
resp := res.Response
|
||||
val, height = resp.Value, resp.Height
|
||||
return val, height, proof, err
|
||||
}
|
||||
|
||||
// GetWithProofOptions is useful if you want full access to the ABCIQueryOptions.
|
||||
// XXX Usage of path? It's not used, and sometimes it's /, sometimes /key, sometimes /store.
|
||||
func GetWithProofOptions(prt *merkle.ProofRuntime, path string, key []byte, opts rpcclient.ABCIQueryOptions,
|
||||
node rpcclient.Client, cert lite.Verifier) (
|
||||
*ctypes.ResultABCIQuery, error) {
|
||||
opts.Prove = true
|
||||
res, err := node.ABCIQueryWithOptions(path, key, opts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
resp := res.Response
|
||||
|
||||
// Validate the response, e.g. height.
|
||||
if resp.IsErr() {
|
||||
err = errors.Errorf("query error for key %d: %d", key, resp.Code)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(resp.Key) == 0 || resp.Proof == nil {
|
||||
return nil, lerr.ErrEmptyTree()
|
||||
}
|
||||
if resp.Height == 0 {
|
||||
return nil, errors.New("height returned is zero")
|
||||
}
|
||||
|
||||
// AppHash for height H is in header H+1
|
||||
signedHeader, err := GetCertifiedCommit(resp.Height+1, node, cert)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Validate the proof against the certified header to ensure data integrity.
|
||||
if resp.Value != nil {
|
||||
// Value exists
|
||||
// XXX How do we encode the key into a string...
|
||||
storeName, err := parseQueryStorePath(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
kp := merkle.KeyPath{}
|
||||
kp = kp.AppendKey([]byte(storeName), merkle.KeyEncodingURL)
|
||||
kp = kp.AppendKey(resp.Key, merkle.KeyEncodingURL)
|
||||
err = prt.VerifyValue(resp.Proof, signedHeader.AppHash, kp.String(), resp.Value)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "couldn't verify value proof")
|
||||
}
|
||||
|
||||
return &ctypes.ResultABCIQuery{Response: resp}, nil
|
||||
}
|
||||
|
||||
// Value absent
|
||||
// Validate the proof against the certified header to ensure data integrity.
|
||||
// XXX How do we encode the key into a string...
|
||||
err = prt.VerifyAbsence(resp.Proof, signedHeader.AppHash, string(resp.Key))
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "couldn't verify absence proof")
|
||||
}
|
||||
|
||||
return &ctypes.ResultABCIQuery{Response: resp}, nil
|
||||
}
|
||||
|
||||
func parseQueryStorePath(path string) (storeName string, err error) {
|
||||
if !strings.HasPrefix(path, "/") {
|
||||
return "", fmt.Errorf("expected path to start with /")
|
||||
}
|
||||
|
||||
paths := strings.SplitN(path[1:], "/", 3)
|
||||
switch {
|
||||
case len(paths) != 3:
|
||||
return "", fmt.Errorf("expected format like /store/<storeName>/key")
|
||||
case paths[0] != "store":
|
||||
return "", fmt.Errorf("expected format like /store/<storeName>/key")
|
||||
case paths[2] != "key":
|
||||
return "", fmt.Errorf("expected format like /store/<storeName>/key")
|
||||
}
|
||||
|
||||
return paths[1], nil
|
||||
}
|
||||
|
||||
// GetCertifiedCommit gets the signed header for a given height and certifies
|
||||
// it. Returns error if unable to get a proven header.
|
||||
func GetCertifiedCommit(h int64, client rpcclient.Client, cert lite.Verifier) (types.SignedHeader, error) {
|
||||
|
||||
// FIXME: cannot use cert.GetByHeight for now, as it also requires
|
||||
// Validators and will fail on querying tendermint for non-current height.
|
||||
// When this is supported, we should use it instead...
|
||||
rpcclient.WaitForHeight(client, h, nil)
|
||||
cresp, err := client.Commit(&h)
|
||||
if err != nil {
|
||||
return types.SignedHeader{}, err
|
||||
}
|
||||
|
||||
// Validate downloaded checkpoint with our request and trust store.
|
||||
sh := cresp.SignedHeader
|
||||
if sh.Height != h {
|
||||
return types.SignedHeader{}, fmt.Errorf("height mismatch: want %v got %v",
|
||||
h, sh.Height)
|
||||
}
|
||||
|
||||
if err = cert.Verify(sh); err != nil {
|
||||
return types.SignedHeader{}, err
|
||||
}
|
||||
|
||||
return sh, nil
|
||||
}
|
||||
@@ -1,163 +0,0 @@
|
||||
package proxy
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/tendermint/tendermint/abci/example/kvstore"
|
||||
"github.com/tendermint/tendermint/crypto/merkle"
|
||||
"github.com/tendermint/tendermint/lite"
|
||||
certclient "github.com/tendermint/tendermint/lite/client"
|
||||
nm "github.com/tendermint/tendermint/node"
|
||||
"github.com/tendermint/tendermint/rpc/client"
|
||||
rpclocal "github.com/tendermint/tendermint/rpc/client/local"
|
||||
rpctest "github.com/tendermint/tendermint/rpc/test"
|
||||
"github.com/tendermint/tendermint/types"
|
||||
)
|
||||
|
||||
var node *nm.Node
|
||||
var chainID = "tendermint_test" // TODO use from config.
|
||||
//nolint:unused
|
||||
var waitForEventTimeout = 5 * time.Second
|
||||
|
||||
// TODO fix tests!!
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
app := kvstore.NewApplication()
|
||||
node = rpctest.StartTendermint(app)
|
||||
|
||||
code := m.Run()
|
||||
|
||||
rpctest.StopTendermint(node)
|
||||
os.Exit(code)
|
||||
}
|
||||
|
||||
func kvstoreTx(k, v []byte) []byte {
|
||||
return []byte(fmt.Sprintf("%s=%s", k, v))
|
||||
}
|
||||
|
||||
// TODO: enable it after general proof format has been adapted
|
||||
// in abci/examples/kvstore.go
|
||||
//nolint:unused,deadcode
|
||||
func _TestAppProofs(t *testing.T) {
|
||||
assert, require := assert.New(t), require.New(t)
|
||||
|
||||
prt := defaultProofRuntime()
|
||||
cl := rpclocal.New(node)
|
||||
client.WaitForHeight(cl, 1, nil)
|
||||
|
||||
// This sets up our trust on the node based on some past point.
|
||||
source := certclient.NewProvider(chainID, cl)
|
||||
seed, err := source.LatestFullCommit(chainID, 1, 1)
|
||||
require.NoError(err, "%#v", err)
|
||||
cert := lite.NewBaseVerifier(chainID, seed.Height(), seed.Validators)
|
||||
|
||||
// Wait for tx confirmation.
|
||||
done := make(chan int64)
|
||||
go func() {
|
||||
evtTyp := types.EventTx
|
||||
_, err = client.WaitForOneEvent(cl, evtTyp, waitForEventTimeout)
|
||||
require.Nil(err, "%#v", err)
|
||||
close(done)
|
||||
}()
|
||||
|
||||
// Submit a transaction.
|
||||
k := []byte("my-key")
|
||||
v := []byte("my-value")
|
||||
tx := kvstoreTx(k, v)
|
||||
br, err := cl.BroadcastTxCommit(tx)
|
||||
require.NoError(err, "%#v", err)
|
||||
require.EqualValues(0, br.CheckTx.Code, "%#v", br.CheckTx)
|
||||
require.EqualValues(0, br.DeliverTx.Code)
|
||||
brh := br.Height
|
||||
|
||||
// Fetch latest after tx commit.
|
||||
<-done
|
||||
latest, err := source.LatestFullCommit(chainID, 1, 1<<63-1)
|
||||
require.NoError(err, "%#v", err)
|
||||
rootHash := latest.SignedHeader.AppHash
|
||||
if rootHash == nil {
|
||||
// Fetch one block later, AppHash hasn't been committed yet.
|
||||
// TODO find a way to avoid doing this.
|
||||
client.WaitForHeight(cl, latest.SignedHeader.Height+1, nil)
|
||||
latest, err = source.LatestFullCommit(chainID, latest.SignedHeader.Height+1, 1<<63-1)
|
||||
require.NoError(err, "%#v", err)
|
||||
rootHash = latest.SignedHeader.AppHash
|
||||
}
|
||||
require.NotNil(rootHash)
|
||||
|
||||
// verify a query before the tx block has no data (and valid non-exist proof)
|
||||
bs, height, proof, err := GetWithProof(prt, k, brh-1, cl, cert)
|
||||
require.NoError(err, "%#v", err)
|
||||
require.NotNil(proof)
|
||||
require.Equal(height, brh-1)
|
||||
// require.NotNil(proof)
|
||||
// TODO: Ensure that *some* keys will be there, ensuring that proof is nil,
|
||||
// (currently there's a race condition)
|
||||
// and ensure that proof proves absence of k.
|
||||
require.Nil(bs)
|
||||
|
||||
// but given that block it is good
|
||||
bs, height, proof, err = GetWithProof(prt, k, brh, cl, cert)
|
||||
require.NoError(err, "%#v", err)
|
||||
require.NotNil(proof)
|
||||
require.Equal(height, brh)
|
||||
|
||||
assert.EqualValues(v, bs)
|
||||
err = prt.VerifyValue(proof, rootHash, string(k), bs) // XXX key encoding
|
||||
assert.NoError(err, "%#v", err)
|
||||
|
||||
// Test non-existing key.
|
||||
missing := []byte("my-missing-key")
|
||||
bs, _, proof, err = GetWithProof(prt, missing, 0, cl, cert)
|
||||
require.NoError(err)
|
||||
require.Nil(bs)
|
||||
require.NotNil(proof)
|
||||
err = prt.VerifyAbsence(proof, rootHash, string(missing)) // XXX VerifyAbsence(), keyencoding
|
||||
assert.NoError(err, "%#v", err)
|
||||
err = prt.VerifyAbsence(proof, rootHash, string(k)) // XXX VerifyAbsence(), keyencoding
|
||||
assert.Error(err, "%#v", err)
|
||||
}
|
||||
|
||||
func TestTxProofs(t *testing.T) {
|
||||
assert, require := assert.New(t), require.New(t)
|
||||
|
||||
cl := rpclocal.New(node)
|
||||
client.WaitForHeight(cl, 1, nil)
|
||||
|
||||
tx := kvstoreTx([]byte("key-a"), []byte("value-a"))
|
||||
br, err := cl.BroadcastTxCommit(tx)
|
||||
require.NoError(err, "%#v", err)
|
||||
require.EqualValues(0, br.CheckTx.Code, "%#v", br.CheckTx)
|
||||
require.EqualValues(0, br.DeliverTx.Code)
|
||||
brh := br.Height
|
||||
|
||||
source := certclient.NewProvider(chainID, cl)
|
||||
seed, err := source.LatestFullCommit(chainID, brh-2, brh-2)
|
||||
require.NoError(err, "%#v", err)
|
||||
cert := lite.NewBaseVerifier(chainID, seed.Height(), seed.Validators)
|
||||
|
||||
// First let's make sure a bogus transaction hash returns a valid non-existence proof.
|
||||
key := types.Tx([]byte("bogus")).Hash()
|
||||
_, err = cl.Tx(key, true)
|
||||
require.NotNil(err)
|
||||
require.Contains(err.Error(), "not found")
|
||||
|
||||
// Now let's check with the real tx root hash.
|
||||
key = types.Tx(tx).Hash()
|
||||
res, err := cl.Tx(key, true)
|
||||
require.NoError(err, "%#v", err)
|
||||
require.NotNil(res)
|
||||
keyHash := merkle.SimpleHashFromByteSlices([][]byte{key})
|
||||
err = res.Proof.Validate(keyHash)
|
||||
assert.NoError(err, "%#v", err)
|
||||
|
||||
commit, err := GetCertifiedCommit(br.Height, cl, cert)
|
||||
require.Nil(err, "%#v", err)
|
||||
require.Equal(res.Proof.RootHash, commit.Header.DataHash)
|
||||
}
|
||||
@@ -1,211 +0,0 @@
|
||||
package proxy_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
"github.com/tendermint/tendermint/lite/proxy"
|
||||
"github.com/tendermint/tendermint/types"
|
||||
)
|
||||
|
||||
var (
|
||||
deadBeefTxs = types.Txs{[]byte("DE"), []byte("AD"), []byte("BE"), []byte("EF")}
|
||||
deadBeefHash = deadBeefTxs.Hash()
|
||||
testTime1 = time.Date(2018, 1, 1, 1, 1, 1, 1, time.UTC)
|
||||
testTime2 = time.Date(2017, 1, 2, 1, 1, 1, 1, time.UTC)
|
||||
)
|
||||
|
||||
var hdrHeight11 = types.Header{
|
||||
Height: 11,
|
||||
Time: testTime1,
|
||||
ValidatorsHash: []byte("Tendermint"),
|
||||
}
|
||||
|
||||
func TestValidateBlock(t *testing.T) {
|
||||
tests := []struct {
|
||||
block *types.Block
|
||||
signedHeader types.SignedHeader
|
||||
wantErr string
|
||||
}{
|
||||
{
|
||||
block: nil, wantErr: "non-nil Block",
|
||||
},
|
||||
{
|
||||
block: &types.Block{}, wantErr: "unexpected empty SignedHeader",
|
||||
},
|
||||
|
||||
// Start Header.Height mismatch test
|
||||
{
|
||||
block: &types.Block{Header: types.Header{Height: 10}},
|
||||
signedHeader: types.SignedHeader{Header: &types.Header{Height: 11}},
|
||||
wantErr: "header heights mismatched",
|
||||
},
|
||||
|
||||
{
|
||||
block: &types.Block{Header: types.Header{Height: 11}},
|
||||
signedHeader: types.SignedHeader{Header: &types.Header{Height: 11}},
|
||||
},
|
||||
// End Header.Height mismatch test
|
||||
|
||||
// Start Header.Hash mismatch test
|
||||
{
|
||||
block: &types.Block{Header: hdrHeight11},
|
||||
signedHeader: types.SignedHeader{Header: &types.Header{Height: 11}},
|
||||
wantErr: "headers don't match",
|
||||
},
|
||||
|
||||
{
|
||||
block: &types.Block{Header: hdrHeight11},
|
||||
signedHeader: types.SignedHeader{Header: &hdrHeight11},
|
||||
},
|
||||
// End Header.Hash mismatch test
|
||||
|
||||
// Start Header.Data hash mismatch test
|
||||
{
|
||||
block: &types.Block{
|
||||
Header: types.Header{Height: 11},
|
||||
Data: types.Data{Txs: []types.Tx{[]byte("0xDE"), []byte("AD")}},
|
||||
},
|
||||
signedHeader: types.SignedHeader{
|
||||
Header: &types.Header{Height: 11},
|
||||
Commit: types.NewCommit(11, 0, types.BlockID{Hash: []byte("0xDEADBEEF")}, nil),
|
||||
},
|
||||
wantErr: "data hash doesn't match header",
|
||||
},
|
||||
{
|
||||
block: &types.Block{
|
||||
Header: types.Header{Height: 11, DataHash: deadBeefHash},
|
||||
Data: types.Data{Txs: deadBeefTxs},
|
||||
},
|
||||
signedHeader: types.SignedHeader{
|
||||
Header: &types.Header{Height: 11},
|
||||
Commit: types.NewCommit(11, 0, types.BlockID{Hash: []byte("DEADBEEF")}, nil),
|
||||
},
|
||||
},
|
||||
// End Header.Data hash mismatch test
|
||||
}
|
||||
|
||||
for i, tt := range tests {
|
||||
err := proxy.ValidateBlock(tt.block, tt.signedHeader)
|
||||
if tt.wantErr != "" {
|
||||
if err == nil {
|
||||
assert.FailNowf(t, "Unexpectedly passed", "#%d", i)
|
||||
} else {
|
||||
assert.Contains(t, err.Error(), tt.wantErr, "#%d should contain the substring\n\n", i)
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
assert.Nil(t, err, "#%d: expecting a nil error", i)
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidateBlockMeta(t *testing.T) {
|
||||
tests := []struct {
|
||||
meta *types.BlockMeta
|
||||
signedHeader types.SignedHeader
|
||||
wantErr string
|
||||
}{
|
||||
{
|
||||
meta: nil, wantErr: "non-nil BlockMeta",
|
||||
},
|
||||
{
|
||||
meta: &types.BlockMeta{}, wantErr: "unexpected empty SignedHeader",
|
||||
},
|
||||
|
||||
// Start Header.Height mismatch test
|
||||
{
|
||||
meta: &types.BlockMeta{Header: types.Header{Height: 10}},
|
||||
signedHeader: types.SignedHeader{Header: &types.Header{Height: 11}},
|
||||
wantErr: "header heights mismatched",
|
||||
},
|
||||
|
||||
{
|
||||
meta: &types.BlockMeta{Header: types.Header{Height: 11}},
|
||||
signedHeader: types.SignedHeader{Header: &types.Header{Height: 11}},
|
||||
},
|
||||
// End Header.Height mismatch test
|
||||
|
||||
// Start Headers don't match test
|
||||
{
|
||||
meta: &types.BlockMeta{Header: hdrHeight11},
|
||||
signedHeader: types.SignedHeader{Header: &types.Header{Height: 11}},
|
||||
wantErr: "headers don't match",
|
||||
},
|
||||
|
||||
{
|
||||
meta: &types.BlockMeta{Header: hdrHeight11},
|
||||
signedHeader: types.SignedHeader{Header: &hdrHeight11},
|
||||
},
|
||||
|
||||
{
|
||||
meta: &types.BlockMeta{
|
||||
Header: types.Header{
|
||||
Height: 11,
|
||||
ValidatorsHash: []byte("lite-test"),
|
||||
// TODO: should be able to use empty time after Amino upgrade
|
||||
Time: testTime1,
|
||||
},
|
||||
},
|
||||
signedHeader: types.SignedHeader{
|
||||
Header: &types.Header{Height: 11, DataHash: deadBeefHash},
|
||||
},
|
||||
wantErr: "headers don't match",
|
||||
},
|
||||
|
||||
{
|
||||
meta: &types.BlockMeta{
|
||||
Header: types.Header{
|
||||
Height: 11, DataHash: deadBeefHash,
|
||||
ValidatorsHash: []byte("Tendermint"),
|
||||
Time: testTime1,
|
||||
},
|
||||
},
|
||||
signedHeader: types.SignedHeader{
|
||||
Header: &types.Header{
|
||||
Height: 11, DataHash: deadBeefHash,
|
||||
ValidatorsHash: []byte("Tendermint"),
|
||||
Time: testTime2,
|
||||
},
|
||||
Commit: types.NewCommit(11, 0, types.BlockID{Hash: []byte("DEADBEEF")}, nil),
|
||||
},
|
||||
wantErr: "headers don't match",
|
||||
},
|
||||
|
||||
{
|
||||
meta: &types.BlockMeta{
|
||||
Header: types.Header{
|
||||
Height: 11, DataHash: deadBeefHash,
|
||||
ValidatorsHash: []byte("Tendermint"),
|
||||
Time: testTime2,
|
||||
},
|
||||
},
|
||||
signedHeader: types.SignedHeader{
|
||||
Header: &types.Header{
|
||||
Height: 11, DataHash: deadBeefHash,
|
||||
ValidatorsHash: []byte("Tendermint-x"),
|
||||
Time: testTime2,
|
||||
},
|
||||
Commit: types.NewCommit(11, 0, types.BlockID{Hash: []byte("DEADBEEF")}, nil),
|
||||
},
|
||||
wantErr: "headers don't match",
|
||||
},
|
||||
// End Headers don't match test
|
||||
}
|
||||
|
||||
for i, tt := range tests {
|
||||
err := proxy.ValidateBlockMeta(tt.meta, tt.signedHeader)
|
||||
if tt.wantErr != "" {
|
||||
if err == nil {
|
||||
assert.FailNowf(t, "Unexpectedly passed", "#%d: wanted error %q", i, tt.wantErr)
|
||||
} else {
|
||||
assert.Contains(t, err.Error(), tt.wantErr, "#%d should contain the substring\n\n", i)
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
assert.Nil(t, err, "#%d: expecting a nil error", i)
|
||||
}
|
||||
}
|
||||
@@ -1,49 +0,0 @@
|
||||
package proxy
|
||||
|
||||
import (
|
||||
"github.com/pkg/errors"
|
||||
|
||||
dbm "github.com/tendermint/tm-db"
|
||||
|
||||
log "github.com/tendermint/tendermint/libs/log"
|
||||
"github.com/tendermint/tendermint/lite"
|
||||
lclient "github.com/tendermint/tendermint/lite/client"
|
||||
)
|
||||
|
||||
func NewVerifier(
|
||||
chainID,
|
||||
rootDir string,
|
||||
client lclient.SignStatusClient,
|
||||
logger log.Logger,
|
||||
cacheSize int,
|
||||
) (*lite.DynamicVerifier, error) {
|
||||
|
||||
logger = logger.With("module", "lite/proxy")
|
||||
logger.Info("lite/proxy/NewVerifier()...", "chainID", chainID, "rootDir", rootDir, "client", client)
|
||||
|
||||
memProvider := lite.NewDBProvider("trusted.mem", dbm.NewMemDB()).SetLimit(cacheSize)
|
||||
lvlProvider := lite.NewDBProvider("trusted.lvl", dbm.NewDB("trust-base", dbm.GoLevelDBBackend, rootDir))
|
||||
trust := lite.NewMultiProvider(
|
||||
memProvider,
|
||||
lvlProvider,
|
||||
)
|
||||
source := lclient.NewProvider(chainID, client)
|
||||
cert := lite.NewDynamicVerifier(chainID, trust, source)
|
||||
cert.SetLogger(logger) // Sets logger recursively.
|
||||
|
||||
// TODO: Make this more secure, e.g. make it interactive in the console?
|
||||
_, err := trust.LatestFullCommit(chainID, 1, 1<<63-1)
|
||||
if err != nil {
|
||||
logger.Info("lite/proxy/NewVerifier found no trusted full commit, initializing from source from height 1...")
|
||||
fc, err := source.LatestFullCommit(chainID, 1, 1)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "fetching source full commit @ height 1")
|
||||
}
|
||||
err = trust.SaveFullCommit(fc)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "saving full commit to trusted")
|
||||
}
|
||||
}
|
||||
|
||||
return cert, nil
|
||||
}
|
||||
@@ -1,275 +0,0 @@
|
||||
package proxy
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/tendermint/tendermint/crypto/merkle"
|
||||
"github.com/tendermint/tendermint/libs/bytes"
|
||||
"github.com/tendermint/tendermint/lite"
|
||||
rpcclient "github.com/tendermint/tendermint/rpc/client"
|
||||
ctypes "github.com/tendermint/tendermint/rpc/core/types"
|
||||
rpctypes "github.com/tendermint/tendermint/rpc/jsonrpc/types"
|
||||
)
|
||||
|
||||
var _ rpcclient.Client = Wrapper{}
|
||||
|
||||
// Wrapper wraps a rpcclient with a Verifier and double-checks any input that is
|
||||
// provable before passing it along. Allows you to make any rpcclient fully secure.
|
||||
type Wrapper struct {
|
||||
rpcclient.Client
|
||||
cert *lite.DynamicVerifier
|
||||
prt *merkle.ProofRuntime
|
||||
}
|
||||
|
||||
// SecureClient uses a given Verifier to wrap an connection to an untrusted
|
||||
// host and return a cryptographically secure rpc client.
|
||||
//
|
||||
// If it is wrapping an HTTP rpcclient, it will also wrap the websocket interface
|
||||
func SecureClient(c rpcclient.Client, cert *lite.DynamicVerifier) Wrapper {
|
||||
prt := defaultProofRuntime()
|
||||
wrap := Wrapper{c, cert, prt}
|
||||
// TODO: no longer possible as no more such interface exposed....
|
||||
// if we wrap http client, then we can swap out the event switch to filter
|
||||
// if hc, ok := c.(*rpcclient.HTTP); ok {
|
||||
// evt := hc.WSEvents.EventSwitch
|
||||
// hc.WSEvents.EventSwitch = WrappedSwitch{evt, wrap}
|
||||
// }
|
||||
return wrap
|
||||
}
|
||||
|
||||
// ABCIQueryWithOptions exposes all options for the ABCI query and verifies the returned proof
|
||||
func (w Wrapper) ABCIQueryWithOptions(path string, data bytes.HexBytes,
|
||||
opts rpcclient.ABCIQueryOptions) (*ctypes.ResultABCIQuery, error) {
|
||||
|
||||
res, err := GetWithProofOptions(w.prt, path, data, opts, w.Client, w.cert)
|
||||
return res, err
|
||||
}
|
||||
|
||||
// ABCIQuery uses default options for the ABCI query and verifies the returned proof
|
||||
func (w Wrapper) ABCIQuery(path string, data bytes.HexBytes) (*ctypes.ResultABCIQuery, error) {
|
||||
return w.ABCIQueryWithOptions(path, data, rpcclient.DefaultABCIQueryOptions)
|
||||
}
|
||||
|
||||
// Tx queries for a given tx and verifies the proof if it was requested
|
||||
func (w Wrapper) Tx(hash []byte, prove bool) (*ctypes.ResultTx, error) {
|
||||
res, err := w.Client.Tx(hash, prove)
|
||||
if !prove || err != nil {
|
||||
return res, err
|
||||
}
|
||||
h := res.Height
|
||||
sh, err := GetCertifiedCommit(h, w.Client, w.cert)
|
||||
if err != nil {
|
||||
return res, err
|
||||
}
|
||||
err = res.Proof.Validate(sh.DataHash)
|
||||
return res, err
|
||||
}
|
||||
|
||||
// BlockchainInfo requests a list of headers and verifies them all...
|
||||
// Rather expensive.
|
||||
//
|
||||
// TODO: optimize this if used for anything needing performance
|
||||
func (w Wrapper) BlockchainInfo(minHeight, maxHeight int64) (*ctypes.ResultBlockchainInfo, error) {
|
||||
r, err := w.Client.BlockchainInfo(minHeight, maxHeight)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// go and verify every blockmeta in the result....
|
||||
for _, meta := range r.BlockMetas {
|
||||
// get a checkpoint to verify from
|
||||
res, err := w.Commit(&meta.Header.Height)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
sh := res.SignedHeader
|
||||
err = ValidateBlockMeta(meta, sh)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return r, nil
|
||||
}
|
||||
|
||||
// Block returns an entire block and verifies all signatures
|
||||
func (w Wrapper) Block(height *int64) (*ctypes.ResultBlock, error) {
|
||||
resBlock, err := w.Client.Block(height)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// get a checkpoint to verify from
|
||||
resCommit, err := w.Commit(height)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
sh := resCommit.SignedHeader
|
||||
|
||||
err = ValidateBlock(resBlock.Block, sh)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return resBlock, nil
|
||||
}
|
||||
|
||||
// BlockByHash returns an entire block and verifies all signatures
|
||||
func (w Wrapper) BlockByHash(hash []byte) (*ctypes.ResultBlock, error) {
|
||||
resBlock, err := w.Client.BlockByHash(hash)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// get a checkpoint to verify from
|
||||
resCommit, err := w.Commit(&resBlock.Block.Height)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
sh := resCommit.SignedHeader
|
||||
|
||||
err = ValidateBlock(resBlock.Block, sh)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return resBlock, nil
|
||||
}
|
||||
|
||||
// Commit downloads the Commit and certifies it with the lite.
|
||||
//
|
||||
// This is the foundation for all other verification in this module
|
||||
func (w Wrapper) Commit(height *int64) (*ctypes.ResultCommit, error) {
|
||||
if height == nil {
|
||||
resStatus, err := w.Client.Status()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// NOTE: If resStatus.CatchingUp, there is a race
|
||||
// condition where the validator set for the next height
|
||||
// isn't available until some time after the blockstore
|
||||
// has height h on the remote node. This isn't an issue
|
||||
// once the node has caught up, and a syncing node likely
|
||||
// won't have this issue esp with the implementation we
|
||||
// have here, but we may have to address this at some
|
||||
// point.
|
||||
height = new(int64)
|
||||
*height = resStatus.SyncInfo.LatestBlockHeight
|
||||
}
|
||||
rpcclient.WaitForHeight(w.Client, *height, nil)
|
||||
res, err := w.Client.Commit(height)
|
||||
// if we got it, then verify it
|
||||
if err == nil {
|
||||
sh := res.SignedHeader
|
||||
err = w.cert.Verify(sh)
|
||||
}
|
||||
return res, err
|
||||
}
|
||||
|
||||
func (w Wrapper) RegisterOpDecoder(typ string, dec merkle.OpDecoder) {
|
||||
w.prt.RegisterOpDecoder(typ, dec)
|
||||
}
|
||||
|
||||
// SubscribeWS subscribes for events using the given query and remote address as
|
||||
// a subscriber, but does not verify responses (UNSAFE)!
|
||||
func (w Wrapper) SubscribeWS(ctx *rpctypes.Context, query string) (*ctypes.ResultSubscribe, error) {
|
||||
out, err := w.Client.Subscribe(context.Background(), ctx.RemoteAddr(), query)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
go func() {
|
||||
for {
|
||||
select {
|
||||
case resultEvent := <-out:
|
||||
// XXX(melekes) We should have a switch here that performs a validation
|
||||
// depending on the event's type.
|
||||
ctx.WSConn.TryWriteRPCResponse(
|
||||
rpctypes.NewRPCSuccessResponse(
|
||||
ctx.WSConn.Codec(),
|
||||
ctx.JSONReq.ID,
|
||||
resultEvent,
|
||||
))
|
||||
case <-w.Client.Quit():
|
||||
return
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
return &ctypes.ResultSubscribe{}, nil
|
||||
}
|
||||
|
||||
// UnsubscribeWS calls original client's Unsubscribe using remote address as a
|
||||
// subscriber.
|
||||
func (w Wrapper) UnsubscribeWS(ctx *rpctypes.Context, query string) (*ctypes.ResultUnsubscribe, error) {
|
||||
err := w.Client.Unsubscribe(context.Background(), ctx.RemoteAddr(), query)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &ctypes.ResultUnsubscribe{}, nil
|
||||
}
|
||||
|
||||
// UnsubscribeAllWS calls original client's UnsubscribeAll using remote address
|
||||
// as a subscriber.
|
||||
func (w Wrapper) UnsubscribeAllWS(ctx *rpctypes.Context) (*ctypes.ResultUnsubscribe, error) {
|
||||
err := w.Client.UnsubscribeAll(context.Background(), ctx.RemoteAddr())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &ctypes.ResultUnsubscribe{}, nil
|
||||
}
|
||||
|
||||
// // WrappedSwitch creates a websocket connection that auto-verifies any info
|
||||
// // coming through before passing it along.
|
||||
// //
|
||||
// // Since the verification takes 1-2 rpc calls, this is obviously only for
|
||||
// // relatively low-throughput situations that can tolerate a bit extra latency
|
||||
// type WrappedSwitch struct {
|
||||
// types.EventSwitch
|
||||
// client rpcclient.Client
|
||||
// }
|
||||
|
||||
// // FireEvent verifies any block or header returned from the eventswitch
|
||||
// func (s WrappedSwitch) FireEvent(event string, data events.EventData) {
|
||||
// tm, ok := data.(types.TMEventData)
|
||||
// if !ok {
|
||||
// fmt.Printf("bad type %#v\n", data)
|
||||
// return
|
||||
// }
|
||||
|
||||
// // check to validate it if possible, and drop if not valid
|
||||
// switch t := tm.(type) {
|
||||
// case types.EventDataNewBlockHeader:
|
||||
// err := verifyHeader(s.client, t.Header)
|
||||
// if err != nil {
|
||||
// fmt.Printf("Invalid header: %#v\n", err)
|
||||
// return
|
||||
// }
|
||||
// case types.EventDataNewBlock:
|
||||
// err := verifyBlock(s.client, t.Block)
|
||||
// if err != nil {
|
||||
// fmt.Printf("Invalid block: %#v\n", err)
|
||||
// return
|
||||
// }
|
||||
// // TODO: can we verify tx as well? anything else
|
||||
// }
|
||||
|
||||
// // looks good, we fire it
|
||||
// s.EventSwitch.FireEvent(event, data)
|
||||
// }
|
||||
|
||||
// func verifyHeader(c rpcclient.Client, head *types.Header) error {
|
||||
// // get a checkpoint to verify from
|
||||
// commit, err := c.Commit(&head.Height)
|
||||
// if err != nil {
|
||||
// return err
|
||||
// }
|
||||
// check := certclient.CommitFromResult(commit)
|
||||
// return ValidateHeader(head, check)
|
||||
// }
|
||||
//
|
||||
// func verifyBlock(c rpcclient.Client, block *types.Block) error {
|
||||
// // get a checkpoint to verify from
|
||||
// commit, err := c.Commit(&block.Height)
|
||||
// if err != nil {
|
||||
// return err
|
||||
// }
|
||||
// check := certclient.CommitFromResult(commit)
|
||||
// return ValidateBlock(block, check)
|
||||
// }
|
||||
@@ -1,13 +0,0 @@
|
||||
package lite
|
||||
|
||||
import (
|
||||
"github.com/tendermint/tendermint/types"
|
||||
)
|
||||
|
||||
// Verifier checks the votes to make sure the block really is signed properly.
|
||||
// Verifier must know the current or recent set of validitors by some other
|
||||
// means.
|
||||
type Verifier interface {
|
||||
Verify(sheader types.SignedHeader) error
|
||||
ChainID() string
|
||||
}
|
||||
@@ -29,7 +29,7 @@ import (
|
||||
"github.com/tendermint/tendermint/libs/log"
|
||||
tmpubsub "github.com/tendermint/tendermint/libs/pubsub"
|
||||
"github.com/tendermint/tendermint/libs/service"
|
||||
lite "github.com/tendermint/tendermint/lite2"
|
||||
"github.com/tendermint/tendermint/light"
|
||||
mempl "github.com/tendermint/tendermint/mempool"
|
||||
"github.com/tendermint/tendermint/p2p"
|
||||
"github.com/tendermint/tendermint/p2p/pex"
|
||||
@@ -574,11 +574,11 @@ func startStateSync(ssR *statesync.Reactor, bcR fastSyncReactor, conR *cs.Reacto
|
||||
if stateProvider == nil {
|
||||
var err error
|
||||
stateProvider, err = statesync.NewLightClientStateProvider(state.ChainID, state.Version,
|
||||
config.RPCServers, lite.TrustOptions{
|
||||
config.RPCServers, light.TrustOptions{
|
||||
Period: config.TrustPeriod,
|
||||
Height: config.TrustHeight,
|
||||
Hash: config.TrustHashBytes(),
|
||||
}, ssR.Logger.With("module", "lite"))
|
||||
}, ssR.Logger.With("module", "light"))
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to set up light client state provider: %w", err)
|
||||
}
|
||||
|
||||
@@ -9,11 +9,11 @@ import (
|
||||
dbm "github.com/tendermint/tm-db"
|
||||
|
||||
"github.com/tendermint/tendermint/libs/log"
|
||||
lite "github.com/tendermint/tendermint/lite2"
|
||||
liteprovider "github.com/tendermint/tendermint/lite2/provider"
|
||||
litehttp "github.com/tendermint/tendermint/lite2/provider/http"
|
||||
literpc "github.com/tendermint/tendermint/lite2/rpc"
|
||||
litedb "github.com/tendermint/tendermint/lite2/store/db"
|
||||
"github.com/tendermint/tendermint/light"
|
||||
lightprovider "github.com/tendermint/tendermint/light/provider"
|
||||
lighthttp "github.com/tendermint/tendermint/light/provider/http"
|
||||
lightrpc "github.com/tendermint/tendermint/light/rpc"
|
||||
lightdb "github.com/tendermint/tendermint/light/store/db"
|
||||
rpchttp "github.com/tendermint/tendermint/rpc/client/http"
|
||||
sm "github.com/tendermint/tendermint/state"
|
||||
"github.com/tendermint/tendermint/types"
|
||||
@@ -34,10 +34,10 @@ type StateProvider interface {
|
||||
|
||||
// lightClientStateProvider is a state provider using the light client.
|
||||
type lightClientStateProvider struct {
|
||||
sync.Mutex // lite.Client is not concurrency-safe
|
||||
lc *lite.Client
|
||||
sync.Mutex // light.Client is not concurrency-safe
|
||||
lc *light.Client
|
||||
version sm.Version
|
||||
providers map[liteprovider.Provider]string
|
||||
providers map[lightprovider.Provider]string
|
||||
}
|
||||
|
||||
// NewLightClientStateProvider creates a new StateProvider using a light client and RPC clients.
|
||||
@@ -45,29 +45,29 @@ func NewLightClientStateProvider(
|
||||
chainID string,
|
||||
version sm.Version,
|
||||
servers []string,
|
||||
trustOptions lite.TrustOptions,
|
||||
trustOptions light.TrustOptions,
|
||||
logger log.Logger,
|
||||
) (StateProvider, error) {
|
||||
if len(servers) < 2 {
|
||||
return nil, fmt.Errorf("at least 2 RPC servers are required, got %v", len(servers))
|
||||
}
|
||||
|
||||
providers := make([]liteprovider.Provider, 0, len(servers))
|
||||
providerRemotes := make(map[liteprovider.Provider]string)
|
||||
providers := make([]lightprovider.Provider, 0, len(servers))
|
||||
providerRemotes := make(map[lightprovider.Provider]string)
|
||||
for _, server := range servers {
|
||||
client, err := rpcClient(server)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to set up RPC client: %w", err)
|
||||
}
|
||||
provider := litehttp.NewWithClient(chainID, client)
|
||||
provider := lighthttp.NewWithClient(chainID, client)
|
||||
providers = append(providers, provider)
|
||||
// We store the RPC addresses keyed by provider, so we can find the address of the primary
|
||||
// provider used by the light client and use it to fetch consensus parameters.
|
||||
providerRemotes[provider] = server
|
||||
}
|
||||
|
||||
lc, err := lite.NewClient(chainID, trustOptions, providers[0], providers[1:],
|
||||
litedb.New(dbm.NewMemDB(), ""), lite.Logger(logger), lite.MaxRetryAttempts(5))
|
||||
lc, err := light.NewClient(chainID, trustOptions, providers[0], providers[1:],
|
||||
lightdb.New(dbm.NewMemDB(), ""), light.Logger(logger), light.MaxRetryAttempts(5))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -155,7 +155,7 @@ func (s *lightClientStateProvider) State(height uint64) (sm.State, error) {
|
||||
if err != nil {
|
||||
return sm.State{}, fmt.Errorf("unable to create RPC client: %w", err)
|
||||
}
|
||||
rpcclient := literpc.NewClient(primaryRPC, s.lc)
|
||||
rpcclient := lightrpc.NewClient(primaryRPC, s.lc)
|
||||
result, err := rpcclient.ConsensusParams(&nextHeader.Height)
|
||||
if err != nil {
|
||||
return sm.State{}, fmt.Errorf("unable to fetch consensus parameters for height %v: %w",
|
||||
|
||||
@@ -902,7 +902,7 @@ func CommitFromProto(cp *tmproto.Commit) (*Commit, error) {
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
// SignedHeader is a header along with the commits that prove it.
|
||||
// It is the basis of the lite client.
|
||||
// It is the basis of the light client.
|
||||
type SignedHeader struct {
|
||||
*Header `json:"header"`
|
||||
|
||||
|
||||
@@ -728,7 +728,7 @@ func (vals *ValidatorSet) VerifyCommit(chainID string, blockID BlockID,
|
||||
// old vals to sign the future commit at H, that way we preserve the property
|
||||
// that if they weren't being truthful about the validator set at H (block hash
|
||||
// -> vals hash) or about the app state (block hash -> app hash) we can slash
|
||||
// > 2/3. Otherwise, the lite client isn't providing the same security
|
||||
// > 2/3. Otherwise, the light client isn't providing the same security
|
||||
// guarantees.
|
||||
//
|
||||
// Even if we added a slashing condition that if you sign a block header with
|
||||
|
||||
Reference in New Issue
Block a user