Skip to content

Commit 5f0dd19

Browse files
committed
added proof RPC
post-merge fixes other part of test post-merge fix
1 parent 642e01d commit 5f0dd19

File tree

10 files changed

+452
-123
lines changed

10 files changed

+452
-123
lines changed

blockchain/claimtrie.go

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package blockchain
33
import (
44
"bytes"
55
"fmt"
6+
"strings"
67

78
"github.com/pkg/errors"
89

@@ -181,3 +182,68 @@ func (b *BlockChain) GetClaimsForName(height int32, name string) (string, *node.
181182
n.SortClaimsByBid()
182183
return string(normalizedName), n, nil
183184
}
185+
186+
func (b *BlockChain) GetProofForName(name, id string, bid, seq int) (chainhash.Hash, int32, *node.Claim, int32, int32, string, []merkletrie.HashSidePair, error) {
187+
// results: block hash, height, claim, bid, takeover, name, pairs, err
188+
189+
b.chainLock.RLock()
190+
defer b.chainLock.RUnlock()
191+
192+
tip := b.bestChain.Tip()
193+
194+
normalizedName := normalization.NormalizeIfNecessary([]byte(name), tip.height)
195+
196+
if tip.height < param.ActiveParams.GrandForkHeight {
197+
err := errors.Errorf("Unable to generate proofs for claims before height %d",
198+
param.ActiveParams.GrandForkHeight)
199+
return tip.hash, tip.height, nil, 0, 0, string(normalizedName), nil, err
200+
}
201+
202+
n, err := b.claimTrie.NodeAt(tip.height, normalizedName)
203+
if n == nil && err == nil {
204+
err = errors.Errorf("Unable to locate a claim with name %s at height %d", normalizedName, tip.height)
205+
}
206+
if err != nil {
207+
return tip.hash, tip.height, nil, 0, 0, string(normalizedName), nil, err
208+
}
209+
210+
// now find the desired claim
211+
n.SortClaimsByBid()
212+
var claim *node.Claim
213+
for i, c := range n.Claims {
214+
if c.Status != node.Activated {
215+
continue
216+
}
217+
if bid >= 0 && i == bid {
218+
claim = c
219+
bid = i
220+
break
221+
}
222+
if seq >= 0 && int(c.Sequence) == seq {
223+
claim = c
224+
bid = i
225+
break
226+
}
227+
if len(id) > 0 && strings.HasPrefix(c.ClaimID.String(), id) {
228+
claim = c
229+
bid = i
230+
break
231+
}
232+
}
233+
if claim == nil {
234+
if bid >= 0 {
235+
err = errors.Errorf("Unable to locate a claim named %s with bid %d at height %d", normalizedName, bid, tip.height)
236+
}
237+
if seq >= 0 {
238+
err = errors.Errorf("Unable to locate a claim named %s with sequence %d at height %d", normalizedName, seq, tip.height)
239+
}
240+
if len(id) > 0 {
241+
err = errors.Errorf("Unable to locate a claim named %s with ID %s at height %d", normalizedName, id, tip.height)
242+
}
243+
return tip.hash, tip.height, nil, 0, 0, string(normalizedName), nil, err
244+
}
245+
246+
pairs := b.claimTrie.MerklePath(normalizedName, n, bid)
247+
248+
return tip.hash, tip.height, claim, int32(bid), n.TakenOverAt, string(normalizedName), pairs, nil
249+
}

btcjson/claimcmds.go

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,10 @@ func init() {
1010
MustRegisterCmd("getclaimsfornamebybid", (*GetClaimsForNameByBidCmd)(nil), flags)
1111
MustRegisterCmd("getclaimsfornamebyseq", (*GetClaimsForNameBySeqCmd)(nil), flags)
1212
MustRegisterCmd("normalize", (*GetNormalizedCmd)(nil), flags)
13+
14+
MustRegisterCmd("getprooffornamebyid", (*GetProofForNameByIDCmd)(nil), flags)
15+
MustRegisterCmd("getprooffornamebybid", (*GetProofForNameByBidCmd)(nil), flags)
16+
MustRegisterCmd("getprooffornamebyseq", (*GetProofForNameBySeqCmd)(nil), flags)
1317
}
1418

1519
// optional inputs are required to be pointers, but they support things like `jsonrpcdefault:"false"`
@@ -93,3 +97,36 @@ type GetNormalizedCmd struct {
9397
type GetNormalizedResult struct {
9498
NormalizedName string `json:"normalizedname"`
9599
}
100+
101+
type GetProofForNameByIDCmd struct {
102+
Name string `json:"name"`
103+
PartialClaimID string `json:"partialclaimid"`
104+
}
105+
106+
type GetProofForNameByBidCmd struct {
107+
Name string `json:"name"`
108+
Bid int `json:"bid"`
109+
}
110+
111+
type GetProofForNameBySeqCmd struct {
112+
Name string `json:"name"`
113+
Sequence int `json:"sequence"`
114+
}
115+
116+
type ProofPairResult struct {
117+
Right bool `json:"right"`
118+
Hash string `json:"hash"`
119+
}
120+
121+
type ProofResult struct { // should we include the claim trie hash?
122+
BlockHash string `json:"blockhash"`
123+
BlockHeight int32 `json:"blockheight"`
124+
NormalizedName string `json:"normalizedname"`
125+
ClaimID string `json:"claimid"`
126+
TXID string `json:"txid"`
127+
N uint32 `json:"n"`
128+
Bid int32 `json:"bid"`
129+
Sequence int32 `json:"sequence"`
130+
Takeover int32 `json:"takeover"`
131+
Pairs []ProofPairResult `json:"pairs"`
132+
}

claimtrie/claimtrie.go

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -304,7 +304,7 @@ func removeDuplicates(names [][]byte) [][]byte { // this might be too expensive;
304304
return names
305305
}
306306

307-
// ResetHeight resets the ClaimTrie to a previous known height..
307+
// ResetHeight resets the ClaimTrie to a previous known height.
308308
func (ct *ClaimTrie) ResetHeight(height int32) error {
309309

310310
names := make([][]byte, 0)
@@ -321,6 +321,9 @@ func (ct *ClaimTrie) ResetHeight(height int32) error {
321321
}
322322

323323
passedHashFork := ct.height >= param.ActiveParams.AllClaimsInMerkleForkHeight && height < param.ActiveParams.AllClaimsInMerkleForkHeight
324+
if !passedHashFork {
325+
passedHashFork = ct.height >= param.ActiveParams.GrandForkHeight && height < param.ActiveParams.GrandForkHeight
326+
}
324327
hash, err := ct.blockRepo.Get(height)
325328
if err != nil {
326329
return err
@@ -469,3 +472,21 @@ func (ct *ClaimTrie) makeNameHashNext(names [][]byte, all bool) chan NameHashNex
469472
}()
470473
return outputs
471474
}
475+
476+
func (ct *ClaimTrie) MerklePath(name []byte, n *node.Node, bid int) []merkletrie.HashSidePair {
477+
pairs := ct.merkleTrie.MerklePath(name)
478+
// TODO: organize this code better
479+
// this is the 2nd half of the above merkle tree computation
480+
// it's done like this so we don't have to create the Node object multiple times
481+
claimHashes := node.ComputeClaimHashes(name, n)
482+
partials := node.ComputeMerklePath(claimHashes, bid)
483+
for i := len(partials) - 1; i >= 0; i-- {
484+
pairs = append(pairs, merkletrie.HashSidePair{Right: ((bid >> i) & 1) > 0, Hash: partials[i]})
485+
}
486+
487+
// reverse the list order:
488+
for i, j := 0, len(pairs)-1; i < j; i, j = i+1, j-1 {
489+
pairs[i], pairs[j] = pairs[j], pairs[i]
490+
}
491+
return pairs
492+
}

claimtrie/claimtrie_test.go

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1009,3 +1009,66 @@ func TestBlock884431(t *testing.T) {
10091009
r.NoError(err)
10101010
r.Equal(o11.String(), n.BestClaim.OutPoint.String())
10111011
}
1012+
1013+
func TestMerklePath(t *testing.T) {
1014+
r := require.New(t)
1015+
setup(t)
1016+
param.ActiveParams.ActiveDelayFactor = 1
1017+
param.ActiveParams.NormalizedNameForkHeight = 5
1018+
param.ActiveParams.AllClaimsInMerkleForkHeight = 6
1019+
param.ActiveParams.GrandForkHeight = 7
1020+
1021+
ct, err := New(cfg)
1022+
r.NoError(err)
1023+
r.NotNil(ct)
1024+
defer ct.Close()
1025+
1026+
hash := chainhash.HashH([]byte{1, 2, 3})
1027+
o1 := wire.OutPoint{Hash: hash, Index: 1}
1028+
o2 := wire.OutPoint{Hash: hash, Index: 2}
1029+
o3 := wire.OutPoint{Hash: hash, Index: 3}
1030+
1031+
err = ct.AddClaim([]byte("test"), o1, change.NewClaimID(o1), 1)
1032+
r.NoError(err)
1033+
1034+
err = ct.AddClaim([]byte("test"), o2, change.NewClaimID(o2), 2)
1035+
r.NoError(err)
1036+
1037+
err = ct.AddClaim([]byte("tester"), o3, change.NewClaimID(o3), 1)
1038+
r.NoError(err)
1039+
1040+
for i := 0; i < 10; i++ {
1041+
err = ct.AppendBlock()
1042+
r.NoError(err)
1043+
}
1044+
1045+
n, err := ct.NodeAt(ct.height, []byte("test"))
1046+
r.NoError(err)
1047+
pairs := ct.MerklePath([]byte("test"), n, 0)
1048+
claimHash, err := node.ComputeBidSeqNameHash([]byte("test"), n.Claims[0], 0, n.TakenOverAt)
1049+
r.NoError(err)
1050+
validatePairs(r, pairs, ct.MerkleHash(), claimHash)
1051+
1052+
pairs = ct.MerklePath([]byte("test"), n, 1)
1053+
claimHash, err = node.ComputeBidSeqNameHash([]byte("test"), n.Claims[1], 1, n.TakenOverAt)
1054+
r.NoError(err)
1055+
validatePairs(r, pairs, ct.MerkleHash(), claimHash)
1056+
1057+
n, err = ct.NodeAt(ct.height, []byte("tester"))
1058+
r.NoError(err)
1059+
pairs = ct.MerklePath([]byte("tester"), n, 0)
1060+
claimHash, err = node.ComputeBidSeqNameHash([]byte("tester"), n.Claims[0], 0, n.TakenOverAt)
1061+
r.NoError(err)
1062+
validatePairs(r, pairs, ct.MerkleHash(), claimHash)
1063+
}
1064+
1065+
func validatePairs(r *require.Assertions, pairs []merkletrie.HashSidePair, target *chainhash.Hash, claimHash *chainhash.Hash) {
1066+
for i := range pairs {
1067+
if pairs[i].Right {
1068+
claimHash = node.HashMerkleBranches(pairs[i].Hash, claimHash)
1069+
} else {
1070+
claimHash = node.HashMerkleBranches(claimHash, pairs[i].Hash)
1071+
}
1072+
}
1073+
r.True(claimHash.IsEqual(target))
1074+
}

claimtrie/merkletrie/merkletrie.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -253,3 +253,7 @@ func (t *PersistentTrie) Dump(s string) {
253253
func (t *PersistentTrie) Flush() error {
254254
return t.repo.Flush()
255255
}
256+
257+
func (t *PersistentTrie) MerklePath(name []byte) []HashSidePair {
258+
panic("MerklePath not implemented in PersistentTrie")
259+
}

claimtrie/merkletrie/ramtrie.go

Lines changed: 61 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ type MerkleTrie interface {
1616
MerkleHash() *chainhash.Hash
1717
MerkleHashAllClaims() *chainhash.Hash
1818
Flush() error
19+
MerklePath(name []byte) []HashSidePair
1920
}
2021

2122
type RamTrie struct {
@@ -111,29 +112,80 @@ func (rt *RamTrie) merkleHashAllClaims(v *collapsedVertex) *chainhash.Hash {
111112
return v.merkleHash
112113
}
113114

114-
childHashes := make([]*chainhash.Hash, 0, len(v.children))
115-
for _, ch := range v.children {
116-
h := rt.merkleHashAllClaims(ch)
117-
childHashes = append(childHashes, h)
118-
}
115+
childHash, hasChildren := rt.computeChildHash(v)
119116

120117
claimHash := NoClaimsHash
121118
if v.claimHash != nil {
122119
claimHash = v.claimHash
123-
} else if len(childHashes) == 0 {
120+
} else if !hasChildren {
124121
return nil
125122
}
126123

124+
v.merkleHash = node.HashMerkleBranches(childHash, claimHash)
125+
return v.merkleHash
126+
}
127+
128+
func (rt *RamTrie) computeChildHash(v *collapsedVertex) (*chainhash.Hash, bool) {
129+
childHashes := make([]*chainhash.Hash, 0, len(v.children))
130+
for _, ch := range v.children {
131+
h := rt.merkleHashAllClaims(ch)
132+
childHashes = append(childHashes, h)
133+
}
127134
childHash := NoChildrenHash
128135
if len(childHashes) > 0 {
129136
// this shouldn't be referencing node; where else can we put this merkle root func?
130137
childHash = node.ComputeMerkleRoot(childHashes)
131138
}
132-
133-
v.merkleHash = node.HashMerkleBranches(childHash, claimHash)
134-
return v.merkleHash
139+
return childHash, len(childHashes) > 0
135140
}
136141

137142
func (rt *RamTrie) Flush() error {
138143
return nil
139144
}
145+
146+
type HashSidePair struct {
147+
Right bool
148+
Hash *chainhash.Hash
149+
}
150+
151+
func (rt *RamTrie) MerklePath(name []byte) []HashSidePair {
152+
153+
// algorithm:
154+
// for each node in the path to key:
155+
// get all the childHashes for that node and the index of our path
156+
// get all the claimHashes for that node as well
157+
// if we're at the end of the path:
158+
// push(true, root(childHashes))
159+
// push all of merklePath(claimHashes, bid)
160+
// else
161+
// push(false, root(claimHashes)
162+
// push all of merklePath(childHashes, child index)
163+
164+
var results []HashSidePair
165+
166+
indexes, path := rt.FindPath(name)
167+
for i := 0; i < len(indexes); i++ {
168+
if i == len(indexes)-1 {
169+
childHash, _ := rt.computeChildHash(path[i])
170+
results = append(results, HashSidePair{Right: true, Hash: childHash})
171+
// letting the caller append the claim hashes at present (needs better code organization)
172+
} else {
173+
ch := path[i].claimHash
174+
if ch == nil {
175+
ch = NoClaimsHash
176+
}
177+
results = append(results, HashSidePair{Right: false, Hash: ch})
178+
childHashes := make([]*chainhash.Hash, 0, len(path[i].children))
179+
for j := range path[i].children {
180+
childHashes = append(childHashes, path[i].children[j].merkleHash)
181+
}
182+
if len(childHashes) > 0 {
183+
partials := node.ComputeMerklePath(childHashes, indexes[i+1])
184+
for i := len(partials) - 1; i >= 0; i-- {
185+
results = append(results, HashSidePair{Right: ((indexes[i+1] >> i) & 1) > 0, Hash: partials[i]})
186+
}
187+
}
188+
}
189+
}
190+
return results
191+
}

0 commit comments

Comments
 (0)