cleanup: refactor, bugfix, and comment.
This commit is contained in:
parent
8e1ae77aad
commit
d3419ea775
8 changed files with 470 additions and 327 deletions
109
claim/claim.go
109
claim/claim.go
|
@ -1,7 +1,6 @@
|
|||
package claim
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
|
@ -14,88 +13,74 @@ type Amount int64
|
|||
// Height ...
|
||||
type Height int64
|
||||
|
||||
var dbg bool
|
||||
// Seq is a strictly increasing sequence number determine relative order between Claims and Supports.
|
||||
type Seq uint64
|
||||
|
||||
// Claim ...
|
||||
type Claim struct {
|
||||
wire.OutPoint
|
||||
|
||||
OutPoint wire.OutPoint
|
||||
ID ID
|
||||
Amt Amount
|
||||
EffAmt Amount
|
||||
Accepted Height
|
||||
ActiveAt Height
|
||||
|
||||
// TODO: Get rid of this. Implement ordered map in upper layer.
|
||||
Seq Seq
|
||||
}
|
||||
|
||||
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.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 {
|
||||
wire.OutPoint
|
||||
|
||||
Amt Amount
|
||||
Accepted Height
|
||||
ActiveAt Height
|
||||
|
||||
SupportedID ID
|
||||
return fmt.Sprintf("C-%-68s amt: %-3d effamt: %-3d accepted: %-3d active: %-3d id: %s",
|
||||
c.OutPoint, c.Amt, c.EffAmt, c.Accepted, c.ActiveAt, c.ID)
|
||||
}
|
||||
|
||||
// MarshalJSON customizes the representation of JSON.
|
||||
func (c *Claim) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(&struct {
|
||||
OutPoint string
|
||||
ID string
|
||||
Amount Amount
|
||||
EffectiveAmount Amount
|
||||
Accepted Height
|
||||
ActiveAt Height
|
||||
OutPoint string
|
||||
ID string
|
||||
Amount Amount
|
||||
EffAmount Amount
|
||||
Accepted Height
|
||||
ActiveAt Height
|
||||
}{
|
||||
OutPoint: c.OutPoint.String(),
|
||||
ID: 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,
|
||||
EffAmount: c.EffAmt,
|
||||
Accepted: c.Accepted,
|
||||
ActiveAt: c.ActiveAt,
|
||||
})
|
||||
}
|
||||
|
||||
// MarshalJSON ...
|
||||
func (s *Support) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(&struct {
|
||||
OutPoint string
|
||||
SupportedClaimID string
|
||||
Amount Amount
|
||||
Accepted Height
|
||||
ActiveAt Height
|
||||
}{
|
||||
OutPoint: s.OutPoint.String(),
|
||||
SupportedClaimID: s.SupportedID.String(),
|
||||
Amount: s.Amt,
|
||||
Accepted: s.Accepted,
|
||||
ActiveAt: s.ActiveAt,
|
||||
})
|
||||
// Support ...
|
||||
type Support struct {
|
||||
OutPoint wire.OutPoint
|
||||
ClaimID ID
|
||||
Amt Amount
|
||||
Accepted Height
|
||||
ActiveAt Height
|
||||
Seq Seq
|
||||
}
|
||||
|
||||
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.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)
|
||||
}
|
||||
return string(b)
|
||||
return fmt.Sprintf("S-%-68s amt: %-3d accepted: %-3d active: %-3d id: %s",
|
||||
s.OutPoint, s.Amt, s.Accepted, s.ActiveAt, s.ClaimID)
|
||||
}
|
||||
|
||||
// MarshalJSON customizes the representation of JSON.
|
||||
func (s *Support) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(&struct {
|
||||
OutPoint string
|
||||
ClaimID string
|
||||
Amount Amount
|
||||
Accepted Height
|
||||
ActiveAt Height
|
||||
}{
|
||||
OutPoint: s.OutPoint.String(),
|
||||
ClaimID: s.ClaimID.String(),
|
||||
Amount: s.Amt,
|
||||
Accepted: s.Accepted,
|
||||
ActiveAt: s.ActiveAt,
|
||||
})
|
||||
}
|
||||
|
|
|
@ -1,11 +1,8 @@
|
|||
package claimnode
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/sha256"
|
||||
"encoding/binary"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"math"
|
||||
"strconv"
|
||||
|
||||
|
@ -16,94 +13,84 @@ import (
|
|||
"github.com/lbryio/claimtrie/memento"
|
||||
)
|
||||
|
||||
var dbg bool
|
||||
|
||||
// Node ...
|
||||
type Node struct {
|
||||
memento.Memento
|
||||
|
||||
mem memento.Memento
|
||||
height claim.Height
|
||||
bestClaims map[claim.Height]*claim.Claim
|
||||
claims map[wire.OutPoint]*claim.Claim
|
||||
supports map[wire.OutPoint]*claim.Support
|
||||
|
||||
// To ensure the Claims and Supports are totally ordered, we assign a
|
||||
// strictly increasing seq to each Claim or Support added to the node.
|
||||
seq claim.Seq
|
||||
claims map[wire.OutPoint]*claim.Claim
|
||||
supports map[wire.OutPoint]*claim.Support
|
||||
|
||||
updateNext bool
|
||||
}
|
||||
|
||||
// NewNode ...
|
||||
// NewNode returns a new Node.
|
||||
func NewNode() *Node {
|
||||
return &Node{
|
||||
Memento: memento.Memento{},
|
||||
mem: memento.Memento{},
|
||||
bestClaims: map[claim.Height]*claim.Claim{0: nil},
|
||||
claims: map[wire.OutPoint]*claim.Claim{},
|
||||
supports: map[wire.OutPoint]*claim.Support{},
|
||||
}
|
||||
}
|
||||
|
||||
// Height ...
|
||||
// Height returns the current height.
|
||||
func (n *Node) Height() claim.Height {
|
||||
return n.height
|
||||
}
|
||||
|
||||
// BestClaim ...
|
||||
// BestClaim returns the best claim at the current height.
|
||||
func (n *Node) BestClaim() *claim.Claim {
|
||||
var latest claim.Height
|
||||
for k := range n.bestClaims {
|
||||
if k > latest {
|
||||
latest = k
|
||||
}
|
||||
}
|
||||
return n.bestClaims[latest]
|
||||
c, _ := BestClaimAt(n, n.height)
|
||||
return c
|
||||
}
|
||||
|
||||
// Tookover ...
|
||||
// Tookover returns the height at which current best claim tookover.
|
||||
func (n *Node) Tookover() claim.Height {
|
||||
var latest claim.Height
|
||||
for k := range n.bestClaims {
|
||||
if k > latest {
|
||||
latest = k
|
||||
}
|
||||
}
|
||||
return latest
|
||||
_, since := BestClaimAt(n, n.height)
|
||||
return since
|
||||
}
|
||||
|
||||
// IncrementBlock ...
|
||||
func (n *Node) IncrementBlock(h claim.Height) error {
|
||||
if h < 0 {
|
||||
return ErrInvalidHeight
|
||||
}
|
||||
for i := claim.Height(0); i < h; i++ {
|
||||
// AdjustTo increments or decrements current height until it reaches the specific height.
|
||||
func (n *Node) AdjustTo(h claim.Height) error {
|
||||
for n.height < h {
|
||||
n.height++
|
||||
n.processBlock()
|
||||
n.Commit()
|
||||
n.mem.Commit()
|
||||
}
|
||||
for n.height > h {
|
||||
n.height--
|
||||
n.mem.Rollback()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// DecrementBlock ...
|
||||
func (n *Node) DecrementBlock(h claim.Height) error {
|
||||
if h < 0 {
|
||||
return ErrInvalidHeight
|
||||
}
|
||||
for i := claim.Height(0); i < h; i++ {
|
||||
n.height--
|
||||
n.Rollback()
|
||||
}
|
||||
// Reset ...
|
||||
func (n *Node) Reset() error {
|
||||
n.mem.RollbackUncommited()
|
||||
return nil
|
||||
}
|
||||
|
||||
// AddClaim ...
|
||||
func (n *Node) AddClaim(op wire.OutPoint, amt claim.Amount) (*claim.Claim, error) {
|
||||
n.seq++
|
||||
c := &claim.Claim{
|
||||
OutPoint: op,
|
||||
ID: claim.NewID(op),
|
||||
Amt: amt,
|
||||
Accepted: n.height + 1,
|
||||
ActiveAt: n.height + 1,
|
||||
Seq: n.seq,
|
||||
}
|
||||
if n.BestClaim() != nil {
|
||||
c.ActiveAt = calActiveHeight(c.Accepted, c.Accepted, n.Tookover())
|
||||
}
|
||||
|
||||
n.Execute(cmdAddClaim{node: n, claim: c})
|
||||
n.mem.Execute(cmdAddClaim{node: n, claim: c})
|
||||
return c, nil
|
||||
}
|
||||
|
||||
|
@ -113,24 +100,27 @@ func (n *Node) RemoveClaim(op wire.OutPoint) error {
|
|||
if !ok {
|
||||
return ErrNotFound
|
||||
}
|
||||
n.Execute(cmdRemoveClaim{node: n, claim: c})
|
||||
n.mem.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.mem.Execute(updateNodeBestClaim{node: n, height: n.Tookover(), old: c, new: nil})
|
||||
n.updateActiveHeights()
|
||||
n.updateNext = true
|
||||
return nil
|
||||
}
|
||||
|
||||
// AddSupport ...
|
||||
func (n *Node) AddSupport(op wire.OutPoint, amt claim.Amount, supported claim.ID) (*claim.Support, error) {
|
||||
n.seq++
|
||||
s := &claim.Support{
|
||||
OutPoint: op,
|
||||
Amt: amt,
|
||||
SupportedID: supported,
|
||||
Accepted: n.height + 1,
|
||||
ActiveAt: n.height + 1,
|
||||
OutPoint: op,
|
||||
Amt: amt,
|
||||
ClaimID: supported,
|
||||
Accepted: n.height + 1,
|
||||
ActiveAt: n.height + 1,
|
||||
Seq: n.seq,
|
||||
}
|
||||
if n.BestClaim() == nil || n.BestClaim().OutPoint != op {
|
||||
s.ActiveAt = calActiveHeight(s.Accepted, s.Accepted, n.Tookover())
|
||||
|
@ -140,7 +130,7 @@ func (n *Node) AddSupport(op wire.OutPoint, amt claim.Amount, supported claim.ID
|
|||
if c.ID != supported {
|
||||
continue
|
||||
}
|
||||
n.Execute(cmdAddSupport{node: n, support: s})
|
||||
n.mem.Execute(cmdAddSupport{node: n, support: s})
|
||||
return s, nil
|
||||
}
|
||||
|
||||
|
@ -154,12 +144,18 @@ func (n *Node) RemoveSupport(op wire.OutPoint) error {
|
|||
if !ok {
|
||||
return ErrNotFound
|
||||
}
|
||||
n.Execute(cmdRemoveSupport{node: n, support: s})
|
||||
n.mem.Execute(cmdRemoveSupport{node: n, support: s})
|
||||
return nil
|
||||
}
|
||||
|
||||
// FindNextUpdateHeights ...
|
||||
func (n *Node) FindNextUpdateHeights() claim.Height {
|
||||
// FindNextUpdateHeight returns the smallest height in the future that the the state of the node might change.
|
||||
// If no such height exists, the current height of the node is returned.
|
||||
func (n *Node) FindNextUpdateHeight() claim.Height {
|
||||
if n.updateNext {
|
||||
n.updateNext = false
|
||||
return n.height + 1
|
||||
}
|
||||
|
||||
next := claim.Height(math.MaxInt64)
|
||||
for _, v := range n.claims {
|
||||
if v.ActiveAt > n.height && v.ActiveAt < next {
|
||||
|
@ -187,63 +183,14 @@ func (n *Node) Hash() chainhash.Hash {
|
|||
|
||||
// MarshalJSON customizes JSON marshaling of the Node.
|
||||
func (n *Node) MarshalJSON() ([]byte, error) {
|
||||
c := make([]*claim.Claim, 0, len(n.claims))
|
||||
for _, v := range n.claims {
|
||||
c = append(c, v)
|
||||
}
|
||||
s := make([]*claim.Support, 0, len(n.supports))
|
||||
for _, v := range n.supports {
|
||||
s = append(s, v)
|
||||
}
|
||||
return json.Marshal(&struct {
|
||||
Height claim.Height
|
||||
Hash string
|
||||
Tookover claim.Height
|
||||
BestClaim *claim.Claim
|
||||
Claims []*claim.Claim
|
||||
Supports []*claim.Support
|
||||
}{
|
||||
Height: n.height,
|
||||
Hash: n.Hash().String(),
|
||||
Tookover: n.Tookover(),
|
||||
BestClaim: n.BestClaim(),
|
||||
Claims: c,
|
||||
Supports: s,
|
||||
})
|
||||
return toJSON(n)
|
||||
}
|
||||
|
||||
// String implements Stringer interface.
|
||||
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.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")
|
||||
}
|
||||
return string(b)
|
||||
return toString(n)
|
||||
}
|
||||
|
||||
func (n *Node) updateEffectiveAmounts() {
|
||||
for _, c := range n.claims {
|
||||
c.EffAmt = c.Amt
|
||||
|
@ -252,7 +199,7 @@ func (n *Node) updateEffectiveAmounts() {
|
|||
continue
|
||||
}
|
||||
for _, s := range n.supports {
|
||||
if s.ActiveAt > n.height || s.SupportedID != c.ID {
|
||||
if s.ActiveAt > n.height || s.ClaimID != c.ID {
|
||||
continue
|
||||
}
|
||||
c.EffAmt += s.Amt
|
||||
|
@ -263,40 +210,80 @@ func (n *Node) updateEffectiveAmounts() {
|
|||
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})
|
||||
n.mem.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})
|
||||
n.mem.Execute(cmdUpdateSupportActiveHeight{support: v, old: old, new: new})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (n *Node) processBlock() {
|
||||
for {
|
||||
n.updateEffectiveAmounts()
|
||||
candidate := findCandiadte(n)
|
||||
if n.BestClaim() == candidate {
|
||||
return
|
||||
}
|
||||
n.Execute(updateNodeBestClaim{node: n, height: n.height, old: n.bestClaims[n.height], new: candidate})
|
||||
n.mem.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 {
|
||||
switch {
|
||||
case v.ActiveAt > n.height:
|
||||
continue
|
||||
}
|
||||
if candidate == nil || v.EffAmt > candidate.EffAmt {
|
||||
case candidate == nil:
|
||||
candidate = v
|
||||
case v.EffAmt > candidate.EffAmt:
|
||||
candidate = v
|
||||
case v.EffAmt == candidate.EffAmt && v.Seq < candidate.Seq:
|
||||
candidate = v
|
||||
}
|
||||
}
|
||||
return candidate
|
||||
}
|
||||
|
||||
// BestClaimAt returns the BestClaim at specified Height along with the height when the claim tookover.
|
||||
func BestClaimAt(n *Node, at claim.Height) (best *claim.Claim, since claim.Height) {
|
||||
var latest claim.Height
|
||||
for k := range n.bestClaims {
|
||||
if k > at {
|
||||
continue
|
||||
}
|
||||
if k > latest {
|
||||
latest = k
|
||||
}
|
||||
}
|
||||
return n.bestClaims[latest], latest
|
||||
}
|
||||
|
||||
// clone copies (deeply) the contents (except memento) of src to dst.
|
||||
func clone(dst, src *Node) {
|
||||
dst.height = src.height
|
||||
for k, v := range src.bestClaims {
|
||||
if v == nil {
|
||||
dst.bestClaims[k] = nil
|
||||
continue
|
||||
}
|
||||
dup := *v
|
||||
dst.bestClaims[k] = &dup
|
||||
}
|
||||
for k, v := range src.claims {
|
||||
dup := *v
|
||||
dst.claims[k] = &dup
|
||||
}
|
||||
for k, v := range src.supports {
|
||||
dup := *v
|
||||
dst.supports[k] = &dup
|
||||
}
|
||||
}
|
||||
|
||||
func calNodeHash(op wire.OutPoint, tookover claim.Height) chainhash.Hash {
|
||||
txHash := chainhash.DoubleHashH(op.Hash[:])
|
||||
|
||||
|
|
|
@ -76,10 +76,10 @@ func Test_History1(t *testing.T) {
|
|||
// no competing bids
|
||||
test1 := func() {
|
||||
c1, _ = n.AddClaim(*newOutPoint(1), 1)
|
||||
n.IncrementBlock(1)
|
||||
n.AdjustTo(1)
|
||||
assert.Equal(t, c1, n.BestClaim())
|
||||
|
||||
n.DecrementBlock(1)
|
||||
n.AdjustTo(0)
|
||||
assert.Nil(t, n.BestClaim())
|
||||
}
|
||||
|
||||
|
@ -87,49 +87,49 @@ func Test_History1(t *testing.T) {
|
|||
test2 := func() {
|
||||
n.AddClaim(*newOutPoint(2), 1)
|
||||
c3, _ = n.AddClaim(*newOutPoint(3), 2)
|
||||
n.IncrementBlock(1)
|
||||
n.AdjustTo(1)
|
||||
assert.Equal(t, c3, n.BestClaim())
|
||||
|
||||
n.DecrementBlock(1)
|
||||
n.AdjustTo(0)
|
||||
assert.Nil(t, n.BestClaim())
|
||||
|
||||
}
|
||||
// make two claims , one older
|
||||
test3 := func() {
|
||||
c4, _ = n.AddClaim(*newOutPoint(4), 1)
|
||||
n.IncrementBlock(1)
|
||||
n.AdjustTo(1)
|
||||
assert.Equal(t, c4, n.BestClaim())
|
||||
n.AddClaim(*newOutPoint(5), 1)
|
||||
n.IncrementBlock(1)
|
||||
n.AdjustTo(2)
|
||||
assert.Equal(t, c4, n.BestClaim())
|
||||
n.IncrementBlock(1)
|
||||
n.AdjustTo(3)
|
||||
assert.Equal(t, c4, n.BestClaim())
|
||||
n.DecrementBlock(1)
|
||||
n.AdjustTo(2)
|
||||
assert.Equal(t, c4, n.BestClaim())
|
||||
n.DecrementBlock(1)
|
||||
n.AdjustTo(1)
|
||||
|
||||
assert.Equal(t, c4, n.BestClaim())
|
||||
n.DecrementBlock(1)
|
||||
n.AdjustTo(0)
|
||||
assert.Nil(t, n.BestClaim())
|
||||
}
|
||||
|
||||
// check claim takeover
|
||||
test4 := func() {
|
||||
c6, _ = n.AddClaim(*newOutPoint(6), 1)
|
||||
n.IncrementBlock(10)
|
||||
n.AdjustTo(10)
|
||||
assert.Equal(t, c6, n.BestClaim())
|
||||
|
||||
c7, _ = n.AddClaim(*newOutPoint(7), 2)
|
||||
n.IncrementBlock(1)
|
||||
n.AdjustTo(11)
|
||||
assert.Equal(t, c6, n.BestClaim())
|
||||
n.IncrementBlock(10)
|
||||
n.AdjustTo(21)
|
||||
assert.Equal(t, c7, n.BestClaim())
|
||||
|
||||
n.DecrementBlock(10)
|
||||
n.AdjustTo(11)
|
||||
assert.Equal(t, c6, n.BestClaim())
|
||||
n.DecrementBlock(10)
|
||||
n.AdjustTo(1)
|
||||
assert.Equal(t, c6, n.BestClaim())
|
||||
n.DecrementBlock(1)
|
||||
n.AdjustTo(0)
|
||||
assert.Nil(t, n.BestClaim())
|
||||
}
|
||||
|
||||
|
@ -137,51 +137,51 @@ func Test_History1(t *testing.T) {
|
|||
test5 := func() {
|
||||
c1, _ = n.AddClaim(*newOutPoint(1), 2)
|
||||
c2, _ = n.AddClaim(*newOutPoint(2), 1)
|
||||
n.IncrementBlock(1)
|
||||
n.AdjustTo(1)
|
||||
assert.Equal(t, c1, n.BestClaim())
|
||||
n.RemoveClaim(c1.OutPoint)
|
||||
n.IncrementBlock(1)
|
||||
n.AdjustTo(2)
|
||||
assert.Equal(t, c2, n.BestClaim())
|
||||
|
||||
n.DecrementBlock(1)
|
||||
n.AdjustTo(1)
|
||||
assert.Equal(t, c1, n.BestClaim())
|
||||
n.DecrementBlock(1)
|
||||
n.AdjustTo(0)
|
||||
assert.Nil(t, n.BestClaim())
|
||||
}
|
||||
|
||||
// spending winning claim will make inactive claim winner
|
||||
test6 := func() {
|
||||
c3, _ = n.AddClaim(*newOutPoint(3), 2)
|
||||
n.IncrementBlock(10)
|
||||
n.AdjustTo(10)
|
||||
assert.Equal(t, c3, n.BestClaim())
|
||||
|
||||
c4, _ = n.AddClaim(*newOutPoint(4), 2)
|
||||
n.IncrementBlock(1)
|
||||
n.AdjustTo(11)
|
||||
assert.Equal(t, c3, n.BestClaim())
|
||||
n.RemoveClaim(c3.OutPoint)
|
||||
n.IncrementBlock(1)
|
||||
n.AdjustTo(12)
|
||||
assert.Equal(t, c4, n.BestClaim())
|
||||
|
||||
n.DecrementBlock(1)
|
||||
n.AdjustTo(11)
|
||||
assert.Equal(t, c3, n.BestClaim())
|
||||
n.DecrementBlock(1)
|
||||
n.AdjustTo(10)
|
||||
assert.Equal(t, c3, n.BestClaim())
|
||||
n.DecrementBlock(10)
|
||||
n.AdjustTo(0)
|
||||
assert.Nil(t, n.BestClaim())
|
||||
}
|
||||
|
||||
// spending winning claim will empty out claim trie
|
||||
test7 := func() {
|
||||
c5, _ = n.AddClaim(*newOutPoint(5), 2)
|
||||
n.IncrementBlock(1)
|
||||
n.AdjustTo(1)
|
||||
assert.Equal(t, c5, n.BestClaim())
|
||||
n.RemoveClaim(c5.OutPoint)
|
||||
n.IncrementBlock(1)
|
||||
n.AdjustTo(2)
|
||||
assert.NotEqual(t, c5, n.BestClaim())
|
||||
|
||||
n.DecrementBlock(1)
|
||||
n.AdjustTo(1)
|
||||
assert.Equal(t, c5, n.BestClaim())
|
||||
n.DecrementBlock(1)
|
||||
n.AdjustTo(0)
|
||||
assert.Nil(t, n.BestClaim())
|
||||
}
|
||||
|
||||
|
@ -191,49 +191,49 @@ func Test_History1(t *testing.T) {
|
|||
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)
|
||||
n.AdjustTo(1)
|
||||
assert.Equal(t, c2, n.BestClaim())
|
||||
assert.Equal(t, claim.Amount(11), n.BestClaim().EffAmt)
|
||||
n.DecrementBlock(1)
|
||||
n.AdjustTo(0)
|
||||
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)
|
||||
n.AdjustTo(10)
|
||||
assert.Equal(t, c4, n.BestClaim())
|
||||
assert.Equal(t, claim.Amount(2), n.BestClaim().EffAmt)
|
||||
s4, _ = n.AddSupport(*newOutPoint(14), 10, c3.ID)
|
||||
n.IncrementBlock(10)
|
||||
n.AdjustTo(20)
|
||||
assert.Equal(t, c4, n.BestClaim())
|
||||
assert.Equal(t, claim.Amount(2), n.BestClaim().EffAmt)
|
||||
n.IncrementBlock(1)
|
||||
n.AdjustTo(21)
|
||||
assert.Equal(t, c3, n.BestClaim())
|
||||
assert.Equal(t, claim.Amount(11), n.BestClaim().EffAmt)
|
||||
|
||||
n.DecrementBlock(1)
|
||||
n.AdjustTo(20)
|
||||
assert.Equal(t, c4, n.BestClaim())
|
||||
assert.Equal(t, claim.Amount(2), n.BestClaim().EffAmt)
|
||||
n.DecrementBlock(10)
|
||||
n.AdjustTo(10)
|
||||
assert.Equal(t, c4, n.BestClaim())
|
||||
assert.Equal(t, claim.Amount(2), n.BestClaim().EffAmt)
|
||||
n.DecrementBlock(10)
|
||||
n.AdjustTo(0)
|
||||
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)
|
||||
n.AdjustTo(1)
|
||||
s1, _ = n.AddSupport(*newOutPoint(11), 1, c1.ID)
|
||||
n.RemoveClaim(c1.OutPoint)
|
||||
n.IncrementBlock(1)
|
||||
n.AdjustTo(2)
|
||||
assert.NotEqual(t, c1, n.BestClaim())
|
||||
|
||||
n.DecrementBlock(1)
|
||||
n.AdjustTo(1)
|
||||
assert.Equal(t, c1, n.BestClaim())
|
||||
n.DecrementBlock(1)
|
||||
n.AdjustTo(0)
|
||||
assert.Nil(t, n.BestClaim())
|
||||
}
|
||||
|
||||
|
@ -241,20 +241,44 @@ func Test_History1(t *testing.T) {
|
|||
test11 := func() {
|
||||
c1, _ = n.AddClaim(*newOutPoint(1), 2)
|
||||
s1, _ = n.AddSupport(*newOutPoint(11), 1, c1.ID)
|
||||
n.IncrementBlock(1)
|
||||
n.AdjustTo(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.OutPoint)
|
||||
n.RemoveSupport(s1.OutPoint)
|
||||
n.IncrementBlock(1)
|
||||
n.AdjustTo(2)
|
||||
assert.Nil(t, n.BestClaim())
|
||||
|
||||
n.DecrementBlock(1)
|
||||
n.AdjustTo(1)
|
||||
assert.Equal(t, c1, n.BestClaim())
|
||||
n.DecrementBlock(1)
|
||||
n.AdjustTo(0)
|
||||
assert.Nil(t, n.BestClaim())
|
||||
}
|
||||
test12 := func() {
|
||||
c1, _ = n.AddClaim(*newOutPoint(1), 3)
|
||||
c2, _ = n.AddClaim(*newOutPoint(2), 2)
|
||||
n.AdjustTo(10)
|
||||
// c1 tookover since 1
|
||||
assert.Equal(t, c1, n.BestClaim())
|
||||
|
||||
// C3 will takeover at 11 + 11 - 1 = 21
|
||||
c3, _ = n.AddClaim(*newOutPoint(3), 5)
|
||||
s1, _ = n.AddSupport(*newOutPoint(11), 2, c2.ID)
|
||||
|
||||
n.AdjustTo(20)
|
||||
assert.Equal(t, c1, n.BestClaim())
|
||||
|
||||
n.AdjustTo(21)
|
||||
assert.Equal(t, c3, n.BestClaim())
|
||||
|
||||
n.RemoveClaim(c3.OutPoint)
|
||||
n.AdjustTo(22)
|
||||
|
||||
// c2 (3+4) should bid over c1(5) at 21
|
||||
assert.Equal(t, c2, n.BestClaim())
|
||||
|
||||
}
|
||||
tests := []func(){
|
||||
test1,
|
||||
test2,
|
||||
|
@ -267,9 +291,10 @@ func Test_History1(t *testing.T) {
|
|||
test9,
|
||||
test10,
|
||||
test11,
|
||||
test12,
|
||||
}
|
||||
for _, tt := range tests {
|
||||
tt()
|
||||
}
|
||||
_ = []func(){test1, test2, test3, test4, test5, test6, test7, test8, test9, test10, test11}
|
||||
_ = []func(){test1, test2, test3, test4, test5, test6, test7, test8, test9, test10, test12}
|
||||
}
|
||||
|
|
86
claimnode/ui.go
Normal file
86
claimnode/ui.go
Normal file
|
@ -0,0 +1,86 @@
|
|||
package claimnode
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"html/template"
|
||||
"sort"
|
||||
|
||||
"github.com/lbryio/claimtrie/claim"
|
||||
)
|
||||
|
||||
func sortedBestClaims(n *Node) []string {
|
||||
var s []string
|
||||
for i := claim.Height(0); i <= n.Tookover(); i++ {
|
||||
v, ok := n.bestClaims[i]
|
||||
if !ok || v == nil {
|
||||
continue
|
||||
}
|
||||
s = append(s, fmt.Sprintf("{%d, %d}, ", i, v.OutPoint.Index))
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
func sortedClaims(n *Node) []*claim.Claim {
|
||||
c := make([]*claim.Claim, 0, len(n.claims))
|
||||
for _, v := range n.claims {
|
||||
c = append(c, v)
|
||||
}
|
||||
sort.Slice(c, func(i, j int) bool { return c[i].Seq < c[j].Seq })
|
||||
return c
|
||||
}
|
||||
|
||||
func sortedSupports(n *Node) []*claim.Support {
|
||||
s := make([]*claim.Support, 0, len(n.supports))
|
||||
for _, v := range n.supports {
|
||||
s = append(s, v)
|
||||
}
|
||||
sort.Slice(s, func(i, j int) bool { return s[i].Seq < s[j].Seq })
|
||||
return s
|
||||
}
|
||||
|
||||
func export(n *Node) interface{} {
|
||||
return &struct {
|
||||
Height claim.Height
|
||||
Hash string
|
||||
BestClaims []string
|
||||
BestClaim *claim.Claim
|
||||
Claims []*claim.Claim
|
||||
Supports []*claim.Support
|
||||
}{
|
||||
Height: n.height,
|
||||
Hash: n.Hash().String(),
|
||||
BestClaims: sortedBestClaims(n),
|
||||
BestClaim: n.BestClaim(),
|
||||
Claims: sortedClaims(n),
|
||||
Supports: sortedSupports(n),
|
||||
}
|
||||
}
|
||||
|
||||
func toString(n *Node) string {
|
||||
ui := ` Height {{.Height}}, {{.Hash}} BestClaims: {{range .BestClaims}}{{.}}{{end}}
|
||||
{{$best := .BestClaim}}
|
||||
{{- if .Claims}}
|
||||
{{range .Claims -}}
|
||||
{{.}} {{if (CMP . $best)}} <B> {{end}}
|
||||
{{end}}
|
||||
{{- end}}
|
||||
{{- if .Supports}}
|
||||
{{range .Supports}}{{.}}
|
||||
{{end}}
|
||||
{{- end}}`
|
||||
|
||||
w := bytes.NewBuffer(nil)
|
||||
t := template.Must(template.New("").Funcs(template.FuncMap{
|
||||
"CMP": func(a, b *claim.Claim) bool { return a == b },
|
||||
}).Parse(ui))
|
||||
if err := t.Execute(w, export(n)); err != nil {
|
||||
fmt.Printf("can't execute template, err: %s\n", err)
|
||||
}
|
||||
return w.String()
|
||||
}
|
||||
|
||||
func toJSON(n *Node) ([]byte, error) {
|
||||
return json.Marshal(export(n))
|
||||
}
|
215
claimtrie.go
215
claimtrie.go
|
@ -1,8 +1,6 @@
|
|||
package claimtrie
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/btcsuite/btcd/chaincfg/chainhash"
|
||||
"github.com/btcsuite/btcd/wire"
|
||||
|
||||
|
@ -16,7 +14,7 @@ import (
|
|||
type ClaimTrie struct {
|
||||
|
||||
// The highest block number commited to the ClaimTrie.
|
||||
bestBlock claim.Height
|
||||
height claim.Height
|
||||
|
||||
// Immutable linear history.
|
||||
head *trie.Commit
|
||||
|
@ -24,8 +22,16 @@ type ClaimTrie struct {
|
|||
// An overlay supporting Copy-on-Write to the current tip commit.
|
||||
stg *trie.Stage
|
||||
|
||||
// pending keeps track update for future block height.
|
||||
pending map[claim.Height][]string
|
||||
// todos tracks pending updates for future block height.
|
||||
//
|
||||
// A claim or support has a dynamic active peroid (ActiveAt, ExipresAt).
|
||||
// This makes the state of each node dynamic as the ClaimTrie increases/decreases its height.
|
||||
// Instead of polling every node for updates everytime ClaimTrie changes, the node is evaluated
|
||||
// for the nearest future height it may change the states, and add that height to the todos.
|
||||
//
|
||||
// When a ClaimTrie at height h1 is committed with h2, the pending updates from todos (h1, h2]
|
||||
// will be applied to bring the nodes up to date.
|
||||
todos map[claim.Height][]string
|
||||
}
|
||||
|
||||
// CommitMeta implements trie.CommitMeta with commit-specific metadata.
|
||||
|
@ -37,71 +43,57 @@ type CommitMeta struct {
|
|||
func New() *ClaimTrie {
|
||||
mt := trie.New()
|
||||
return &ClaimTrie{
|
||||
head: trie.NewCommit(nil, CommitMeta{0}, mt),
|
||||
stg: trie.NewStage(mt),
|
||||
pending: map[claim.Height][]string{},
|
||||
head: trie.NewCommit(nil, CommitMeta{0}, mt),
|
||||
stg: trie.NewStage(mt),
|
||||
todos: map[claim.Height][]string{},
|
||||
}
|
||||
}
|
||||
|
||||
func updateStageNode(stg *trie.Stage, name string, modifier func(n *claimnode.Node) error) error {
|
||||
v, err := stg.Get(trie.Key(name))
|
||||
if err != nil && err != trie.ErrKeyNotFound {
|
||||
return err
|
||||
}
|
||||
var n *claimnode.Node
|
||||
if v == nil {
|
||||
n = claimnode.NewNode()
|
||||
} else {
|
||||
n = v.(*claimnode.Node)
|
||||
}
|
||||
if err = modifier(n); err != nil {
|
||||
return err
|
||||
}
|
||||
return stg.Update(trie.Key(name), n)
|
||||
// Height returns the highest height of blocks commited to the ClaimTrie.
|
||||
func (ct *ClaimTrie) Height() claim.Height {
|
||||
return ct.height
|
||||
}
|
||||
|
||||
// Head returns the tip commit in the commit database.
|
||||
func (ct *ClaimTrie) Head() *trie.Commit {
|
||||
return ct.head
|
||||
}
|
||||
|
||||
// AddClaim adds a Claim to the Stage of ClaimTrie.
|
||||
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)
|
||||
modifier := func(n *claimnode.Node) error {
|
||||
_, err := n.AddClaim(op, amt)
|
||||
return err
|
||||
})
|
||||
}
|
||||
return updateNode(ct, ct.height, name, modifier)
|
||||
}
|
||||
|
||||
// AddSupport adds a Support to the Stage of ClaimTrie.
|
||||
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 {
|
||||
modifier := 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
|
||||
})
|
||||
}
|
||||
return updateNode(ct, ct.height, name, modifier)
|
||||
}
|
||||
|
||||
// SpendClaim removes a Claim in the Stage.
|
||||
func (ct *ClaimTrie) SpendClaim(name string, op wire.OutPoint) error {
|
||||
return updateStageNode(ct.stg, name, func(n *claimnode.Node) error {
|
||||
next := ct.bestBlock + 1
|
||||
ct.pending[next] = append(ct.pending[next], name)
|
||||
modifier := func(n *claimnode.Node) error {
|
||||
return n.RemoveClaim(op)
|
||||
})
|
||||
}
|
||||
return updateNode(ct, ct.height, name, modifier)
|
||||
}
|
||||
|
||||
// SpendSupport removes a Support in the Stage.
|
||||
func (ct *ClaimTrie) SpendSupport(name string, op wire.OutPoint) error {
|
||||
return updateStageNode(ct.stg, name, func(n *claimnode.Node) error {
|
||||
next := ct.bestBlock + 1
|
||||
ct.pending[next] = append(ct.pending[next], name)
|
||||
modifier := func(n *claimnode.Node) error {
|
||||
return n.RemoveSupport(op)
|
||||
})
|
||||
}
|
||||
return updateNode(ct, ct.height, name, modifier)
|
||||
}
|
||||
|
||||
// Traverse visits Nodes in the Stage of the ClaimTrie.
|
||||
// Traverse visits Nodes in the Stage.
|
||||
func (ct *ClaimTrie) Traverse(visit trie.Visit, update, valueOnly bool) error {
|
||||
return ct.stg.Traverse(visit, update, valueOnly)
|
||||
}
|
||||
|
@ -111,72 +103,121 @@ func (ct *ClaimTrie) MerkleHash() chainhash.Hash {
|
|||
return ct.stg.MerkleHash()
|
||||
}
|
||||
|
||||
// BestBlock returns the highest height of blocks commited to the ClaimTrie.
|
||||
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.
|
||||
// Commit commits the current Stage into commit database.
|
||||
// If h is lower than the current height, ErrInvalidHeight is returned.
|
||||
//
|
||||
// As Stage can be always cleanly reset to a specific commited snapshot,
|
||||
// any error occurred during the commit would leave the Stage partially updated
|
||||
// so the caller can inspect the status if interested.
|
||||
//
|
||||
// Changes to the ClaimTrie status, such as height or todos, are all or nothing.
|
||||
func (ct *ClaimTrie) Commit(h claim.Height) error {
|
||||
if h <= ct.bestBlock {
|
||||
|
||||
// Already caught up.
|
||||
if h <= ct.height {
|
||||
return ErrInvalidHeight
|
||||
}
|
||||
|
||||
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 *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 {
|
||||
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 {
|
||||
// Apply pending updates in todos (ct.Height, h].
|
||||
// Note that ct.Height is excluded while h is included.
|
||||
for i := ct.height + 1; i <= h; i++ {
|
||||
for _, name := range ct.todos[i] {
|
||||
// dummy modifier to have the node brought up to date.
|
||||
modifier := func(n *claimnode.Node) error { return nil }
|
||||
if err := updateNode(ct, i, name, modifier); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
delete(ct.pending, i)
|
||||
}
|
||||
commit, err := ct.stg.Commit(ct.head, CommitMeta{Height: h})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// No more errors. Change the ClaimTrie status.
|
||||
ct.head = commit
|
||||
ct.bestBlock = h
|
||||
for i := ct.height + 1; i <= h; i++ {
|
||||
delete(ct.todos, i)
|
||||
}
|
||||
ct.height = h
|
||||
return nil
|
||||
}
|
||||
|
||||
// Reset reverts the Stage to a specified commit by height.
|
||||
func (ct *ClaimTrie) Reset(h claim.Height) error {
|
||||
visit := func(prefix trie.Key, value trie.Value) error {
|
||||
if h > ct.height {
|
||||
return ErrInvalidHeight
|
||||
}
|
||||
|
||||
// Find the most recent commit that is equal or earlier than h.
|
||||
commit := ct.head
|
||||
for commit != nil {
|
||||
if commit.Meta.(CommitMeta).Height <= h {
|
||||
break
|
||||
}
|
||||
commit = commit.Prev
|
||||
}
|
||||
|
||||
// The commit history is not deep enough.
|
||||
if commit == nil {
|
||||
return ErrInvalidHeight
|
||||
}
|
||||
|
||||
// Drop (rollback) any uncommited change, and adjust to the specified height.
|
||||
rollback := func(prefix trie.Key, value trie.Value) error {
|
||||
n := value.(*claimnode.Node)
|
||||
return n.DecrementBlock(n.Height() - claim.Height(h))
|
||||
n.Reset()
|
||||
return n.AdjustTo(h)
|
||||
}
|
||||
if err := ct.stg.Traverse(visit, true, true); err != nil {
|
||||
return err
|
||||
if err := ct.stg.Traverse(rollback, true, true); err != nil {
|
||||
// Rollback a node to a known state can't go wrong.
|
||||
// It's a programming error, and can't recover.
|
||||
panic(err)
|
||||
}
|
||||
for commit := ct.head; commit != nil; commit = commit.Prev {
|
||||
meta := commit.Meta.(CommitMeta)
|
||||
if meta.Height <= h {
|
||||
ct.head = commit
|
||||
ct.bestBlock = h
|
||||
ct.stg = trie.NewStage(commit.MerkleTrie)
|
||||
return nil
|
||||
|
||||
// Update ClaimTrie status
|
||||
ct.head = commit
|
||||
ct.height = h
|
||||
for k := range ct.todos {
|
||||
if k >= h {
|
||||
delete(ct.todos, k)
|
||||
}
|
||||
}
|
||||
return ErrInvalidHeight
|
||||
ct.stg = trie.NewStage(commit.MerkleTrie)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Head returns the current tip commit in the commit database.
|
||||
func (ct *ClaimTrie) Head() *trie.Commit {
|
||||
return ct.head
|
||||
// updateNode implements a get-modify-set sequence to the node associated with name.
|
||||
// After the modifier is applied, the node is evaluated for how soon in the
|
||||
// nearest future change. And register it, if any, to the todos for the next updateNode.
|
||||
func updateNode(ct *ClaimTrie, h claim.Height, name string, modifier func(n *claimnode.Node) error) error {
|
||||
|
||||
// Get the node from the Stage, or create one if it did not exist yet.
|
||||
v, err := ct.stg.Get(trie.Key(name))
|
||||
if err == trie.ErrKeyNotFound {
|
||||
v = claimnode.NewNode()
|
||||
} else if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
n := v.(*claimnode.Node)
|
||||
|
||||
// Bring the node state up to date.
|
||||
if err = n.AdjustTo(h); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Apply the modifier on the node.
|
||||
if err = modifier(n); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Register pending update, if any, for future height.
|
||||
next := n.FindNextUpdateHeight()
|
||||
if next > h {
|
||||
ct.todos[next] = append(ct.todos[next], name)
|
||||
}
|
||||
|
||||
// Store the modified value back to the Stage, clearing out all the Merkle Hash on the path.
|
||||
return ct.stg.Update(trie.Key(name), n)
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@ package main
|
|||
import (
|
||||
"bufio"
|
||||
"crypto/rand"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"math/big"
|
||||
"os"
|
||||
|
@ -26,7 +27,8 @@ var (
|
|||
flagHeight = cli.Int64Flag{Name: "height, ht", Usage: "Height"}
|
||||
flagName = cli.StringFlag{Name: "name, n", Value: "Hello", Usage: "Name"}
|
||||
flagID = cli.StringFlag{Name: "id", Usage: "Claim ID"}
|
||||
flagOutPoint = cli.StringFlag{Name: "outpoint, op", Usage: "Outpoint. (HASH:INDEX) "}
|
||||
flagOutPoint = cli.StringFlag{Name: "outpoint, op", Usage: "Outpoint. (HASH:INDEX)"}
|
||||
flagJSON = cli.BoolFlag{Name: "json, j", Usage: "Show Claim / Support in JSON format."}
|
||||
)
|
||||
|
||||
var (
|
||||
|
@ -40,7 +42,6 @@ func main() {
|
|||
app.Usage = "A CLI tool for ClaimTrie"
|
||||
app.Version = "0.0.1"
|
||||
app.Action = cli.ShowAppHelp
|
||||
|
||||
app.Commands = []cli.Command{
|
||||
{
|
||||
Name: "add-claim",
|
||||
|
@ -75,7 +76,7 @@ func main() {
|
|||
Aliases: []string{"s"},
|
||||
Usage: "Show the Key-Value pairs of the Stage or specified commit. (links nodes are showed if -a is also specified)",
|
||||
Action: cmdShow,
|
||||
Flags: []cli.Flag{flagAll, flagHeight},
|
||||
Flags: []cli.Flag{flagAll, flagJSON, flagHeight},
|
||||
},
|
||||
{
|
||||
Name: "merkle",
|
||||
|
@ -154,7 +155,7 @@ func cmdAddClaim(c *cli.Context) error {
|
|||
|
||||
// height := claim.Height(c.Int64("height"))
|
||||
// if !c.IsSet("height") {
|
||||
// height = ct.BestBlock()
|
||||
// height = ct.Height()
|
||||
// }
|
||||
|
||||
outPoint, err := newOutPoint(c.String("outpoint"))
|
||||
|
@ -176,7 +177,7 @@ func cmdAddSupport(c *cli.Context) error {
|
|||
|
||||
// height := claim.Height(c.Int64("height"))
|
||||
// if !c.IsSet("height") {
|
||||
// height = ct.BestBlock()
|
||||
// height = ct.Height()
|
||||
// }
|
||||
|
||||
outPoint, err := newOutPoint(c.String("outpoint"))
|
||||
|
@ -215,14 +216,23 @@ func cmdShow(c *cli.Context) error {
|
|||
fmt.Printf("%-8s:\n", prefix)
|
||||
return nil
|
||||
}
|
||||
fmt.Printf("%-8s: %v\n", prefix, val)
|
||||
if !c.IsSet("json") {
|
||||
fmt.Printf("%-8s: %v\n", prefix, val)
|
||||
return nil
|
||||
}
|
||||
b, err := json.MarshalIndent(val, "", " ")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Printf("%-8s: %s\n", prefix, b)
|
||||
return nil
|
||||
}
|
||||
height := claim.Height(c.Int64("height"))
|
||||
|
||||
if !c.IsSet("height") {
|
||||
fmt.Printf("<ClaimTrie Height %d>\n", ct.BestBlock())
|
||||
fmt.Printf("<ClaimTrie Height %d>\n", ct.Height())
|
||||
return ct.Traverse(dump, false, !c.Bool("all"))
|
||||
}
|
||||
height := claim.Height(c.Int64("height"))
|
||||
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)
|
||||
|
@ -243,7 +253,7 @@ func cmdMerkle(c *cli.Context) error {
|
|||
func cmdCommit(c *cli.Context) error {
|
||||
height := claim.Height(c.Int64("height"))
|
||||
if !c.IsSet("height") {
|
||||
height = ct.BestBlock() + 1
|
||||
height = ct.Height() + 1
|
||||
}
|
||||
return ct.Commit(height)
|
||||
}
|
||||
|
|
|
@ -28,9 +28,19 @@ func (m *Memento) Commit() {
|
|||
|
||||
// Rollback ...
|
||||
func (m *Memento) Rollback() {
|
||||
cmds := m.stack[len(m.stack)-1]
|
||||
m.stack = m.stack[:len(m.stack)-1]
|
||||
n := len(m.stack)
|
||||
cmds := m.stack[n-1]
|
||||
m.stack = m.stack[:n-1]
|
||||
m.cmds = nil
|
||||
for i := len(cmds) - 1; i >= 0; i-- {
|
||||
cmds[i].Undo()
|
||||
}
|
||||
}
|
||||
|
||||
// RollbackUncommited ...
|
||||
func (m *Memento) RollbackUncommited() {
|
||||
for i := len(m.cmds) - 1; i >= 0; i-- {
|
||||
m.cmds[i].Undo()
|
||||
}
|
||||
m.cmds = nil
|
||||
}
|
||||
|
|
|
@ -22,6 +22,7 @@ func (s *Stage) Update(key Key, val Value) error {
|
|||
defer s.mu.Unlock()
|
||||
|
||||
n := s.root
|
||||
n.hash = nil
|
||||
for _, k := range key {
|
||||
org := n.links[k]
|
||||
n.links[k] = newNode(nil)
|
||||
|
@ -31,10 +32,8 @@ func (s *Stage) Update(key Key, val Value) error {
|
|||
n.hash = nil
|
||||
n = n.links[k]
|
||||
}
|
||||
if n.value != val {
|
||||
n.value = val
|
||||
n.hash = nil
|
||||
}
|
||||
n.value = val
|
||||
n.hash = nil
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue