2018-06-28 07:14:30 +02:00
|
|
|
package claimtrie
|
|
|
|
|
|
|
|
import (
|
|
|
|
"crypto/sha256"
|
|
|
|
"encoding/binary"
|
|
|
|
"encoding/json"
|
|
|
|
"strconv"
|
|
|
|
|
|
|
|
"github.com/btcsuite/btcd/chaincfg/chainhash"
|
|
|
|
"github.com/btcsuite/btcd/wire"
|
|
|
|
)
|
|
|
|
|
|
|
|
type node struct {
|
|
|
|
tookover Height
|
2018-07-04 02:44:19 +02:00
|
|
|
bestClaim *claim
|
2018-06-28 07:14:30 +02:00
|
|
|
|
2018-07-04 02:44:19 +02:00
|
|
|
claims map[string]*claim
|
|
|
|
supports map[string]*support
|
2018-06-28 07:14:30 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
func newNode() *node {
|
|
|
|
return &node{
|
2018-07-04 02:44:19 +02:00
|
|
|
claims: map[string]*claim{},
|
|
|
|
supports: map[string]*support{},
|
2018-06-28 07:14:30 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-07-04 02:44:19 +02:00
|
|
|
func (n *node) addClaim(c *claim) error {
|
2018-06-28 07:14:30 +02:00
|
|
|
if _, ok := n.claims[c.op.String()]; ok {
|
|
|
|
return ErrDuplicate
|
|
|
|
}
|
2018-07-04 02:44:19 +02:00
|
|
|
c.activeAt = calActiveHeight(c.accepted, c.accepted, n.tookover)
|
2018-06-28 07:14:30 +02:00
|
|
|
n.claims[c.op.String()] = c
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (n *node) removeClaim(op wire.OutPoint) error {
|
|
|
|
c, ok := n.claims[op.String()]
|
|
|
|
if !ok {
|
|
|
|
return ErrNotFound
|
|
|
|
}
|
|
|
|
delete(n.claims, op.String())
|
|
|
|
if n.bestClaim == c {
|
|
|
|
n.bestClaim = nil
|
|
|
|
}
|
|
|
|
for _, v := range n.supports {
|
|
|
|
if c.id == v.supportedID {
|
|
|
|
v.supportedClaim = nil
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2018-07-04 02:44:19 +02:00
|
|
|
func (n *node) addSupport(s *support) error {
|
2018-06-28 07:14:30 +02:00
|
|
|
if _, ok := n.supports[s.op.String()]; ok {
|
|
|
|
return ErrDuplicate
|
|
|
|
}
|
|
|
|
for _, v := range n.claims {
|
|
|
|
if v.id == s.supportedID {
|
2018-07-04 02:44:19 +02:00
|
|
|
s.activeAt = calActiveHeight(s.accepted, s.accepted, n.tookover)
|
2018-06-28 07:14:30 +02:00
|
|
|
s.supportedClaim = v
|
|
|
|
n.supports[s.op.String()] = s
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return ErrNotFound
|
|
|
|
}
|
|
|
|
|
|
|
|
func (n *node) removeSupport(op wire.OutPoint) error {
|
|
|
|
if _, ok := n.supports[op.String()]; !ok {
|
|
|
|
return ErrNotFound
|
|
|
|
}
|
|
|
|
delete(n.supports, op.String())
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// Hash calculates the Hash value based on the OutPoint and at which height it tookover.
|
|
|
|
func (n *node) Hash() chainhash.Hash {
|
|
|
|
return calNodeHash(n.bestClaim.op, n.tookover)
|
|
|
|
}
|
|
|
|
|
|
|
|
// MarshalJSON customizes JSON marshaling of the Node.
|
|
|
|
func (n *node) MarshalJSON() ([]byte, error) {
|
2018-07-04 02:44:19 +02:00
|
|
|
c := make([]*claim, 0, len(n.claims))
|
2018-06-28 07:14:30 +02:00
|
|
|
for _, v := range n.claims {
|
|
|
|
c = append(c, v)
|
|
|
|
}
|
2018-07-04 02:44:19 +02:00
|
|
|
s := make([]*support, 0, len(n.supports))
|
2018-06-28 07:14:30 +02:00
|
|
|
for _, v := range n.supports {
|
|
|
|
s = append(s, v)
|
|
|
|
}
|
|
|
|
return json.Marshal(&struct {
|
|
|
|
Hash string
|
|
|
|
Tookover Height
|
2018-07-04 02:44:19 +02:00
|
|
|
BestClaim *claim
|
|
|
|
Claims []*claim
|
|
|
|
Supports []*support
|
2018-06-28 07:14:30 +02:00
|
|
|
}{
|
|
|
|
Hash: n.Hash().String(),
|
|
|
|
Tookover: n.tookover,
|
|
|
|
BestClaim: n.bestClaim,
|
|
|
|
Claims: c,
|
|
|
|
Supports: s,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
// String implements Stringer interface.
|
|
|
|
func (n *node) String() string {
|
|
|
|
b, err := json.MarshalIndent(n, "", " ")
|
|
|
|
if err != nil {
|
|
|
|
panic("can't marshal Node")
|
|
|
|
}
|
|
|
|
return string(b)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (n *node) updateEffectiveAmounts(curr Height) {
|
|
|
|
for _, v := range n.claims {
|
|
|
|
v.effAmt = v.amt
|
|
|
|
}
|
|
|
|
for _, v := range n.supports {
|
2018-07-04 02:44:19 +02:00
|
|
|
if v.supportedClaim == n.bestClaim || v.activeAt <= curr {
|
2018-06-28 07:14:30 +02:00
|
|
|
v.supportedClaim.effAmt += v.amt
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (n *node) updateBestClaim(curr Height) {
|
2018-07-04 02:44:19 +02:00
|
|
|
findCandiadte := func() *claim {
|
2018-06-28 07:14:30 +02:00
|
|
|
candidate := n.bestClaim
|
|
|
|
for _, v := range n.claims {
|
2018-07-04 02:44:19 +02:00
|
|
|
if v.activeAt > curr {
|
|
|
|
// Accepted claim, but noy activated yet.
|
2018-06-28 07:14:30 +02:00
|
|
|
continue
|
|
|
|
}
|
|
|
|
if candidate == nil || v.effAmt > candidate.effAmt {
|
|
|
|
candidate = v
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return candidate
|
|
|
|
}
|
2018-07-04 02:44:19 +02:00
|
|
|
takeover := func(candidate *claim) {
|
|
|
|
n.bestClaim = candidate
|
|
|
|
n.tookover = curr
|
|
|
|
for _, v := range n.claims {
|
|
|
|
v.activeAt = calActiveHeight(v.accepted, curr, curr)
|
|
|
|
}
|
|
|
|
}
|
2018-06-28 07:14:30 +02:00
|
|
|
for {
|
|
|
|
n.updateEffectiveAmounts(curr)
|
|
|
|
candidate := findCandiadte()
|
2018-07-04 02:44:19 +02:00
|
|
|
if n.bestClaim == nil {
|
|
|
|
takeover(candidate)
|
2018-06-28 07:14:30 +02:00
|
|
|
return
|
2018-07-04 02:44:19 +02:00
|
|
|
|
2018-06-28 07:14:30 +02:00
|
|
|
}
|
2018-07-04 02:44:19 +02:00
|
|
|
if n.bestClaim == candidate {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
takeover(candidate)
|
2018-06-28 07:14:30 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (n *node) clone() *node {
|
|
|
|
clone := newNode()
|
|
|
|
|
|
|
|
// shallow copy of value fields.
|
|
|
|
*clone = *n
|
|
|
|
|
|
|
|
// deep copy of reference and pointer fields.
|
2018-07-04 02:44:19 +02:00
|
|
|
clone.claims = map[string]*claim{}
|
2018-06-28 07:14:30 +02:00
|
|
|
for k, v := range n.claims {
|
|
|
|
clone.claims[k] = v
|
|
|
|
}
|
2018-07-04 02:44:19 +02:00
|
|
|
clone.supports = map[string]*support{}
|
2018-06-28 07:14:30 +02:00
|
|
|
for k, v := range n.supports {
|
|
|
|
clone.supports[k] = v
|
|
|
|
}
|
|
|
|
return clone
|
|
|
|
}
|
|
|
|
|
|
|
|
func calNodeHash(op wire.OutPoint, tookover Height) chainhash.Hash {
|
|
|
|
txHash := chainhash.DoubleHashH(op.Hash[:])
|
|
|
|
|
|
|
|
nOut := []byte(strconv.Itoa(int(op.Index)))
|
|
|
|
nOutHash := chainhash.DoubleHashH(nOut)
|
|
|
|
|
|
|
|
buf := make([]byte, 8)
|
|
|
|
binary.BigEndian.PutUint64(buf, uint64(tookover))
|
|
|
|
heightHash := chainhash.DoubleHashH(buf)
|
|
|
|
|
|
|
|
h := make([]byte, 0, sha256.Size*3)
|
|
|
|
h = append(h, txHash[:]...)
|
|
|
|
h = append(h, nOutHash[:]...)
|
|
|
|
h = append(h, heightHash[:]...)
|
|
|
|
|
|
|
|
return chainhash.DoubleHashH(h)
|
|
|
|
}
|