order+rpcserver: enforce order duration limit and multiple

This commit is contained in:
Oliver Gugger 2020-08-18 13:15:39 +02:00
parent 36f50534dd
commit 7fb4ef9782
No known key found for this signature in database
GPG key ID: 8E4256593F177720
3 changed files with 45 additions and 25 deletions

View file

@ -123,11 +123,11 @@ func (m *Manager) Stop() {
// PrepareOrder validates an order, signs it and then stores it locally.
func (m *Manager) PrepareOrder(ctx context.Context, order Order,
acct *account.Account, feeSchedule terms.FeeSchedule) (
*ServerOrderParams, error) {
acct *account.Account,
terms *terms.AuctioneerTerms) (*ServerOrderParams, error) {
// Verify incoming request for formal validity.
err := m.validateOrder(order, acct, feeSchedule)
err := m.validateOrder(order, acct, terms)
if err != nil {
return nil, err
}
@ -196,7 +196,7 @@ func (m *Manager) PrepareOrder(ctx context.Context, order Order,
// validateOrder makes sure an order is formally correct and that the associated
// account contains enough balance to execute the order.
func (m *Manager) validateOrder(order Order, acct *account.Account,
feeSchedule terms.FeeSchedule) error {
terms *terms.AuctioneerTerms) error {
// First parse order type specific fields.
switch o := order.(type) {
@ -205,12 +205,30 @@ func (m *Manager) validateOrder(order Order, acct *account.Account,
return fmt.Errorf("invalid max duration, must be "+
"at least %d", MinimumOrderDurationBlocks)
}
if o.MaxDuration > terms.MaxOrderDuration {
return fmt.Errorf("invalid max duration, must be "+
"smaller than or equal to %d",
terms.MaxOrderDuration)
}
if o.MaxDuration%MinimumOrderDurationBlocks != 0 {
return fmt.Errorf("invalid max duration, must be "+
"multiple of %d", MinimumOrderDurationBlocks)
}
case *Bid:
if o.MinDuration < MinimumOrderDurationBlocks {
return fmt.Errorf("invalid min duration, must be "+
"at least %d", MinimumOrderDurationBlocks)
}
if o.MinDuration > terms.MaxOrderDuration {
return fmt.Errorf("invalid min duration, must be "+
"smaller than or equal to %d",
terms.MaxOrderDuration)
}
if o.MinDuration%MinimumOrderDurationBlocks != 0 {
return fmt.Errorf("invalid min duration, must be "+
"multiple of %d", MinimumOrderDurationBlocks)
}
default:
return fmt.Errorf("invalid order type: %v", o)
@ -232,6 +250,7 @@ func (m *Manager) validateOrder(order Order, acct *account.Account,
// value when adding this order.
var acctKey [33]byte
copy(acctKey[:], acct.TraderKey.PubKey.SerializeCompressed())
feeSchedule := terms.FeeSchedule()
reserved := order.ReservedValue(feeSchedule)
for _, o := range dbOrders {
// Only tally the reserved balance if this order waas submited

View file

@ -50,12 +50,14 @@ func TestValidateOrderAccountIsolation(t *testing.T) {
MaxDuration: 144,
}
simpleFeeSchedule := terms.NewLinearFeeSchedule(1, 100)
testTerms := &terms.AuctioneerTerms{
MaxOrderDuration: 100 * MinimumOrderDurationBlocks,
OrderExecBaseFee: 1,
OrderExecFeeRate: 100,
}
// Submitting this order for account B should pass validation.
err := orderManager.validateOrder(
orderB, &accountB, simpleFeeSchedule,
)
err := orderManager.validateOrder(orderB, &accountB, testTerms)
if err != nil {
t.Fatalf("order B validation failed: %v", err)
}
@ -82,9 +84,7 @@ func TestValidateOrderAccountIsolation(t *testing.T) {
MaxDuration: 144,
}
err = orderManager.validateOrder(
orderA, &accountA, simpleFeeSchedule,
)
err = orderManager.validateOrder(orderA, &accountA, testTerms)
if err != nil {
t.Fatalf("order A failed validation: %v", err)
}

View file

@ -938,14 +938,16 @@ func (s *rpcServer) ListAccounts(ctx context.Context,
// Get the current fee schedule so we can compute the worst-case
// account debit assuming all our standing orders were matched.
auctionFeeSchedule, err := s.auctioneer.FeeQuote(ctx)
terms, err := s.auctioneer.Terms(ctx)
if err != nil {
return nil, err
return nil, fmt.Errorf("unable to query auctioneer terms: %v",
err)
}
// For each active account, consume the worst-case account delta if the
// order were to be matched.
accountDebits := make(map[[33]byte]btcutil.Amount)
auctionFeeSchedule := terms.FeeSchedule()
for _, account := range accounts {
var (
debitAmt btcutil.Amount
@ -1354,18 +1356,16 @@ func (s *rpcServer) SubmitOrder(ctx context.Context,
"make order", o.Details().AcctKey[:], acct.State)
}
// Get the current fee schedule to ensure we have enough balance to pay
// the fees.
feeSchedule, err := s.auctioneer.FeeQuote(ctx)
// We also need to know the current maximum order duration.
terms, err := s.auctioneer.Terms(ctx)
if err != nil {
return nil, err
return nil, fmt.Errorf("could not query auctioneer terms: %v",
err)
}
// Collect all the order data and sign it before sending it to the
// auction server.
serverParams, err := s.orderManager.PrepareOrder(
ctx, o, acct, feeSchedule,
)
serverParams, err := s.orderManager.PrepareOrder(ctx, o, acct, terms)
if err != nil {
return nil, err
}
@ -1691,18 +1691,19 @@ func (s *rpcServer) sendSignBatch(batch *order.Batch, sigs order.BatchSignature,
// AuctionFee returns the current fee rate charged for matched orders within
// the auction.
func (s *rpcServer) AuctionFee(ctx context.Context,
req *clmrpc.AuctionFeeRequest) (*clmrpc.AuctionFeeResponse, error) {
_ *clmrpc.AuctionFeeRequest) (*clmrpc.AuctionFeeResponse, error) {
feeSchedule, err := s.auctioneer.FeeQuote(ctx)
terms, err := s.auctioneer.Terms(ctx)
if err != nil {
return nil, err
return nil, fmt.Errorf("unable to query auctioneer terms: %v",
err)
}
// TODO(roasbeef): accept the amt of order instead?
return &clmrpc.AuctionFeeResponse{
ExecutionFee: &clmrpc.ExecutionFee{
BaseFee: uint64(feeSchedule.BaseFee()),
FeeRate: uint64(feeSchedule.FeeRate()),
BaseFee: uint64(terms.OrderExecBaseFee),
FeeRate: uint64(terms.OrderExecFeeRate),
},
}, nil
}