Skip to content

Commit e9b2cbb

Browse files
committed
added proof RPC
post-merge fixes other part of test post-merge fix
1 parent 70d1d18 commit e9b2cbb

File tree

10 files changed

+500
-121
lines changed

10 files changed

+500
-121
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"`
@@ -95,3 +99,36 @@ type GetNormalizedCmd struct {
9599
type GetNormalizedResult struct {
96100
NormalizedName string `json:"normalizedname"`
97101
}
102+
103+
type GetProofForNameByIDCmd struct {
104+
Name string `json:"name"`
105+
PartialClaimID string `json:"partialclaimid"`
106+
}
107+
108+
type GetProofForNameByBidCmd struct {
109+
Name string `json:"name"`
110+
Bid int `json:"bid"`
111+
}
112+
113+
type GetProofForNameBySeqCmd struct {
114+
Name string `json:"name"`
115+
Sequence int `json:"sequence"`
116+
}
117+
118+
type ProofPairResult struct {
119+
Right bool `json:"right"`
120+
Hash string `json:"hash"`
121+
}
122+
123+
type ProofResult struct { // should we include the claim trie hash?
124+
BlockHash string `json:"blockhash"`
125+
BlockHeight int32 `json:"blockheight"`
126+
NormalizedName string `json:"normalizedname"`
127+
ClaimID string `json:"claimid"`
128+
TXID string `json:"txid"`
129+
N uint32 `json:"n"`
130+
Bid int32 `json:"bid"`
131+
Sequence int32 `json:"sequence"`
132+
Takeover int32 `json:"takeover"`
133+
Pairs []ProofPairResult `json:"pairs"`
134+
}

claimtrie/claimtrie.go

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

309-
// ResetHeight resets the ClaimTrie to a previous known height..
309+
// ResetHeight resets the ClaimTrie to a previous known height.
310310
func (ct *ClaimTrie) ResetHeight(height int32) error {
311311

312312
names := make([][]byte, 0)
@@ -323,6 +323,9 @@ func (ct *ClaimTrie) ResetHeight(height int32) error {
323323
}
324324

325325
passedHashFork := ct.height >= param.ActiveParams.AllClaimsInMerkleForkHeight && height < param.ActiveParams.AllClaimsInMerkleForkHeight
326+
if !passedHashFork {
327+
passedHashFork = ct.height >= param.ActiveParams.GrandForkHeight && height < param.ActiveParams.GrandForkHeight
328+
}
326329
hash, err := ct.blockRepo.Get(height)
327330
if err != nil {
328331
return err
@@ -454,3 +457,71 @@ func interruptRequested(interrupted <-chan struct{}) bool {
454457

455458
return false
456459
}
460+
461+
func (ct *ClaimTrie) makeNameHashNext(names [][]byte, all bool, interrupt <-chan struct{}) chan NameHashNext {
462+
inputs := make(chan []byte, 512)
463+
outputs := make(chan NameHashNext, 512)
464+
465+
var wg sync.WaitGroup
466+
hashComputationWorker := func() {
467+
for name := range inputs {
468+
hash, next := ct.nodeManager.Hash(name)
469+
outputs <- NameHashNext{name, hash, next}
470+
}
471+
wg.Done()
472+
}
473+
474+
threads := int(0.8 * float32(runtime.NumCPU()))
475+
if threads < 1 {
476+
threads = 1
477+
}
478+
for threads > 0 {
479+
threads--
480+
wg.Add(1)
481+
go hashComputationWorker()
482+
}
483+
go func() {
484+
if all {
485+
ct.nodeManager.IterateNames(func(name []byte) bool {
486+
if interruptRequested(interrupt) {
487+
return false
488+
}
489+
clone := make([]byte, len(name))
490+
copy(clone, name) // iteration name buffer is reused on future loops
491+
inputs <- clone
492+
return true
493+
})
494+
} else {
495+
for _, name := range names {
496+
if interruptRequested(interrupt) {
497+
break
498+
}
499+
inputs <- name
500+
}
501+
}
502+
close(inputs)
503+
}()
504+
go func() {
505+
wg.Wait()
506+
close(outputs)
507+
}()
508+
return outputs
509+
}
510+
511+
func (ct *ClaimTrie) MerklePath(name []byte, n *node.Node, bid int) []merkletrie.HashSidePair {
512+
pairs := ct.merkleTrie.MerklePath(name)
513+
// TODO: organize this code better
514+
// this is the 2nd half of the above merkle tree computation
515+
// it's done like this so we don't have to create the Node object multiple times
516+
claimHashes := node.ComputeClaimHashes(name, n)
517+
partials := node.ComputeMerklePath(claimHashes, bid)
518+
for i := len(partials) - 1; i >= 0; i-- {
519+
pairs = append(pairs, merkletrie.HashSidePair{Right: ((bid >> i) & 1) > 0, Hash: partials[i]})
520+
}
521+
522+
// reverse the list order:
523+
for i, j := 0, len(pairs)-1; i < j; i, j = i+1, j-1 {
524+
pairs[i], pairs[j] = pairs[j], pairs[i]
525+
}
526+
return pairs
527+
}

claimtrie/claimtrie_test.go

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

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)