cleanup: reorg the code for dependencies.

Move self-contained pieces into their own package.
This spells out the depedencies and the context each
sub-package focuses on.

Not the final structure as it's WIP.
Just tying the knots while climbing up the rope.
This commit is contained in:
Tzu-Jung Lee 2018-07-09 09:56:15 -07:00
parent fa2e276f3d
commit ed4478bc72
11 changed files with 357 additions and 325 deletions

View file

@ -22,7 +22,7 @@ go get -u -v github.com/lbryio/claimtrie
Refer to [claimtrie](https://github.com/lbryio/claimtrie/blob/master/cmd/claimtrie) for an interactive CLI tool.
``` bash
``` quote
NAME:
claimtrie - A CLI tool for ClaimTrie

View file

@ -1,4 +1,4 @@
package claimtrie
package claim
import (
"bytes"
@ -8,22 +8,29 @@ import (
"github.com/btcsuite/btcd/wire"
)
// Amount ...
type Amount int64
// Height ...
type Height int64
var dbg bool
// Claim ...
type Claim struct {
op wire.OutPoint
id ClaimID
amt Amount
effAmt Amount
accepted Height
activeAt Height
wire.OutPoint
ID ID
Amt Amount
EffAmt Amount
Accepted Height
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)
fmt.Fprintf(w, "C%-3d amt: %2d, effamt: %v, accepted: %2d, active: %2d, id: %s", c.Index, c.Amt, c.EffAmt, c.Accepted, c.ActiveAt, c.ID)
return w.String()
}
b, err := json.MarshalIndent(c, "", " ")
@ -35,30 +42,31 @@ func (c *Claim) String() string {
// Support ...
type Support struct {
op wire.OutPoint
amt Amount
accepted Height
activeAt Height
wire.OutPoint
supportedID ClaimID
Amt Amount
Accepted Height
ActiveAt Height
SupportedID ID
}
// MarshalJSON customizes the representation of JSON.
func (c *Claim) MarshalJSON() ([]byte, error) {
return json.Marshal(&struct {
OutPoint string
ClaimID string
ID string
Amount Amount
EffectiveAmount Amount
Accepted Height
ActiveAt Height
}{
OutPoint: c.op.String(),
ClaimID: c.id.String(),
Amount: c.amt,
EffectiveAmount: c.effAmt,
Accepted: c.accepted,
ActiveAt: c.activeAt,
OutPoint: c.OutPoint.String(),
ID: c.ID.String(),
Amount: c.Amt,
EffectiveAmount: c.EffAmt,
Accepted: c.Accepted,
ActiveAt: c.ActiveAt,
})
}
@ -71,18 +79,18 @@ func (s *Support) MarshalJSON() ([]byte, error) {
Accepted Height
ActiveAt Height
}{
OutPoint: s.op.String(),
SupportedClaimID: s.supportedID.String(),
Amount: s.amt,
Accepted: s.accepted,
ActiveAt: s.activeAt,
OutPoint: s.OutPoint.String(),
SupportedClaimID: s.SupportedID.String(),
Amount: s.Amt,
Accepted: s.Accepted,
ActiveAt: s.ActiveAt,
})
}
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)
fmt.Fprintf(w, "S%-3d amt: %2d, accepted: %2d, active: %2d, id: %s", s.Index, s.Amt, s.Accepted, s.ActiveAt, s.SupportedID)
return w.String()
}
b, err := json.MarshalIndent(s, "", " ")

View file

@ -1,4 +1,4 @@
package claimtrie
package claim
import (
"bytes"
@ -9,28 +9,28 @@ import (
"github.com/btcsuite/btcutil"
)
// NewClaimID ...
func NewClaimID(op wire.OutPoint) ClaimID {
// NewID ...
func NewID(op wire.OutPoint) ID {
w := bytes.NewBuffer(op.Hash[:])
if err := binary.Write(w, binary.BigEndian, op.Index); err != nil {
panic(err)
}
var id ClaimID
var id ID
copy(id[:], btcutil.Hash160(w.Bytes()))
return id
}
// NewClaimIDFromString ...
func NewClaimIDFromString(s string) (ClaimID, error) {
// NewIDFromString ...
func NewIDFromString(s string) (ID, error) {
b, err := hex.DecodeString(s)
var id ClaimID
var id ID
copy(id[:], b)
return id, err
}
// ClaimID ...
type ClaimID [20]byte
// ID ...
type ID [20]byte
func (id ClaimID) String() string {
func (id ID) String() string {
return hex.EncodeToString(id[:])
}

14
claimnode/error.go Normal file
View file

@ -0,0 +1,14 @@
package claimnode
import "fmt"
var (
// ErrInvalidHeight is returned when the height is invalid.
ErrInvalidHeight = fmt.Errorf("invalid height")
// ErrNotFound is returned when the Claim or Support is not found.
ErrNotFound = fmt.Errorf("not found")
// ErrDuplicate is returned when the Claim or Support already exists in the node.
ErrDuplicate = fmt.Errorf("duplicate")
)

74
claimnode/memento.go Normal file
View file

@ -0,0 +1,74 @@
package claimnode
import "github.com/lbryio/claimtrie/claim"
type cmdAddClaim struct {
node *Node
claim *claim.Claim
}
func (c cmdAddClaim) Execute() { c.node.claims[c.claim.OutPoint] = c.claim }
func (c cmdAddClaim) Undo() { delete(c.node.claims, c.claim.OutPoint) }
type cmdRemoveClaim struct {
node *Node
claim *claim.Claim
}
func (c cmdRemoveClaim) Execute() { delete(c.node.claims, c.claim.OutPoint) }
func (c cmdRemoveClaim) Undo() { c.node.claims[c.claim.OutPoint] = c.claim }
type cmdAddSupport struct {
node *Node
support *claim.Support
}
func (c cmdAddSupport) Execute() { c.node.supports[c.support.OutPoint] = c.support }
func (c cmdAddSupport) Undo() { delete(c.node.supports, c.support.OutPoint) }
type cmdRemoveSupport struct {
node *Node
support *claim.Support
}
func (c cmdRemoveSupport) Execute() { delete(c.node.supports, c.support.OutPoint) }
func (c cmdRemoveSupport) Undo() { c.node.supports[c.support.OutPoint] = c.support }
type cmdUpdateClaimActiveHeight struct {
claim *claim.Claim
old claim.Height
new claim.Height
}
func (c cmdUpdateClaimActiveHeight) Execute() { c.claim.ActiveAt = c.new }
func (c cmdUpdateClaimActiveHeight) Undo() { c.claim.ActiveAt = c.old }
type cmdUpdateSupportActiveHeight struct {
support *claim.Support
old claim.Height
new claim.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 claim.Height
old *claim.Claim
new *claim.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)
}
}

View file

@ -1,4 +1,4 @@
package claimtrie
package claimnode
import (
"bytes"
@ -11,38 +11,41 @@ import (
"github.com/btcsuite/btcd/chaincfg/chainhash"
"github.com/btcsuite/btcd/wire"
"github.com/lbryio/claimtrie/claim"
"github.com/lbryio/claimtrie/memento"
)
// Amount ...
type Amount int64
// Height ...
type Height int64
var dbg bool
// Node ...
type Node struct {
memento.Memento
height Height
bestClaims map[Height]*Claim
claims map[wire.OutPoint]*Claim
supports map[wire.OutPoint]*Support
height claim.Height
bestClaims map[claim.Height]*claim.Claim
claims map[wire.OutPoint]*claim.Claim
supports map[wire.OutPoint]*claim.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{},
bestClaims: map[claim.Height]*claim.Claim{0: nil},
claims: map[wire.OutPoint]*claim.Claim{},
supports: map[wire.OutPoint]*claim.Support{},
}
}
// Height ...
func (n *Node) Height() claim.Height {
return n.height
}
// BestClaim ...
func (n *Node) BestClaim() *Claim {
var latest Height
func (n *Node) BestClaim() *claim.Claim {
var latest claim.Height
for k := range n.bestClaims {
if k > latest {
latest = k
@ -52,8 +55,8 @@ func (n *Node) BestClaim() *Claim {
}
// Tookover ...
func (n *Node) Tookover() Height {
var latest Height
func (n *Node) Tookover() claim.Height {
var latest claim.Height
for k := range n.bestClaims {
if k > latest {
latest = k
@ -63,11 +66,11 @@ func (n *Node) Tookover() Height {
}
// IncrementBlock ...
func (n *Node) IncrementBlock(h Height) error {
func (n *Node) IncrementBlock(h claim.Height) error {
if h < 0 {
return ErrInvalidHeight
}
for i := Height(0); i < h; i++ {
for i := claim.Height(0); i < h; i++ {
n.height++
n.processBlock()
n.Commit()
@ -76,34 +79,36 @@ func (n *Node) IncrementBlock(h Height) error {
}
// DecrementBlock ...
func (n *Node) DecrementBlock(h Height) error {
func (n *Node) DecrementBlock(h claim.Height) error {
if h < 0 {
return ErrInvalidHeight
}
for i := Height(0); i < h; i++ {
for i := claim.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,
// AddClaim ...
func (n *Node) AddClaim(op wire.OutPoint, amt claim.Amount) (*claim.Claim, error) {
c := &claim.Claim{
OutPoint: op,
ID: claim.NewID(op),
Amt: amt,
Accepted: n.height + 1,
ActiveAt: n.height + 1,
}
if n.BestClaim() != nil {
c.activeAt = calActiveHeight(c.accepted, c.accepted, n.Tookover())
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 {
// RemoveClaim ...
func (n *Node) RemoveClaim(op wire.OutPoint) error {
c, ok := n.claims[op]
if !ok {
return ErrNotFound
@ -118,20 +123,21 @@ func (n *Node) removeClaim(op wire.OutPoint) error {
return nil
}
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,
// AddSupport ...
func (n *Node) AddSupport(op wire.OutPoint, amt claim.Amount, supported claim.ID) (*claim.Support, error) {
s := &claim.Support{
OutPoint: op,
Amt: amt,
SupportedID: supported,
Accepted: n.height + 1,
ActiveAt: n.height + 1,
}
if n.BestClaim() == nil || n.BestClaim().op != op {
s.activeAt = calActiveHeight(s.accepted, s.accepted, n.Tookover())
if n.BestClaim() == nil || n.BestClaim().OutPoint != op {
s.ActiveAt = calActiveHeight(s.Accepted, s.Accepted, n.Tookover())
}
for _, c := range n.claims {
if c.id != supported {
if c.ID != supported {
continue
}
n.Execute(cmdAddSupport{node: n, support: s})
@ -142,7 +148,8 @@ func (n *Node) addSupport(op wire.OutPoint, amt Amount, supported ClaimID) (*Sup
return nil, ErrNotFound
}
func (n *Node) removeSupport(op wire.OutPoint) error {
// RemoveSupport ...
func (n *Node) RemoveSupport(op wire.OutPoint) error {
s, ok := n.supports[op]
if !ok {
return ErrNotFound
@ -151,58 +158,20 @@ func (n *Node) removeSupport(op wire.OutPoint) error {
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() {
// FindNextUpdateHeights ...
func (n *Node) FindNextUpdateHeights() claim.Height {
next := claim.Height(math.MaxInt64)
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})
if v.ActiveAt > n.height && v.ActiveAt < next {
next = v.ActiveAt
}
}
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})
if v.ActiveAt > n.height && v.ActiveAt < next {
next = v.ActiveAt
}
}
}
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) {
if next == claim.Height(math.MaxInt64) {
return n.height
}
return next
@ -213,26 +182,26 @@ func (n *Node) Hash() chainhash.Hash {
if n.BestClaim() == nil {
return chainhash.Hash{}
}
return calNodeHash(n.BestClaim().op, n.Tookover())
return calNodeHash(n.BestClaim().OutPoint, n.Tookover())
}
// MarshalJSON customizes JSON marshaling of the Node.
func (n *Node) MarshalJSON() ([]byte, error) {
c := make([]*Claim, 0, len(n.claims))
c := make([]*claim.Claim, 0, len(n.claims))
for _, v := range n.claims {
c = append(c, v)
}
s := make([]*Support, 0, len(n.supports))
s := make([]*claim.Support, 0, len(n.supports))
for _, v := range n.supports {
s = append(s, v)
}
return json.Marshal(&struct {
Height Height
Height claim.Height
Hash string
Tookover Height
BestClaim *Claim
Claims []*Claim
Supports []*Support
Tookover claim.Height
BestClaim *claim.Claim
Claims []*claim.Claim
Supports []*claim.Support
}{
Height: n.height,
Hash: n.Hash().String(),
@ -253,7 +222,7 @@ func (n *Node) String() string {
fmt.Fprintf(w, "{%d, nil}, ", k)
continue
}
fmt.Fprintf(w, "{%d, %d}, ", k, v.op.Index)
fmt.Fprintf(w, "{%d, %d}, ", k, v.Index)
}
fmt.Fprintf(w, "\n")
for _, v := range n.claims {
@ -275,40 +244,60 @@ func (n *Node) String() string {
}
return string(b)
}
// 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 {
if v.activeAt > n.height {
func (n *Node) updateEffectiveAmounts() {
for _, c := range n.claims {
c.EffAmt = c.Amt
if c.ActiveAt > n.height {
c.EffAmt = 0
continue
}
if candidate == nil || v.effAmt > candidate.effAmt {
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 findCandiadte(n *Node) *claim.Claim {
n.updateEffectiveAmounts()
var candidate *claim.Claim
for _, v := range n.claims {
if v.ActiveAt > n.height {
continue
}
if candidate == nil || v.EffAmt > candidate.EffAmt {
candidate = v
}
}
return candidate
}
func calNodeHash(op wire.OutPoint, tookover Height) chainhash.Hash {
func calNodeHash(op wire.OutPoint, tookover claim.Height) chainhash.Hash {
txHash := chainhash.DoubleHashH(op.Hash[:])
nOut := []byte(strconv.Itoa(int(op.Index)))
@ -326,12 +315,12 @@ func calNodeHash(op wire.OutPoint, tookover Height) chainhash.Hash {
return chainhash.DoubleHashH(h)
}
var proportionalDelayFactor = Height(32)
var proportionalDelayFactor = claim.Height(32)
func calActiveHeight(accepted, curr, tookover Height) Height {
func calActiveHeight(Accepted, curr, tookover claim.Height) claim.Height {
delay := (curr - tookover) / proportionalDelayFactor
if delay > 4032 {
delay = 4032
}
return accepted + delay
return Accepted + delay
}

View file

@ -1,4 +1,4 @@
package claimtrie
package claimnode
import (
"reflect"
@ -6,8 +6,9 @@ import (
"github.com/btcsuite/btcd/chaincfg/chainhash"
"github.com/btcsuite/btcd/wire"
"github.com/stretchr/testify/assert"
"github.com/lbryio/claimtrie/claim"
)
func newHash(s string) *chainhash.Hash {
@ -27,7 +28,7 @@ func newOutPoint(idx int) *wire.OutPoint {
func Test_calNodeHash(t *testing.T) {
type args struct {
op wire.OutPoint
h Height
h claim.Height
}
tests := []struct {
name string
@ -64,8 +65,8 @@ func Test_calNodeHash(t *testing.T) {
}
}
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 c1, c2, c3, c4, c5, c6, c7, c8, c9, c10 *claim.Claim
var s1, s2, s3, s4, s5, s6, s7, s8, s9, s10 *claim.Support
func Test_History1(t *testing.T) {
@ -74,7 +75,7 @@ func Test_History1(t *testing.T) {
// no competing bids
test1 := func() {
c1, _ = n.addClaim(*newOutPoint(1), 1)
c1, _ = n.AddClaim(*newOutPoint(1), 1)
n.IncrementBlock(1)
assert.Equal(t, c1, n.BestClaim())
@ -84,8 +85,8 @@ func Test_History1(t *testing.T) {
// there is a competing bid inserted same height
test2 := func() {
n.addClaim(*newOutPoint(2), 1)
c3, _ = n.addClaim(*newOutPoint(3), 2)
n.AddClaim(*newOutPoint(2), 1)
c3, _ = n.AddClaim(*newOutPoint(3), 2)
n.IncrementBlock(1)
assert.Equal(t, c3, n.BestClaim())
@ -95,10 +96,10 @@ func Test_History1(t *testing.T) {
}
// make two claims , one older
test3 := func() {
c4, _ = n.addClaim(*newOutPoint(4), 1)
c4, _ = n.AddClaim(*newOutPoint(4), 1)
n.IncrementBlock(1)
assert.Equal(t, c4, n.BestClaim())
n.addClaim(*newOutPoint(5), 1)
n.AddClaim(*newOutPoint(5), 1)
n.IncrementBlock(1)
assert.Equal(t, c4, n.BestClaim())
n.IncrementBlock(1)
@ -114,11 +115,11 @@ func Test_History1(t *testing.T) {
// check claim takeover
test4 := func() {
c6, _ = n.addClaim(*newOutPoint(6), 1)
c6, _ = n.AddClaim(*newOutPoint(6), 1)
n.IncrementBlock(10)
assert.Equal(t, c6, n.BestClaim())
c7, _ = n.addClaim(*newOutPoint(7), 2)
c7, _ = n.AddClaim(*newOutPoint(7), 2)
n.IncrementBlock(1)
assert.Equal(t, c6, n.BestClaim())
n.IncrementBlock(10)
@ -134,11 +135,11 @@ func Test_History1(t *testing.T) {
// spending winning claim will make losing active claim winner
test5 := func() {
c1, _ = n.addClaim(*newOutPoint(1), 2)
c2, _ = n.addClaim(*newOutPoint(2), 1)
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.RemoveClaim(c1.OutPoint)
n.IncrementBlock(1)
assert.Equal(t, c2, n.BestClaim())
@ -150,14 +151,14 @@ func Test_History1(t *testing.T) {
// spending winning claim will make inactive claim winner
test6 := func() {
c3, _ = n.addClaim(*newOutPoint(3), 2)
c3, _ = n.AddClaim(*newOutPoint(3), 2)
n.IncrementBlock(10)
assert.Equal(t, c3, n.BestClaim())
c4, _ = n.addClaim(*newOutPoint(4), 2)
c4, _ = n.AddClaim(*newOutPoint(4), 2)
n.IncrementBlock(1)
assert.Equal(t, c3, n.BestClaim())
n.removeClaim(c3.op)
n.RemoveClaim(c3.OutPoint)
n.IncrementBlock(1)
assert.Equal(t, c4, n.BestClaim())
@ -171,10 +172,10 @@ func Test_History1(t *testing.T) {
// spending winning claim will empty out claim trie
test7 := func() {
c5, _ = n.addClaim(*newOutPoint(5), 2)
c5, _ = n.AddClaim(*newOutPoint(5), 2)
n.IncrementBlock(1)
assert.Equal(t, c5, n.BestClaim())
n.removeClaim(c5.op)
n.RemoveClaim(c5.OutPoint)
n.IncrementBlock(1)
assert.NotEqual(t, c5, n.BestClaim())
@ -186,47 +187,47 @@ func Test_History1(t *testing.T) {
// 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)
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)
assert.Equal(t, claim.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)
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)
assert.Equal(t, claim.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)
assert.Equal(t, claim.Amount(2), n.BestClaim().EffAmt)
n.IncrementBlock(1)
assert.Equal(t, c3, n.BestClaim())
assert.Equal(t, Amount(11), n.BestClaim().effAmt)
assert.Equal(t, claim.Amount(11), n.BestClaim().EffAmt)
n.DecrementBlock(1)
assert.Equal(t, c4, n.BestClaim())
assert.Equal(t, Amount(2), n.BestClaim().effAmt)
assert.Equal(t, claim.Amount(2), n.BestClaim().EffAmt)
n.DecrementBlock(10)
assert.Equal(t, c4, n.BestClaim())
assert.Equal(t, Amount(2), n.BestClaim().effAmt)
assert.Equal(t, claim.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)
c1, _ = n.AddClaim(*newOutPoint(1), 2)
n.IncrementBlock(1)
s1, _ = n.addSupport(*newOutPoint(11), 1, c1.id)
n.removeClaim(c1.op)
s1, _ = n.AddSupport(*newOutPoint(11), 1, c1.ID)
n.RemoveClaim(c1.OutPoint)
n.IncrementBlock(1)
assert.NotEqual(t, c1, n.BestClaim())
@ -238,14 +239,14 @@ func Test_History1(t *testing.T) {
// support on abandon2
test11 := func() {
c1, _ = n.addClaim(*newOutPoint(1), 2)
s1, _ = n.addSupport(*newOutPoint(11), 1, c1.id)
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.RemoveClaim(c1.OutPoint)
n.RemoveSupport(s1.OutPoint)
n.IncrementBlock(1)
assert.Nil(t, n.BestClaim())

View file

@ -6,6 +6,9 @@ import (
"github.com/btcsuite/btcd/chaincfg/chainhash"
"github.com/btcsuite/btcd/wire"
"github.com/lbryio/claimtrie/claim"
"github.com/lbryio/claimtrie/claimnode"
"github.com/lbryio/merkletrie"
)
@ -13,7 +16,7 @@ import (
type ClaimTrie struct {
// The highest block number commited to the ClaimTrie.
bestBlock Height
bestBlock claim.Height
// Immutable linear history.
head *merkletrie.Commit
@ -22,12 +25,12 @@ type ClaimTrie struct {
stg *merkletrie.Stage
// pending keeps track update for future block height.
pending map[Height][]string
pending map[claim.Height][]string
}
// CommitMeta implements merkletrie.CommitMeta with commit-specific metadata.
type CommitMeta struct {
Height Height
Height claim.Height
}
// New returns a ClaimTrie.
@ -36,20 +39,20 @@ func New() *ClaimTrie {
return &ClaimTrie{
head: merkletrie.NewCommit(nil, CommitMeta{0}, mt),
stg: merkletrie.NewStage(mt),
pending: map[Height][]string{},
pending: map[claim.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 *claimnode.Node) error) error {
v, err := stg.Get(merkletrie.Key(name))
if err != nil && err != merkletrie.ErrKeyNotFound {
return err
}
var n *Node
var n *claimnode.Node
if v == nil {
n = NewNode()
n = claimnode.NewNode()
} else {
n = v.(*Node)
n = v.(*claimnode.Node)
}
if err = modifier(n); err != nil {
return err
@ -58,10 +61,12 @@ 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 {
n.IncrementBlock(ct.bestBlock - n.height)
_, err := n.addClaim(op, amt)
func (ct *ClaimTrie) AddClaim(name string, op wire.OutPoint, amt claim.Amount) error {
return updateStageNode(ct.stg, name, func(n *claimnode.Node) error {
if err := n.IncrementBlock(claim.Height(ct.bestBlock) - n.Height()); err != nil {
return err
}
_, err := n.AddClaim(op, claim.Amount(amt))
next := ct.bestBlock + 1
ct.pending[next] = append(ct.pending[next], name)
return err
@ -69,9 +74,9 @@ func (ct *ClaimTrie) AddClaim(name string, op wire.OutPoint, amt Amount, accepte
}
// 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 {
_, err := n.addSupport(op, amt, supported)
func (ct *ClaimTrie) AddSupport(name string, op wire.OutPoint, amt claim.Amount, supported claim.ID) error {
return updateStageNode(ct.stg, name, func(n *claimnode.Node) error {
_, err := n.AddSupport(op, amt, supported)
next := ct.bestBlock + 1
ct.pending[next] = append(ct.pending[next], name)
return err
@ -80,19 +85,19 @@ func (ct *ClaimTrie) AddSupport(name string, op wire.OutPoint, amt Amount, accep
// 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 *claimnode.Node) error {
next := ct.bestBlock + 1
ct.pending[next] = append(ct.pending[next], name)
return n.removeClaim(op)
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 *claimnode.Node) error {
next := ct.bestBlock + 1
ct.pending[next] = append(ct.pending[next], name)
return n.removeSupport(op)
return n.RemoveSupport(op)
})
}
@ -107,27 +112,27 @@ func (ct *ClaimTrie) MerkleHash() chainhash.Hash {
}
// BestBlock returns the highest height of blocks commited to the ClaimTrie.
func (ct *ClaimTrie) BestBlock() Height {
func (ct *ClaimTrie) BestBlock() claim.Height {
return ct.bestBlock
}
// Commit commits the current Stage into commit database, and updates the BestBlock with the associated height.
// The height must be higher than the current BestBlock, or ErrInvalidHeight is returned.
func (ct *ClaimTrie) Commit(h Height) error {
func (ct *ClaimTrie) Commit(h claim.Height) error {
if h <= ct.bestBlock {
return ErrInvalidHeight
}
for i := ct.bestBlock + 1; i <= h; i++ {
for i := claim.Height(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 {
catchup := func(n *claimnode.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 {
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)
}
@ -151,10 +156,10 @@ func (ct *ClaimTrie) Commit(h Height) error {
}
// Reset reverts the Stage to a specified commit by height.
func (ct *ClaimTrie) Reset(h Height) error {
func (ct *ClaimTrie) Reset(h claim.Height) error {
visit := func(prefix merkletrie.Key, value merkletrie.Value) error {
n := value.(*Node)
return n.DecrementBlock(n.height - h)
n := value.(*claimnode.Node)
return n.DecrementBlock(n.Height() - claim.Height(h))
}
if err := ct.stg.Traverse(visit, true, true); err != nil {
return err

View file

@ -4,6 +4,9 @@ import (
"testing"
"github.com/btcsuite/btcd/chaincfg/chainhash"
"github.com/btcsuite/btcd/wire"
"github.com/lbryio/claimtrie/claim"
)
// pending ...
@ -12,8 +15,8 @@ func TestClaimTrie_Commit(t *testing.T) {
tests := []struct {
name string
curr Height
amt Amount
curr claim.Height
amt claim.Amount
want chainhash.Hash
}{
{name: "0-0", curr: 5, amt: 11},
@ -30,10 +33,19 @@ func TestClaimTrie_Commit(t *testing.T) {
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if tt.amt != 0 {
ct.AddClaim("HELLO", *newOutPoint(0), tt.amt, tt.curr)
ct.AddClaim("HELLO", *newOutPoint(0), tt.amt)
}
ct.Commit(tt.curr)
// fmt.Printf("ct.Merkle[%2d]: %s, amt: %d\n", ct.BestBlock(), ct.MerkleHash(), tt.amt)
})
}
}
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))
return wire.NewOutPoint(new(chainhash.Hash), uint32(idx))
}

View file

@ -13,11 +13,12 @@ import (
"github.com/btcsuite/btcd/chaincfg/chainhash"
"github.com/btcsuite/btcd/wire"
"github.com/urfave/cli"
"github.com/lbryio/claimtrie"
"github.com/lbryio/merkletrie"
"github.com/lbryio/claimtrie/claim"
"github.com/urfave/cli"
"github.com/lbryio/merkletrie"
)
var (
@ -143,41 +144,41 @@ func newOutPoint(s string) (*wire.OutPoint, error) {
}
func cmdAddClaim(c *cli.Context) error {
amount := claimtrie.Amount(c.Int64("amount"))
amount := claim.Amount(c.Int64("amount"))
if !c.IsSet("amount") {
i, err := rand.Int(rand.Reader, big.NewInt(100))
if err != nil {
return err
}
amount = 1 + claimtrie.Amount(i.Int64())
amount = 1 + claim.Amount(i.Int64())
}
height := claimtrie.Height(c.Int64("height"))
if !c.IsSet("height") {
height = ct.BestBlock()
}
// height := claim.Height(c.Int64("height"))
// if !c.IsSet("height") {
// height = ct.BestBlock()
// }
outPoint, err := newOutPoint(c.String("outpoint"))
if err != nil {
return nil
}
return ct.AddClaim(c.String("name"), *outPoint, amount, height)
return ct.AddClaim(c.String("name"), *outPoint, amount)
}
func cmdAddSupport(c *cli.Context) error {
amount := claimtrie.Amount(c.Int64("amount"))
amount := claim.Amount(c.Int64("amount"))
if !c.IsSet("amount") {
i, err := rand.Int(rand.Reader, big.NewInt(100))
if err != nil {
return err
}
amount = 1 + claimtrie.Amount(i.Int64())
amount = 1 + claim.Amount(i.Int64())
}
height := claimtrie.Height(c.Int64("height"))
if !c.IsSet("height") {
height = ct.BestBlock()
}
// height := claim.Height(c.Int64("height"))
// if !c.IsSet("height") {
// height = ct.BestBlock()
// }
outPoint, err := newOutPoint(c.String("outpoint"))
if err != nil {
@ -187,11 +188,11 @@ func cmdAddSupport(c *cli.Context) error {
if !c.IsSet("id") {
return fmt.Errorf("flag -id is required")
}
cid, err := claimtrie.NewClaimIDFromString(c.String("id"))
cid, err := claim.NewIDFromString(c.String("id"))
if err != nil {
return err
}
return ct.AddSupport(c.String("name"), *outPoint, amount, height, cid)
return ct.AddSupport(c.String("name"), *outPoint, amount, cid)
}
func cmdSpendClaim(c *cli.Context) error {
@ -218,7 +219,7 @@ func cmdShow(c *cli.Context) error {
fmt.Printf("%-8s: %v\n", prefix, val)
return nil
}
height := claimtrie.Height(c.Int64("height"))
height := claim.Height(c.Int64("height"))
if !c.IsSet("height") {
fmt.Printf("<ClaimTrie Height %d>\n", ct.BestBlock())
return ct.Traverse(dump, false, !c.Bool("all"))
@ -241,7 +242,7 @@ func cmdMerkle(c *cli.Context) error {
}
func cmdCommit(c *cli.Context) error {
height := claimtrie.Height(c.Int64("height"))
height := claim.Height(c.Int64("height"))
if !c.IsSet("height") {
height = ct.BestBlock() + 1
}
@ -249,7 +250,7 @@ func cmdCommit(c *cli.Context) error {
}
func cmdReset(c *cli.Context) error {
height := claimtrie.Height(c.Int64("height"))
height := claim.Height(c.Int64("height"))
return ct.Reset(height)
}

View file

@ -1,72 +0,0 @@
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)
}
}