diff --git a/order/batch.go b/order/batch.go index 4808a7f..7bf5cd9 100644 --- a/order/batch.go +++ b/order/batch.go @@ -221,7 +221,7 @@ type BatchSigner interface { type BatchStorer interface { // StorePendingBatch makes sure all changes executed by a pending batch // are correctly and atomically stored to the database. - StorePendingBatch(*Batch) error + StorePendingBatch(_ *Batch, bestHeight uint32) error // MarkBatchComplete marks a pending batch as complete, allowing a // trader to participate in a new batch. diff --git a/order/batch_storer.go b/order/batch_storer.go index 78d031a..ad9a142 100644 --- a/order/batch_storer.go +++ b/order/batch_storer.go @@ -9,6 +9,13 @@ import ( "github.com/lightninglabs/llm/clmrpc" ) +const ( + // heightHintPadding is the padding we add to our best known height to + // avoid any discrepancies in block propagation between us and the + // auctioneer. + heightHintPadding = -3 +) + // batchStorer is a type that implements BatchStorer and can persist a batch to // the local trader database. type batchStorer struct { @@ -23,7 +30,7 @@ type batchStorer struct { // modifications will be applied atomically as a result of MarkBatchComplete. // // NOTE: This method is part of the BatchStorer interface. -func (s *batchStorer) StorePendingBatch(batch *Batch) error { +func (s *batchStorer) StorePendingBatch(batch *Batch, bestHeight uint32) error { // Prepare the order modifications first. orders := make([]Nonce, len(batch.MatchedOrders)) orderModifiers := make([][]Modifier, len(orders)) @@ -64,6 +71,13 @@ func (s *batchStorer) StorePendingBatch(batch *Batch) error { // Next create our account modifiers. accounts := make([]*account.Account, len(batch.AccountDiffs)) accountModifiers := make([][]account.Modifier, len(accounts)) + + // Each account will have the same height hint applied. + heightHint := int64(bestHeight) + heightHintPadding + if heightHint < 0 { + heightHint = 0 + } + for idx, diff := range batch.AccountDiffs { // Get the current state of the account first so we can create // a proper diff. @@ -107,10 +121,14 @@ func (s *batchStorer) StorePendingBatch(batch *Batch) error { diff.EndingState) } - // Finally update the account value and expiry. - accountModifiers[idx] = append( + // Finally update the account value and height hint. + modifiers = append( modifiers, account.ValueModifier(diff.EndingBalance), ) + modifiers = append( + modifiers, account.HeightHintModifier(uint32(heightHint)), + ) + accountModifiers[idx] = modifiers } // Everything is ready to be persisted now. diff --git a/order/batch_storer_test.go b/order/batch_storer_test.go index 316dd1d..db6e3da 100644 --- a/order/batch_storer_test.go +++ b/order/batch_storer_test.go @@ -15,6 +15,7 @@ import ( func TestBatchStorer(t *testing.T) { t.Parallel() + const bestHeight = 1337 var ( storeMock = newMockStore() storer = &batchStorer{ @@ -119,7 +120,7 @@ func TestBatchStorer(t *testing.T) { } // Pass the assembled batch to the storer now. - err := storer.StorePendingBatch(batch) + err := storer.StorePendingBatch(batch, bestHeight) if err != nil { t.Fatalf("error storing batch: %v", err) } @@ -165,6 +166,11 @@ func TestBatchStorer(t *testing.T) { t.Fatalf("invalid account expiry, got %d wanted %d", smallAcct.Value, 144) } + heightHint := uint32(bestHeight + heightHintPadding) + if smallAcct.HeightHint != heightHint { + t.Fatalf("invalid account height hint, got %d wanted %d", + smallAcct.Value, heightHint) + } if bigAcct.State != account.StatePendingUpdate { t.Fatalf("invalid account state, got %d wanted %d", @@ -178,6 +184,10 @@ func TestBatchStorer(t *testing.T) { t.Fatalf("invalid account expiry, got %d wanted %d", bigAcct.Value, 144) } + if bigAcct.HeightHint != heightHint { + t.Fatalf("invalid account height hint, got %d wanted %d", + bigAcct.Value, heightHint) + } } func newKit(nonce Nonce, units SupplyUnit) Kit { diff --git a/order/manager.go b/order/manager.go index e9b9730..4ff106a 100644 --- a/order/manager.go +++ b/order/manager.go @@ -239,13 +239,14 @@ func (m *Manager) PendingBatch() *Batch { // belong to the trader. Before sending off the signature to the auctioneer, // we'll also persist the batch to disk as pending to ensure we can recover // after a crash. -func (m *Manager) BatchSign() (BatchSignature, error) { +func (m *Manager) BatchSign(bestHeight uint32) (BatchSignature, error) { sig, err := m.batchSigner.Sign(m.pendingBatch) if err != nil { return nil, err } - if err := m.batchStorer.StorePendingBatch(m.pendingBatch); err != nil { + err = m.batchStorer.StorePendingBatch(m.pendingBatch, bestHeight) + if err != nil { return nil, fmt.Errorf("unable to store batch: %v", err) } diff --git a/rpcserver.go b/rpcserver.go index d2657e4..b2c25d0 100644 --- a/rpcserver.go +++ b/rpcserver.go @@ -643,7 +643,8 @@ func (s *rpcServer) handleServerMessage(rpcMsg *clmrpc.ServerAuctionMessage) err } // Sign for the accounts in the batch. - sigs, err := s.orderManager.BatchSign() + bestHeight := atomic.LoadUint32(&s.bestHeight) + sigs, err := s.orderManager.BatchSign(bestHeight) if err != nil { log.Errorf("Error signing batch: %v", err) return s.sendRejectBatch(batch, err)