diff --git a/blockchain/claimtrie.go b/blockchain/claimtrie.go index b9913203..f821537a 100644 --- a/blockchain/claimtrie.go +++ b/blockchain/claimtrie.go @@ -44,7 +44,7 @@ func (b *BlockChain) ParseClaimScripts(block *btcutil.Block, bn *blockNode, view } } - err := b.claimTrie.AppendBlock() + err := b.claimTrie.AppendBlock(bn == nil) if err != nil { return errors.Wrapf(err, "in append block") } diff --git a/claimtrie/claimtrie.go b/claimtrie/claimtrie.go index 05d423be..68ef6488 100644 --- a/claimtrie/claimtrie.go +++ b/claimtrie/claimtrie.go @@ -133,7 +133,7 @@ func New(cfg config.Config) (*ClaimTrie, error) { ct.Close() // TODO: the cleanups aren't run when we exit with an err above here (but should be) return nil, errors.Wrap(err, "block repo get") } - _, err = nodeManager.IncrementHeightTo(previousHeight) + _, err = nodeManager.IncrementHeightTo(previousHeight, false) if err != nil { ct.Close() return nil, errors.Wrap(err, "increment height to") @@ -221,11 +221,11 @@ func (ct *ClaimTrie) SpendSupport(name []byte, op wire.OutPoint, id change.Claim } // AppendBlock increases block by one. -func (ct *ClaimTrie) AppendBlock() error { +func (ct *ClaimTrie) AppendBlock(temporary bool) error { ct.height++ - names, err := ct.nodeManager.IncrementHeightTo(ct.height) + names, err := ct.nodeManager.IncrementHeightTo(ct.height, temporary) if err != nil { return errors.Wrap(err, "node manager increment") } @@ -258,7 +258,7 @@ func (ct *ClaimTrie) AppendBlock() error { updateNames = append(updateNames, newName) updateHeights = append(updateHeights, nhn.Next) } - if len(updateNames) != 0 { + if !temporary && len(updateNames) > 0 { err = ct.temporalRepo.SetNodesAt(updateNames, updateHeights) if err != nil { return errors.Wrap(err, "temporal repo set") @@ -266,9 +266,11 @@ func (ct *ClaimTrie) AppendBlock() error { } hitFork := ct.updateTrieForHashForkIfNecessary() - h := ct.MerkleHash() - ct.blockRepo.Set(ct.height, h) + + if !temporary { + ct.blockRepo.Set(ct.height, h) + } if hitFork { err = ct.merkleTrie.SetRoot(h) // for clearing the memory entirely @@ -311,7 +313,7 @@ func (ct *ClaimTrie) ResetHeight(height int32) error { } names = append(names, results...) } - err := ct.nodeManager.DecrementHeightTo(names, height) + names, err := ct.nodeManager.DecrementHeightTo(names, height) if err != nil { return err } diff --git a/claimtrie/claimtrie_test.go b/claimtrie/claimtrie_test.go index 7cd1432b..61194f1a 100644 --- a/claimtrie/claimtrie_test.go +++ b/claimtrie/claimtrie_test.go @@ -81,7 +81,7 @@ func TestEmptyHashFork(t *testing.T) { defer ct.Close() for i := 0; i < 5; i++ { - err := ct.AppendBlock() + err := ct.AppendBlock(false) r.NoError(err) } } @@ -378,7 +378,7 @@ func incrementBlock(r *require.Assertions, ct *ClaimTrie, c int32) { r.NoError(err) } else { for ; c > 0; c-- { - err := ct.AppendBlock() + err := ct.AppendBlock(false) r.NoError(err) } } @@ -996,7 +996,7 @@ func TestBlock884431(t *testing.T) { o6 := add("testing", 20) for i := 0; i < 10; i++ { - err = ct.AppendBlock() + err = ct.AppendBlock(false) r.NoError(err) } n, err := ct.NodeAt(ct.height, []byte("go")) diff --git a/claimtrie/cmd/cmd/chain.go b/claimtrie/cmd/cmd/chain.go index 92e52816..b16ec599 100644 --- a/claimtrie/cmd/cmd/chain.go +++ b/claimtrie/cmd/cmd/chain.go @@ -193,7 +193,7 @@ func NewChainReplayCommand() *cobra.Command { func appendBlock(ct *claimtrie.ClaimTrie, chain *blockchain.BlockChain) error { - err := ct.AppendBlock() + err := ct.AppendBlock(false) if err != nil { return errors.Wrapf(err, "append block: %w") } diff --git a/claimtrie/node/manager.go b/claimtrie/node/manager.go index 605b7123..814bfc80 100644 --- a/claimtrie/node/manager.go +++ b/claimtrie/node/manager.go @@ -13,8 +13,8 @@ import ( type Manager interface { AppendChange(chg change.Change) - IncrementHeightTo(height int32) ([][]byte, error) - DecrementHeightTo(affectedNames [][]byte, height int32) error + IncrementHeightTo(height int32, temporary bool) ([][]byte, error) + DecrementHeightTo(affectedNames [][]byte, height int32) ([][]byte, error) Height() int32 Close() error NodeAt(height int32, name []byte) (*Node, error) @@ -28,6 +28,8 @@ type BaseManager struct { height int32 changes []change.Change + + tempChanges map[string][]change.Change } func NewBaseManager(repo Repo) (*BaseManager, error) { @@ -46,6 +48,10 @@ func (nm *BaseManager) NodeAt(height int32, name []byte) (*Node, error) { return nil, errors.Wrap(err, "in load changes") } + if nm.tempChanges != nil { // making an assumption that we only ever have tempChanges for a single block + changes = append(changes, nm.tempChanges[string(name)]...) + } + n, err := nm.newNodeFromChanges(changes, height) if err != nil { return nil, errors.Wrap(err, "in new node") @@ -187,7 +193,7 @@ func collectChildNames(changes []change.Change) { // } //} -func (nm *BaseManager) IncrementHeightTo(height int32) ([][]byte, error) { +func (nm *BaseManager) IncrementHeightTo(height int32, temporary bool) ([][]byte, error) { if height <= nm.height { panic("invalid height") @@ -198,13 +204,25 @@ func (nm *BaseManager) IncrementHeightTo(height int32) ([][]byte, error) { collectChildNames(nm.changes) } + if temporary { + if nm.tempChanges != nil { + return nil, errors.Errorf("expected nil temporary changes") + } + nm.tempChanges = map[string][]change.Change{} + } names := make([][]byte, 0, len(nm.changes)) for i := range nm.changes { names = append(names, nm.changes[i].Name) + if temporary { + name := string(nm.changes[i].Name) + nm.tempChanges[name] = append(nm.tempChanges[name], nm.changes[i]) + } } - if err := nm.repo.AppendChanges(nm.changes); err != nil { // destroys names - return nil, errors.Wrap(err, "in append changes") + if !temporary { + if err := nm.repo.AppendChanges(nm.changes); err != nil { // destroys names + return nil, errors.Wrap(err, "in append changes") + } } // Truncate the buffer size to zero. @@ -218,20 +236,29 @@ func (nm *BaseManager) IncrementHeightTo(height int32) ([][]byte, error) { return names, nil } -func (nm *BaseManager) DecrementHeightTo(affectedNames [][]byte, height int32) error { +func (nm *BaseManager) DecrementHeightTo(affectedNames [][]byte, height int32) ([][]byte, error) { if height >= nm.height { - return errors.Errorf("invalid height of %d for %d", height, nm.height) + return affectedNames, errors.Errorf("invalid height of %d for %d", height, nm.height) } - for _, name := range affectedNames { - if err := nm.repo.DropChanges(name, height); err != nil { - return errors.Wrap(err, "in drop changes") + if nm.tempChanges != nil { + if height != nm.height-1 { + return affectedNames, errors.Errorf("invalid temporary rollback at %d to %d", height, nm.height) + } + for key := range nm.tempChanges { + affectedNames = append(affectedNames, []byte(key)) + } + nm.tempChanges = nil + } else { + for _, name := range affectedNames { + if err := nm.repo.DropChanges(name, height); err != nil { + return affectedNames, errors.Wrap(err, "in drop changes") + } } } - nm.height = height - return nil + return affectedNames, nil } func (nm *BaseManager) getDelayForName(n *Node, chg change.Change) int32 { diff --git a/claimtrie/node/manager_test.go b/claimtrie/node/manager_test.go index 705080c8..c907bb4c 100644 --- a/claimtrie/node/manager_test.go +++ b/claimtrie/node/manager_test.go @@ -54,17 +54,17 @@ func TestSimpleAddClaim(t *testing.T) { r.NoError(err) defer m.Close() - _, err = m.IncrementHeightTo(10) + _, err = m.IncrementHeightTo(10, false) r.NoError(err) chg := change.NewChange(change.AddClaim).SetName(name1).SetOutPoint(out1).SetHeight(11) m.AppendChange(chg) - _, err = m.IncrementHeightTo(11) + _, err = m.IncrementHeightTo(11, false) r.NoError(err) chg = chg.SetName(name2).SetOutPoint(out2).SetHeight(12) m.AppendChange(chg) - _, err = m.IncrementHeightTo(12) + _, err = m.IncrementHeightTo(12, false) r.NoError(err) n1, err := m.node(name1) @@ -77,13 +77,13 @@ func TestSimpleAddClaim(t *testing.T) { r.Equal(1, len(n2.Claims)) r.NotNil(n2.Claims.find(byOut(*out2))) - err = m.DecrementHeightTo([][]byte{name2}, 11) + _, err = m.DecrementHeightTo([][]byte{name2}, 11) r.NoError(err) n2, err = m.node(name2) r.NoError(err) r.Nil(n2) - err = m.DecrementHeightTo([][]byte{name1}, 1) + _, err = m.DecrementHeightTo([][]byte{name1}, 1) r.NoError(err) n2, err = m.node(name1) r.NoError(err) @@ -102,7 +102,7 @@ func TestSupportAmounts(t *testing.T) { r.NoError(err) defer m.Close() - _, err = m.IncrementHeightTo(10) + _, err = m.IncrementHeightTo(10, false) r.NoError(err) chg := change.NewChange(change.AddClaim).SetName(name1).SetOutPoint(out1).SetHeight(11).SetAmount(3) @@ -113,7 +113,7 @@ func TestSupportAmounts(t *testing.T) { chg.ClaimID = change.NewClaimID(*out2) m.AppendChange(chg) - _, err = m.IncrementHeightTo(11) + _, err = m.IncrementHeightTo(11, false) r.NoError(err) chg = change.NewChange(change.AddSupport).SetName(name1).SetOutPoint(out3).SetHeight(12).SetAmount(2) @@ -128,7 +128,7 @@ func TestSupportAmounts(t *testing.T) { chg.ClaimID = change.NewClaimID(*out2) m.AppendChange(chg) - _, err = m.IncrementHeightTo(20) + _, err = m.IncrementHeightTo(20, false) r.NoError(err) n1, err := m.node(name1) @@ -194,14 +194,14 @@ func TestHasChildren(t *testing.T) { chg := change.NewChange(change.AddClaim).SetName([]byte("a")).SetOutPoint(out1).SetHeight(1).SetAmount(2) chg.ClaimID = change.NewClaimID(*out1) m.AppendChange(chg) - _, err = m.IncrementHeightTo(1) + _, err = m.IncrementHeightTo(1, false) r.NoError(err) r.False(m.hasChildren([]byte("a"), 1, nil, 1)) chg = change.NewChange(change.AddClaim).SetName([]byte("ab")).SetOutPoint(out2).SetHeight(2).SetAmount(2) chg.ClaimID = change.NewClaimID(*out2) m.AppendChange(chg) - _, err = m.IncrementHeightTo(2) + _, err = m.IncrementHeightTo(2, false) r.NoError(err) r.False(m.hasChildren([]byte("a"), 2, nil, 2)) r.True(m.hasChildren([]byte("a"), 2, nil, 1)) @@ -209,14 +209,14 @@ func TestHasChildren(t *testing.T) { chg = change.NewChange(change.AddClaim).SetName([]byte("abc")).SetOutPoint(out3).SetHeight(3).SetAmount(2) chg.ClaimID = change.NewClaimID(*out3) m.AppendChange(chg) - _, err = m.IncrementHeightTo(3) + _, err = m.IncrementHeightTo(3, false) r.NoError(err) r.False(m.hasChildren([]byte("a"), 3, nil, 2)) chg = change.NewChange(change.AddClaim).SetName([]byte("ac")).SetOutPoint(out1).SetHeight(4).SetAmount(2) chg.ClaimID = change.NewClaimID(*out4) m.AppendChange(chg) - _, err = m.IncrementHeightTo(4) + _, err = m.IncrementHeightTo(4, false) r.NoError(err) r.True(m.hasChildren([]byte("a"), 4, nil, 2)) } @@ -248,3 +248,52 @@ func TestCollectChildren(t *testing.T) { r.Len(c[7].SpentChildren, 0) } + +func TestTemporaryAddClaim(t *testing.T) { + + r := require.New(t) + + param.SetNetwork(wire.TestNet) + repo, err := noderepo.NewPebble(t.TempDir()) + r.NoError(err) + + m, err := NewBaseManager(repo) + r.NoError(err) + defer m.Close() + + _, err = m.IncrementHeightTo(10, false) + r.NoError(err) + + chg := change.NewChange(change.AddClaim).SetName(name1).SetOutPoint(out1).SetHeight(11) + m.AppendChange(chg) + _, err = m.IncrementHeightTo(11, false) + r.NoError(err) + + chg = chg.SetName(name2).SetOutPoint(out2).SetHeight(12) + m.AppendChange(chg) + _, err = m.IncrementHeightTo(12, true) + r.NoError(err) + + n1, err := m.node(name1) + r.NoError(err) + r.Equal(1, len(n1.Claims)) + r.NotNil(n1.Claims.find(byOut(*out1))) + + n2, err := m.node(name2) + r.NoError(err) + r.Equal(1, len(n2.Claims)) + r.NotNil(n2.Claims.find(byOut(*out2))) + + names, err := m.DecrementHeightTo([][]byte{name2}, 11) + r.Equal(names[0], name2) + r.NoError(err) + n2, err = m.node(name2) + r.NoError(err) + r.Nil(n2) + + _, err = m.DecrementHeightTo([][]byte{name1}, 1) + r.NoError(err) + n2, err = m.node(name1) + r.NoError(err) + r.Nil(n2) +} diff --git a/claimtrie/node/normalizing_manager.go b/claimtrie/node/normalizing_manager.go index d35403cd..2f6c4cfe 100644 --- a/claimtrie/node/normalizing_manager.go +++ b/claimtrie/node/normalizing_manager.go @@ -26,12 +26,12 @@ func (nm *NormalizingManager) AppendChange(chg change.Change) { nm.Manager.AppendChange(chg) } -func (nm *NormalizingManager) IncrementHeightTo(height int32) ([][]byte, error) { +func (nm *NormalizingManager) IncrementHeightTo(height int32, temporary bool) ([][]byte, error) { nm.addNormalizationForkChangesIfNecessary(height) - return nm.Manager.IncrementHeightTo(height) + return nm.Manager.IncrementHeightTo(height, temporary) } -func (nm *NormalizingManager) DecrementHeightTo(affectedNames [][]byte, height int32) error { +func (nm *NormalizingManager) DecrementHeightTo(affectedNames [][]byte, height int32) ([][]byte, error) { if nm.normalizedAt > height { nm.normalizedAt = -1 }