added proof RPC
post-merge fixes other part of test post-merge fix
This commit is contained in:
parent
494a39c1ab
commit
9477313d74
10 changed files with 455 additions and 126 deletions
|
@ -3,6 +3,7 @@ package blockchain
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
|
|
||||||
|
@ -181,3 +182,68 @@ func (b *BlockChain) GetClaimsForName(height int32, name string) (string, *node.
|
||||||
n.SortClaimsByBid()
|
n.SortClaimsByBid()
|
||||||
return string(normalizedName), n, nil
|
return string(normalizedName), n, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (b *BlockChain) GetProofForName(name, id string, bid, seq int) (chainhash.Hash, int32, *node.Claim, int32, int32, string, []merkletrie.HashSidePair, error) {
|
||||||
|
// results: block hash, height, claim, bid, takeover, name, pairs, err
|
||||||
|
|
||||||
|
b.chainLock.RLock()
|
||||||
|
defer b.chainLock.RUnlock()
|
||||||
|
|
||||||
|
tip := b.bestChain.Tip()
|
||||||
|
|
||||||
|
normalizedName := normalization.NormalizeIfNecessary([]byte(name), tip.height)
|
||||||
|
|
||||||
|
if tip.height < param.ActiveParams.GrandForkHeight {
|
||||||
|
err := errors.Errorf("Unable to generate proofs for claims before height %d",
|
||||||
|
param.ActiveParams.GrandForkHeight)
|
||||||
|
return tip.hash, tip.height, nil, 0, 0, string(normalizedName), nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
n, err := b.claimTrie.NodeAt(tip.height, normalizedName)
|
||||||
|
if n == nil && err == nil {
|
||||||
|
err = errors.Errorf("Unable to locate a claim with name %s at height %d", normalizedName, tip.height)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return tip.hash, tip.height, nil, 0, 0, string(normalizedName), nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// now find the desired claim
|
||||||
|
n.SortClaimsByBid()
|
||||||
|
var claim *node.Claim
|
||||||
|
for i, c := range n.Claims {
|
||||||
|
if c.Status != node.Activated {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if bid >= 0 && i == bid {
|
||||||
|
claim = c
|
||||||
|
bid = i
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if seq >= 0 && int(c.Sequence) == seq {
|
||||||
|
claim = c
|
||||||
|
bid = i
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if len(id) > 0 && strings.HasPrefix(c.ClaimID.String(), id) {
|
||||||
|
claim = c
|
||||||
|
bid = i
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if claim == nil {
|
||||||
|
if bid >= 0 {
|
||||||
|
err = errors.Errorf("Unable to locate a claim named %s with bid %d at height %d", normalizedName, bid, tip.height)
|
||||||
|
}
|
||||||
|
if seq >= 0 {
|
||||||
|
err = errors.Errorf("Unable to locate a claim named %s with sequence %d at height %d", normalizedName, seq, tip.height)
|
||||||
|
}
|
||||||
|
if len(id) > 0 {
|
||||||
|
err = errors.Errorf("Unable to locate a claim named %s with ID %s at height %d", normalizedName, id, tip.height)
|
||||||
|
}
|
||||||
|
return tip.hash, tip.height, nil, 0, 0, string(normalizedName), nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
pairs := b.claimTrie.MerklePath(normalizedName, n, bid)
|
||||||
|
|
||||||
|
return tip.hash, tip.height, claim, int32(bid), n.TakenOverAt, string(normalizedName), pairs, nil
|
||||||
|
}
|
||||||
|
|
|
@ -10,6 +10,10 @@ func init() {
|
||||||
MustRegisterCmd("getclaimsfornamebybid", (*GetClaimsForNameByBidCmd)(nil), flags)
|
MustRegisterCmd("getclaimsfornamebybid", (*GetClaimsForNameByBidCmd)(nil), flags)
|
||||||
MustRegisterCmd("getclaimsfornamebyseq", (*GetClaimsForNameBySeqCmd)(nil), flags)
|
MustRegisterCmd("getclaimsfornamebyseq", (*GetClaimsForNameBySeqCmd)(nil), flags)
|
||||||
MustRegisterCmd("normalize", (*GetNormalizedCmd)(nil), flags)
|
MustRegisterCmd("normalize", (*GetNormalizedCmd)(nil), flags)
|
||||||
|
|
||||||
|
MustRegisterCmd("getprooffornamebyid", (*GetProofForNameByIDCmd)(nil), flags)
|
||||||
|
MustRegisterCmd("getprooffornamebybid", (*GetProofForNameByBidCmd)(nil), flags)
|
||||||
|
MustRegisterCmd("getprooffornamebyseq", (*GetProofForNameBySeqCmd)(nil), flags)
|
||||||
}
|
}
|
||||||
|
|
||||||
// optional inputs are required to be pointers, but they support things like `jsonrpcdefault:"false"`
|
// optional inputs are required to be pointers, but they support things like `jsonrpcdefault:"false"`
|
||||||
|
@ -95,3 +99,36 @@ type GetNormalizedCmd struct {
|
||||||
type GetNormalizedResult struct {
|
type GetNormalizedResult struct {
|
||||||
NormalizedName string `json:"normalizedname"`
|
NormalizedName string `json:"normalizedname"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type GetProofForNameByIDCmd struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
PartialClaimID string `json:"partialclaimid"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type GetProofForNameByBidCmd struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
Bid int `json:"bid"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type GetProofForNameBySeqCmd struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
Sequence int `json:"sequence"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ProofPairResult struct {
|
||||||
|
Right bool `json:"right"`
|
||||||
|
Hash string `json:"hash"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ProofResult struct { // should we include the claim trie hash?
|
||||||
|
BlockHash string `json:"blockhash"`
|
||||||
|
BlockHeight int32 `json:"blockheight"`
|
||||||
|
NormalizedName string `json:"normalizedname"`
|
||||||
|
ClaimID string `json:"claimid"`
|
||||||
|
TXID string `json:"txid"`
|
||||||
|
N uint32 `json:"n"`
|
||||||
|
Bid int32 `json:"bid"`
|
||||||
|
Sequence int32 `json:"sequence"`
|
||||||
|
Takeover int32 `json:"takeover"`
|
||||||
|
Pairs []ProofPairResult `json:"pairs"`
|
||||||
|
}
|
||||||
|
|
|
@ -303,7 +303,7 @@ func removeDuplicates(names [][]byte) [][]byte { // this might be too expensive;
|
||||||
return names
|
return names
|
||||||
}
|
}
|
||||||
|
|
||||||
// ResetHeight resets the ClaimTrie to a previous known height..
|
// ResetHeight resets the ClaimTrie to a previous known height.
|
||||||
func (ct *ClaimTrie) ResetHeight(height int32) error {
|
func (ct *ClaimTrie) ResetHeight(height int32) error {
|
||||||
|
|
||||||
names := make([][]byte, 0)
|
names := make([][]byte, 0)
|
||||||
|
@ -320,6 +320,9 @@ func (ct *ClaimTrie) ResetHeight(height int32) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
passedHashFork := ct.height >= param.ActiveParams.AllClaimsInMerkleForkHeight && height < param.ActiveParams.AllClaimsInMerkleForkHeight
|
passedHashFork := ct.height >= param.ActiveParams.AllClaimsInMerkleForkHeight && height < param.ActiveParams.AllClaimsInMerkleForkHeight
|
||||||
|
if !passedHashFork {
|
||||||
|
passedHashFork = ct.height >= param.ActiveParams.GrandForkHeight && height < param.ActiveParams.GrandForkHeight
|
||||||
|
}
|
||||||
hash, err := ct.blockRepo.Get(height)
|
hash, err := ct.blockRepo.Get(height)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -486,3 +489,21 @@ func (ct *ClaimTrie) makeNameHashNext(names [][]byte, all bool, interrupt <-chan
|
||||||
}()
|
}()
|
||||||
return outputs
|
return outputs
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (ct *ClaimTrie) MerklePath(name []byte, n *node.Node, bid int) []merkletrie.HashSidePair {
|
||||||
|
pairs := ct.merkleTrie.MerklePath(name)
|
||||||
|
// TODO: organize this code better
|
||||||
|
// this is the 2nd half of the above merkle tree computation
|
||||||
|
// it's done like this so we don't have to create the Node object multiple times
|
||||||
|
claimHashes := node.ComputeClaimHashes(name, n)
|
||||||
|
partials := node.ComputeMerklePath(claimHashes, bid)
|
||||||
|
for i := len(partials) - 1; i >= 0; i-- {
|
||||||
|
pairs = append(pairs, merkletrie.HashSidePair{Right: ((bid >> i) & 1) > 0, Hash: partials[i]})
|
||||||
|
}
|
||||||
|
|
||||||
|
// reverse the list order:
|
||||||
|
for i, j := 0, len(pairs)-1; i < j; i, j = i+1, j-1 {
|
||||||
|
pairs[i], pairs[j] = pairs[j], pairs[i]
|
||||||
|
}
|
||||||
|
return pairs
|
||||||
|
}
|
||||||
|
|
|
@ -1025,3 +1025,66 @@ func TestBlock884431(t *testing.T) {
|
||||||
r.NoError(err)
|
r.NoError(err)
|
||||||
r.Equal(o11.String(), n.BestClaim.OutPoint.String())
|
r.Equal(o11.String(), n.BestClaim.OutPoint.String())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestMerklePath(t *testing.T) {
|
||||||
|
r := require.New(t)
|
||||||
|
setup(t)
|
||||||
|
param.ActiveParams.ActiveDelayFactor = 1
|
||||||
|
param.ActiveParams.NormalizedNameForkHeight = 5
|
||||||
|
param.ActiveParams.AllClaimsInMerkleForkHeight = 6
|
||||||
|
param.ActiveParams.GrandForkHeight = 7
|
||||||
|
|
||||||
|
ct, err := New(cfg)
|
||||||
|
r.NoError(err)
|
||||||
|
r.NotNil(ct)
|
||||||
|
defer ct.Close()
|
||||||
|
|
||||||
|
hash := chainhash.HashH([]byte{1, 2, 3})
|
||||||
|
o1 := wire.OutPoint{Hash: hash, Index: 1}
|
||||||
|
o2 := wire.OutPoint{Hash: hash, Index: 2}
|
||||||
|
o3 := wire.OutPoint{Hash: hash, Index: 3}
|
||||||
|
|
||||||
|
err = ct.AddClaim([]byte("test"), o1, change.NewClaimID(o1), 1)
|
||||||
|
r.NoError(err)
|
||||||
|
|
||||||
|
err = ct.AddClaim([]byte("test"), o2, change.NewClaimID(o2), 2)
|
||||||
|
r.NoError(err)
|
||||||
|
|
||||||
|
err = ct.AddClaim([]byte("tester"), o3, change.NewClaimID(o3), 1)
|
||||||
|
r.NoError(err)
|
||||||
|
|
||||||
|
for i := 0; i < 10; i++ {
|
||||||
|
err = ct.AppendBlock()
|
||||||
|
r.NoError(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
n, err := ct.NodeAt(ct.height, []byte("test"))
|
||||||
|
r.NoError(err)
|
||||||
|
pairs := ct.MerklePath([]byte("test"), n, 0)
|
||||||
|
claimHash, err := node.ComputeBidSeqNameHash([]byte("test"), n.Claims[0], 0, n.TakenOverAt)
|
||||||
|
r.NoError(err)
|
||||||
|
validatePairs(r, pairs, ct.MerkleHash(), claimHash)
|
||||||
|
|
||||||
|
pairs = ct.MerklePath([]byte("test"), n, 1)
|
||||||
|
claimHash, err = node.ComputeBidSeqNameHash([]byte("test"), n.Claims[1], 1, n.TakenOverAt)
|
||||||
|
r.NoError(err)
|
||||||
|
validatePairs(r, pairs, ct.MerkleHash(), claimHash)
|
||||||
|
|
||||||
|
n, err = ct.NodeAt(ct.height, []byte("tester"))
|
||||||
|
r.NoError(err)
|
||||||
|
pairs = ct.MerklePath([]byte("tester"), n, 0)
|
||||||
|
claimHash, err = node.ComputeBidSeqNameHash([]byte("tester"), n.Claims[0], 0, n.TakenOverAt)
|
||||||
|
r.NoError(err)
|
||||||
|
validatePairs(r, pairs, ct.MerkleHash(), claimHash)
|
||||||
|
}
|
||||||
|
|
||||||
|
func validatePairs(r *require.Assertions, pairs []merkletrie.HashSidePair, target *chainhash.Hash, claimHash *chainhash.Hash) {
|
||||||
|
for i := range pairs {
|
||||||
|
if pairs[i].Right {
|
||||||
|
claimHash = node.HashMerkleBranches(pairs[i].Hash, claimHash)
|
||||||
|
} else {
|
||||||
|
claimHash = node.HashMerkleBranches(claimHash, pairs[i].Hash)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
r.True(claimHash.IsEqual(target))
|
||||||
|
}
|
||||||
|
|
|
@ -253,3 +253,7 @@ func (t *PersistentTrie) Dump(s string) {
|
||||||
func (t *PersistentTrie) Flush() error {
|
func (t *PersistentTrie) Flush() error {
|
||||||
return t.repo.Flush()
|
return t.repo.Flush()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (t *PersistentTrie) MerklePath(name []byte) []HashSidePair {
|
||||||
|
panic("MerklePath not implemented in PersistentTrie")
|
||||||
|
}
|
||||||
|
|
|
@ -16,6 +16,7 @@ type MerkleTrie interface {
|
||||||
MerkleHash() *chainhash.Hash
|
MerkleHash() *chainhash.Hash
|
||||||
MerkleHashAllClaims() *chainhash.Hash
|
MerkleHashAllClaims() *chainhash.Hash
|
||||||
Flush() error
|
Flush() error
|
||||||
|
MerklePath(name []byte) []HashSidePair
|
||||||
}
|
}
|
||||||
|
|
||||||
type RamTrie struct {
|
type RamTrie struct {
|
||||||
|
@ -111,29 +112,80 @@ func (rt *RamTrie) merkleHashAllClaims(v *collapsedVertex) *chainhash.Hash {
|
||||||
return v.merkleHash
|
return v.merkleHash
|
||||||
}
|
}
|
||||||
|
|
||||||
childHashes := make([]*chainhash.Hash, 0, len(v.children))
|
childHash, hasChildren := rt.computeChildHash(v)
|
||||||
for _, ch := range v.children {
|
|
||||||
h := rt.merkleHashAllClaims(ch)
|
|
||||||
childHashes = append(childHashes, h)
|
|
||||||
}
|
|
||||||
|
|
||||||
claimHash := NoClaimsHash
|
claimHash := NoClaimsHash
|
||||||
if v.claimHash != nil {
|
if v.claimHash != nil {
|
||||||
claimHash = v.claimHash
|
claimHash = v.claimHash
|
||||||
} else if len(childHashes) == 0 {
|
} else if !hasChildren {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
childHash := NoChildrenHash
|
|
||||||
if len(childHashes) > 0 {
|
|
||||||
// this shouldn't be referencing node; where else can we put this merkle root func?
|
|
||||||
childHash = node.ComputeMerkleRoot(childHashes)
|
|
||||||
}
|
|
||||||
|
|
||||||
v.merkleHash = node.HashMerkleBranches(childHash, claimHash)
|
v.merkleHash = node.HashMerkleBranches(childHash, claimHash)
|
||||||
return v.merkleHash
|
return v.merkleHash
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (rt *RamTrie) computeChildHash(v *collapsedVertex) (*chainhash.Hash, bool) {
|
||||||
|
childHashes := make([]*chainhash.Hash, 0, len(v.children))
|
||||||
|
for _, ch := range v.children {
|
||||||
|
h := rt.merkleHashAllClaims(ch)
|
||||||
|
childHashes = append(childHashes, h)
|
||||||
|
}
|
||||||
|
childHash := NoChildrenHash
|
||||||
|
if len(childHashes) > 0 {
|
||||||
|
// this shouldn't be referencing node; where else can we put this merkle root func?
|
||||||
|
childHash = node.ComputeMerkleRoot(childHashes)
|
||||||
|
}
|
||||||
|
return childHash, len(childHashes) > 0
|
||||||
|
}
|
||||||
|
|
||||||
func (rt *RamTrie) Flush() error {
|
func (rt *RamTrie) Flush() error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type HashSidePair struct {
|
||||||
|
Right bool
|
||||||
|
Hash *chainhash.Hash
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rt *RamTrie) MerklePath(name []byte) []HashSidePair {
|
||||||
|
|
||||||
|
// algorithm:
|
||||||
|
// for each node in the path to key:
|
||||||
|
// get all the childHashes for that node and the index of our path
|
||||||
|
// get all the claimHashes for that node as well
|
||||||
|
// if we're at the end of the path:
|
||||||
|
// push(true, root(childHashes))
|
||||||
|
// push all of merklePath(claimHashes, bid)
|
||||||
|
// else
|
||||||
|
// push(false, root(claimHashes)
|
||||||
|
// push all of merklePath(childHashes, child index)
|
||||||
|
|
||||||
|
var results []HashSidePair
|
||||||
|
|
||||||
|
indexes, path := rt.FindPath(name)
|
||||||
|
for i := 0; i < len(indexes); i++ {
|
||||||
|
if i == len(indexes)-1 {
|
||||||
|
childHash, _ := rt.computeChildHash(path[i])
|
||||||
|
results = append(results, HashSidePair{Right: true, Hash: childHash})
|
||||||
|
// letting the caller append the claim hashes at present (needs better code organization)
|
||||||
|
} else {
|
||||||
|
ch := path[i].claimHash
|
||||||
|
if ch == nil {
|
||||||
|
ch = NoClaimsHash
|
||||||
|
}
|
||||||
|
results = append(results, HashSidePair{Right: false, Hash: ch})
|
||||||
|
childHashes := make([]*chainhash.Hash, 0, len(path[i].children))
|
||||||
|
for j := range path[i].children {
|
||||||
|
childHashes = append(childHashes, path[i].children[j].merkleHash)
|
||||||
|
}
|
||||||
|
if len(childHashes) > 0 {
|
||||||
|
partials := node.ComputeMerklePath(childHashes, indexes[i+1])
|
||||||
|
for i := len(partials) - 1; i >= 0; i-- {
|
||||||
|
results = append(results, HashSidePair{Right: ((indexes[i+1] >> i) & 1) > 0, Hash: partials[i]})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return results
|
||||||
|
}
|
||||||
|
|
|
@ -1,111 +0,0 @@
|
||||||
package node
|
|
||||||
|
|
||||||
import (
|
|
||||||
"crypto/sha256"
|
|
||||||
"encoding/binary"
|
|
||||||
|
|
||||||
"github.com/btcsuite/btcd/chaincfg/chainhash"
|
|
||||||
"github.com/btcsuite/btcd/claimtrie/change"
|
|
||||||
"github.com/btcsuite/btcd/claimtrie/param"
|
|
||||||
)
|
|
||||||
|
|
||||||
type HashV2Manager struct {
|
|
||||||
Manager
|
|
||||||
}
|
|
||||||
|
|
||||||
type HashV3Manager struct {
|
|
||||||
Manager
|
|
||||||
}
|
|
||||||
|
|
||||||
func (nm *HashV2Manager) claimHashes(name []byte) (*chainhash.Hash, int32) {
|
|
||||||
|
|
||||||
n, err := nm.NodeAt(nm.Height(), name)
|
|
||||||
if err != nil || n == nil {
|
|
||||||
return nil, 0
|
|
||||||
}
|
|
||||||
|
|
||||||
n.SortClaimsByBid()
|
|
||||||
claimHashes := make([]*chainhash.Hash, 0, len(n.Claims))
|
|
||||||
for _, c := range n.Claims {
|
|
||||||
if c.Status == Activated { // TODO: unit test this line
|
|
||||||
claimHashes = append(claimHashes, calculateNodeHash(c.OutPoint, n.TakenOverAt))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if len(claimHashes) > 0 {
|
|
||||||
return ComputeMerkleRoot(claimHashes), n.NextUpdate(nm.Height())
|
|
||||||
}
|
|
||||||
return nil, n.NextUpdate(nm.Height())
|
|
||||||
}
|
|
||||||
|
|
||||||
func (nm *HashV2Manager) Hash(name []byte) (*chainhash.Hash, int32) {
|
|
||||||
|
|
||||||
if nm.Height() >= param.ActiveParams.AllClaimsInMerkleForkHeight {
|
|
||||||
return nm.claimHashes(name)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nm.Manager.Hash(name)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (nm *HashV3Manager) AppendChange(chg change.Change) {
|
|
||||||
if nm.Height() >= param.ActiveParams.GrandForkHeight && len(chg.Name) == 0 {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
nm.Manager.AppendChange(chg)
|
|
||||||
}
|
|
||||||
|
|
||||||
func calculateBidSeqNameHash(name []byte, c *Claim, bid, takeover int32) (*chainhash.Hash, error) {
|
|
||||||
|
|
||||||
s := sha256.New()
|
|
||||||
|
|
||||||
s.Write(c.OutPoint.Hash[:])
|
|
||||||
|
|
||||||
var temp [4]byte
|
|
||||||
binary.BigEndian.PutUint32(temp[:], c.OutPoint.Index)
|
|
||||||
s.Write(temp[:])
|
|
||||||
|
|
||||||
binary.BigEndian.PutUint32(temp[:], uint32(bid))
|
|
||||||
s.Write(temp[:])
|
|
||||||
|
|
||||||
binary.BigEndian.PutUint32(temp[:], uint32(c.Sequence))
|
|
||||||
s.Write(temp[:])
|
|
||||||
|
|
||||||
binary.BigEndian.PutUint32(temp[:], uint32(takeover))
|
|
||||||
s.Write(temp[:])
|
|
||||||
|
|
||||||
s.Write(name)
|
|
||||||
|
|
||||||
var m [sha256.Size]byte
|
|
||||||
return chainhash.NewHash(s.Sum(m[:0]))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (nm *HashV3Manager) bidSeqNameHash(name []byte) (*chainhash.Hash, int32) {
|
|
||||||
n, err := nm.NodeAt(nm.Height(), name)
|
|
||||||
if err != nil || n == nil {
|
|
||||||
return nil, 0
|
|
||||||
}
|
|
||||||
|
|
||||||
n.SortClaimsByBid()
|
|
||||||
claimHashes := make([]*chainhash.Hash, 0, len(n.Claims))
|
|
||||||
for i, c := range n.Claims {
|
|
||||||
if c.Status == Activated {
|
|
||||||
h, _ := calculateBidSeqNameHash(name, c, int32(i), n.TakenOverAt)
|
|
||||||
claimHashes = append(claimHashes, h)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if len(claimHashes) > 0 {
|
|
||||||
return ComputeMerkleRoot(claimHashes), n.NextUpdate(nm.Height())
|
|
||||||
}
|
|
||||||
return nil, n.NextUpdate(nm.Height())
|
|
||||||
}
|
|
||||||
|
|
||||||
func (nm *HashV3Manager) Hash(name []byte) (*chainhash.Hash, int32) {
|
|
||||||
|
|
||||||
if nm.Height() >= param.ActiveParams.GrandForkHeight {
|
|
||||||
if len(name) == 0 {
|
|
||||||
return nil, 0 // empty name's claims are not included in the hash
|
|
||||||
}
|
|
||||||
return nm.bidSeqNameHash(name)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nm.Manager.Hash(name)
|
|
||||||
}
|
|
|
@ -1,6 +1,9 @@
|
||||||
package node
|
package node
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"crypto/sha256"
|
||||||
|
"encoding/binary"
|
||||||
|
|
||||||
"github.com/lbryio/lbcd/chaincfg/chainhash"
|
"github.com/lbryio/lbcd/chaincfg/chainhash"
|
||||||
"github.com/lbryio/lbcd/claimtrie/param"
|
"github.com/lbryio/lbcd/claimtrie/param"
|
||||||
)
|
)
|
||||||
|
@ -9,6 +12,10 @@ type HashV2Manager struct {
|
||||||
Manager
|
Manager
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type HashV3Manager struct {
|
||||||
|
Manager
|
||||||
|
}
|
||||||
|
|
||||||
func (nm *HashV2Manager) computeClaimHashes(name []byte) (*chainhash.Hash, int32) {
|
func (nm *HashV2Manager) computeClaimHashes(name []byte) (*chainhash.Hash, int32) {
|
||||||
|
|
||||||
n, err := nm.NodeAt(nm.Height(), name)
|
n, err := nm.NodeAt(nm.Height(), name)
|
||||||
|
@ -24,9 +31,9 @@ func (nm *HashV2Manager) computeClaimHashes(name []byte) (*chainhash.Hash, int32
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if len(claimHashes) > 0 {
|
if len(claimHashes) > 0 {
|
||||||
return ComputeMerkleRoot(claimHashes), n.NextUpdate()
|
return ComputeMerkleRoot(claimHashes), n.NextUpdate(nm.Height())
|
||||||
}
|
}
|
||||||
return nil, n.NextUpdate()
|
return nil, n.NextUpdate(nm.Height())
|
||||||
}
|
}
|
||||||
|
|
||||||
func (nm *HashV2Manager) Hash(name []byte) (*chainhash.Hash, int32) {
|
func (nm *HashV2Manager) Hash(name []byte) (*chainhash.Hash, int32) {
|
||||||
|
@ -37,3 +44,72 @@ func (nm *HashV2Manager) Hash(name []byte) (*chainhash.Hash, int32) {
|
||||||
|
|
||||||
return nm.Manager.Hash(name)
|
return nm.Manager.Hash(name)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (nm *HashV3Manager) AppendChange(chg change.Change) {
|
||||||
|
if nm.Height() >= param.ActiveParams.GrandForkHeight && len(chg.Name) == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
nm.Manager.AppendChange(chg)
|
||||||
|
}
|
||||||
|
|
||||||
|
func ComputeBidSeqNameHash(name []byte, c *Claim, bid, takeover int32) (*chainhash.Hash, error) {
|
||||||
|
|
||||||
|
s := sha256.New()
|
||||||
|
|
||||||
|
s.Write(c.OutPoint.Hash[:])
|
||||||
|
|
||||||
|
var temp [4]byte
|
||||||
|
binary.BigEndian.PutUint32(temp[:], c.OutPoint.Index)
|
||||||
|
s.Write(temp[:])
|
||||||
|
|
||||||
|
binary.BigEndian.PutUint32(temp[:], uint32(bid))
|
||||||
|
s.Write(temp[:])
|
||||||
|
|
||||||
|
binary.BigEndian.PutUint32(temp[:], uint32(c.Sequence))
|
||||||
|
s.Write(temp[:])
|
||||||
|
|
||||||
|
binary.BigEndian.PutUint32(temp[:], uint32(takeover))
|
||||||
|
s.Write(temp[:])
|
||||||
|
|
||||||
|
s.Write(name)
|
||||||
|
|
||||||
|
var m [sha256.Size]byte
|
||||||
|
return chainhash.NewHash(s.Sum(m[:0]))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (nm *HashV3Manager) bidSeqNameHash(name []byte) (*chainhash.Hash, int32) {
|
||||||
|
n, err := nm.NodeAt(nm.Height(), name)
|
||||||
|
if err != nil || n == nil {
|
||||||
|
return nil, 0
|
||||||
|
}
|
||||||
|
|
||||||
|
claimHashes := ComputeClaimHashes(name, n)
|
||||||
|
if len(claimHashes) > 0 {
|
||||||
|
return ComputeMerkleRoot(claimHashes), n.NextUpdate(nm.Height())
|
||||||
|
}
|
||||||
|
return nil, n.NextUpdate(nm.Height())
|
||||||
|
}
|
||||||
|
|
||||||
|
func ComputeClaimHashes(name []byte, n *Node) []*chainhash.Hash {
|
||||||
|
n.SortClaimsByBid()
|
||||||
|
claimHashes := make([]*chainhash.Hash, 0, len(n.Claims))
|
||||||
|
for i, c := range n.Claims {
|
||||||
|
if c.Status == Activated {
|
||||||
|
h, _ := ComputeBidSeqNameHash(name, c, int32(i), n.TakenOverAt)
|
||||||
|
claimHashes = append(claimHashes, h)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return claimHashes
|
||||||
|
}
|
||||||
|
|
||||||
|
func (nm *HashV3Manager) Hash(name []byte) (*chainhash.Hash, int32) {
|
||||||
|
|
||||||
|
if nm.Height() >= param.ActiveParams.GrandForkHeight {
|
||||||
|
if len(name) == 0 {
|
||||||
|
return nil, 0 // empty name's claims are not included in the hash
|
||||||
|
}
|
||||||
|
return nm.bidSeqNameHash(name)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nm.Manager.Hash(name)
|
||||||
|
}
|
||||||
|
|
|
@ -55,3 +55,59 @@ func calculateNodeHash(op wire.OutPoint, takeover int32) *chainhash.Hash {
|
||||||
|
|
||||||
return &hh
|
return &hh
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func ComputeMerklePath(hashes []*chainhash.Hash, idx int) []*chainhash.Hash {
|
||||||
|
count := 0
|
||||||
|
matchlevel := -1
|
||||||
|
matchh := false
|
||||||
|
var h *chainhash.Hash
|
||||||
|
var res []*chainhash.Hash
|
||||||
|
var inner [32]*chainhash.Hash // old code had 32; dunno if it's big enough for all scenarios
|
||||||
|
|
||||||
|
iterateInner := func(level int) int {
|
||||||
|
for ; (count & (1 << level)) == 0; level++ {
|
||||||
|
ihash := inner[level]
|
||||||
|
if matchh {
|
||||||
|
res = append(res, ihash)
|
||||||
|
} else if matchlevel == level {
|
||||||
|
res = append(res, h)
|
||||||
|
matchh = true
|
||||||
|
}
|
||||||
|
h = HashMerkleBranches(ihash, h)
|
||||||
|
}
|
||||||
|
return level
|
||||||
|
}
|
||||||
|
|
||||||
|
for count < len(hashes) {
|
||||||
|
h = hashes[count]
|
||||||
|
matchh = count == idx
|
||||||
|
count++
|
||||||
|
level := iterateInner(0)
|
||||||
|
// Store the resulting hash at inner position level.
|
||||||
|
inner[level] = h
|
||||||
|
if matchh {
|
||||||
|
matchlevel = level
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
level := 0
|
||||||
|
for (count & (1 << level)) == 0 {
|
||||||
|
level++
|
||||||
|
}
|
||||||
|
|
||||||
|
h = inner[level]
|
||||||
|
matchh = matchlevel == level
|
||||||
|
|
||||||
|
for count != (1 << level) {
|
||||||
|
// If we reach this point, h is an inner value that is not the top.
|
||||||
|
if matchh {
|
||||||
|
res = append(res, h)
|
||||||
|
}
|
||||||
|
h = HashMerkleBranches(h, h)
|
||||||
|
// Increment count to the value it would have if two entries at this
|
||||||
|
count += 1 << level
|
||||||
|
level++
|
||||||
|
level = iterateInner(level)
|
||||||
|
}
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
|
@ -22,6 +22,10 @@ var claimtrieHandlers = map[string]commandHandler{
|
||||||
"getclaimsfornamebybid": handleGetClaimsForNameByBid,
|
"getclaimsfornamebybid": handleGetClaimsForNameByBid,
|
||||||
"getclaimsfornamebyseq": handleGetClaimsForNameBySeq,
|
"getclaimsfornamebyseq": handleGetClaimsForNameBySeq,
|
||||||
"normalize": handleGetNormalized,
|
"normalize": handleGetNormalized,
|
||||||
|
|
||||||
|
"getprooffornamebyid": handleGetProofForNameByID,
|
||||||
|
"getprooffornamebybid": handleGetProofForNameByBid,
|
||||||
|
"getprooffornamebyseq": handleGetProofForNameBySeq,
|
||||||
}
|
}
|
||||||
|
|
||||||
func handleGetChangesInBlock(s *rpcServer, cmd interface{}, _ <-chan struct{}) (interface{}, error) {
|
func handleGetChangesInBlock(s *rpcServer, cmd interface{}, _ <-chan struct{}) (interface{}, error) {
|
||||||
|
@ -350,3 +354,64 @@ func handleGetNormalized(_ *rpcServer, cmd interface{}, _ <-chan struct{}) (inte
|
||||||
}
|
}
|
||||||
return r, nil
|
return r, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func handleGetProofForNameByID(s *rpcServer, cmd interface{}, _ <-chan struct{}) (interface{}, error) {
|
||||||
|
|
||||||
|
c := cmd.(*btcjson.GetProofForNameByIDCmd)
|
||||||
|
return getProof(s, c.Name, c.PartialClaimID, -1, -1)
|
||||||
|
}
|
||||||
|
|
||||||
|
func handleGetProofForNameByBid(s *rpcServer, cmd interface{}, _ <-chan struct{}) (interface{}, error) {
|
||||||
|
|
||||||
|
c := cmd.(*btcjson.GetProofForNameByBidCmd)
|
||||||
|
return getProof(s, c.Name, "", c.Bid, -1)
|
||||||
|
}
|
||||||
|
|
||||||
|
func handleGetProofForNameBySeq(s *rpcServer, cmd interface{}, _ <-chan struct{}) (interface{}, error) {
|
||||||
|
|
||||||
|
c := cmd.(*btcjson.GetProofForNameBySeqCmd)
|
||||||
|
return getProof(s, c.Name, "", -1, c.Sequence)
|
||||||
|
}
|
||||||
|
|
||||||
|
func getProof(s *rpcServer, name, id string, bid, seq int) (interface{}, error) {
|
||||||
|
|
||||||
|
if !s.cfg.Chain.IsCurrent() {
|
||||||
|
return nil, &btcjson.RPCError{
|
||||||
|
Code: btcjson.ErrRPCClientInInitialDownload,
|
||||||
|
Message: "Unable to query the chain tip during initial download",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(name) == 0 {
|
||||||
|
return nil, &btcjson.RPCError{
|
||||||
|
Code: btcjson.ErrRPCInvalidParameter,
|
||||||
|
Message: "name is required and cannot be empty",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bh, h, c, b, t, n, pairs, err := s.cfg.Chain.GetProofForName(name, id, bid, seq)
|
||||||
|
if err != nil {
|
||||||
|
return nil, &btcjson.RPCError{
|
||||||
|
Code: btcjson.ErrRPCMisc,
|
||||||
|
Message: "Message: " + err.Error(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
results := make([]btcjson.ProofPairResult, 0, len(pairs))
|
||||||
|
for i := 0; i < len(pairs); i++ {
|
||||||
|
results = append(results, btcjson.ProofPairResult{Right: pairs[i].Right, Hash: pairs[i].Hash.String()})
|
||||||
|
}
|
||||||
|
|
||||||
|
return btcjson.ProofResult{
|
||||||
|
BlockHash: bh.String(),
|
||||||
|
BlockHeight: h,
|
||||||
|
NormalizedName: n,
|
||||||
|
ClaimID: c.ClaimID.String(),
|
||||||
|
TXID: c.OutPoint.Hash.String(),
|
||||||
|
N: c.OutPoint.Index,
|
||||||
|
Bid: b,
|
||||||
|
Sequence: c.Sequence,
|
||||||
|
Takeover: t,
|
||||||
|
Pairs: results,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue