Skip to content

Commit 78640e5

Browse files
committed
added proof RPC
post-merge fixes other part of test
1 parent ea7d936 commit 78640e5

File tree

10 files changed

+457
-123
lines changed

10 files changed

+457
-123
lines changed

blockchain/claimtrie.go

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

78
"github.com/pkg/errors"
89

910
"github.com/btcsuite/btcd/txscript"
1011
"github.com/btcsuite/btcd/wire"
1112
"github.com/btcsuite/btcutil"
1213

14+
"github.com/btcsuite/btcd/chaincfg/chainhash"
1315
"github.com/btcsuite/btcd/claimtrie"
1416
"github.com/btcsuite/btcd/claimtrie/change"
17+
"github.com/btcsuite/btcd/claimtrie/merkletrie"
1518
"github.com/btcsuite/btcd/claimtrie/node"
19+
"github.com/btcsuite/btcd/claimtrie/param"
1620
)
1721

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

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
@@ -299,7 +299,7 @@ func removeDuplicates(names [][]byte) [][]byte { // this might be too expensive;
299299
return names
300300
}
301301

302-
// ResetHeight resets the ClaimTrie to a previous known height..
302+
// ResetHeight resets the ClaimTrie to a previous known height.
303303
func (ct *ClaimTrie) ResetHeight(height int32) error {
304304

305305
names := make([][]byte, 0)
@@ -316,6 +316,9 @@ func (ct *ClaimTrie) ResetHeight(height int32) error {
316316
}
317317

318318
passedHashFork := ct.height >= param.ActiveParams.AllClaimsInMerkleForkHeight && height < param.ActiveParams.AllClaimsInMerkleForkHeight
319+
if !passedHashFork {
320+
passedHashFork = ct.height >= param.ActiveParams.GrandForkHeight && height < param.ActiveParams.GrandForkHeight
321+
}
319322
ct.height = height
320323
hash, err := ct.blockRepo.Get(height)
321324
if err != nil {
@@ -463,3 +466,21 @@ func (ct *ClaimTrie) makeNameHashNext(names [][]byte, all bool) chan NameHashNex
463466
}()
464467
return outputs
465468
}
469+
470+
func (ct *ClaimTrie) MerklePath(name []byte, n *node.Node, bid int) []merkletrie.HashSidePair {
471+
pairs := ct.merkleTrie.MerklePath(name)
472+
// TODO: organize this code better
473+
// this is the 2nd half of the above merkle tree computation
474+
// it's done like this so we don't have to create the Node object multiple times
475+
claimHashes := node.ComputeClaimHashes(name, n)
476+
partials := node.ComputeMerklePath(claimHashes, bid)
477+
for i := len(partials) - 1; i >= 0; i-- {
478+
pairs = append(pairs, merkletrie.HashSidePair{Right: ((bid >> i) & 1) > 0, Hash: partials[i]})
479+
}
480+
481+
// reverse the list order:
482+
for i, j := 0, len(pairs)-1; i < j; i, j = i+1, j-1 {
483+
pairs[i], pairs[j] = pairs[j], pairs[i]
484+
}
485+
return pairs
486+
}

claimtrie/claimtrie_test.go

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import (
88
"github.com/btcsuite/btcd/claimtrie/change"
99
"github.com/btcsuite/btcd/claimtrie/config"
1010
"github.com/btcsuite/btcd/claimtrie/merkletrie"
11+
"github.com/btcsuite/btcd/claimtrie/node"
1112
"github.com/btcsuite/btcd/claimtrie/param"
1213

1314
"github.com/btcsuite/btcd/chaincfg/chainhash"
@@ -982,3 +983,66 @@ func TestBlock884431(t *testing.T) {
982983
r.NoError(err)
983984
r.Equal(o11.String(), n.BestClaim.OutPoint.String())
984985
}
986+
987+
func TestMerklePath(t *testing.T) {
988+
r := require.New(t)
989+
setup(t)
990+
param.ActiveParams.ActiveDelayFactor = 1
991+
param.ActiveParams.NormalizedNameForkHeight = 5
992+
param.ActiveParams.AllClaimsInMerkleForkHeight = 6
993+
param.ActiveParams.GrandForkHeight = 7
994+
995+
ct, err := New(cfg)
996+
r.NoError(err)
997+
r.NotNil(ct)
998+
defer ct.Close()
999+
1000+
hash := chainhash.HashH([]byte{1, 2, 3})
1001+
o1 := wire.OutPoint{Hash: hash, Index: 1}
1002+
o2 := wire.OutPoint{Hash: hash, Index: 2}
1003+
o3 := wire.OutPoint{Hash: hash, Index: 3}
1004+
1005+
err = ct.AddClaim([]byte("test"), o1, change.NewClaimID(o1), 1)
1006+
r.NoError(err)
1007+
1008+
err = ct.AddClaim([]byte("test"), o2, change.NewClaimID(o2), 2)
1009+
r.NoError(err)
1010+
1011+
err = ct.AddClaim([]byte("tester"), o3, change.NewClaimID(o3), 1)
1012+
r.NoError(err)
1013+
1014+
for i := 0; i < 10; i++ {
1015+
err = ct.AppendBlock()
1016+
r.NoError(err)
1017+
}
1018+
1019+
n, err := ct.NodeAt(ct.height, []byte("test"))
1020+
r.NoError(err)
1021+
pairs := ct.MerklePath([]byte("test"), n, 0)
1022+
claimHash, err := node.ComputeBidSeqNameHash([]byte("test"), n.Claims[0], 0, n.TakenOverAt)
1023+
r.NoError(err)
1024+
validatePairs(r, pairs, ct.MerkleHash(), claimHash)
1025+
1026+
pairs = ct.MerklePath([]byte("test"), n, 1)
1027+
claimHash, err = node.ComputeBidSeqNameHash([]byte("test"), n.Claims[1], 1, n.TakenOverAt)
1028+
r.NoError(err)
1029+
validatePairs(r, pairs, ct.MerkleHash(), claimHash)
1030+
1031+
n, err = ct.NodeAt(ct.height, []byte("tester"))
1032+
r.NoError(err)
1033+
pairs = ct.MerklePath([]byte("tester"), n, 0)
1034+
claimHash, err = node.ComputeBidSeqNameHash([]byte("tester"), n.Claims[0], 0, n.TakenOverAt)
1035+
r.NoError(err)
1036+
validatePairs(r, pairs, ct.MerkleHash(), claimHash)
1037+
}
1038+
1039+
func validatePairs(r *require.Assertions, pairs []merkletrie.HashSidePair, target *chainhash.Hash, claimHash *chainhash.Hash) {
1040+
for i := range pairs {
1041+
if pairs[i].Right {
1042+
claimHash = node.HashMerkleBranches(pairs[i].Hash, claimHash)
1043+
} else {
1044+
claimHash = node.HashMerkleBranches(claimHash, pairs[i].Hash)
1045+
}
1046+
}
1047+
r.True(claimHash.IsEqual(target))
1048+
}

claimtrie/merkletrie/merkletrie.go

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

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 {
@@ -117,29 +118,80 @@ func (rt *RamTrie) merkleHashAllClaims(v *collapsedVertex) *chainhash.Hash {
117118
return v.merkleHash
118119
}
119120

120-
childHashes := make([]*chainhash.Hash, 0, len(v.children))
121-
for _, ch := range v.children {
122-
h := rt.merkleHashAllClaims(ch)
123-
childHashes = append(childHashes, h)
124-
}
121+
childHash, hasChildren := rt.computeChildHash(v)
125122

126123
claimHash := NoClaimsHash
127124
if v.claimHash != nil {
128125
claimHash = v.claimHash
129-
} else if len(childHashes) == 0 {
126+
} else if !hasChildren {
130127
return nil
131128
}
132129

130+
v.merkleHash = node.HashMerkleBranches(childHash, claimHash)
131+
return v.merkleHash
132+
}
133+
134+
func (rt *RamTrie) computeChildHash(v *collapsedVertex) (*chainhash.Hash, bool) {
135+
childHashes := make([]*chainhash.Hash, 0, len(v.children))
136+
for _, ch := range v.children {
137+
h := rt.merkleHashAllClaims(ch)
138+
childHashes = append(childHashes, h)
139+
}
133140
childHash := NoChildrenHash
134141
if len(childHashes) > 0 {
135142
// this shouldn't be referencing node; where else can we put this merkle root func?
136143
childHash = node.ComputeMerkleRoot(childHashes)
137144
}
138-
139-
v.merkleHash = node.HashMerkleBranches(childHash, claimHash)
140-
return v.merkleHash
145+
return childHash, len(childHashes) > 0
141146
}
142147

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

0 commit comments

Comments
 (0)