Skip to content
This repository was archived by the owner on Dec 15, 2024. It is now read-only.

Commit 993f678

Browse files
committed
[client & types] implement Waypoint and ValidatorChangeProof
The client is able to learn validator set from the server (via a trusted Waypoint). No need to specify a trusted peers list anymore.
1 parent fb82fda commit 993f678

24 files changed

+558
-342
lines changed

client/accumulator.go

-43
This file was deleted.

client/client.go

+21-19
Original file line numberDiff line numberDiff line change
@@ -24,23 +24,23 @@ to restore the known-version state.
2424
package client
2525

2626
import (
27-
"encoding/hex"
27+
"fmt"
2828
"sync"
2929

30-
"github.com/the729/go-libra/crypto/sha3libra"
3130
"github.com/the729/go-libra/generated/pbac"
31+
"github.com/the729/go-libra/types"
3232
"github.com/the729/go-libra/types/proof/accumulator"
33-
"github.com/the729/go-libra/types/validator"
3433
)
3534

3635
// Client is a Libra client.
3736
// It has a gRPC client to a Libra RPC server, with public keys to trusted peers.
3837
type Client struct {
39-
closeFunc func()
40-
ac pbac.AdmissionControlClient
41-
verifier validator.Verifier
42-
acc *accumulator.Accumulator
43-
accMu sync.RWMutex
38+
closeFunc func()
39+
ac pbac.AdmissionControlClient
40+
verifier types.LedgerInfoVerifier
41+
acc *accumulator.Accumulator
42+
accMu sync.RWMutex
43+
lastWaypoint string
4444
}
4545

4646
// New creates a new Libra Client.
@@ -50,22 +50,18 @@ type Client struct {
5050
//
5151
// For use with Javascript, ServerAddr is in http://host:port format. TrustedPeer is a TOML formated
5252
// text of the trusted peers config.
53-
func New(ServerAddr, TrustedPeer string) (*Client, error) {
53+
func New(ServerAddr, Waypoint string) (*Client, error) {
54+
return NewFromState(ServerAddr, &ClientState{Waypoint: Waypoint})
55+
}
56+
57+
func NewFromState(ServerAddr string, state *ClientState) (*Client, error) {
5458
c := &Client{}
55-
if err := c.loadTrustedPeers(TrustedPeer); err != nil {
56-
return nil, err
59+
if err := c.SetState(state); err != nil {
60+
return nil, fmt.Errorf("invalid state: %v", err)
5761
}
5862
if err := c.connect(ServerAddr); err != nil {
5963
return nil, err
6064
}
61-
62-
genesisHash, _ := hex.DecodeString("c7f50b86fdbb6857333afc6c3450ed61871a9c788ec6818e741cfbd1be08faad")
63-
c.acc = &accumulator.Accumulator{
64-
Hasher: sha3libra.NewTransactionAccumulator(),
65-
FrozenSubtreeRoots: [][]byte{genesisHash},
66-
NumLeaves: 1,
67-
}
68-
6965
return c, nil
7066
}
7167

@@ -75,3 +71,9 @@ func (c *Client) Close() {
7571
c.closeFunc()
7672
}
7773
}
74+
75+
// GetLatestWaypoint returns the latest waypoint string that this client has
76+
// reached.
77+
func (c *Client) GetLatestWaypoint() string {
78+
return c.lastWaypoint
79+
}

client/client_go.go

-15
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,7 @@ import (
77

88
"google.golang.org/grpc"
99

10-
"github.com/the729/go-libra/config"
1110
"github.com/the729/go-libra/generated/pbac"
12-
"github.com/the729/go-libra/types/validator"
1311
)
1412

1513
func (c *Client) connect(server string) error {
@@ -22,16 +20,3 @@ func (c *Client) connect(server string) error {
2220
c.ac = pbac.NewAdmissionControlClient(conn)
2321
return nil
2422
}
25-
26-
func (c *Client) loadTrustedPeers(file string) error {
27-
peerconf, err := config.LoadTrustedPeersFromFile(file)
28-
if err != nil {
29-
return fmt.Errorf("load conf err: %v", err)
30-
}
31-
verifier, err := validator.NewConsensusVerifier(peerconf)
32-
if err != nil {
33-
return fmt.Errorf("new verifier err: %v", err)
34-
}
35-
c.verifier = verifier
36-
return nil
37-
}

client/client_js.go

-17
Original file line numberDiff line numberDiff line change
@@ -3,27 +3,10 @@
33
package client
44

55
import (
6-
"fmt"
7-
8-
"github.com/the729/go-libra/config"
96
"github.com/the729/go-libra/generated/pbac"
10-
"github.com/the729/go-libra/types/validator"
117
)
128

139
func (c *Client) connect(server string) error {
1410
c.ac = pbac.NewAdmissionControlClient(server)
1511
return nil
1612
}
17-
18-
func (c *Client) loadTrustedPeers(tomlData string) error {
19-
peerconf, err := config.LoadTrustedPeers(tomlData)
20-
if err != nil {
21-
return fmt.Errorf("load conf err: %v", err)
22-
}
23-
verifier, err := validator.NewConsensusVerifier(peerconf)
24-
if err != nil {
25-
return fmt.Errorf("new verifier err: %v", err)
26-
}
27-
c.verifier = verifier
28-
return nil
29-
}

client/query_ledger.go

+34-1
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,35 @@ func (c *Client) verifyLedgerInfoAndConsistency(
4040

4141
li := &types.LedgerInfoWithSignatures{}
4242
li.FromProto(resp.LedgerInfoWithSigs)
43-
pli, err := li.Verify(c.verifier)
43+
44+
verifier := c.verifier
45+
lastWaypoint := ""
46+
if verifier.EpochChangeVerificationRequired(li.Epoch) {
47+
vcp := &types.ValidatorChangeProof{}
48+
if err := vcp.FromProto(resp.ValidatorChangeProof); err != nil {
49+
return nil, fmt.Errorf("validator change proof invalid: %v", err)
50+
}
51+
epochChangeLI, err := vcp.Verify(verifier)
52+
if err != nil {
53+
return nil, fmt.Errorf("validator change proof verification error: %v", err)
54+
}
55+
if epochChangeLI.GetVersion() == 0 {
56+
// this is the genesis block, update accumulator
57+
numLeaves = 1
58+
frozenSubtreeRoots = [][]byte{epochChangeLI.GetTransactionAccumulatorHash()}
59+
}
60+
v, err := epochChangeLI.ToVerifier()
61+
if err != nil {
62+
return nil, err
63+
}
64+
65+
// log.Printf("Ledger info verifier updated, epoch = %d", epochChangeLI.GetEpochNum()+1)
66+
67+
verifier = v
68+
lastWaypointB, _ := (&types.Waypoint{}).FromProvenLedgerInfo(epochChangeLI).MarshalText()
69+
lastWaypoint = string(lastWaypointB)
70+
}
71+
pli, err := li.Verify(verifier)
4472
if err != nil {
4573
return nil, fmt.Errorf("ledger info verification failed: %v", err)
4674
}
@@ -52,10 +80,15 @@ func (c *Client) verifyLedgerInfoAndConsistency(
5280
if err != nil {
5381
return nil, fmt.Errorf("ledger not consistent with known version: %v", err)
5482
}
83+
5584
c.accMu.Lock()
5685
if numLeaves > c.acc.NumLeaves {
5786
c.acc.FrozenSubtreeRoots, c.acc.NumLeaves = frozenSubtreeRoots, numLeaves
5887
}
88+
if lastWaypoint != "" {
89+
c.verifier = verifier
90+
c.lastWaypoint = lastWaypoint
91+
}
5992
c.accMu.Unlock()
6093

6194
return pli, nil

client/state.go

+123
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
package client
2+
3+
import (
4+
"encoding/hex"
5+
"fmt"
6+
7+
"github.com/the729/go-libra/crypto/sha3libra"
8+
"github.com/the729/go-libra/types"
9+
"github.com/the729/go-libra/types/proof/accumulator"
10+
)
11+
12+
type HashValue []byte
13+
14+
func (h *HashValue) UnmarshalText(txt []byte) error {
15+
data, err := hex.DecodeString(string(txt))
16+
if err != nil {
17+
return fmt.Errorf("hash value decode error: %v", err)
18+
}
19+
*h = data
20+
return nil
21+
}
22+
23+
func (h HashValue) MarshalText() (text []byte, err error) {
24+
return []byte(hex.EncodeToString(h)), nil
25+
}
26+
27+
type ClientState struct {
28+
Waypoint string `toml:"waypoint" json:"waypoint"`
29+
ValidatorSet types.ValidatorSet `toml:"validator_set" json:"validator_set,omitempty"`
30+
Epoch uint64 `toml:"epoch" json:"epoch"`
31+
KnownVersion uint64 `toml:"known_version" json:"known_version"`
32+
Subtrees []HashValue `toml:"subtrees" json:"subtrees"`
33+
}
34+
35+
func (c *Client) GetState() *ClientState {
36+
c.accMu.RLock()
37+
defer c.accMu.RUnlock()
38+
39+
cs := &ClientState{}
40+
cs.Waypoint = c.lastWaypoint
41+
if vv, ok := c.verifier.(*types.ValidatorVerifier); ok {
42+
cs.ValidatorSet, cs.Epoch = vv.ToValidatorSet()
43+
}
44+
cs.KnownVersion = c.acc.NumLeaves - 1
45+
cs.Subtrees = cloneSubtrees2(c.acc.FrozenSubtreeRoots)
46+
47+
return cs
48+
}
49+
50+
func (c *Client) SetState(cs *ClientState) error {
51+
var verifier types.LedgerInfoVerifier
52+
if len(cs.ValidatorSet) > 0 {
53+
vv := &types.ValidatorVerifier{}
54+
if err := vv.FromValidatorSet(cs.ValidatorSet, cs.Epoch); err != nil {
55+
return fmt.Errorf("restore validator set error: %v", err)
56+
}
57+
verifier = vv
58+
} else if cs.Waypoint == "insecure" {
59+
verifier = &types.ValidatorVerifier{}
60+
println("Warning: INSECURE! No waypoint or validator set specified.")
61+
} else {
62+
wp := &types.Waypoint{}
63+
if err := wp.UnmarshalText([]byte(cs.Waypoint)); err != nil {
64+
return fmt.Errorf("restore waypoint error: %v", err)
65+
}
66+
verifier = wp
67+
}
68+
69+
acc := &accumulator.Accumulator{
70+
Hasher: sha3libra.NewTransactionAccumulator(),
71+
FrozenSubtreeRoots: cloneSubtrees1(cs.Subtrees),
72+
NumLeaves: cs.KnownVersion + 1,
73+
}
74+
if _, err := acc.RootHash(); err != nil {
75+
return fmt.Errorf("known accumulator invalid: %v", err)
76+
}
77+
78+
c.accMu.Lock()
79+
defer c.accMu.Unlock()
80+
c.acc = acc
81+
c.verifier = verifier
82+
c.lastWaypoint = cs.Waypoint
83+
return nil
84+
}
85+
86+
func cloneSubtrees(in [][]byte) [][]byte {
87+
if in == nil {
88+
return nil
89+
}
90+
out := make([][]byte, 0, len(in))
91+
for _, h := range in {
92+
h1 := make([]byte, len(h))
93+
copy(h1, h)
94+
out = append(out, h1)
95+
}
96+
return out
97+
}
98+
99+
func cloneSubtrees1(in []HashValue) [][]byte {
100+
if in == nil {
101+
return nil
102+
}
103+
out := make([][]byte, 0, len(in))
104+
for _, h := range in {
105+
h1 := make([]byte, len(h))
106+
copy(h1, h)
107+
out = append(out, h1)
108+
}
109+
return out
110+
}
111+
112+
func cloneSubtrees2(in [][]byte) []HashValue {
113+
if in == nil {
114+
return nil
115+
}
116+
out := make([]HashValue, 0, len(in))
117+
for _, h := range in {
118+
h1 := make([]byte, len(h))
119+
copy(h1, h)
120+
out = append(out, HashValue(h1))
121+
}
122+
return out
123+
}

config/peers_test.config.toml

-9
This file was deleted.

config/trusted_peers.go

-39
This file was deleted.

0 commit comments

Comments
 (0)