added rpc methods to get cliam data by name
fix misuse of normalized name post-merge fix
This commit is contained in:
parent
15ded4ed0f
commit
353d08bb91
14 changed files with 544 additions and 63 deletions
|
@ -139,3 +139,30 @@ func (h *handler) handleTxOuts(ct *claimtrie.ClaimTrie) error {
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (b *BlockChain) GetNamesChangedInBlock(height int32) ([]string, error) {
|
||||||
|
b.chainLock.RLock()
|
||||||
|
defer b.chainLock.RUnlock()
|
||||||
|
|
||||||
|
return b.claimTrie.NamesChangedInBlock(height)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *BlockChain) GetClaimsForName(height int32, name string) (string, *node.Node, error) {
|
||||||
|
|
||||||
|
normalizedName := node.NormalizeIfNecessary([]byte(name), height)
|
||||||
|
|
||||||
|
b.chainLock.RLock()
|
||||||
|
defer b.chainLock.RUnlock()
|
||||||
|
|
||||||
|
n, err := b.claimTrie.NodeAt(height, normalizedName)
|
||||||
|
if err != nil {
|
||||||
|
return string(normalizedName), nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if n == nil {
|
||||||
|
return string(normalizedName), nil, fmt.Errorf("name does not exist at height %d: %s", height, name)
|
||||||
|
}
|
||||||
|
|
||||||
|
n.SortClaimsByBid()
|
||||||
|
return string(normalizedName), n, nil
|
||||||
|
}
|
||||||
|
|
95
btcjson/claimcmds.go
Normal file
95
btcjson/claimcmds.go
Normal file
|
@ -0,0 +1,95 @@
|
||||||
|
package btcjson
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
// No special flags for commands in this file.
|
||||||
|
flags := UsageFlag(0)
|
||||||
|
|
||||||
|
MustRegisterCmd("getchangesinblock", (*GetChangesInBlockCmd)(nil), flags)
|
||||||
|
MustRegisterCmd("getclaimsforname", (*GetClaimsForNameCmd)(nil), flags)
|
||||||
|
MustRegisterCmd("getclaimsfornamebyid", (*GetClaimsForNameByIDCmd)(nil), flags)
|
||||||
|
MustRegisterCmd("getclaimsfornamebybid", (*GetClaimsForNameByBidCmd)(nil), flags)
|
||||||
|
MustRegisterCmd("getclaimsfornamebyseq", (*GetClaimsForNameBySeqCmd)(nil), flags)
|
||||||
|
MustRegisterCmd("normalize", (*GetNormalizedCmd)(nil), flags)
|
||||||
|
}
|
||||||
|
|
||||||
|
// optional inputs are required to be pointers, but they support things like `jsonrpcdefault:"false"`
|
||||||
|
// optional inputs have to be at the bottom of the struct
|
||||||
|
// optional outputs require ",omitempty"
|
||||||
|
// traditional bitcoin fields are all lowercase
|
||||||
|
|
||||||
|
type GetChangesInBlockCmd struct {
|
||||||
|
HashOrHeight *string `json:"hashorheight" jsonrpcdefault:""`
|
||||||
|
}
|
||||||
|
|
||||||
|
type GetChangesInBlockResult struct {
|
||||||
|
Hash string `json:"hash"`
|
||||||
|
Height int32 `json:"height"`
|
||||||
|
Names []string `json:"names"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type GetClaimsForNameCmd struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
HashOrHeight *string `json:"hashorheight" jsonrpcdefault:""`
|
||||||
|
IncludeValues *bool `json:"includevalues" jsonrpcdefault:"false"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type GetClaimsForNameByIDCmd struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
PartialClaimIDs []string `json:"partialclaimids"`
|
||||||
|
HashOrHeight *string `json:"hashorheight" jsonrpcdefault:""`
|
||||||
|
IncludeValues *bool `json:"includevalues" jsonrpcdefault:"false"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type GetClaimsForNameByBidCmd struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
Bids []int32 `json:"bids"`
|
||||||
|
HashOrHeight *string `json:"hashorheight" jsonrpcdefault:""`
|
||||||
|
IncludeValues *bool `json:"includevalues" jsonrpcdefault:"false"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type GetClaimsForNameBySeqCmd struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
Sequences []int32 `json:"sequences" jsonrpcusage:"[sequence,...]"`
|
||||||
|
HashOrHeight *string `json:"hashorheight" jsonrpcdefault:""`
|
||||||
|
IncludeValues *bool `json:"includevalues" jsonrpcdefault:"false"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type GetClaimsForNameResult struct {
|
||||||
|
Hash string `json:"hash"`
|
||||||
|
Height int32 `json:"height"`
|
||||||
|
NormalizedName string `json:"normalizedname"`
|
||||||
|
Claims []ClaimResult `json:"claims"`
|
||||||
|
// UnclaimedSupports []SupportResult `json:"unclaimedSupports"` how would this work with other constraints?
|
||||||
|
}
|
||||||
|
|
||||||
|
type SupportResult struct {
|
||||||
|
TXID string `json:"txid"`
|
||||||
|
N uint32 `json:"n"`
|
||||||
|
Height int32 `json:"height"`
|
||||||
|
ValidAtHeight int32 `json:"validatheight"`
|
||||||
|
Amount int64 `json:"amount"`
|
||||||
|
Address string `json:"address,omitempty"`
|
||||||
|
Value string `json:"value,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ClaimResult struct {
|
||||||
|
ClaimID string `json:"claimid"`
|
||||||
|
TXID string `json:"txid"`
|
||||||
|
N uint32 `json:"n"`
|
||||||
|
Bid int32 `json:"bid"`
|
||||||
|
Sequence int32 `json:"sequence"`
|
||||||
|
Height int32 `json:"height"`
|
||||||
|
ValidAtHeight int32 `json:"validatheight"`
|
||||||
|
EffectiveAmount int64 `json:"effectiveamount"`
|
||||||
|
Supports []SupportResult `json:"supports,omitempty"`
|
||||||
|
Address string `json:"address,omitempty"`
|
||||||
|
Value string `json:"value,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type GetNormalizedCmd struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type GetNormalizedResult struct {
|
||||||
|
NormalizedName string `json:"normalizedname"`
|
||||||
|
}
|
|
@ -359,8 +359,17 @@ func (ct *ClaimTrie) forwardNodeChange(chg change.Change) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ct *ClaimTrie) Node(name []byte) (*node.Node, error) {
|
func (ct *ClaimTrie) NodeAt(height int32, name []byte) (*node.Node, error) {
|
||||||
return ct.nodeManager.Node(name)
|
return ct.nodeManager.NodeAt(height, name)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ct *ClaimTrie) NamesChangedInBlock(height int32) ([]string, error) {
|
||||||
|
hits, err := ct.temporalRepo.NodesAt(height)
|
||||||
|
r := make([]string, len(hits))
|
||||||
|
for i := range hits {
|
||||||
|
r[i] = string(hits[i])
|
||||||
|
}
|
||||||
|
return r, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ct *ClaimTrie) FlushToDisk() {
|
func (ct *ClaimTrie) FlushToDisk() {
|
||||||
|
|
|
@ -110,7 +110,7 @@ func TestNormalizationFork(t *testing.T) {
|
||||||
r.NoError(err)
|
r.NoError(err)
|
||||||
r.NotEqual(merkletrie.EmptyTrieHash[:], ct.MerkleHash()[:])
|
r.NotEqual(merkletrie.EmptyTrieHash[:], ct.MerkleHash()[:])
|
||||||
|
|
||||||
n, err := ct.nodeManager.Node([]byte("AÑEJO"))
|
n, err := ct.nodeManager.NodeAt(ct.nodeManager.Height(), []byte("AÑEJO"))
|
||||||
r.NoError(err)
|
r.NoError(err)
|
||||||
r.NotNil(n.BestClaim)
|
r.NotNil(n.BestClaim)
|
||||||
r.Equal(int32(1), n.TakenOverAt)
|
r.Equal(int32(1), n.TakenOverAt)
|
||||||
|
@ -123,7 +123,7 @@ func TestNormalizationFork(t *testing.T) {
|
||||||
r.NoError(err)
|
r.NoError(err)
|
||||||
r.NotEqual(merkletrie.EmptyTrieHash[:], ct.MerkleHash()[:])
|
r.NotEqual(merkletrie.EmptyTrieHash[:], ct.MerkleHash()[:])
|
||||||
|
|
||||||
n, err = ct.nodeManager.Node([]byte("añejo"))
|
n, err = ct.nodeManager.NodeAt(ct.nodeManager.Height(), []byte("añejo"))
|
||||||
r.NoError(err)
|
r.NoError(err)
|
||||||
r.Equal(3, len(n.Claims))
|
r.Equal(3, len(n.Claims))
|
||||||
r.Equal(uint32(1), n.BestClaim.OutPoint.Index)
|
r.Equal(uint32(1), n.BestClaim.OutPoint.Index)
|
||||||
|
@ -212,7 +212,7 @@ func verifyBestIndex(t *testing.T, ct *ClaimTrie, name string, idx uint32, claim
|
||||||
|
|
||||||
r := require.New(t)
|
r := require.New(t)
|
||||||
|
|
||||||
n, err := ct.nodeManager.Node([]byte(name))
|
n, err := ct.nodeManager.NodeAt(ct.nodeManager.Height(), []byte(name))
|
||||||
r.NoError(err)
|
r.NoError(err)
|
||||||
r.Equal(claims, len(n.Claims))
|
r.Equal(claims, len(n.Claims))
|
||||||
if claims > 0 {
|
if claims > 0 {
|
||||||
|
|
|
@ -90,12 +90,7 @@ var nodeReplayCmd = &cobra.Command{
|
||||||
}
|
}
|
||||||
nm := node.NewNormalizingManager(bm)
|
nm := node.NewNormalizingManager(bm)
|
||||||
|
|
||||||
_, err = nm.IncrementHeightTo(int32(height))
|
n, err := nm.NodeAt(int32(height), name)
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("increment height: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
n, err := nm.Node(name)
|
|
||||||
if err != nil || n == nil {
|
if err != nil || n == nil {
|
||||||
return fmt.Errorf("get node: %w", err)
|
return fmt.Errorf("get node: %w", err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -54,7 +54,7 @@ func showNode(n *node.Node) {
|
||||||
|
|
||||||
fmt.Printf("%s\n", strings.Repeat("-", 200))
|
fmt.Printf("%s\n", strings.Repeat("-", 200))
|
||||||
fmt.Printf("Last Node Takeover: %d\n\n", n.TakenOverAt)
|
fmt.Printf("Last Node Takeover: %d\n\n", n.TakenOverAt)
|
||||||
n.SortClaims()
|
n.SortClaimsByBid()
|
||||||
for _, c := range n.Claims {
|
for _, c := range n.Claims {
|
||||||
showClaim(c, n)
|
showClaim(c, n)
|
||||||
for _, s := range n.Supports {
|
for _, s := range n.Supports {
|
||||||
|
|
|
@ -181,18 +181,18 @@ func (pt *collapsedTrie) FindPath(value KeyType) ([]int, []*collapsedVertex) {
|
||||||
|
|
||||||
// IterateFrom can be used to find a value and run a function on that value.
|
// IterateFrom can be used to find a value and run a function on that value.
|
||||||
// If the handler returns true it continues to iterate through the children of value.
|
// If the handler returns true it continues to iterate through the children of value.
|
||||||
func (pt *collapsedTrie) IterateFrom(start KeyType, handler func(value *collapsedVertex) bool) {
|
func (pt *collapsedTrie) IterateFrom(start KeyType, handler func(name KeyType, value *collapsedVertex) bool) {
|
||||||
node := find(start, pt.Root, nil, nil)
|
node := find(start, pt.Root, nil, nil)
|
||||||
if node == nil {
|
if node == nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
iterateFrom(node, handler)
|
iterateFrom(start, node, handler)
|
||||||
}
|
}
|
||||||
|
|
||||||
func iterateFrom(node *collapsedVertex, handler func(value *collapsedVertex) bool) {
|
func iterateFrom(name KeyType, node *collapsedVertex, handler func(name KeyType, value *collapsedVertex) bool) {
|
||||||
for handler(node) {
|
for handler(name, node) {
|
||||||
for _, child := range node.children {
|
for _, child := range node.children {
|
||||||
iterateFrom(child, handler)
|
iterateFrom(append(name, child.key...), child, handler)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,10 +24,13 @@ type Claim struct {
|
||||||
OutPoint wire.OutPoint
|
OutPoint wire.OutPoint
|
||||||
ClaimID change.ClaimID
|
ClaimID change.ClaimID
|
||||||
Amount int64
|
Amount int64
|
||||||
AcceptedAt int32 // when arrived (aka, originally landed in block)
|
// CreatedAt int32 // the very first block, unused at present
|
||||||
|
AcceptedAt int32 // the latest update height
|
||||||
ActiveAt int32 // AcceptedAt + actual delay
|
ActiveAt int32 // AcceptedAt + actual delay
|
||||||
Status Status
|
|
||||||
VisibleAt int32
|
VisibleAt int32
|
||||||
|
|
||||||
|
Status Status `msgpack:",omitempty"`
|
||||||
|
Sequence int32 `msgpack:",omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Claim) setOutPoint(op wire.OutPoint) *Claim {
|
func (c *Claim) setOutPoint(op wire.OutPoint) *Claim {
|
||||||
|
|
|
@ -21,7 +21,7 @@ type Manager interface {
|
||||||
DecrementHeightTo(affectedNames [][]byte, height int32) error
|
DecrementHeightTo(affectedNames [][]byte, height int32) error
|
||||||
Height() int32
|
Height() int32
|
||||||
Close() error
|
Close() error
|
||||||
Node(name []byte) (*Node, error)
|
NodeAt(height int32, name []byte) (*Node, error)
|
||||||
NextUpdateHeightOfNode(name []byte) ([]byte, int32)
|
NextUpdateHeightOfNode(name []byte) ([]byte, int32)
|
||||||
IterateNames(predicate func(name []byte) bool)
|
IterateNames(predicate func(name []byte) bool)
|
||||||
Hash(name []byte) *chainhash.Hash
|
Hash(name []byte) *chainhash.Hash
|
||||||
|
@ -97,9 +97,24 @@ func NewBaseManager(repo Repo) (*BaseManager, error) {
|
||||||
return nm, nil
|
return nm, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (nm *BaseManager) NodeAt(height int32, name []byte) (*Node, error) {
|
||||||
|
|
||||||
|
changes, err := nm.repo.LoadChanges(name)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrap(err, "in load changes")
|
||||||
|
}
|
||||||
|
|
||||||
|
n, err := nm.newNodeFromChanges(changes, height)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrap(err, "in new node")
|
||||||
|
}
|
||||||
|
|
||||||
|
return n, nil
|
||||||
|
}
|
||||||
|
|
||||||
// Node returns a node at the current height.
|
// Node returns a node at the current height.
|
||||||
// The returned node may have pending changes.
|
// The returned node may have pending changes.
|
||||||
func (nm *BaseManager) Node(name []byte) (*Node, error) {
|
func (nm *BaseManager) node(name []byte) (*Node, error) {
|
||||||
|
|
||||||
nameStr := string(name)
|
nameStr := string(name)
|
||||||
n := nm.cache.Get(nameStr)
|
n := nm.cache.Get(nameStr)
|
||||||
|
@ -107,22 +122,13 @@ func (nm *BaseManager) Node(name []byte) (*Node, error) {
|
||||||
return n.AdjustTo(nm.height, -1, name), nil
|
return n.AdjustTo(nm.height, -1, name), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
changes, err := nm.repo.LoadChanges(name)
|
n, err := nm.NodeAt(nm.height, name)
|
||||||
if err != nil {
|
|
||||||
return nil, errors.Wrap(err, "in load changes")
|
|
||||||
}
|
|
||||||
|
|
||||||
n, err = nm.newNodeFromChanges(changes, nm.height)
|
|
||||||
if err != nil {
|
|
||||||
return nil, errors.Wrap(err, "in new node")
|
|
||||||
}
|
|
||||||
|
|
||||||
if n == nil { // they've requested a nonexistent or expired name
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
|
if n != nil && err == nil {
|
||||||
nm.cache.Put(nameStr, n)
|
nm.cache.Put(nameStr, n)
|
||||||
return n, nil
|
}
|
||||||
|
|
||||||
|
return n, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// newNodeFromChanges returns a new Node constructed from the changes.
|
// newNodeFromChanges returns a new Node constructed from the changes.
|
||||||
|
@ -341,7 +347,7 @@ func calculateDelay(curr, tookOver int32) int32 {
|
||||||
|
|
||||||
func (nm BaseManager) NextUpdateHeightOfNode(name []byte) ([]byte, int32) {
|
func (nm BaseManager) NextUpdateHeightOfNode(name []byte) ([]byte, int32) {
|
||||||
|
|
||||||
n, err := nm.Node(name)
|
n, err := nm.node(name)
|
||||||
if err != nil || n == nil {
|
if err != nil || n == nil {
|
||||||
return name, 0
|
return name, 0
|
||||||
}
|
}
|
||||||
|
@ -393,12 +399,12 @@ func (nm *BaseManager) IterateNames(predicate func(name []byte) bool) {
|
||||||
|
|
||||||
func (nm *BaseManager) claimHashes(name []byte) *chainhash.Hash {
|
func (nm *BaseManager) claimHashes(name []byte) *chainhash.Hash {
|
||||||
|
|
||||||
n, err := nm.Node(name)
|
n, err := nm.node(name)
|
||||||
if err != nil || n == nil {
|
if err != nil || n == nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
n.SortClaims()
|
n.SortClaimsByBid()
|
||||||
claimHashes := make([]*chainhash.Hash, 0, len(n.Claims))
|
claimHashes := make([]*chainhash.Hash, 0, len(n.Claims))
|
||||||
for _, c := range n.Claims {
|
for _, c := range n.Claims {
|
||||||
if c.Status == Activated { // TODO: unit test this line
|
if c.Status == Activated { // TODO: unit test this line
|
||||||
|
@ -417,7 +423,7 @@ func (nm *BaseManager) Hash(name []byte) *chainhash.Hash {
|
||||||
return nm.claimHashes(name)
|
return nm.claimHashes(name)
|
||||||
}
|
}
|
||||||
|
|
||||||
n, err := nm.Node(name)
|
n, err := nm.node(name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -69,25 +69,25 @@ func TestSimpleAddClaim(t *testing.T) {
|
||||||
_, err = m.IncrementHeightTo(12)
|
_, err = m.IncrementHeightTo(12)
|
||||||
r.NoError(err)
|
r.NoError(err)
|
||||||
|
|
||||||
n1, err := m.Node(name1)
|
n1, err := m.node(name1)
|
||||||
r.NoError(err)
|
r.NoError(err)
|
||||||
r.Equal(1, len(n1.Claims))
|
r.Equal(1, len(n1.Claims))
|
||||||
r.NotNil(n1.Claims.find(byOut(*out1)))
|
r.NotNil(n1.Claims.find(byOut(*out1)))
|
||||||
|
|
||||||
n2, err := m.Node(name2)
|
n2, err := m.node(name2)
|
||||||
r.NoError(err)
|
r.NoError(err)
|
||||||
r.Equal(1, len(n2.Claims))
|
r.Equal(1, len(n2.Claims))
|
||||||
r.NotNil(n2.Claims.find(byOut(*out2)))
|
r.NotNil(n2.Claims.find(byOut(*out2)))
|
||||||
|
|
||||||
err = m.DecrementHeightTo([][]byte{name2}, 11)
|
err = m.DecrementHeightTo([][]byte{name2}, 11)
|
||||||
r.NoError(err)
|
r.NoError(err)
|
||||||
n2, err = m.Node(name2)
|
n2, err = m.node(name2)
|
||||||
r.NoError(err)
|
r.NoError(err)
|
||||||
r.Nil(n2)
|
r.Nil(n2)
|
||||||
|
|
||||||
err = m.DecrementHeightTo([][]byte{name1}, 1)
|
err = m.DecrementHeightTo([][]byte{name1}, 1)
|
||||||
r.NoError(err)
|
r.NoError(err)
|
||||||
n2, err = m.Node(name1)
|
n2, err = m.node(name1)
|
||||||
r.NoError(err)
|
r.NoError(err)
|
||||||
r.Nil(n2)
|
r.Nil(n2)
|
||||||
}
|
}
|
||||||
|
@ -138,7 +138,7 @@ func TestSupportAmounts(t *testing.T) {
|
||||||
_, err = m.IncrementHeightTo(20)
|
_, err = m.IncrementHeightTo(20)
|
||||||
r.NoError(err)
|
r.NoError(err)
|
||||||
|
|
||||||
n1, err := m.Node(name1)
|
n1, err := m.node(name1)
|
||||||
r.NoError(err)
|
r.NoError(err)
|
||||||
r.Equal(2, len(n1.Claims))
|
r.Equal(2, len(n1.Claims))
|
||||||
r.Equal(int64(5), n1.BestClaim.Amount+n1.SupportSums[n1.BestClaim.ClaimID.Key()])
|
r.Equal(int64(5), n1.BestClaim.Amount+n1.SupportSums[n1.BestClaim.ClaimID.Key()])
|
||||||
|
@ -178,7 +178,7 @@ func TestClaimSort(t *testing.T) {
|
||||||
n.Claims = append(n.Claims, &Claim{OutPoint: *out3, AcceptedAt: 3, Amount: 2, ClaimID: change.ClaimID{3}})
|
n.Claims = append(n.Claims, &Claim{OutPoint: *out3, AcceptedAt: 3, Amount: 2, ClaimID: change.ClaimID{3}})
|
||||||
n.Claims = append(n.Claims, &Claim{OutPoint: *out3, AcceptedAt: 4, Amount: 2, ClaimID: change.ClaimID{4}})
|
n.Claims = append(n.Claims, &Claim{OutPoint: *out3, AcceptedAt: 4, Amount: 2, ClaimID: change.ClaimID{4}})
|
||||||
n.Claims = append(n.Claims, &Claim{OutPoint: *out1, AcceptedAt: 3, Amount: 4, ClaimID: change.ClaimID{1}})
|
n.Claims = append(n.Claims, &Claim{OutPoint: *out1, AcceptedAt: 3, Amount: 4, ClaimID: change.ClaimID{1}})
|
||||||
n.SortClaims()
|
n.SortClaimsByBid()
|
||||||
|
|
||||||
r.Equal(int64(4), n.Claims[0].Amount)
|
r.Equal(int64(4), n.Claims[0].Amount)
|
||||||
r.Equal(int64(3), n.Claims[1].Amount)
|
r.Equal(int64(3), n.Claims[1].Amount)
|
||||||
|
|
|
@ -2,7 +2,6 @@ package node
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/pkg/errors"
|
|
||||||
"math"
|
"math"
|
||||||
"sort"
|
"sort"
|
||||||
|
|
||||||
|
@ -40,14 +39,16 @@ func (n *Node) ApplyChange(chg change.Change, delay int32) error {
|
||||||
OutPoint: chg.OutPoint,
|
OutPoint: chg.OutPoint,
|
||||||
Amount: chg.Amount,
|
Amount: chg.Amount,
|
||||||
ClaimID: chg.ClaimID,
|
ClaimID: chg.ClaimID,
|
||||||
AcceptedAt: chg.Height, // not tracking original height in this version (but we could)
|
// CreatedAt: chg.Height,
|
||||||
|
AcceptedAt: chg.Height,
|
||||||
ActiveAt: chg.Height + delay,
|
ActiveAt: chg.Height + delay,
|
||||||
VisibleAt: visibleAt,
|
VisibleAt: visibleAt,
|
||||||
|
Sequence: int32(len(n.Claims)),
|
||||||
}
|
}
|
||||||
old := n.Claims.find(byOut(chg.OutPoint)) // TODO: remove this after proving ResetHeight works
|
// old := n.Claims.find(byOut(chg.OutPoint)) // TODO: remove this after proving ResetHeight works
|
||||||
if old != nil {
|
// if old != nil {
|
||||||
return errors.Errorf("CONFLICT WITH EXISTING TXO! Name: %s, Height: %d", chg.Name, chg.Height)
|
// return errors.Errorf("CONFLICT WITH EXISTING TXO! Name: %s, Height: %d", chg.Name, chg.Height)
|
||||||
}
|
// }
|
||||||
n.Claims = append(n.Claims, c)
|
n.Claims = append(n.Claims, c)
|
||||||
|
|
||||||
case change.SpendClaim:
|
case change.SpendClaim:
|
||||||
|
@ -72,10 +73,10 @@ func (n *Node) ApplyChange(chg change.Change, delay int32) error {
|
||||||
c.setStatus(Accepted) // it was Deactivated in the spend (but we only activate at the end of the block)
|
c.setStatus(Accepted) // it was Deactivated in the spend (but we only activate at the end of the block)
|
||||||
// that's because the old code would put all insertions into the "queue" that was processed at block's end
|
// that's because the old code would put all insertions into the "queue" that was processed at block's end
|
||||||
|
|
||||||
// It's a bug, but the old code would update these.
|
// This forces us to be newer, which may in an unintentional takeover if there's an older one.
|
||||||
// That forces this to be newer, which may in an unintentional takeover if there's an older one.
|
// TODO: reconsider these updates in future hard forks.
|
||||||
c.setAccepted(chg.Height) // TODO: Fork this out
|
c.setAccepted(chg.Height)
|
||||||
c.setActiveAt(chg.Height + delay) // TODO: Fork this out
|
c.setActiveAt(chg.Height + delay)
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
LogOnce(fmt.Sprintf("Updating claim but missing existing claim with ID %s", chg.ClaimID))
|
LogOnce(fmt.Sprintf("Updating claim but missing existing claim with ID %s", chg.ClaimID))
|
||||||
|
@ -291,7 +292,7 @@ func (n *Node) activateAllClaims(height int32) int {
|
||||||
return count
|
return count
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *Node) SortClaims() {
|
func (n *Node) SortClaimsByBid() {
|
||||||
|
|
||||||
// purposefully sorting by descent
|
// purposefully sorting by descent
|
||||||
sort.Slice(n.Claims, func(j, i int) bool {
|
sort.Slice(n.Claims, func(j, i int) bool {
|
||||||
|
|
|
@ -72,7 +72,7 @@ func (nm *NormalizingManager) addNormalizationForkChangesIfNecessary(height int3
|
||||||
|
|
||||||
// by loading changes for norm here, you can determine if there will be a conflict
|
// by loading changes for norm here, you can determine if there will be a conflict
|
||||||
|
|
||||||
n, err := nm.Manager.Node(clone)
|
n, err := nm.Manager.NodeAt(nm.Manager.Height(), clone)
|
||||||
if err != nil || n == nil {
|
if err != nil || n == nil {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
342
rpcclaimtrie.go
Normal file
342
rpcclaimtrie.go
Normal file
|
@ -0,0 +1,342 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/hex"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/btcsuite/btcd/btcjson"
|
||||||
|
"github.com/btcsuite/btcd/chaincfg/chainhash"
|
||||||
|
"github.com/btcsuite/btcd/claimtrie/node"
|
||||||
|
"github.com/btcsuite/btcd/database"
|
||||||
|
"github.com/btcsuite/btcd/txscript"
|
||||||
|
"github.com/btcsuite/btcd/wire"
|
||||||
|
)
|
||||||
|
|
||||||
|
var claimtrieHandlers = map[string]commandHandler{
|
||||||
|
"getchangesinblock": handleGetChangesInBlock,
|
||||||
|
"getclaimsforname": handleGetClaimsForName,
|
||||||
|
"getclaimsfornamebyid": handleGetClaimsForNameByID,
|
||||||
|
"getclaimsfornamebybid": handleGetClaimsForNameByBid,
|
||||||
|
"getclaimsfornamebyseq": handleGetClaimsForNameBySeq,
|
||||||
|
"normalize": handleGetNormalized,
|
||||||
|
}
|
||||||
|
|
||||||
|
func handleGetChangesInBlock(s *rpcServer, cmd interface{}, _ <-chan struct{}) (interface{}, error) {
|
||||||
|
|
||||||
|
c := cmd.(*btcjson.GetChangesInBlockCmd)
|
||||||
|
hash, height, err := parseHashOrHeight(s, c.HashOrHeight)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
names, err := s.cfg.Chain.GetNamesChangedInBlock(height)
|
||||||
|
if err != nil {
|
||||||
|
return nil, &btcjson.RPCError{
|
||||||
|
Code: btcjson.ErrRPCMisc,
|
||||||
|
Message: "Message: " + err.Error(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return btcjson.GetChangesInBlockResult{
|
||||||
|
Hash: hash,
|
||||||
|
Height: height,
|
||||||
|
Names: names,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseHashOrHeight(s *rpcServer, hashOrHeight *string) (string, int32, error) {
|
||||||
|
if hashOrHeight == nil || len(*hashOrHeight) == 0 {
|
||||||
|
|
||||||
|
if !s.cfg.Chain.IsCurrent() {
|
||||||
|
return "", 0, &btcjson.RPCError{
|
||||||
|
Code: btcjson.ErrRPCClientInInitialDownload,
|
||||||
|
Message: "Unable to query the chain tip during initial download",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// just give them the latest block if a specific one wasn't requested
|
||||||
|
best := s.cfg.Chain.BestSnapshot()
|
||||||
|
return best.Hash.String(), best.Height, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
ht, err := strconv.ParseInt(*hashOrHeight, 10, 32)
|
||||||
|
if err == nil && len(*hashOrHeight) < 32 {
|
||||||
|
hs, err := s.cfg.Chain.BlockHashByHeight(int32(ht))
|
||||||
|
if err != nil {
|
||||||
|
return "", 0, &btcjson.RPCError{
|
||||||
|
Code: btcjson.ErrRPCBlockNotFound,
|
||||||
|
Message: "Unable to locate a block at height " + *hashOrHeight + ": " + err.Error(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return hs.String(), int32(ht), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
hs, err := chainhash.NewHashFromStr(*hashOrHeight)
|
||||||
|
if err != nil {
|
||||||
|
return "", 0, &btcjson.RPCError{
|
||||||
|
Code: btcjson.ErrRPCInvalidParameter,
|
||||||
|
Message: "Unable to parse a height or hash from " + *hashOrHeight + ": " + err.Error(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
h, err := s.cfg.Chain.BlockHeightByHash(hs)
|
||||||
|
if err != nil {
|
||||||
|
return hs.String(), h, &btcjson.RPCError{
|
||||||
|
Code: btcjson.ErrRPCBlockNotFound,
|
||||||
|
Message: "Unable to find a block with hash " + hs.String() + ": " + err.Error(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return hs.String(), h, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func handleGetClaimsForName(s *rpcServer, cmd interface{}, _ <-chan struct{}) (interface{}, error) {
|
||||||
|
|
||||||
|
c := cmd.(*btcjson.GetClaimsForNameCmd)
|
||||||
|
hash, height, err := parseHashOrHeight(s, c.HashOrHeight)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
name, n, err := s.cfg.Chain.GetClaimsForName(height, c.Name)
|
||||||
|
if err != nil {
|
||||||
|
return nil, &btcjson.RPCError{
|
||||||
|
Code: btcjson.ErrRPCMisc,
|
||||||
|
Message: "Message: " + err.Error(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var results []btcjson.ClaimResult
|
||||||
|
for i := range n.Claims {
|
||||||
|
cr, err := toClaimResult(s, int32(i), n, c.IncludeValues)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
results = append(results, cr)
|
||||||
|
}
|
||||||
|
|
||||||
|
return btcjson.GetClaimsForNameResult{
|
||||||
|
Hash: hash,
|
||||||
|
Height: height,
|
||||||
|
NormalizedName: name,
|
||||||
|
Claims: results,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func handleGetClaimsForNameByID(s *rpcServer, cmd interface{}, _ <-chan struct{}) (interface{}, error) {
|
||||||
|
|
||||||
|
c := cmd.(*btcjson.GetClaimsForNameByIDCmd)
|
||||||
|
hash, height, err := parseHashOrHeight(s, c.HashOrHeight)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
name, n, err := s.cfg.Chain.GetClaimsForName(height, c.Name)
|
||||||
|
if err != nil {
|
||||||
|
return nil, &btcjson.RPCError{
|
||||||
|
Code: btcjson.ErrRPCMisc,
|
||||||
|
Message: "Message: " + err.Error(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var results []btcjson.ClaimResult
|
||||||
|
for i := 0; i < len(n.Claims); i++ {
|
||||||
|
for _, id := range c.PartialClaimIDs {
|
||||||
|
if strings.HasPrefix(n.Claims[i].ClaimID.String(), id) {
|
||||||
|
cr, err := toClaimResult(s, int32(i), n, c.IncludeValues)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
results = append(results, cr)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return btcjson.GetClaimsForNameResult{
|
||||||
|
Hash: hash,
|
||||||
|
Height: height,
|
||||||
|
NormalizedName: name,
|
||||||
|
Claims: results,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func handleGetClaimsForNameByBid(s *rpcServer, cmd interface{}, _ <-chan struct{}) (interface{}, error) {
|
||||||
|
|
||||||
|
c := cmd.(*btcjson.GetClaimsForNameByBidCmd)
|
||||||
|
hash, height, err := parseHashOrHeight(s, c.HashOrHeight)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
name, n, err := s.cfg.Chain.GetClaimsForName(height, c.Name)
|
||||||
|
if err != nil {
|
||||||
|
return nil, &btcjson.RPCError{
|
||||||
|
Code: btcjson.ErrRPCMisc,
|
||||||
|
Message: "Message: " + err.Error(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var results []btcjson.ClaimResult
|
||||||
|
for _, b := range c.Bids { // claims are already sorted in bid order
|
||||||
|
if b >= 0 && int(b) < len(n.Claims) {
|
||||||
|
cr, err := toClaimResult(s, b, n, c.IncludeValues)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
results = append(results, cr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return btcjson.GetClaimsForNameResult{
|
||||||
|
Hash: hash,
|
||||||
|
Height: height,
|
||||||
|
NormalizedName: name,
|
||||||
|
Claims: results,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func handleGetClaimsForNameBySeq(s *rpcServer, cmd interface{}, _ <-chan struct{}) (interface{}, error) {
|
||||||
|
|
||||||
|
c := cmd.(*btcjson.GetClaimsForNameBySeqCmd)
|
||||||
|
hash, height, err := parseHashOrHeight(s, c.HashOrHeight)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
name, n, err := s.cfg.Chain.GetClaimsForName(height, c.Name)
|
||||||
|
if err != nil {
|
||||||
|
return nil, &btcjson.RPCError{
|
||||||
|
Code: btcjson.ErrRPCMisc,
|
||||||
|
Message: "Message: " + err.Error(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sm := map[int32]bool{}
|
||||||
|
for _, seq := range c.Sequences {
|
||||||
|
sm[seq] = true
|
||||||
|
}
|
||||||
|
|
||||||
|
var results []btcjson.ClaimResult
|
||||||
|
for i := 0; i < len(n.Claims); i++ {
|
||||||
|
if sm[n.Claims[i].Sequence] {
|
||||||
|
cr, err := toClaimResult(s, int32(i), n, c.IncludeValues)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
results = append(results, cr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return btcjson.GetClaimsForNameResult{
|
||||||
|
Hash: hash,
|
||||||
|
Height: height,
|
||||||
|
NormalizedName: name,
|
||||||
|
Claims: results,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func toClaimResult(s *rpcServer, i int32, node *node.Node, includeValues *bool) (btcjson.ClaimResult, error) {
|
||||||
|
claim := node.Claims[i]
|
||||||
|
address, value, err := lookupValue(s, claim.OutPoint, includeValues)
|
||||||
|
supports, err := toSupportResults(s, i, node, includeValues)
|
||||||
|
return btcjson.ClaimResult{
|
||||||
|
ClaimID: claim.ClaimID.String(),
|
||||||
|
Height: claim.AcceptedAt,
|
||||||
|
ValidAtHeight: claim.ActiveAt,
|
||||||
|
TXID: claim.OutPoint.Hash.String(),
|
||||||
|
N: claim.OutPoint.Index,
|
||||||
|
Bid: i, // assuming sorted by bid
|
||||||
|
EffectiveAmount: claim.Amount + node.SupportSums[claim.ClaimID.Key()],
|
||||||
|
Sequence: claim.Sequence,
|
||||||
|
Supports: supports,
|
||||||
|
Address: address,
|
||||||
|
Value: value,
|
||||||
|
}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func toSupportResults(s *rpcServer, i int32, n *node.Node, includeValues *bool) ([]btcjson.SupportResult, error) {
|
||||||
|
var results []btcjson.SupportResult
|
||||||
|
c := n.Claims[i]
|
||||||
|
for _, sup := range n.Supports {
|
||||||
|
if sup.Status == node.Activated && c.ClaimID == sup.ClaimID {
|
||||||
|
address, value, err := lookupValue(s, sup.OutPoint, includeValues)
|
||||||
|
if err != nil {
|
||||||
|
return results, err
|
||||||
|
}
|
||||||
|
results = append(results, btcjson.SupportResult{
|
||||||
|
TXID: sup.OutPoint.Hash.String(),
|
||||||
|
N: sup.OutPoint.Index,
|
||||||
|
Height: sup.AcceptedAt,
|
||||||
|
ValidAtHeight: sup.ActiveAt,
|
||||||
|
Amount: sup.Amount,
|
||||||
|
Value: value,
|
||||||
|
Address: address,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return results, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func lookupValue(s *rpcServer, outpoint wire.OutPoint, includeValues *bool) (string, string, error) {
|
||||||
|
if includeValues == nil || !*includeValues {
|
||||||
|
return "", "", nil
|
||||||
|
}
|
||||||
|
// TODO: maybe use addrIndex if the txIndex is not available
|
||||||
|
|
||||||
|
if s.cfg.TxIndex == nil {
|
||||||
|
return "", "", &btcjson.RPCError{
|
||||||
|
Code: btcjson.ErrRPCNoTxInfo,
|
||||||
|
Message: "The transaction index must be " +
|
||||||
|
"enabled to query the blockchain " +
|
||||||
|
"(specify --txindex)",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
txHash := &outpoint.Hash
|
||||||
|
blockRegion, err := s.cfg.TxIndex.TxBlockRegion(txHash)
|
||||||
|
if err != nil {
|
||||||
|
context := "Failed to retrieve transaction location"
|
||||||
|
return "", "", internalRPCError(err.Error(), context)
|
||||||
|
}
|
||||||
|
if blockRegion == nil {
|
||||||
|
return "", "", rpcNoTxInfoError(txHash)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load the raw transaction bytes from the database.
|
||||||
|
var txBytes []byte
|
||||||
|
err = s.cfg.DB.View(func(dbTx database.Tx) error {
|
||||||
|
var err error
|
||||||
|
txBytes, err = dbTx.FetchBlockRegion(blockRegion)
|
||||||
|
return err
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return "", "", rpcNoTxInfoError(txHash)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deserialize the transaction
|
||||||
|
var msgTx wire.MsgTx
|
||||||
|
err = msgTx.Deserialize(bytes.NewReader(txBytes))
|
||||||
|
if err != nil {
|
||||||
|
context := "Failed to deserialize transaction"
|
||||||
|
return "", "", internalRPCError(err.Error(), context)
|
||||||
|
}
|
||||||
|
|
||||||
|
txo := msgTx.TxOut[outpoint.Index]
|
||||||
|
cs, err := txscript.DecodeClaimScript(txo.PkScript)
|
||||||
|
if err != nil {
|
||||||
|
context := "Failed to decode the claim script"
|
||||||
|
return "", "", internalRPCError(err.Error(), context)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, addresses, _, _ := txscript.ExtractPkScriptAddrs(txo.PkScript[cs.Size():], s.cfg.ChainParams)
|
||||||
|
return addresses[0].EncodeAddress(), hex.EncodeToString(cs.Value()), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func handleGetNormalized(_ *rpcServer, cmd interface{}, _ <-chan struct{}) (interface{}, error) {
|
||||||
|
c := cmd.(*btcjson.GetNormalizedCmd)
|
||||||
|
r := btcjson.GetNormalizedResult{
|
||||||
|
NormalizedName: string(node.Normalize([]byte(c.Name))),
|
||||||
|
}
|
||||||
|
return r, nil
|
||||||
|
}
|
|
@ -4688,5 +4688,8 @@ func (s *rpcServer) handleBlockchainNotification(notification *blockchain.Notifi
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
rpcHandlers = rpcHandlersBeforeInit
|
rpcHandlers = rpcHandlersBeforeInit
|
||||||
|
for key := range claimtrieHandlers {
|
||||||
|
rpcHandlers[key] = claimtrieHandlers[key]
|
||||||
|
}
|
||||||
rand.Seed(time.Now().UnixNano())
|
rand.Seed(time.Now().UnixNano())
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue