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

Commit e6cf02a

Browse files
committed
ledger consistency proof
1 parent 651dd1c commit e6cf02a

19 files changed

+522
-34
lines changed

.gitignore

+2-1
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,9 @@
1111
# Output of the go coverage tool, specifically when used with LiteIDE
1212
*.out
1313

14-
wallet.toml*
14+
wallet.toml
1515
example/cli_client/cli_client
16+
known_version.toml
1617

1718
ignore/
1819
node_modules/

client/accumulator.go

+43
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
package client
2+
3+
import (
4+
"fmt"
5+
6+
"github.com/the729/go-libra/crypto/sha3libra"
7+
"github.com/the729/go-libra/types/proof/accumulator"
8+
)
9+
10+
func (c *Client) SetKnownVersion(knownVersion uint64, subtrees [][]byte) error {
11+
acc := &accumulator.Accumulator{
12+
Hasher: sha3libra.NewTransactionAccumulator(),
13+
FrozenSubtreeRoots: cloneSubtrees(subtrees),
14+
NumLeaves: knownVersion + 1,
15+
}
16+
_, err := acc.RootHash()
17+
if err != nil {
18+
return fmt.Errorf("known accumulator invalid: %s", err)
19+
}
20+
c.accMu.Lock()
21+
defer c.accMu.Unlock()
22+
c.acc = acc
23+
return nil
24+
}
25+
26+
func (c *Client) GetKnownVersion() (knownVersion uint64, subtrees [][]byte) {
27+
c.accMu.RLock()
28+
defer c.accMu.RUnlock()
29+
return c.acc.NumLeaves - 1, cloneSubtrees(c.acc.FrozenSubtreeRoots)
30+
}
31+
32+
func cloneSubtrees(in [][]byte) [][]byte {
33+
if in == nil {
34+
return nil
35+
}
36+
out := make([][]byte, 0, len(in))
37+
for _, h := range in {
38+
h1 := make([]byte, len(h))
39+
copy(h1, h)
40+
out = append(out, h1)
41+
}
42+
return out
43+
}

client/client.go

+14
Original file line numberDiff line numberDiff line change
@@ -15,12 +15,16 @@ All queries are cryptographically verified to proof their inclusion and integrit
1515
package client
1616

1717
import (
18+
"encoding/hex"
1819
"fmt"
20+
"sync"
1921

2022
"google.golang.org/grpc"
2123

2224
"github.com/the729/go-libra/config"
25+
"github.com/the729/go-libra/crypto/sha3libra"
2326
"github.com/the729/go-libra/generated/pbac"
27+
"github.com/the729/go-libra/types/proof/accumulator"
2428
"github.com/the729/go-libra/types/validator"
2529
)
2630

@@ -30,6 +34,8 @@ type Client struct {
3034
conn *grpc.ClientConn
3135
ac pbac.AdmissionControlClient
3236
verifier validator.Verifier
37+
acc *accumulator.Accumulator
38+
accMu sync.RWMutex
3339
}
3440

3541
// New creates a new Libra Client.
@@ -41,6 +47,14 @@ func New(ServerAddr, TrustedPeerFile string) (*Client, error) {
4147
if err := c.connect(ServerAddr); err != nil {
4248
return nil, err
4349
}
50+
51+
genesisHash, _ := hex.DecodeString("b1f2c172f22b8a9e7fd89a64f75b3b10d64431ccbb1f89a00aff5725e4284fb1")
52+
c.acc = &accumulator.Accumulator{
53+
Hasher: sha3libra.NewTransactionAccumulator(),
54+
FrozenSubtreeRoots: [][]byte{genesisHash},
55+
NumLeaves: 1,
56+
}
57+
4458
return c, nil
4559
}
4660

client/client_js.go

+4
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,11 @@ package client
1616

1717
import (
1818
"fmt"
19+
"sync"
1920

2021
"github.com/the729/go-libra/config"
2122
"github.com/the729/go-libra/generated/pbac"
23+
"github.com/the729/go-libra/types/proof/accumulator"
2224
"github.com/the729/go-libra/types/validator"
2325
)
2426

@@ -27,6 +29,8 @@ import (
2729
type Client struct {
2830
ac pbac.AdmissionControlClient
2931
verifier validator.Verifier
32+
acc *accumulator.Accumulator
33+
accMu sync.RWMutex
3034
}
3135

3236
// New creates a new Libra Client.

client/query_account.go

+8-5
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,13 @@ import (
1212
// QueryAccountState queries account state from RPC server by account address, and does necessary
1313
// crypto verifications.
1414
func (c *Client) QueryAccountState(ctx context.Context, addr types.AccountAddress) (*types.ProvenAccountState, error) {
15+
c.accMu.RLock()
16+
frozenSubtreeRoots := cloneSubtrees(c.acc.FrozenSubtreeRoots)
17+
numLeaves := c.acc.NumLeaves
18+
c.accMu.RUnlock()
19+
1520
resp, err := c.ac.UpdateToLatestLedger(ctx, &pbtypes.UpdateToLatestLedgerRequest{
16-
ClientKnownVersion: 0,
21+
ClientKnownVersion: numLeaves - 1,
1722
RequestedItems: []*pbtypes.RequestItem{
1823
&pbtypes.RequestItem{
1924
RequestedItems: &pbtypes.RequestItem_GetAccountStateRequest{
@@ -30,11 +35,9 @@ func (c *Client) QueryAccountState(ctx context.Context, addr types.AccountAddres
3035
// respj, _ := json.MarshalIndent(resp, "", " ")
3136
// log.Println(string(respj))
3237

33-
li := &types.LedgerInfoWithSignatures{}
34-
li.FromProto(resp.LedgerInfoWithSigs)
35-
pli, err := li.Verify(c.verifier)
38+
pli, err := c.verifyLedgerInfoAndConsistency(resp, numLeaves, frozenSubtreeRoots)
3639
if err != nil {
37-
return nil, fmt.Errorf("ledger info verification failed: %v", err)
40+
return nil, err
3841
}
3942

4043
account := &types.AccountStateWithProof{}

client/query_event.go

+8-5
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,13 @@ import (
1111

1212
// QueryEventsByAccessPath queries list of events by access path does necessary crypto verifications.
1313
func (c *Client) QueryEventsByAccessPath(ctx context.Context, ap *types.AccessPath, start uint64, ascending bool, limit uint64) ([]*types.ProvenEvent, error) {
14+
c.accMu.RLock()
15+
frozenSubtreeRoots := cloneSubtrees(c.acc.FrozenSubtreeRoots)
16+
numLeaves := c.acc.NumLeaves
17+
c.accMu.RUnlock()
18+
1419
resp, err := c.ac.UpdateToLatestLedger(ctx, &pbtypes.UpdateToLatestLedgerRequest{
20+
ClientKnownVersion: numLeaves - 1,
1521
RequestedItems: []*pbtypes.RequestItem{
1622
{
1723
RequestedItems: &pbtypes.RequestItem_GetEventsByEventAccessPathRequest{
@@ -32,13 +38,10 @@ func (c *Client) QueryEventsByAccessPath(ctx context.Context, ap *types.AccessPa
3238
return nil, err
3339
}
3440

35-
li := &types.LedgerInfoWithSignatures{}
36-
li.FromProto(resp.LedgerInfoWithSigs)
37-
pli, err := li.Verify(c.verifier)
41+
pli, err := c.verifyLedgerInfoAndConsistency(resp, numLeaves, frozenSubtreeRoots)
3842
if err != nil {
39-
return nil, fmt.Errorf("ledger info verification failed: %v", err)
43+
return nil, err
4044
}
41-
// log.Printf("Ledger info: version %d, time %d", li.LedgerInfo.Version, li.LedgerInfo.TimestampUsec)
4245

4346
resp1 := resp.ResponseItems[0].GetGetEventsByEventAccessPathResponse()
4447
if resp1 == nil {

client/query_ledger.go

+29-2
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,14 @@ import (
99
)
1010

1111
// QueryLedgerInfo queries ledger info from RPC server, and does necessary crypto verifications.
12-
func (c *Client) QueryLedgerInfo(ctx context.Context, knownVersion uint64) (*types.ProvenLedgerInfo, error) {
12+
func (c *Client) QueryLedgerInfo(ctx context.Context) (*types.ProvenLedgerInfo, error) {
13+
c.accMu.RLock()
14+
frozenSubtreeRoots := cloneSubtrees(c.acc.FrozenSubtreeRoots)
15+
numLeaves := c.acc.NumLeaves
16+
c.accMu.RUnlock()
17+
1318
resp, err := c.ac.UpdateToLatestLedger(ctx, &pbtypes.UpdateToLatestLedgerRequest{
14-
ClientKnownVersion: knownVersion,
19+
ClientKnownVersion: numLeaves - 1,
1520
})
1621
if err != nil {
1722
return nil, err
@@ -20,12 +25,34 @@ func (c *Client) QueryLedgerInfo(ctx context.Context, knownVersion uint64) (*typ
2025
// respj, _ := json.MarshalIndent(resp, "", " ")
2126
// log.Println(string(respj))
2227

28+
pli, err := c.verifyLedgerInfoAndConsistency(resp, numLeaves, frozenSubtreeRoots)
29+
if err != nil {
30+
return nil, err
31+
}
32+
33+
return pli, nil
34+
}
35+
36+
func (c *Client) verifyLedgerInfoAndConsistency(
37+
resp *pbtypes.UpdateToLatestLedgerResponse,
38+
numLeaves uint64, frozenSubtreeRoots [][]byte,
39+
) (*types.ProvenLedgerInfo, error) {
40+
2341
li := &types.LedgerInfoWithSignatures{}
2442
li.FromProto(resp.LedgerInfoWithSigs)
2543
pli, err := li.Verify(c.verifier)
2644
if err != nil {
2745
return nil, fmt.Errorf("ledger info verification failed: %v", err)
2846
}
47+
numLeaves, frozenSubtreeRoots, err = pli.VerifyConsistency(numLeaves, frozenSubtreeRoots, resp.LedgerConsistencyProof.Subtrees)
48+
if err != nil {
49+
return nil, fmt.Errorf("ledger not consistent with known version: %v", err)
50+
}
51+
c.accMu.Lock()
52+
if numLeaves > c.acc.NumLeaves {
53+
c.acc.FrozenSubtreeRoots, c.acc.NumLeaves = frozenSubtreeRoots, numLeaves
54+
}
55+
c.accMu.Unlock()
2956

3057
return pli, nil
3158
}

client/query_txn.go

+16-10
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,13 @@ import (
1212
// QueryTransactionRange queries a list of transactions from RPC server, and does necessary
1313
// crypto verifications.
1414
func (c *Client) QueryTransactionRange(ctx context.Context, start, limit uint64, withEvents bool) (*types.ProvenTransactionList, error) {
15+
c.accMu.RLock()
16+
frozenSubtreeRoots := cloneSubtrees(c.acc.FrozenSubtreeRoots)
17+
numLeaves := c.acc.NumLeaves
18+
c.accMu.RUnlock()
19+
1520
resp, err := c.ac.UpdateToLatestLedger(ctx, &pbtypes.UpdateToLatestLedgerRequest{
21+
ClientKnownVersion: numLeaves - 1,
1622
RequestedItems: []*pbtypes.RequestItem{
1723
{
1824
RequestedItems: &pbtypes.RequestItem_GetTransactionsRequest{
@@ -32,13 +38,10 @@ func (c *Client) QueryTransactionRange(ctx context.Context, start, limit uint64,
3238
// b, err := json.MarshalIndent(resp, "", " ")
3339
// log.Printf("resp: %s", string(b))
3440

35-
li := &types.LedgerInfoWithSignatures{}
36-
li.FromProto(resp.LedgerInfoWithSigs)
37-
pli, err := li.Verify(c.verifier)
41+
pli, err := c.verifyLedgerInfoAndConsistency(resp, numLeaves, frozenSubtreeRoots)
3842
if err != nil {
39-
return nil, fmt.Errorf("ledger info verification failed: %v", err)
43+
return nil, err
4044
}
41-
// log.Printf("Ledger info: version %d, time %d", li.LedgerInfo.Version, li.LedgerInfo.TimestampUsec)
4245

4346
txnList := &types.TransactionListWithProof{}
4447
err = txnList.FromProtoResponse(resp.ResponseItems[0].GetGetTransactionsResponse())
@@ -59,7 +62,13 @@ func (c *Client) QueryTransactionRange(ctx context.Context, start, limit uint64,
5962
// QueryTransactionByAccountSeq queries the transaction that is sent from a specific account at a specific sequence number,
6063
// and does necessary crypto verifications.
6164
func (c *Client) QueryTransactionByAccountSeq(ctx context.Context, addr types.AccountAddress, sequence uint64, withEvents bool) (*types.ProvenTransaction, error) {
65+
c.accMu.RLock()
66+
frozenSubtreeRoots := cloneSubtrees(c.acc.FrozenSubtreeRoots)
67+
numLeaves := c.acc.NumLeaves
68+
c.accMu.RUnlock()
69+
6270
resp, err := c.ac.UpdateToLatestLedger(ctx, &pbtypes.UpdateToLatestLedgerRequest{
71+
ClientKnownVersion: numLeaves - 1,
6372
RequestedItems: []*pbtypes.RequestItem{
6473
{
6574
RequestedItems: &pbtypes.RequestItem_GetAccountTransactionBySequenceNumberRequest{
@@ -79,13 +88,10 @@ func (c *Client) QueryTransactionByAccountSeq(ctx context.Context, addr types.Ac
7988
// b, err := json.MarshalIndent(resp, "", " ")
8089
// log.Printf("resp: %s", string(b))
8190

82-
li := &types.LedgerInfoWithSignatures{}
83-
li.FromProto(resp.LedgerInfoWithSigs)
84-
pli, err := li.Verify(c.verifier)
91+
pli, err := c.verifyLedgerInfoAndConsistency(resp, numLeaves, frozenSubtreeRoots)
8592
if err != nil {
86-
return nil, fmt.Errorf("ledger info verification failed: %v", err)
93+
return nil, err
8794
}
88-
// log.Printf("Ledger info: version %d, time %d", li.LedgerInfo.Version, li.LedgerInfo.TimestampUsec)
8995

9096
resp1 := resp.ResponseItems[0].GetGetAccountTransactionBySequenceNumberResponse()
9197
if resp1 == nil {

example/cli_client/cmd_query.go

+11-5
Original file line numberDiff line numberDiff line change
@@ -22,12 +22,10 @@ func cmdQueryLedgerInfo(ctx *cli.Context) error {
2222
log.Fatal(err)
2323
}
2424
defer c.Close()
25+
loadKnownVersion(c, KnownVersionFile)
26+
defer saveKnownVersion(c, KnownVersionFile)
2527

26-
knownVersion, err := strconv.ParseUint(ctx.Args().Get(0), 10, 64)
27-
if err != nil {
28-
return err
29-
}
30-
ledgerInfo, err := c.QueryLedgerInfo(context.Background(), knownVersion)
28+
ledgerInfo, err := c.QueryLedgerInfo(context.Background())
3129
if err != nil {
3230
return err
3331
}
@@ -42,6 +40,8 @@ func cmdQueryAccountState(ctx *cli.Context) error {
4240
log.Fatal(err)
4341
}
4442
defer c.Close()
43+
loadKnownVersion(c, KnownVersionFile)
44+
defer saveKnownVersion(c, KnownVersionFile)
4545

4646
wallet, err := LoadAccounts(WalletFile)
4747
if err != nil {
@@ -88,6 +88,8 @@ func cmdQueryTransactionRange(ctx *cli.Context) error {
8888
log.Fatal(err)
8989
}
9090
defer c.Close()
91+
loadKnownVersion(c, KnownVersionFile)
92+
defer saveKnownVersion(c, KnownVersionFile)
9193

9294
start, err := strconv.Atoi(ctx.Args().Get(0))
9395
if err != nil {
@@ -118,6 +120,8 @@ func cmdQueryTransactionByAccountSeq(ctx *cli.Context) error {
118120
log.Fatal(err)
119121
}
120122
defer c.Close()
123+
loadKnownVersion(c, KnownVersionFile)
124+
defer saveKnownVersion(c, KnownVersionFile)
121125

122126
wallet, err := LoadAccounts(WalletFile)
123127
if err != nil {
@@ -149,6 +153,8 @@ func cmdQueryEvents(ctx *cli.Context) error {
149153
log.Fatal(err)
150154
}
151155
defer c.Close()
156+
loadKnownVersion(c, KnownVersionFile)
157+
defer saveKnownVersion(c, KnownVersionFile)
152158

153159
wallet, err := LoadAccounts(WalletFile)
154160
if err != nil {

example/cli_client/cmd_transfer.go

+2
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ func cmdTransfer(ctx *cli.Context) error {
1818
log.Fatal(err)
1919
}
2020
defer c.Close()
21+
loadKnownVersion(c, KnownVersionFile)
22+
defer saveKnownVersion(c, KnownVersionFile)
2123

2224
wallet, err := LoadAccounts(WalletFile)
2325
if err != nil {

example/cli_client/known_version.go

+45
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
package main
2+
3+
import (
4+
"fmt"
5+
"log"
6+
"os"
7+
8+
"github.com/BurntSushi/toml"
9+
"github.com/the729/go-libra/client"
10+
)
11+
12+
type knownVersionState struct {
13+
KnownVersion uint64 `toml:"known_version"`
14+
Subtrees [][]byte `toml:"subtrees"`
15+
}
16+
17+
func loadKnownVersion(c *client.Client, filepath string) error {
18+
v := &knownVersionState{}
19+
_, err := toml.DecodeFile(filepath, v)
20+
if err != nil {
21+
return fmt.Errorf("toml decode file error: %v", err)
22+
}
23+
if len(v.Subtrees) == 0 {
24+
return fmt.Errorf("empty subtree")
25+
}
26+
err = c.SetKnownVersion(v.KnownVersion, v.Subtrees)
27+
if err != nil {
28+
return fmt.Errorf("set accumulator error: %v", err)
29+
}
30+
return nil
31+
}
32+
33+
func saveKnownVersion(c *client.Client, filepath string) error {
34+
v := &knownVersionState{}
35+
v.KnownVersion, v.Subtrees = c.GetKnownVersion()
36+
f, err := os.Create(filepath)
37+
if err != nil {
38+
return fmt.Errorf("create file error: %v", err)
39+
}
40+
err = toml.NewEncoder(f).Encode(v)
41+
if err != nil {
42+
log.Printf("cannot encode toml file: %v", err)
43+
}
44+
return nil
45+
}

0 commit comments

Comments
 (0)