Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow distinct clearing price per lease duration bucket #142

Closed
wants to merge 11 commits into from
3 changes: 1 addition & 2 deletions account/mock_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -214,8 +214,7 @@ func (a *mockAuctioneer) SubscribeAccountUpdates(_ context.Context,

func (a *mockAuctioneer) Terms(context.Context) (*terms.AuctioneerTerms, error) {
return &terms.AuctioneerTerms{
MaxAccountValue: maxAccountValue,
MaxOrderDuration: maxAccountExpiry,
MaxAccountValue: maxAccountValue,
}, nil
}

Expand Down
2 changes: 1 addition & 1 deletion auctioneer/account_subscription.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ func (s *acctSubscription) authenticate(ctx context.Context) error {
Msg: &poolrpc.ClientAuctionMessage_Commit{
Commit: &poolrpc.AccountCommitment{
CommitHash: s.commitHash[:],
BatchVersion: uint32(order.CurrentVersion),
BatchVersion: uint32(order.CurrentBatchVersion),
},
},
})
Expand Down
15 changes: 7 additions & 8 deletions auctioneer/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -1162,14 +1162,13 @@ func (c *Client) Terms(ctx context.Context) (*terms.AuctioneerTerms, error) {
}

return &terms.AuctioneerTerms{
MaxAccountValue: btcutil.Amount(resp.MaxAccountValue),
MaxOrderDuration: resp.MaxOrderDurationBlocks,
OrderExecBaseFee: btcutil.Amount(resp.ExecutionFee.BaseFee),
OrderExecFeeRate: btcutil.Amount(resp.ExecutionFee.FeeRate),
LeaseDurations: resp.LeaseDurations,
NextBatchConfTarget: resp.NextBatchConfTarget,
NextBatchFeeRate: chainfee.SatPerKWeight(resp.NextBatchFeeRateSatPerKw),
NextBatchClear: time.Unix(int64(resp.NextBatchClearTimestamp), 0),
MaxAccountValue: btcutil.Amount(resp.MaxAccountValue),
OrderExecBaseFee: btcutil.Amount(resp.ExecutionFee.BaseFee),
OrderExecFeeRate: btcutil.Amount(resp.ExecutionFee.FeeRate),
LeaseDurationBuckets: resp.LeaseDurationBuckets,
NextBatchConfTarget: resp.NextBatchConfTarget,
NextBatchFeeRate: chainfee.SatPerKWeight(resp.NextBatchFeeRateSatPerKw),
NextBatchClear: time.Unix(int64(resp.NextBatchClearTimestamp), 0),
}, nil
}

Expand Down
66 changes: 57 additions & 9 deletions clientdb/batch_snapshot.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,8 +65,9 @@ type LocalBatchSnapshot struct {
// BatchID is the batch's unique ID.
BatchID order.BatchID

// ClearingPrice is the fixed rate the orders were cleared at.
ClearingPrice order.FixedRatePremium
// ClearingPrices is a map of the lease duration markets and the fixed
// rate the orders were cleared at within that market.
ClearingPrices map[uint32]order.FixedRatePremium

// ExecutionFee is the FeeSchedule that was used by the server to
// calculate the execution fee.
Expand Down Expand Up @@ -120,7 +121,7 @@ func NewSnapshot(batch *order.Batch, ourOrders []order.Order,
snapshot := &LocalBatchSnapshot{
Version: batch.Version,
BatchID: batch.ID,
ClearingPrice: batch.ClearingPrice,
ClearingPrices: batch.ClearingPrices,
ExecutionFee: *feeSched,
BatchTX: batch.BatchTX,
BatchTxFeeRate: batch.BatchTxFeeRate,
Expand Down Expand Up @@ -398,8 +399,12 @@ func getSnapshotBuckets(tx *bbolt.Tx) (*bbolt.Bucket, *bbolt.Bucket,
}

func serializeLocalBatchSnapshot(w io.Writer, b *LocalBatchSnapshot) error {
// The previous batch versions had a single clearing price but because
// we now always store the price map afterwards, we signal a new batch
// by storing an explicit zero price.
var zeroPrice order.FixedRatePremium
err := WriteElements(
w, uint32(b.Version), b.BatchID[:], b.ClearingPrice,
w, b.Version, b.BatchID[:], zeroPrice,
b.ExecutionFee, b.BatchTX, b.BatchTxFeeRate,
)
if err != nil {
Expand Down Expand Up @@ -442,22 +447,37 @@ func serializeLocalBatchSnapshot(w io.Writer, b *LocalBatchSnapshot) error {
}
}

// New batch versions have an additional map of duration->price that we
guggero marked this conversation as resolved.
Show resolved Hide resolved
// need to serialize. Since both values are uint32 this is pretty
// straightforward.
numPrices := uint32(len(b.ClearingPrices))
err = WriteElements(w, numPrices)
if err != nil {
return err
}
for duration, price := range b.ClearingPrices {
err = WriteElements(w, duration, price)
if err != nil {
return err
}
}

return nil
}

func deserializeLocalBatchSnapshot(r io.Reader) (*LocalBatchSnapshot, error) {
b := &LocalBatchSnapshot{}
var version uint32
b := &LocalBatchSnapshot{
ClearingPrices: make(map[uint32]order.FixedRatePremium),
}
var clearingPrice order.FixedRatePremium
err := ReadElements(
r, &version, b.BatchID[:], &b.ClearingPrice, &b.ExecutionFee,
r, &b.Version, b.BatchID[:], &clearingPrice, &b.ExecutionFee,
&b.BatchTX, &b.BatchTxFeeRate,
)
if err != nil {
return nil, err
}

b.Version = order.BatchVersion(version)

b.Accounts, err = deserializeAccounts(r)
if err != nil {
return nil, err
Expand All @@ -484,6 +504,34 @@ func deserializeLocalBatchSnapshot(r io.Reader) (*LocalBatchSnapshot, error) {
b.MatchedOrders[nonce] = append(b.MatchedOrders[nonce], m)
}

// Older batches had a single clearing price instead of a map. If we
// have a non-zero price, it means this is an old snapshot and we don't
// need to read any further.
if clearingPrice > 0 {
guggero marked this conversation as resolved.
Show resolved Hide resolved
b.ClearingPrices[order.LegacyLeaseDurationBucket] = clearingPrice
wpaulino marked this conversation as resolved.
Show resolved Hide resolved

guggero marked this conversation as resolved.
Show resolved Hide resolved
return b, nil
}

var numPrices uint32
err = ReadElements(r, &numPrices)
if err != nil {
return nil, err
}

for i := uint32(0); i < numPrices; i++ {
var (
price order.FixedRatePremium
duration uint32
)
err = ReadElements(r, &duration, &price)
guggero marked this conversation as resolved.
Show resolved Hide resolved
if err != nil {
return nil, err
}

b.ClearingPrices[duration] = price
}

return b, nil
}

Expand Down
152 changes: 90 additions & 62 deletions clientdb/batch_snapshot_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package clientdb

import (
"bytes"
"fmt"
"net"
"reflect"
"testing"
Expand All @@ -15,46 +16,37 @@ import (
"go.etcd.io/bbolt"
)

var testAccount = &account.Account{
Value: btcutil.SatoshiPerBitcoin,
Expiry: 1337,
TraderKey: testTraderKeyDesc,
AuctioneerKey: testAuctioneerKey,
BatchKey: testBatchKey,
Secret: sharedSecret,
State: account.StateInitiated,
HeightHint: 1,
}
var (
testAccount = &account.Account{
Value: btcutil.SatoshiPerBitcoin,
Expiry: 1337,
TraderKey: testTraderKeyDesc,
AuctioneerKey: testAuctioneerKey,
BatchKey: testBatchKey,
Secret: sharedSecret,
State: account.StateInitiated,
HeightHint: 1,
}

var testNonce1 = order.Nonce([32]byte{1, 1, 1})
var testNonce2 = order.Nonce([32]byte{2, 2, 2})
testNonce1 = order.Nonce([32]byte{1, 1, 1})
testNonce2 = order.Nonce([32]byte{2, 2, 2})

func newOrderKit(nonce order.Nonce, duration uint32) *order.Kit {
kit := order.NewKit(nonce)
kit.LeaseDuration = duration
return kit
}
testDuration uint32 = 144

var testSnapshot = &LocalBatchSnapshot{
Version: 5,
BatchID: testBatchID,
ClearingPrice: 999,
ExecutionFee: *terms.NewLinearFeeSchedule(101, 900),
BatchTX: testBatchTx,
BatchTxFeeRate: 123456,
Accounts: map[[33]byte]*account.Account{
testAccounts = map[[33]byte]*account.Account{
testRawTraderKeyArr: testAccount,
},
Orders: map[order.Nonce]order.Order{
}

testOrders = map[order.Nonce]order.Order{
testNonce1: &order.Bid{
Kit: *newOrderKit(testNonce1, 144),
Kit: *newOrderKit(testNonce1, testDuration),
},
testNonce2: &order.Ask{
Kit: *newOrderKit(testNonce2, 1024),
Kit: *newOrderKit(testNonce2, testDuration),
},
},
}

MatchedOrders: map[order.Nonce][]*order.MatchedOrder{
testMatchedOrders = map[order.Nonce][]*order.MatchedOrder{
testNonce1: {
{
Order: &order.Ask{
Expand All @@ -64,7 +56,9 @@ var testSnapshot = &LocalBatchSnapshot{
NodeKey: [33]byte{1, 2, 3},
NodeAddrs: []net.Addr{
&net.TCPAddr{
IP: net.IP{0x12, 0x34, 0x56, 0x78},
IP: net.IP{
0x12, 0x34, 0x56, 0x78,
},
Port: 8080,
},
},
Expand All @@ -78,7 +72,9 @@ var testSnapshot = &LocalBatchSnapshot{
NodeKey: [33]byte{2, 2, 3},
NodeAddrs: []net.Addr{
&net.TCPAddr{
IP: net.IP{0x12, 0x34, 0x56, 0x78},
IP: net.IP{
0x12, 0x34, 0x56, 0x78,
},
Port: 8080,
},
},
Expand All @@ -95,7 +91,9 @@ var testSnapshot = &LocalBatchSnapshot{
NodeKey: [33]byte{1, 2, 3},
NodeAddrs: []net.Addr{
&net.TCPAddr{
IP: net.IP{0x12, 0x34, 0x56, 0x78},
IP: net.IP{
0x12, 0x34, 0x56, 0x78,
},
Port: 8080,
},
},
Expand All @@ -110,44 +108,77 @@ var testSnapshot = &LocalBatchSnapshot{
NodeKey: [33]byte{2, 2, 3},
NodeAddrs: []net.Addr{
&net.TCPAddr{
IP: net.IP{0x12, 0x34, 0x56, 0x78},
IP: net.IP{
0x12, 0x34, 0x56, 0x78,
},
Port: 8080,
},
},

UnitsFilled: 10,
},
},
},
}

testSnapshot = &LocalBatchSnapshot{
Version: order.DefaultBatchVersion,
BatchID: testBatchID,
ClearingPrices: map[uint32]order.FixedRatePremium{
testDuration: 999,
testDuration * 2: 1234,
},
ExecutionFee: *terms.NewLinearFeeSchedule(101, 900),
BatchTX: testBatchTx,
BatchTxFeeRate: 123456,
Accounts: testAccounts,
Orders: testOrders,
MatchedOrders: testMatchedOrders,
}

allSnapshots = []*LocalBatchSnapshot{
testSnapshot,
{
Version: order.DefaultBatchVersion,
BatchID: testBatchID,
ClearingPrices: map[uint32]order.FixedRatePremium{
order.LegacyLeaseDurationBucket: 999,
},
ExecutionFee: *terms.NewLinearFeeSchedule(101, 900),
BatchTX: testBatchTx,
BatchTxFeeRate: 123456,
Accounts: testAccounts,
Orders: testOrders,
MatchedOrders: testMatchedOrders,
},
}
)

func newOrderKit(nonce order.Nonce, duration uint32) *order.Kit {
kit := order.NewKit(nonce)
kit.LeaseDuration = duration
return kit
}

// TestSerializeLocalBatchSnapshot checks that (de)serialization of local batch
// snapshots worsk as expected.
// snapshots works as expected.
func TestSerializeLocalBatchSnapshot(t *testing.T) {
pre := testSnapshot
buf := bytes.Buffer{}
if err := serializeLocalBatchSnapshot(&buf, pre); err != nil {
t.Fatal(err)
}
for idx, testSnapshot := range allSnapshots {
pre := testSnapshot
t.Run(fmt.Sprintf("snapshot-%d", idx), func(t *testing.T) {
t.Parallel()

post, err := deserializeLocalBatchSnapshot(&buf)
if err != nil {
t.Fatal(err)
}
buf := bytes.Buffer{}
if err := serializeLocalBatchSnapshot(&buf, pre); err != nil {
t.Fatal(err)
}

if !reflect.DeepEqual(pre, post) {
for _, a := range pre.Accounts {
a.TraderKey.PubKey.Curve = nil
a.AuctioneerKey.Curve = nil
a.BatchKey.Curve = nil
}
for _, a := range post.Accounts {
a.TraderKey.PubKey.Curve = nil
a.AuctioneerKey.Curve = nil
a.BatchKey.Curve = nil
}
post, err := deserializeLocalBatchSnapshot(&buf)
if err != nil {
t.Fatal(err)
}

t.Fatalf("mismatch: %v vs %v", spew.Sdump(pre), spew.Sdump(post))
require.Equal(t, pre, post)
})
}
}

Expand Down Expand Up @@ -198,10 +229,7 @@ func TestGetLocalBatchSnapshots(t *testing.T) {

for i, snapshot := range snapshots {
testSnapshot.BatchID[0] = byte(i)
if !reflect.DeepEqual(testSnapshot, snapshot) {
t.Fatalf("mismatch: %v vs %v",
spew.Sdump(testSnapshot), spew.Sdump(snapshot))
}
require.Equal(t, testSnapshot, snapshot)
}

// Store and delete a pending snapshot, and make sure all other batches are
Expand Down
Loading