Skip to content

Commit

Permalink
Merge pull request #323 from lightninglabs/caretaker_atomic_batch_state
Browse files Browse the repository at this point in the history
caretaker: make batch state atomic
  • Loading branch information
guggero authored May 30, 2023
2 parents 573ba81 + 837b930 commit 97c045e
Show file tree
Hide file tree
Showing 8 changed files with 87 additions and 25 deletions.
8 changes: 5 additions & 3 deletions rpcserver.go
Original file line number Diff line number Diff line change
Expand Up @@ -1881,7 +1881,9 @@ func marshalMintingBatch(batch *tapgarden.MintingBatch) (*mintrpc.MintingBatch,
func marshalBatchState(batch *tapgarden.MintingBatch) (mintrpc.BatchState,
error) {

switch batch.BatchState {
currentBatchState := batch.State()

switch currentBatchState {
case tapgarden.BatchStatePending:
return mintrpc.BatchState_BATCH_STATE_PEDNING, nil

Expand All @@ -1907,8 +1909,8 @@ func marshalBatchState(batch *tapgarden.MintingBatch) (mintrpc.BatchState,
return mintrpc.BatchState_BATCH_STATE_SPROUT_CANCELLED, nil

default:
return 0, fmt.Errorf("unknown batch state: %d",
batch.BatchState)
return 0, fmt.Errorf("unknown batch state: %v",
currentBatchState.String())
}
}

Expand Down
12 changes: 8 additions & 4 deletions tapdb/asset_minting.go
Original file line number Diff line number Diff line change
Expand Up @@ -832,9 +832,6 @@ func marshalMintingBatch(ctx context.Context, q PendingAssetStore,
// For each batch, we'll assemble an intermediate batch struct, then
// fill in all the seedlings with another sub-query.
batch := &tapgarden.MintingBatch{
BatchState: tapgarden.BatchState(
dbBatch.BatchState,
),
BatchKey: keychain.KeyDescriptor{
KeyLocator: keychain.KeyLocator{
Family: keychain.KeyFamily(
Expand All @@ -848,6 +845,13 @@ func marshalMintingBatch(ctx context.Context, q PendingAssetStore,
CreationTime: dbBatch.CreationTimeUnix.UTC(),
}

batchState, err := tapgarden.NewBatchState(uint8(dbBatch.BatchState))
if err != nil {
return nil, err
}

batch.UpdateState(batchState)

if dbBatch.MintingTxPsbt != nil {
genesisPkt, err := psbt.NewFromRawBytes(
bytes.NewReader(dbBatch.MintingTxPsbt), false,
Expand All @@ -867,7 +871,7 @@ func marshalMintingBatch(ctx context.Context, q PendingAssetStore,
// either fetch the set of seedlings (asset
// descriptions w/ no real assets), or the set of
// sprouts (full defined assets, but not yet mined).
switch batch.BatchState {
switch batchState {
case tapgarden.BatchStatePending,
tapgarden.BatchStateFrozen,
tapgarden.BatchStateSeedlingCancelled:
Expand Down
4 changes: 2 additions & 2 deletions tapdb/asset_minting_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,14 +53,14 @@ func newAssetStore(t *testing.T) (*AssetMintingStore, *AssetStore,
func assertBatchState(t *testing.T, batch *tapgarden.MintingBatch,
state tapgarden.BatchState) {

require.Equal(t, state, batch.BatchState)
require.Equal(t, state, batch.State())
}

func assertBatchEqual(t *testing.T, a, b *tapgarden.MintingBatch) {
t.Helper()

require.Equal(t, a.CreationTime.Unix(), b.CreationTime.Unix())
require.Equal(t, a.BatchState, b.BatchState)
require.Equal(t, a.State(), b.State())
require.Equal(t, a.BatchKey, b.BatchKey)
require.Equal(t, a.Seedlings, b.Seedlings)
require.Equal(t, a.GenesisPacket, b.GenesisPacket)
Expand Down
21 changes: 19 additions & 2 deletions tapgarden/batch.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package tapgarden

import (
"fmt"
"sync/atomic"
"time"

"github.com/btcsuite/btcd/btcec/v2"
Expand Down Expand Up @@ -30,8 +31,8 @@ type MintingBatch struct {
// transaction.
HeightHint uint32

// BatchState is the state of the batch.
BatchState BatchState
// batchState is the state of the batch.
batchState atomic.Uint32

// BatchKey is the unique identifier for a batch.
BatchKey keychain.KeyDescriptor
Expand Down Expand Up @@ -132,3 +133,19 @@ func (m *MintingBatch) genesisScript() ([]byte, error) {

return tapscript.PayToTaprootScript(mintingOutputKey)
}

// State returns the private state of the batch.
func (m *MintingBatch) State() BatchState {
currentBatchState := m.batchState.Load()

// Drop the error when converting the stored state to a BatchState, as
// we verify the batch state before storing it.
batchStateCopy, _ := NewBatchState(uint8(currentBatchState))
return batchStateCopy
}

// UpdateState updates the state of a batch to a value that has been verified to
// be a valid batch state.
func (m *MintingBatch) UpdateState(state BatchState) {
m.batchState.Store(uint32(state))
}
14 changes: 8 additions & 6 deletions tapgarden/caretaker.go
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,7 @@ func (b *BatchCaretaker) Cancel() CancelResp {
defer cancel()

batchKey := b.cfg.Batch.BatchKey.PubKey.SerializeCompressed()
batchState := b.cfg.Batch.BatchState
batchState := b.cfg.Batch.State()
// This function can only be called before the caretaker state stepping
// function, so the batch state read is the next state that has not yet
// been executed. Seedlings are converted to asset sprouts in the Frozen
Expand Down Expand Up @@ -235,7 +235,7 @@ func (b *BatchCaretaker) advanceStateUntil(currentState,

currentState = nextState

b.cfg.Batch.BatchState = currentState
b.cfg.Batch.UpdateState(currentState)
}

return currentState, nil
Expand All @@ -248,9 +248,10 @@ func (b *BatchCaretaker) advanceStateUntil(currentState,
func (b *BatchCaretaker) assetCultivator() {
defer b.Wg.Done()

currentBatchState := b.cfg.Batch.State()
// If the batch is already marked as confirmed, then we just need to
// advance it one more level to be finalized.
if b.cfg.Batch.BatchState == BatchStateConfirmed {
if currentBatchState == BatchStateConfirmed {
log.Infof("MintingBatch(%x): already confirmed!", b.batchKey[:])

_, err := b.advanceStateUntil(
Expand All @@ -271,7 +272,7 @@ func (b *BatchCaretaker) assetCultivator() {
// confirmation notification, which'll let us advance to the final
// state.
_, err := b.advanceStateUntil(
b.cfg.Batch.BatchState, BatchStateBroadcast,
currentBatchState, BatchStateBroadcast,
)
if err != nil {
log.Errorf("unable to advance state machine: %v", err)
Expand All @@ -295,11 +296,12 @@ func (b *BatchCaretaker) assetCultivator() {
confInfo.BlockHash, confInfo.BlockHeight)

b.confInfo = confInfo
b.cfg.Batch.BatchState = BatchStateConfirmed
b.cfg.Batch.UpdateState(BatchStateConfirmed)
currentBatchState = b.cfg.Batch.State()

// TODO(roasbeef): use a "trigger" here instead?
_, err = b.advanceStateUntil(
b.cfg.Batch.BatchState, BatchStateFinalized,
currentBatchState, BatchStateFinalized,
)
if err != nil {
log.Error(err)
Expand Down
36 changes: 35 additions & 1 deletion tapgarden/interface.go
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,41 @@ func (b BatchState) String() string {
return "BatchStateSproutCancelled"

default:
return fmt.Sprintf("UnknownState(%v)", int(b))
return fmt.Sprintf("UnknownState(%d)", b)
}
}

// NewBatchState creates a BatchState from a uint8, returning an error if the
// input value does not map to a valid BatchState.
func NewBatchState(state uint8) (BatchState, error) {
switch BatchState(state) {
case BatchStatePending:
return BatchStatePending, nil

case BatchStateFrozen:
return BatchStateFrozen, nil

case BatchStateCommitted:
return BatchStateCommitted, nil

case BatchStateBroadcast:
return BatchStateBroadcast, nil

case BatchStateConfirmed:
return BatchStateConfirmed, nil

case BatchStateFinalized:
return BatchStateFinalized, nil

case BatchStateSeedlingCancelled:
return BatchStateSeedlingCancelled, nil

case BatchStateSproutCancelled:
return BatchStateSproutCancelled, nil

default:
return BatchStateSproutCancelled,
fmt.Errorf("unknown batch state: %v", state)
}
}

Expand Down
8 changes: 5 additions & 3 deletions tapgarden/planter.go
Original file line number Diff line number Diff line change
Expand Up @@ -254,8 +254,10 @@ func (c *ChainPlanter) Start() error {
// caretaker which'll handle progressing each batch to
// completion. We'll skip batches that were cancelled.
for _, batch := range nonFinalBatches {
if batch.BatchState == BatchStateSeedlingCancelled ||
batch.BatchState == BatchStateSproutCancelled {
batchState := batch.State()

if batchState == BatchStateSeedlingCancelled ||
batchState == BatchStateSproutCancelled {

continue
}
Expand Down Expand Up @@ -748,13 +750,13 @@ func (c *ChainPlanter) prepAssetSeedling(ctx context.Context,
newBatch := &MintingBatch{
CreationTime: time.Now(),
HeightHint: currentHeight,
BatchState: BatchStatePending,
BatchKey: newInternalKey,
Seedlings: map[string]*Seedling{
req.AssetName: req,
},
AssetMetas: make(AssetMetas),
}
newBatch.UpdateState(BatchStatePending)
ctx, cancel = c.WithCtxQuit()
defer cancel()
err = c.cfg.Log.CommitMintingBatch(ctx, newBatch)
Expand Down
9 changes: 5 additions & 4 deletions tapgarden/planter_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -363,8 +363,9 @@ func (t *mintingTestHarness) assertSeedlingsExist(
}

func isCancelledBatch(batch *tapgarden.MintingBatch) bool {
return batch.BatchState == tapgarden.BatchStateSeedlingCancelled ||
batch.BatchState == tapgarden.BatchStateSproutCancelled
batchState := batch.State()
return batchState == tapgarden.BatchStateSeedlingCancelled ||
batchState == tapgarden.BatchStateSproutCancelled
}

func (t *mintingTestHarness) assertBatchState(batchKey *btcec.PublicKey,
Expand All @@ -377,7 +378,7 @@ func (t *mintingTestHarness) assertBatchState(batchKey *btcec.PublicKey,
require.Len(t, batches, 1)

batch := batches[0]
require.Equal(t, batchState, batch.BatchState)
require.Equal(t, batchState, batch.State())
}

// assertSeedlingsMatchSprouts asserts that the seedlings were properly matched
Expand All @@ -398,7 +399,7 @@ func (t *mintingTestHarness) assertSeedlingsMatchSprouts(

// Filter out any cancelled batches.
isCommittedBatch := func(batch *tapgarden.MintingBatch) bool {
return batch.BatchState == tapgarden.BatchStateCommitted
return batch.State() == tapgarden.BatchStateCommitted
}
batch, err := chanutils.First(pendingBatches, isCommittedBatch)
if err != nil {
Expand Down

0 comments on commit 97c045e

Please sign in to comment.