fix Bidding logics, and port some tests cases from lbrycrd
This commit is contained in:
parent
84c64c1018
commit
fa2e276f3d
10 changed files with 671 additions and 266 deletions
|
@ -72,4 +72,4 @@ Our PGP key is [here](https://keybase.io/lbry/key.asc) if you need it.
|
|||
|
||||
## Contact
|
||||
|
||||
The primary contact for this project is [@roylee17](https://github.com/roylee) (roylee@lbry.io)
|
||||
The primary contact for this project is [@roylee17](https://github.com/roylee17) (roylee@lbry.io)
|
75
claim.go
75
claim.go
|
@ -1,23 +1,17 @@
|
|||
package claimtrie
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
"github.com/btcsuite/btcd/wire"
|
||||
)
|
||||
|
||||
// newClaim ...
|
||||
func newClaim(op wire.OutPoint, amt Amount, accepted Height) *claim {
|
||||
return &claim{
|
||||
op: op,
|
||||
id: NewClaimID(op),
|
||||
amt: amt,
|
||||
accepted: accepted,
|
||||
}
|
||||
}
|
||||
var dbg bool
|
||||
|
||||
type claim struct {
|
||||
// Claim ...
|
||||
type Claim struct {
|
||||
op wire.OutPoint
|
||||
id ClaimID
|
||||
amt Amount
|
||||
|
@ -26,8 +20,31 @@ type claim struct {
|
|||
activeAt Height
|
||||
}
|
||||
|
||||
func (c *Claim) String() string {
|
||||
if dbg {
|
||||
w := bytes.NewBuffer(nil)
|
||||
fmt.Fprintf(w, "C%-3d amt: %2d, effamt: %v, accepted: %2d, active: %2d, id: %s", c.op.Index, c.amt, c.effAmt, c.accepted, c.activeAt, c.id)
|
||||
return w.String()
|
||||
}
|
||||
b, err := json.MarshalIndent(c, "", " ")
|
||||
if err != nil {
|
||||
fmt.Printf("can't marshal, err :%s", err)
|
||||
}
|
||||
return string(b)
|
||||
}
|
||||
|
||||
// Support ...
|
||||
type Support struct {
|
||||
op wire.OutPoint
|
||||
amt Amount
|
||||
accepted Height
|
||||
activeAt Height
|
||||
|
||||
supportedID ClaimID
|
||||
}
|
||||
|
||||
// MarshalJSON customizes the representation of JSON.
|
||||
func (c *claim) MarshalJSON() ([]byte, error) {
|
||||
func (c *Claim) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(&struct {
|
||||
OutPoint string
|
||||
ClaimID string
|
||||
|
@ -45,35 +62,8 @@ func (c *claim) MarshalJSON() ([]byte, error) {
|
|||
})
|
||||
}
|
||||
|
||||
func (c *claim) String() string {
|
||||
b, err := json.MarshalIndent(c, "", " ")
|
||||
if err != nil {
|
||||
fmt.Printf("can't marshal, err :%s", err)
|
||||
}
|
||||
return string(b)
|
||||
}
|
||||
|
||||
func newSupport(op wire.OutPoint, amt Amount, accepted Height, supported ClaimID) *support {
|
||||
return &support{
|
||||
op: op,
|
||||
amt: amt,
|
||||
accepted: accepted,
|
||||
supportedID: supported,
|
||||
}
|
||||
}
|
||||
|
||||
type support struct {
|
||||
op wire.OutPoint
|
||||
amt Amount
|
||||
accepted Height
|
||||
activeAt Height
|
||||
|
||||
supportedID ClaimID
|
||||
supportedClaim *claim
|
||||
}
|
||||
|
||||
// MarshalJSON ...
|
||||
func (s *support) MarshalJSON() ([]byte, error) {
|
||||
func (s *Support) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(&struct {
|
||||
OutPoint string
|
||||
SupportedClaimID string
|
||||
|
@ -89,7 +79,12 @@ func (s *support) MarshalJSON() ([]byte, error) {
|
|||
})
|
||||
}
|
||||
|
||||
func (s *support) String() string {
|
||||
func (s *Support) String() string {
|
||||
if dbg {
|
||||
w := bytes.NewBuffer(nil)
|
||||
fmt.Fprintf(w, "S%-3d amt: %2d, accepted: %2d, active: %2d, id: %s", s.op.Index, s.amt, s.accepted, s.activeAt, s.supportedID)
|
||||
return w.String()
|
||||
}
|
||||
b, err := json.MarshalIndent(s, "", " ")
|
||||
if err != nil {
|
||||
fmt.Printf("can't marshal, err :%s", err)
|
||||
|
|
|
@ -34,12 +34,3 @@ type ClaimID [20]byte
|
|||
func (id ClaimID) String() string {
|
||||
return hex.EncodeToString(id[:])
|
||||
}
|
||||
|
||||
func calActiveHeight(accepted, curr, tookover Height) Height {
|
||||
factor := Height(32)
|
||||
delay := (curr - tookover) / factor
|
||||
if delay > 4032 {
|
||||
delay = 4032
|
||||
}
|
||||
return accepted + delay
|
||||
}
|
||||
|
|
96
claimtrie.go
96
claimtrie.go
|
@ -1,18 +1,14 @@
|
|||
package claimtrie
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/btcsuite/btcd/chaincfg/chainhash"
|
||||
"github.com/btcsuite/btcd/wire"
|
||||
|
||||
"github.com/lbryio/merkletrie"
|
||||
)
|
||||
|
||||
// Height ...
|
||||
type Height int64
|
||||
|
||||
// Amount ...
|
||||
type Amount int64
|
||||
|
||||
// ClaimTrie implements a Merkle Trie supporting linear history of commits.
|
||||
type ClaimTrie struct {
|
||||
|
||||
|
@ -24,6 +20,9 @@ type ClaimTrie struct {
|
|||
|
||||
// An overlay supporting Copy-on-Write to the current tip commit.
|
||||
stg *merkletrie.Stage
|
||||
|
||||
// pending keeps track update for future block height.
|
||||
pending map[Height][]string
|
||||
}
|
||||
|
||||
// CommitMeta implements merkletrie.CommitMeta with commit-specific metadata.
|
||||
|
@ -35,21 +34,22 @@ type CommitMeta struct {
|
|||
func New() *ClaimTrie {
|
||||
mt := merkletrie.New()
|
||||
return &ClaimTrie{
|
||||
head: merkletrie.NewCommit(nil, CommitMeta{0}, mt),
|
||||
stg: merkletrie.NewStage(mt),
|
||||
head: merkletrie.NewCommit(nil, CommitMeta{0}, mt),
|
||||
stg: merkletrie.NewStage(mt),
|
||||
pending: map[Height][]string{},
|
||||
}
|
||||
}
|
||||
|
||||
func updateStageNode(stg *merkletrie.Stage, name string, modifier func(n *node) error) error {
|
||||
func updateStageNode(stg *merkletrie.Stage, name string, modifier func(n *Node) error) error {
|
||||
v, err := stg.Get(merkletrie.Key(name))
|
||||
if err != nil && err != merkletrie.ErrKeyNotFound {
|
||||
return err
|
||||
}
|
||||
var n *node
|
||||
var n *Node
|
||||
if v == nil {
|
||||
n = newNode()
|
||||
n = NewNode()
|
||||
} else {
|
||||
n = v.(*node).clone()
|
||||
n = v.(*Node)
|
||||
}
|
||||
if err = modifier(n); err != nil {
|
||||
return err
|
||||
|
@ -59,51 +59,50 @@ func updateStageNode(stg *merkletrie.Stage, name string, modifier func(n *node)
|
|||
|
||||
// AddClaim adds a Claim to the Stage of ClaimTrie.
|
||||
func (ct *ClaimTrie) AddClaim(name string, op wire.OutPoint, amt Amount, accepted Height) error {
|
||||
return updateStageNode(ct.stg, name, func(n *node) error {
|
||||
return n.addClaim(newClaim(op, amt, accepted))
|
||||
return updateStageNode(ct.stg, name, func(n *Node) error {
|
||||
n.IncrementBlock(ct.bestBlock - n.height)
|
||||
_, err := n.addClaim(op, amt)
|
||||
next := ct.bestBlock + 1
|
||||
ct.pending[next] = append(ct.pending[next], name)
|
||||
return err
|
||||
})
|
||||
}
|
||||
|
||||
// AddSupport adds a Support to the Stage of ClaimTrie.
|
||||
func (ct *ClaimTrie) AddSupport(name string, op wire.OutPoint, amt Amount, accepted Height, supported ClaimID) error {
|
||||
return updateStageNode(ct.stg, name, func(n *node) error {
|
||||
return n.addSupport(newSupport(op, amt, accepted, supported))
|
||||
return updateStageNode(ct.stg, name, func(n *Node) error {
|
||||
_, err := n.addSupport(op, amt, supported)
|
||||
next := ct.bestBlock + 1
|
||||
ct.pending[next] = append(ct.pending[next], name)
|
||||
return err
|
||||
})
|
||||
}
|
||||
|
||||
// SpendClaim removes a Claim in the Stage.
|
||||
func (ct *ClaimTrie) SpendClaim(name string, op wire.OutPoint) error {
|
||||
return updateStageNode(ct.stg, name, func(n *node) error {
|
||||
return updateStageNode(ct.stg, name, func(n *Node) error {
|
||||
next := ct.bestBlock + 1
|
||||
ct.pending[next] = append(ct.pending[next], name)
|
||||
return n.removeClaim(op)
|
||||
})
|
||||
}
|
||||
|
||||
// SpendSupport removes a Support in the Stage.
|
||||
func (ct *ClaimTrie) SpendSupport(name string, op wire.OutPoint) error {
|
||||
return updateStageNode(ct.stg, name, func(n *node) error {
|
||||
return updateStageNode(ct.stg, name, func(n *Node) error {
|
||||
next := ct.bestBlock + 1
|
||||
ct.pending[next] = append(ct.pending[next], name)
|
||||
return n.removeSupport(op)
|
||||
})
|
||||
}
|
||||
|
||||
// Traverse visits Nodes in the Stage of the ClaimTrie.
|
||||
func (ct *ClaimTrie) Traverse(visit merkletrie.Visit, update, valueOnly bool) error {
|
||||
// wrapper function to make sure the node is updated before it's observed externally.
|
||||
fn := func(prefix merkletrie.Key, v merkletrie.Value) error {
|
||||
if v != nil {
|
||||
v.(*node).updateBestClaim(ct.bestBlock)
|
||||
}
|
||||
return visit(prefix, v)
|
||||
}
|
||||
return ct.stg.Traverse(fn, update, valueOnly)
|
||||
return ct.stg.Traverse(visit, update, valueOnly)
|
||||
}
|
||||
|
||||
// MerkleHash returns the Merkle Hash of the Stage.
|
||||
func (ct *ClaimTrie) MerkleHash() chainhash.Hash {
|
||||
visit := func(prefix merkletrie.Key, v merkletrie.Value) error {
|
||||
v.(*node).updateBestClaim(ct.bestBlock)
|
||||
return nil
|
||||
}
|
||||
ct.Traverse(visit, true, true)
|
||||
return ct.stg.MerkleHash()
|
||||
}
|
||||
|
||||
|
@ -118,12 +117,30 @@ func (ct *ClaimTrie) Commit(h Height) error {
|
|||
if h <= ct.bestBlock {
|
||||
return ErrInvalidHeight
|
||||
}
|
||||
visit := func(prefix merkletrie.Key, v merkletrie.Value) error {
|
||||
v.(*node).updateBestClaim(h)
|
||||
return nil
|
||||
}
|
||||
ct.Traverse(visit, true, true)
|
||||
|
||||
for i := ct.bestBlock + 1; i <= h; i++ {
|
||||
for _, prefix := range ct.pending[i] {
|
||||
// Brings the value node to date.
|
||||
catchup := func(n *Node) error {
|
||||
if err := n.IncrementBlock(i - n.height); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// After the update, the node may subscribe to another pending update.
|
||||
if next := n.findNextUpdateHeights(); next > i {
|
||||
fmt.Printf("Subscribe pendings for %v to future Height at %d\n", prefix, next)
|
||||
ct.pending[next] = append(ct.pending[next], prefix)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Update the node with the catchup modifier, and clear the Merkle Hash along the way.
|
||||
if err := updateStageNode(ct.stg, prefix, catchup); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
delete(ct.pending, i)
|
||||
}
|
||||
commit, err := ct.stg.Commit(ct.head, CommitMeta{Height: h})
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -135,6 +152,13 @@ func (ct *ClaimTrie) Commit(h Height) error {
|
|||
|
||||
// Reset reverts the Stage to a specified commit by height.
|
||||
func (ct *ClaimTrie) Reset(h Height) error {
|
||||
visit := func(prefix merkletrie.Key, value merkletrie.Value) error {
|
||||
n := value.(*Node)
|
||||
return n.DecrementBlock(n.height - h)
|
||||
}
|
||||
if err := ct.stg.Traverse(visit, true, true); err != nil {
|
||||
return err
|
||||
}
|
||||
for commit := ct.head; commit != nil; commit = commit.Prev {
|
||||
meta := commit.Meta.(CommitMeta)
|
||||
if meta.Height <= h {
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
package claimtrie
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/btcsuite/btcd/chaincfg/chainhash"
|
||||
)
|
||||
|
||||
// pending ...
|
||||
func TestClaimTrie_Commit(t *testing.T) {
|
||||
ct := New()
|
||||
|
||||
|
@ -33,7 +33,7 @@ func TestClaimTrie_Commit(t *testing.T) {
|
|||
ct.AddClaim("HELLO", *newOutPoint(0), tt.amt, tt.curr)
|
||||
}
|
||||
ct.Commit(tt.curr)
|
||||
fmt.Printf("ct.Merkle[%2d]: %s, amt: %d\n", ct.BestBlock(), ct.MerkleHash(), tt.amt)
|
||||
// fmt.Printf("ct.Merkle[%2d]: %s, amt: %d\n", ct.BestBlock(), ct.MerkleHash(), tt.amt)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -128,6 +128,9 @@ func newOutPoint(s string) (*wire.OutPoint, error) {
|
|||
return wire.NewOutPoint(&h, uint32(h[0])), nil
|
||||
}
|
||||
fields := strings.Split(s, ":")
|
||||
if len(fields) != 2 {
|
||||
return nil, fmt.Errorf("invalid outpoint format (HASH:INDEX)")
|
||||
}
|
||||
h, err := chainhash.NewHashFromStr(fields[0])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -217,11 +220,13 @@ func cmdShow(c *cli.Context) error {
|
|||
}
|
||||
height := claimtrie.Height(c.Int64("height"))
|
||||
if !c.IsSet("height") {
|
||||
fmt.Printf("<BestBlock: %d>\n", ct.BestBlock())
|
||||
fmt.Printf("<ClaimTrie Height %d>\n", ct.BestBlock())
|
||||
return ct.Traverse(dump, false, !c.Bool("all"))
|
||||
}
|
||||
fmt.Printf("NOTE: peeking to the past is broken for now. Try RESET command instead\n")
|
||||
for commit := ct.Head(); commit != nil; commit = commit.Prev {
|
||||
meta := commit.Meta.(claimtrie.CommitMeta)
|
||||
fmt.Printf("HEAD: %d/%d\n", height, meta.Height)
|
||||
if height == meta.Height {
|
||||
return commit.MerkleTrie.Traverse(dump, false, !c.Bool("all"))
|
||||
}
|
||||
|
@ -251,7 +256,7 @@ func cmdReset(c *cli.Context) error {
|
|||
func cmdLog(c *cli.Context) error {
|
||||
commitVisit := func(c *merkletrie.Commit) {
|
||||
meta := c.Meta.(claimtrie.CommitMeta)
|
||||
fmt.Printf("height: %d, commit %s\n\n", meta.Height, c.MerkleTrie.MerkleHash())
|
||||
fmt.Printf("height: %d, commit %s\n", meta.Height, c.MerkleTrie.MerkleHash())
|
||||
}
|
||||
|
||||
fmt.Printf("\n")
|
||||
|
|
72
memento.go
Normal file
72
memento.go
Normal file
|
@ -0,0 +1,72 @@
|
|||
package claimtrie
|
||||
|
||||
type cmdAddClaim struct {
|
||||
node *Node
|
||||
claim *Claim
|
||||
}
|
||||
|
||||
func (c cmdAddClaim) Execute() { c.node.claims[c.claim.op] = c.claim }
|
||||
func (c cmdAddClaim) Undo() { delete(c.node.claims, c.claim.op) }
|
||||
|
||||
type cmdRemoveClaim struct {
|
||||
node *Node
|
||||
claim *Claim
|
||||
}
|
||||
|
||||
func (c cmdRemoveClaim) Execute() { delete(c.node.claims, c.claim.op) }
|
||||
func (c cmdRemoveClaim) Undo() { c.node.claims[c.claim.op] = c.claim }
|
||||
|
||||
type cmdAddSupport struct {
|
||||
node *Node
|
||||
support *Support
|
||||
}
|
||||
|
||||
func (c cmdAddSupport) Execute() { c.node.supports[c.support.op] = c.support }
|
||||
func (c cmdAddSupport) Undo() { delete(c.node.supports, c.support.op) }
|
||||
|
||||
type cmdRemoveSupport struct {
|
||||
node *Node
|
||||
support *Support
|
||||
}
|
||||
|
||||
func (c cmdRemoveSupport) Execute() { delete(c.node.supports, c.support.op) }
|
||||
func (c cmdRemoveSupport) Undo() { c.node.supports[c.support.op] = c.support }
|
||||
|
||||
type cmdUpdateClaimActiveHeight struct {
|
||||
claim *Claim
|
||||
old Height
|
||||
new Height
|
||||
}
|
||||
|
||||
func (c cmdUpdateClaimActiveHeight) Execute() { c.claim.activeAt = c.new }
|
||||
func (c cmdUpdateClaimActiveHeight) Undo() { c.claim.activeAt = c.old }
|
||||
|
||||
type cmdUpdateSupportActiveHeight struct {
|
||||
support *Support
|
||||
old Height
|
||||
new Height
|
||||
}
|
||||
|
||||
func (c cmdUpdateSupportActiveHeight) Execute() { c.support.activeAt = c.new }
|
||||
func (c cmdUpdateSupportActiveHeight) Undo() { c.support.activeAt = c.old }
|
||||
|
||||
type updateNodeBestClaim struct {
|
||||
node *Node
|
||||
height Height
|
||||
old *Claim
|
||||
new *Claim
|
||||
}
|
||||
|
||||
func (c updateNodeBestClaim) Execute() {
|
||||
c.node.bestClaims[c.height] = c.new
|
||||
if c.node.bestClaims[c.height] == nil {
|
||||
delete(c.node.bestClaims, c.height)
|
||||
}
|
||||
}
|
||||
|
||||
func (c updateNodeBestClaim) Undo() {
|
||||
c.node.bestClaims[c.height] = c.old
|
||||
if c.node.bestClaims[c.height] == nil {
|
||||
delete(c.node.bestClaims, c.height)
|
||||
}
|
||||
}
|
36
memento/memento.go
Normal file
36
memento/memento.go
Normal file
|
@ -0,0 +1,36 @@
|
|||
package memento
|
||||
|
||||
// Command ...
|
||||
type Command interface {
|
||||
Execute()
|
||||
Undo()
|
||||
}
|
||||
|
||||
type commands []Command
|
||||
|
||||
// Memento ...
|
||||
type Memento struct {
|
||||
stack []commands
|
||||
cmds commands
|
||||
}
|
||||
|
||||
// Execute ...
|
||||
func (m *Memento) Execute(cmd Command) {
|
||||
m.cmds = append(m.cmds, cmd)
|
||||
cmd.Execute()
|
||||
}
|
||||
|
||||
// Commit ...
|
||||
func (m *Memento) Commit() {
|
||||
m.stack = append(m.stack, m.cmds)
|
||||
m.cmds = nil
|
||||
}
|
||||
|
||||
// Rollback ...
|
||||
func (m *Memento) Rollback() {
|
||||
cmds := m.stack[len(m.stack)-1]
|
||||
m.stack = m.stack[:len(m.stack)-1]
|
||||
for i := len(cmds) - 1; i >= 0; i-- {
|
||||
cmds[i].Undo()
|
||||
}
|
||||
}
|
357
node.go
357
node.go
|
@ -1,112 +1,274 @@
|
|||
package claimtrie
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/sha256"
|
||||
"encoding/binary"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"math"
|
||||
"strconv"
|
||||
|
||||
"github.com/btcsuite/btcd/chaincfg/chainhash"
|
||||
"github.com/btcsuite/btcd/wire"
|
||||
"github.com/lbryio/claimtrie/memento"
|
||||
)
|
||||
|
||||
type node struct {
|
||||
tookover Height
|
||||
bestClaim *claim
|
||||
// Amount ...
|
||||
type Amount int64
|
||||
|
||||
claims map[string]*claim
|
||||
supports map[string]*support
|
||||
// Height ...
|
||||
type Height int64
|
||||
|
||||
// Node ...
|
||||
type Node struct {
|
||||
memento.Memento
|
||||
|
||||
height Height
|
||||
bestClaims map[Height]*Claim
|
||||
claims map[wire.OutPoint]*Claim
|
||||
supports map[wire.OutPoint]*Support
|
||||
}
|
||||
|
||||
func newNode() *node {
|
||||
return &node{
|
||||
claims: map[string]*claim{},
|
||||
supports: map[string]*support{},
|
||||
// NewNode ...
|
||||
func NewNode() *Node {
|
||||
return &Node{
|
||||
Memento: memento.Memento{},
|
||||
bestClaims: map[Height]*Claim{0: nil},
|
||||
claims: map[wire.OutPoint]*Claim{},
|
||||
supports: map[wire.OutPoint]*Support{},
|
||||
}
|
||||
}
|
||||
|
||||
func (n *node) addClaim(c *claim) error {
|
||||
if _, ok := n.claims[c.op.String()]; ok {
|
||||
return ErrDuplicate
|
||||
// BestClaim ...
|
||||
func (n *Node) BestClaim() *Claim {
|
||||
var latest Height
|
||||
for k := range n.bestClaims {
|
||||
if k > latest {
|
||||
latest = k
|
||||
}
|
||||
}
|
||||
return n.bestClaims[latest]
|
||||
}
|
||||
|
||||
// Tookover ...
|
||||
func (n *Node) Tookover() Height {
|
||||
var latest Height
|
||||
for k := range n.bestClaims {
|
||||
if k > latest {
|
||||
latest = k
|
||||
}
|
||||
}
|
||||
return latest
|
||||
}
|
||||
|
||||
// IncrementBlock ...
|
||||
func (n *Node) IncrementBlock(h Height) error {
|
||||
if h < 0 {
|
||||
return ErrInvalidHeight
|
||||
}
|
||||
for i := Height(0); i < h; i++ {
|
||||
n.height++
|
||||
n.processBlock()
|
||||
n.Commit()
|
||||
}
|
||||
c.activeAt = calActiveHeight(c.accepted, c.accepted, n.tookover)
|
||||
n.claims[c.op.String()] = c
|
||||
return nil
|
||||
}
|
||||
|
||||
func (n *node) removeClaim(op wire.OutPoint) error {
|
||||
c, ok := n.claims[op.String()]
|
||||
// DecrementBlock ...
|
||||
func (n *Node) DecrementBlock(h Height) error {
|
||||
if h < 0 {
|
||||
return ErrInvalidHeight
|
||||
}
|
||||
for i := Height(0); i < h; i++ {
|
||||
n.height--
|
||||
n.Rollback()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (n *Node) addClaim(op wire.OutPoint, amt Amount) (*Claim, error) {
|
||||
c := &Claim{
|
||||
op: op,
|
||||
id: NewClaimID(op),
|
||||
amt: amt,
|
||||
accepted: n.height + 1,
|
||||
activeAt: n.height + 1,
|
||||
}
|
||||
if n.BestClaim() != nil {
|
||||
c.activeAt = calActiveHeight(c.accepted, c.accepted, n.Tookover())
|
||||
}
|
||||
|
||||
n.Execute(cmdAddClaim{node: n, claim: c})
|
||||
return c, nil
|
||||
}
|
||||
|
||||
func (n *Node) removeClaim(op wire.OutPoint) error {
|
||||
c, ok := n.claims[op]
|
||||
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
|
||||
}
|
||||
n.Execute(cmdRemoveClaim{node: n, claim: c})
|
||||
|
||||
if n.BestClaim() != c {
|
||||
return nil
|
||||
}
|
||||
n.Execute(updateNodeBestClaim{node: n, height: n.Tookover(), old: c, new: nil})
|
||||
n.updateActiveHeights()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (n *node) addSupport(s *support) error {
|
||||
if _, ok := n.supports[s.op.String()]; ok {
|
||||
return ErrDuplicate
|
||||
func (n *Node) addSupport(op wire.OutPoint, amt Amount, supported ClaimID) (*Support, error) {
|
||||
s := &Support{
|
||||
op: op,
|
||||
amt: amt,
|
||||
supportedID: supported,
|
||||
accepted: n.height + 1,
|
||||
activeAt: n.height + 1,
|
||||
}
|
||||
for _, v := range n.claims {
|
||||
if v.id == s.supportedID {
|
||||
s.activeAt = calActiveHeight(s.accepted, s.accepted, n.tookover)
|
||||
s.supportedClaim = v
|
||||
n.supports[s.op.String()] = s
|
||||
return nil
|
||||
if n.BestClaim() == nil || n.BestClaim().op != op {
|
||||
s.activeAt = calActiveHeight(s.accepted, s.accepted, n.Tookover())
|
||||
}
|
||||
|
||||
for _, c := range n.claims {
|
||||
if c.id != supported {
|
||||
continue
|
||||
}
|
||||
n.Execute(cmdAddSupport{node: n, support: s})
|
||||
return s, nil
|
||||
}
|
||||
return ErrNotFound
|
||||
|
||||
// Is supporting an non-existing Claim aceepted?
|
||||
return nil, ErrNotFound
|
||||
}
|
||||
|
||||
func (n *node) removeSupport(op wire.OutPoint) error {
|
||||
if _, ok := n.supports[op.String()]; !ok {
|
||||
func (n *Node) removeSupport(op wire.OutPoint) error {
|
||||
s, ok := n.supports[op]
|
||||
if !ok {
|
||||
return ErrNotFound
|
||||
}
|
||||
delete(n.supports, op.String())
|
||||
n.Execute(cmdRemoveSupport{node: n, support: s})
|
||||
return nil
|
||||
}
|
||||
|
||||
func (n *Node) updateEffectiveAmounts() {
|
||||
for _, c := range n.claims {
|
||||
c.effAmt = c.amt
|
||||
if c.activeAt > n.height {
|
||||
c.effAmt = 0
|
||||
continue
|
||||
}
|
||||
for _, s := range n.supports {
|
||||
if s.activeAt > n.height || s.supportedID != c.id {
|
||||
continue
|
||||
}
|
||||
c.effAmt += s.amt
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (n *Node) updateActiveHeights() {
|
||||
for _, v := range n.claims {
|
||||
if old, new := v.activeAt, calActiveHeight(v.accepted, n.height, n.height); old != new {
|
||||
n.Execute(cmdUpdateClaimActiveHeight{claim: v, old: old, new: new})
|
||||
}
|
||||
}
|
||||
for _, v := range n.supports {
|
||||
if old, new := v.activeAt, calActiveHeight(v.accepted, n.height, n.height); old != new {
|
||||
n.Execute(cmdUpdateSupportActiveHeight{support: v, old: old, new: new})
|
||||
}
|
||||
}
|
||||
}
|
||||
func (n *Node) processBlock() {
|
||||
for {
|
||||
candidate := findCandiadte(n)
|
||||
if n.BestClaim() == candidate {
|
||||
return
|
||||
}
|
||||
n.Execute(updateNodeBestClaim{node: n, height: n.height, old: n.bestClaims[n.height], new: candidate})
|
||||
n.updateActiveHeights()
|
||||
}
|
||||
}
|
||||
|
||||
func (n *Node) findNextUpdateHeights() Height {
|
||||
next := Height(math.MaxInt64)
|
||||
for _, v := range n.claims {
|
||||
if v.activeAt > n.height && v.activeAt < next {
|
||||
next = v.activeAt
|
||||
}
|
||||
}
|
||||
for _, v := range n.supports {
|
||||
if v.activeAt > n.height && v.activeAt < next {
|
||||
next = v.activeAt
|
||||
}
|
||||
}
|
||||
if next == Height(math.MaxInt64) {
|
||||
return n.height
|
||||
}
|
||||
return next
|
||||
}
|
||||
|
||||
// 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)
|
||||
func (n *Node) Hash() chainhash.Hash {
|
||||
if n.BestClaim() == nil {
|
||||
return chainhash.Hash{}
|
||||
}
|
||||
return calNodeHash(n.BestClaim().op, n.Tookover())
|
||||
}
|
||||
|
||||
// MarshalJSON customizes JSON marshaling of the Node.
|
||||
func (n *node) MarshalJSON() ([]byte, error) {
|
||||
c := make([]*claim, 0, len(n.claims))
|
||||
func (n *Node) MarshalJSON() ([]byte, error) {
|
||||
c := make([]*Claim, 0, len(n.claims))
|
||||
for _, v := range n.claims {
|
||||
c = append(c, v)
|
||||
}
|
||||
s := make([]*support, 0, len(n.supports))
|
||||
s := make([]*Support, 0, len(n.supports))
|
||||
for _, v := range n.supports {
|
||||
s = append(s, v)
|
||||
}
|
||||
return json.Marshal(&struct {
|
||||
Height Height
|
||||
Hash string
|
||||
Tookover Height
|
||||
BestClaim *claim
|
||||
Claims []*claim
|
||||
Supports []*support
|
||||
BestClaim *Claim
|
||||
Claims []*Claim
|
||||
Supports []*Support
|
||||
}{
|
||||
Height: n.height,
|
||||
Hash: n.Hash().String(),
|
||||
Tookover: n.tookover,
|
||||
BestClaim: n.bestClaim,
|
||||
Tookover: n.Tookover(),
|
||||
BestClaim: n.BestClaim(),
|
||||
Claims: c,
|
||||
Supports: s,
|
||||
})
|
||||
}
|
||||
|
||||
// String implements Stringer interface.
|
||||
func (n *node) String() string {
|
||||
func (n *Node) String() string {
|
||||
if dbg {
|
||||
w := bytes.NewBuffer(nil)
|
||||
fmt.Fprintf(w, "H: %2d BestClaims: ", n.height)
|
||||
for k, v := range n.bestClaims {
|
||||
if v == nil {
|
||||
fmt.Fprintf(w, "{%d, nil}, ", k)
|
||||
continue
|
||||
}
|
||||
fmt.Fprintf(w, "{%d, %d}, ", k, v.op.Index)
|
||||
}
|
||||
fmt.Fprintf(w, "\n")
|
||||
for _, v := range n.claims {
|
||||
fmt.Fprintf(w, "\n %v", v)
|
||||
if v == n.BestClaim() {
|
||||
fmt.Fprintf(w, " <B> ")
|
||||
}
|
||||
}
|
||||
for _, v := range n.supports {
|
||||
fmt.Fprintf(w, "\n %v", v)
|
||||
}
|
||||
fmt.Fprintf(w, "\n")
|
||||
return w.String()
|
||||
|
||||
}
|
||||
b, err := json.MarshalIndent(n, "", " ")
|
||||
if err != nil {
|
||||
panic("can't marshal Node")
|
||||
|
@ -114,69 +276,36 @@ func (n *node) String() string {
|
|||
return string(b)
|
||||
}
|
||||
|
||||
func (n *node) updateEffectiveAmounts(curr Height) {
|
||||
// func (n *Node) clone() *Node {
|
||||
// clone := NewNode()
|
||||
|
||||
// // shallow copy of value fields.
|
||||
// *clone = *n
|
||||
|
||||
// // deep copy of reference and pointer fields.
|
||||
// clone.claims = map[wire.OutPoint]*Claim{}
|
||||
// for k, v := range n.claims {
|
||||
// clone.claims[k] = v
|
||||
// }
|
||||
// clone.supports = map[wire.OutPoint]*Support{}
|
||||
// for k, v := range n.supports {
|
||||
// clone.supports[k] = v
|
||||
// }
|
||||
// return clone
|
||||
// }
|
||||
|
||||
func findCandiadte(n *Node) *Claim {
|
||||
n.updateEffectiveAmounts()
|
||||
var candidate *Claim
|
||||
for _, v := range n.claims {
|
||||
v.effAmt = v.amt
|
||||
}
|
||||
for _, v := range n.supports {
|
||||
if v.supportedClaim == n.bestClaim || v.activeAt <= curr {
|
||||
v.supportedClaim.effAmt += v.amt
|
||||
if v.activeAt > n.height {
|
||||
continue
|
||||
}
|
||||
if candidate == nil || v.effAmt > candidate.effAmt {
|
||||
candidate = v
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (n *node) updateBestClaim(curr Height) {
|
||||
findCandiadte := func() *claim {
|
||||
candidate := n.bestClaim
|
||||
for _, v := range n.claims {
|
||||
if v.activeAt > curr {
|
||||
// Accepted claim, but noy activated yet.
|
||||
continue
|
||||
}
|
||||
if candidate == nil || v.effAmt > candidate.effAmt {
|
||||
candidate = v
|
||||
}
|
||||
}
|
||||
return candidate
|
||||
}
|
||||
takeover := func(candidate *claim) {
|
||||
n.bestClaim = candidate
|
||||
n.tookover = curr
|
||||
for _, v := range n.claims {
|
||||
v.activeAt = calActiveHeight(v.accepted, curr, curr)
|
||||
}
|
||||
}
|
||||
for {
|
||||
n.updateEffectiveAmounts(curr)
|
||||
candidate := findCandiadte()
|
||||
if n.bestClaim == nil {
|
||||
takeover(candidate)
|
||||
return
|
||||
|
||||
}
|
||||
if n.bestClaim == candidate {
|
||||
return
|
||||
}
|
||||
takeover(candidate)
|
||||
}
|
||||
}
|
||||
|
||||
func (n *node) clone() *node {
|
||||
clone := newNode()
|
||||
|
||||
// shallow copy of value fields.
|
||||
*clone = *n
|
||||
|
||||
// deep copy of reference and pointer fields.
|
||||
clone.claims = map[string]*claim{}
|
||||
for k, v := range n.claims {
|
||||
clone.claims[k] = v
|
||||
}
|
||||
clone.supports = map[string]*support{}
|
||||
for k, v := range n.supports {
|
||||
clone.supports[k] = v
|
||||
}
|
||||
return clone
|
||||
return candidate
|
||||
}
|
||||
|
||||
func calNodeHash(op wire.OutPoint, tookover Height) chainhash.Hash {
|
||||
|
@ -196,3 +325,13 @@ func calNodeHash(op wire.OutPoint, tookover Height) chainhash.Hash {
|
|||
|
||||
return chainhash.DoubleHashH(h)
|
||||
}
|
||||
|
||||
var proportionalDelayFactor = Height(32)
|
||||
|
||||
func calActiveHeight(accepted, curr, tookover Height) Height {
|
||||
delay := (curr - tookover) / proportionalDelayFactor
|
||||
if delay > 4032 {
|
||||
delay = 4032
|
||||
}
|
||||
return accepted + delay
|
||||
}
|
||||
|
|
277
node_test.go
277
node_test.go
|
@ -1,12 +1,13 @@
|
|||
package claimtrie
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/btcsuite/btcd/chaincfg/chainhash"
|
||||
"github.com/btcsuite/btcd/wire"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func newHash(s string) *chainhash.Hash {
|
||||
|
@ -15,11 +16,12 @@ func newHash(s string) *chainhash.Hash {
|
|||
}
|
||||
|
||||
func newOutPoint(idx int) *wire.OutPoint {
|
||||
var h chainhash.Hash
|
||||
if _, err := rand.Read(h[:]); err != nil {
|
||||
return nil
|
||||
}
|
||||
return wire.NewOutPoint(&h, uint32(idx))
|
||||
// var h chainhash.Hash
|
||||
// if _, err := rand.Read(h[:]); err != nil {
|
||||
// return nil
|
||||
// }
|
||||
// return wire.NewOutPoint(&h, uint32(idx))
|
||||
return wire.NewOutPoint(new(chainhash.Hash), uint32(idx))
|
||||
}
|
||||
|
||||
func Test_calNodeHash(t *testing.T) {
|
||||
|
@ -61,71 +63,212 @@ func Test_calNodeHash(t *testing.T) {
|
|||
})
|
||||
}
|
||||
}
|
||||
func Test_BestClaim(t *testing.T) {
|
||||
cA := newClaim(*newOutPoint(1), 0, 0)
|
||||
cB := newClaim(*newOutPoint(2), 0, 0)
|
||||
cC := newClaim(*newOutPoint(3), 0, 0)
|
||||
cD := newClaim(*newOutPoint(4), 0, 0)
|
||||
|
||||
s1 := newSupport(*newOutPoint(91), 0, 0, cA.id)
|
||||
var c1, c2, c3, c4, c5, c6, c7, c8, c9, c10 *Claim
|
||||
var s1, s2, s3, s4, s5, s6, s7, s8, s9, s10 *Support
|
||||
|
||||
var n *node
|
||||
type operation int
|
||||
const (
|
||||
opReset = 1 << iota
|
||||
opAddClaim
|
||||
opRemoveClaim
|
||||
opAddSupport
|
||||
opRemoveSupport
|
||||
opCheck
|
||||
)
|
||||
tests := []struct {
|
||||
name string
|
||||
op operation
|
||||
claim *claim
|
||||
support *support
|
||||
amount Amount
|
||||
curr Height
|
||||
want *claim
|
||||
}{
|
||||
{name: "0-0", op: opReset},
|
||||
{name: "0-1", op: opAddClaim, claim: cA, amount: 10, curr: 13, want: cA}, // A(10) is controlling
|
||||
{name: "0-2", op: opAddClaim, claim: cB, amount: 20, curr: 1001, want: cA}, // A(10) is controlling, B(20) is accepted. Act(B) = 1001 + (1001-13)/32 = 1031
|
||||
{name: "0-3", op: opAddSupport, claim: cA, support: s1, amount: 14, curr: 1010, want: cA}, // A(10+14) is controlling, B(20) is accepted.
|
||||
{name: "0-4", op: opAddClaim, claim: cC, amount: 50, curr: 1020, want: cA}, // A(10+14) is controlling, B(20) is accepted, C(50) is accepted. Act(C) = 1020 + (1020-13)/32 = 1051
|
||||
{name: "0-5", op: opCheck, curr: 1031, want: cA}, // A(10+14) is controlling, B(20) is active, C(50) is accepted.
|
||||
{name: "0-6", op: opAddClaim, claim: cD, amount: 300, curr: 1040, want: cA}, // A(10+14) is controlling, B(20) is active, C(50) is accepted, D(300) is accepted. Act(C) = 1040 + (1040-13)/32 = 1072
|
||||
{name: "0-7", op: opCheck, curr: 1051, want: cD}, // A(10+14) is active, B(20) is active, C(50) is active, D(300) is controlling.
|
||||
func Test_History1(t *testing.T) {
|
||||
|
||||
proportionalDelayFactor = 1
|
||||
n := NewNode()
|
||||
|
||||
// no competing bids
|
||||
test1 := func() {
|
||||
c1, _ = n.addClaim(*newOutPoint(1), 1)
|
||||
n.IncrementBlock(1)
|
||||
assert.Equal(t, c1, n.BestClaim())
|
||||
|
||||
n.DecrementBlock(1)
|
||||
assert.Nil(t, n.BestClaim())
|
||||
}
|
||||
|
||||
// there is a competing bid inserted same height
|
||||
test2 := func() {
|
||||
n.addClaim(*newOutPoint(2), 1)
|
||||
c3, _ = n.addClaim(*newOutPoint(3), 2)
|
||||
n.IncrementBlock(1)
|
||||
assert.Equal(t, c3, n.BestClaim())
|
||||
|
||||
n.DecrementBlock(1)
|
||||
assert.Nil(t, n.BestClaim())
|
||||
|
||||
}
|
||||
// make two claims , one older
|
||||
test3 := func() {
|
||||
c4, _ = n.addClaim(*newOutPoint(4), 1)
|
||||
n.IncrementBlock(1)
|
||||
assert.Equal(t, c4, n.BestClaim())
|
||||
n.addClaim(*newOutPoint(5), 1)
|
||||
n.IncrementBlock(1)
|
||||
assert.Equal(t, c4, n.BestClaim())
|
||||
n.IncrementBlock(1)
|
||||
assert.Equal(t, c4, n.BestClaim())
|
||||
n.DecrementBlock(1)
|
||||
assert.Equal(t, c4, n.BestClaim())
|
||||
n.DecrementBlock(1)
|
||||
|
||||
assert.Equal(t, c4, n.BestClaim())
|
||||
n.DecrementBlock(1)
|
||||
assert.Nil(t, n.BestClaim())
|
||||
}
|
||||
|
||||
// check claim takeover
|
||||
test4 := func() {
|
||||
c6, _ = n.addClaim(*newOutPoint(6), 1)
|
||||
n.IncrementBlock(10)
|
||||
assert.Equal(t, c6, n.BestClaim())
|
||||
|
||||
c7, _ = n.addClaim(*newOutPoint(7), 2)
|
||||
n.IncrementBlock(1)
|
||||
assert.Equal(t, c6, n.BestClaim())
|
||||
n.IncrementBlock(10)
|
||||
assert.Equal(t, c7, n.BestClaim())
|
||||
|
||||
n.DecrementBlock(10)
|
||||
assert.Equal(t, c6, n.BestClaim())
|
||||
n.DecrementBlock(10)
|
||||
assert.Equal(t, c6, n.BestClaim())
|
||||
n.DecrementBlock(1)
|
||||
assert.Nil(t, n.BestClaim())
|
||||
}
|
||||
|
||||
// spending winning claim will make losing active claim winner
|
||||
test5 := func() {
|
||||
c1, _ = n.addClaim(*newOutPoint(1), 2)
|
||||
c2, _ = n.addClaim(*newOutPoint(2), 1)
|
||||
n.IncrementBlock(1)
|
||||
assert.Equal(t, c1, n.BestClaim())
|
||||
n.removeClaim(c1.op)
|
||||
n.IncrementBlock(1)
|
||||
assert.Equal(t, c2, n.BestClaim())
|
||||
|
||||
n.DecrementBlock(1)
|
||||
assert.Equal(t, c1, n.BestClaim())
|
||||
n.DecrementBlock(1)
|
||||
assert.Nil(t, n.BestClaim())
|
||||
}
|
||||
|
||||
// spending winning claim will make inactive claim winner
|
||||
test6 := func() {
|
||||
c3, _ = n.addClaim(*newOutPoint(3), 2)
|
||||
n.IncrementBlock(10)
|
||||
assert.Equal(t, c3, n.BestClaim())
|
||||
|
||||
c4, _ = n.addClaim(*newOutPoint(4), 2)
|
||||
n.IncrementBlock(1)
|
||||
assert.Equal(t, c3, n.BestClaim())
|
||||
n.removeClaim(c3.op)
|
||||
n.IncrementBlock(1)
|
||||
assert.Equal(t, c4, n.BestClaim())
|
||||
|
||||
n.DecrementBlock(1)
|
||||
assert.Equal(t, c3, n.BestClaim())
|
||||
n.DecrementBlock(1)
|
||||
assert.Equal(t, c3, n.BestClaim())
|
||||
n.DecrementBlock(10)
|
||||
assert.Nil(t, n.BestClaim())
|
||||
}
|
||||
|
||||
// spending winning claim will empty out claim trie
|
||||
test7 := func() {
|
||||
c5, _ = n.addClaim(*newOutPoint(5), 2)
|
||||
n.IncrementBlock(1)
|
||||
assert.Equal(t, c5, n.BestClaim())
|
||||
n.removeClaim(c5.op)
|
||||
n.IncrementBlock(1)
|
||||
assert.NotEqual(t, c5, n.BestClaim())
|
||||
|
||||
n.DecrementBlock(1)
|
||||
assert.Equal(t, c5, n.BestClaim())
|
||||
n.DecrementBlock(1)
|
||||
assert.Nil(t, n.BestClaim())
|
||||
}
|
||||
|
||||
// check claim with more support wins
|
||||
test8 := func() {
|
||||
c1, _ = n.addClaim(*newOutPoint(1), 2)
|
||||
c2, _ = n.addClaim(*newOutPoint(2), 1)
|
||||
s1, _ = n.addSupport(*newOutPoint(11), 1, c1.id)
|
||||
s2, _ = n.addSupport(*newOutPoint(12), 10, c2.id)
|
||||
n.IncrementBlock(1)
|
||||
assert.Equal(t, c2, n.BestClaim())
|
||||
assert.Equal(t, Amount(11), n.BestClaim().effAmt)
|
||||
n.DecrementBlock(1)
|
||||
assert.Nil(t, n.BestClaim())
|
||||
}
|
||||
// check support delay
|
||||
test9 := func() {
|
||||
c3, _ = n.addClaim(*newOutPoint(3), 1)
|
||||
c4, _ = n.addClaim(*newOutPoint(4), 2)
|
||||
n.IncrementBlock(10)
|
||||
assert.Equal(t, c4, n.BestClaim())
|
||||
assert.Equal(t, Amount(2), n.BestClaim().effAmt)
|
||||
s4, _ = n.addSupport(*newOutPoint(14), 10, c3.id)
|
||||
n.IncrementBlock(10)
|
||||
assert.Equal(t, c4, n.BestClaim())
|
||||
assert.Equal(t, Amount(2), n.BestClaim().effAmt)
|
||||
n.IncrementBlock(1)
|
||||
assert.Equal(t, c3, n.BestClaim())
|
||||
assert.Equal(t, Amount(11), n.BestClaim().effAmt)
|
||||
|
||||
n.DecrementBlock(1)
|
||||
assert.Equal(t, c4, n.BestClaim())
|
||||
assert.Equal(t, Amount(2), n.BestClaim().effAmt)
|
||||
n.DecrementBlock(10)
|
||||
assert.Equal(t, c4, n.BestClaim())
|
||||
assert.Equal(t, Amount(2), n.BestClaim().effAmt)
|
||||
n.DecrementBlock(10)
|
||||
assert.Nil(t, n.BestClaim())
|
||||
}
|
||||
|
||||
// supporting and abandoing on the same block will cause it to crash
|
||||
test10 := func() {
|
||||
c1, _ = n.addClaim(*newOutPoint(1), 2)
|
||||
n.IncrementBlock(1)
|
||||
s1, _ = n.addSupport(*newOutPoint(11), 1, c1.id)
|
||||
n.removeClaim(c1.op)
|
||||
n.IncrementBlock(1)
|
||||
assert.NotEqual(t, c1, n.BestClaim())
|
||||
|
||||
n.DecrementBlock(1)
|
||||
assert.Equal(t, c1, n.BestClaim())
|
||||
n.DecrementBlock(1)
|
||||
assert.Nil(t, n.BestClaim())
|
||||
}
|
||||
|
||||
// support on abandon2
|
||||
test11 := func() {
|
||||
c1, _ = n.addClaim(*newOutPoint(1), 2)
|
||||
s1, _ = n.addSupport(*newOutPoint(11), 1, c1.id)
|
||||
n.IncrementBlock(1)
|
||||
assert.Equal(t, c1, n.BestClaim())
|
||||
|
||||
//abandoning a support and abandoing claim on the same block will cause it to crash
|
||||
n.removeClaim(c1.op)
|
||||
n.removeSupport(s1.op)
|
||||
n.IncrementBlock(1)
|
||||
assert.Nil(t, n.BestClaim())
|
||||
|
||||
n.DecrementBlock(1)
|
||||
assert.Equal(t, c1, n.BestClaim())
|
||||
n.DecrementBlock(1)
|
||||
assert.Nil(t, n.BestClaim())
|
||||
}
|
||||
tests := []func(){
|
||||
test1,
|
||||
test2,
|
||||
test3,
|
||||
test4,
|
||||
test5,
|
||||
test6,
|
||||
test7,
|
||||
test8,
|
||||
test9,
|
||||
test10,
|
||||
test11,
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
var err error
|
||||
switch tt.op {
|
||||
case opReset:
|
||||
n = newNode()
|
||||
case opAddClaim:
|
||||
tt.claim.amt = tt.amount
|
||||
tt.claim.accepted = tt.curr
|
||||
err = n.addClaim(tt.claim)
|
||||
case opRemoveClaim:
|
||||
err = n.removeClaim(tt.claim.op)
|
||||
case opAddSupport:
|
||||
tt.support.accepted = tt.curr
|
||||
tt.support.amt = tt.amount
|
||||
tt.support.supportedID = tt.claim.id
|
||||
err = n.addSupport(tt.support)
|
||||
case opRemoveSupport:
|
||||
}
|
||||
if err != nil {
|
||||
t.Errorf("BestClaim() failed, err: %s", err)
|
||||
}
|
||||
n.updateBestClaim(tt.curr)
|
||||
got := n.bestClaim
|
||||
if !reflect.DeepEqual(got, tt.want) {
|
||||
t.Errorf("BestClaim() = %d, want %d", got.op.Index, tt.want.op.Index)
|
||||
}
|
||||
})
|
||||
|
||||
tt()
|
||||
}
|
||||
_ = []func(){test1, test2, test3, test4, test5, test6, test7, test8, test9, test10, test11}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue