Skip to content

Commit 0797cca

Browse files
committed
added proof RPC
post-merge fixes other part of test post-merge fix
1 parent 097f201 commit 0797cca

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,17 +3,21 @@ 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"
1619
"github.com/btcsuite/btcd/claimtrie/normalization"
20+
"github.com/btcsuite/btcd/claimtrie/param"
1721
)
1822

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

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

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 {
@@ -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)