diff --git a/README.md b/README.md index 1df45b0..3b882ca 100644 --- a/README.md +++ b/README.md @@ -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 diff --git a/claim.go b/claim/claim.go similarity index 61% rename from claim.go rename to claim/claim.go index dc51809..a295b26 100644 --- a/claim.go +++ b/claim/claim.go @@ -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, "", " ") diff --git a/claimid.go b/claim/id.go similarity index 59% rename from claimid.go rename to claim/id.go index 5f8721d..373d360 100644 --- a/claimid.go +++ b/claim/id.go @@ -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[:]) } diff --git a/claimnode/error.go b/claimnode/error.go new file mode 100644 index 0000000..9a7c4af --- /dev/null +++ b/claimnode/error.go @@ -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") +) diff --git a/claimnode/memento.go b/claimnode/memento.go new file mode 100644 index 0000000..91bc143 --- /dev/null +++ b/claimnode/memento.go @@ -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) + } +} diff --git a/node.go b/claimnode/node.go similarity index 59% rename from node.go rename to claimnode/node.go index 8372df4..3cadeed 100644 --- a/node.go +++ b/claimnode/node.go @@ -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 } diff --git a/node_test.go b/claimnode/node_test.go similarity index 77% rename from node_test.go rename to claimnode/node_test.go index 8e44372..030ead6 100644 --- a/node_test.go +++ b/claimnode/node_test.go @@ -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()) diff --git a/claimtrie.go b/claimtrie.go index affcd48..b807496 100644 --- a/claimtrie.go +++ b/claimtrie.go @@ -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 diff --git a/claimtrie_test.go b/claimtrie_test.go index 6053676..b9f2c25 100644 --- a/claimtrie_test.go +++ b/claimtrie_test.go @@ -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)) +} diff --git a/cmd/claimtrie/main.go b/cmd/claimtrie/main.go index ec4da62..4fbe8d2 100644 --- a/cmd/claimtrie/main.go +++ b/cmd/claimtrie/main.go @@ -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("\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) } diff --git a/memento.go b/memento.go deleted file mode 100644 index a614931..0000000 --- a/memento.go +++ /dev/null @@ -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) - } -}