WIP: next hard fork #5
10 changed files with 455 additions and 126 deletions
|
@ -3,6 +3,7 @@ package blockchain
|
|||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
|
||||
|
@ -181,3 +182,68 @@ func (b *BlockChain) GetClaimsForName(height int32, name string) (string, *node.
|
|||
n.SortClaimsByBid()
|
||||
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("getclaimsfornamebyseq", (*GetClaimsForNameBySeqCmd)(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"`
|
||||
|
@ -95,3 +99,36 @@ type GetNormalizedCmd struct {
|
|||
type GetNormalizedResult struct {
|
||||
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
|
||||
}
|
||||
|
||||
// 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 {
|
||||
|
||||
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
|
||||
if !passedHashFork {
|
||||
passedHashFork = ct.height >= param.ActiveParams.GrandForkHeight && height < param.ActiveParams.GrandForkHeight
|
||||
}
|
||||
hash, err := ct.blockRepo.Get(height)
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -486,3 +489,21 @@ func (ct *ClaimTrie) makeNameHashNext(names [][]byte, all bool, interrupt <-chan
|
|||
}()
|
||||
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.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 {
|
||||
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
|
||||
MerkleHashAllClaims() *chainhash.Hash
|
||||
Flush() error
|
||||
MerklePath(name []byte) []HashSidePair
|
||||
}
|
||||
|
||||
type RamTrie struct {
|
||||
|
@ -111,29 +112,80 @@ func (rt *RamTrie) merkleHashAllClaims(v *collapsedVertex) *chainhash.Hash {
|
|||
return v.merkleHash
|
||||
}
|
||||
|
||||
childHashes := make([]*chainhash.Hash, 0, len(v.children))
|
||||
for _, ch := range v.children {
|
||||
h := rt.merkleHashAllClaims(ch)
|
||||
childHashes = append(childHashes, h)
|
||||
}
|
||||
childHash, hasChildren := rt.computeChildHash(v)
|
||||
|
||||
claimHash := NoClaimsHash
|
||||
if v.claimHash != nil {
|
||||
claimHash = v.claimHash
|
||||
} else if len(childHashes) == 0 {
|
||||
} else if !hasChildren {
|
||||
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)
|
||||
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 {
|
||||
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
|
||||
|
||||
import (
|
||||
"crypto/sha256"
|
||||
"encoding/binary"
|
||||
|
||||
"github.com/lbryio/lbcd/chaincfg/chainhash"
|
||||
"github.com/lbryio/lbcd/claimtrie/param"
|
||||
)
|
||||
|
@ -9,6 +12,10 @@ type HashV2Manager struct {
|
|||
Manager
|
||||
}
|
||||
|
||||
type HashV3Manager struct {
|
||||
Manager
|
||||
}
|
||||
|
||||
func (nm *HashV2Manager) computeClaimHashes(name []byte) (*chainhash.Hash, int32) {
|
||||
|
||||
n, err := nm.NodeAt(nm.Height(), name)
|
||||
|
@ -24,9 +31,9 @@ func (nm *HashV2Manager) computeClaimHashes(name []byte) (*chainhash.Hash, int32
|
|||
}
|
||||
}
|
||||
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) {
|
||||
|
@ -37,3 +44,72 @@ func (nm *HashV2Manager) Hash(name []byte) (*chainhash.Hash, int32) {
|
|||
|
||||
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
|
||||
}
|
||||
|
||||
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,
|
||||
"getclaimsfornamebyseq": handleGetClaimsForNameBySeq,
|
||||
"normalize": handleGetNormalized,
|
||||
|
||||
"getprooffornamebyid": handleGetProofForNameByID,
|
||||
"getprooffornamebybid": handleGetProofForNameByBid,
|
||||
"getprooffornamebyseq": handleGetProofForNameBySeq,
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
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