Merge pull request #300 from lightninglabs/lease-script-enfroce

multi: script lease enforcement
This commit is contained in:
Olaoluwa Osuntokun 2021-11-09 17:32:04 -08:00 committed by GitHub
commit f6eebcb8a5
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
22 changed files with 1873 additions and 1507 deletions

View file

@ -406,6 +406,17 @@ func (c *Client) SubmitOrder(ctx context.Context, o order.Order,
Addr: addr.String(),
})
}
var channelType auctioneerrpc.OrderChannelType
switch o.Details().ChannelType {
case order.ChannelTypePeerDependent:
channelType = auctioneerrpc.OrderChannelType_ORDER_CHANNEL_TYPE_PEER_DEPENDENT
case order.ChannelTypeScriptEnforced:
channelType = auctioneerrpc.OrderChannelType_ORDER_CHANNEL_TYPE_SCRIPT_ENFORCED
default:
return fmt.Errorf("unhandled channel type %v", c)
}
details := &auctioneerrpc.ServerOrder{
TraderKey: o.Details().AcctKey[:],
RateFixed: o.Details().FixedRate,
@ -416,6 +427,7 @@ func (c *Client) SubmitOrder(ctx context.Context, o order.Order,
MultiSigKey: serverParams.MultiSigKey[:],
NodePub: serverParams.NodePubkey[:],
NodeAddr: nodeAddrs,
ChannelType: channelType,
MaxBatchFeeRateSatPerKw: uint64(o.Details().MaxBatchFeeRate),
}

File diff suppressed because it is too large Load diff

View file

@ -345,6 +345,13 @@ enum ChannelType {
// The channel uses an anchor-based commitment.
ANCHORS = 1;
/*
The channel build upon the anchor-based commitment and requires an
additional CLTV of the channel lease maturity on any commitment and HTLC
outputs that pay directly to the channel initiator (the seller).
*/
SCRIPT_ENFORCED_LEASE = 2;
}
message ChannelInfo {
@ -545,6 +552,13 @@ message OrderMatchPrepare {
within and the discovered market clearing price.
*/
map<uint32, MatchedMarket> matched_markets = 10;
/*
The earliest absolute height in the chain in which the batch transaction can
be found within. This will be used by traders to base off their absolute
channel lease maturity height.
*/
uint32 batch_height_hint = 11;
}
message OrderMatchSignBegin {
@ -565,11 +579,8 @@ message OrderMatchFinalize {
*/
bytes batch_txid = 2;
/*
The current block height at the time the batch transaction was published to
the network.
*/
uint32 height_hint = 3;
// Don't re-use, this was a field that was removed.
reserved 3;
}
message SubscribeError {
@ -783,6 +794,24 @@ message AccountDiff {
bytes trader_key = 4;
}
enum OrderChannelType {
// Used to set defaults when a trader doesn't specify a channel type.
ORDER_CHANNEL_TYPE_UNKNOWN = 0;
/*
The channel type will vary per matched channel based on the features shared
between its participants.
*/
ORDER_CHANNEL_TYPE_PEER_DEPENDENT = 1;
/*
A channel type that builds upon the anchors commitment format to enforce
channel lease maturities in the commitment and HTLC outputs that pay to the
channel initiator/seller.
*/
ORDER_CHANNEL_TYPE_SCRIPT_ENFORCED = 2;
}
message ServerOrder {
/*
The trader's account key of the account to use for the order.
@ -843,7 +872,7 @@ message ServerOrder {
/*
The type of the channel that should be opened.
*/
uint32 chan_type = 12;
OrderChannelType channel_type = 12;
/*
Maximum fee rate the trader is willing to pay for the batch transaction,
@ -1202,7 +1231,7 @@ message AskSnapshot {
uint32 rate_fixed = 3;
// The channel type to be created.
uint32 chan_type = 4;
OrderChannelType chan_type = 4;
}
message BidSnapshot {
// The version of the order.
@ -1215,7 +1244,7 @@ message BidSnapshot {
uint32 rate_fixed = 3;
// The channel type to be created.
uint32 chan_type = 4;
OrderChannelType chan_type = 4;
}
message MatchedOrderSnapshot {
// The full ask order that was matched.

View file

@ -152,7 +152,8 @@ func isSupportedBackupVersion(backup *chanbackup.Single) bool {
switch backup.Version {
case chanbackup.TweaklessCommitVersion,
chanbackup.AnchorsCommitVersion,
chanbackup.AnchorsZeroFeeHtlcTxCommitVersion:
chanbackup.AnchorsZeroFeeHtlcTxCommitVersion,
chanbackup.ScriptEnforcedLeaseVersion:
return true
default:

View file

@ -7,6 +7,7 @@ import (
"github.com/lightninglabs/lndclient"
"github.com/lightninglabs/pool/order"
"github.com/lightningnetwork/lnd/lnwallet"
"github.com/lightningnetwork/lnd/lnwire"
)
@ -139,5 +140,39 @@ func (s *ChannelAcceptor) acceptChannel(_ context.Context,
}, nil
}
switch expectedChanBid.ChannelType {
// The bid doesn't have specific requirements for the channel type.
case order.ChannelTypePeerDependent:
break
// The bid expects a channel type that enforces the channel lease
// maturity in its output scripts.
case order.ChannelTypeScriptEnforced:
if req.CommitmentType == nil {
return &lndclient.AcceptorResponse{
Accept: false,
Error: "expected explicit channel negotiation",
}, nil
}
switch *req.CommitmentType {
case lnwallet.CommitmentTypeScriptEnforcedLease:
default:
return &lndclient.AcceptorResponse{
Accept: false,
Error: "expected script enforced channel " +
"lease commitment type",
}, nil
}
default:
log.Warnf("Unhandled channel type %v for bid %v",
expectedChanBid.ChannelType, expectedChanBid.Nonce())
return &lndclient.AcceptorResponse{
Accept: false,
Error: "internal error",
}, nil
}
return &lndclient.AcceptorResponse{Accept: true}, nil
}

View file

@ -22,6 +22,10 @@ const (
// bidSidecarTicketType is the tlv type we use to store the sidecar
// ticket on bid orders.
bidSidecarTicketType tlv.Type = 2
// orderChannelType is the tlv type we use to store the desired channel
// type resulting from a matched order.
orderChannelType tlv.Type = 3
)
var (
@ -637,6 +641,7 @@ func deserializeOrderTlvData(r io.Reader, o order.Order) error {
var (
selfChanBalance uint64
sidecarTicket []byte
channelType uint8
)
// We'll add records for all possible additional order data fields here
@ -647,6 +652,7 @@ func deserializeOrderTlvData(r io.Reader, o order.Order) error {
bidSelfChanBalanceType, &selfChanBalance,
),
tlv.MakePrimitiveRecord(bidSidecarTicketType, &sidecarTicket),
tlv.MakePrimitiveRecord(orderChannelType, &channelType),
)
if err != nil {
return err
@ -679,6 +685,10 @@ func deserializeOrderTlvData(r io.Reader, o order.Order) error {
}
}
if t, ok := parsedTypes[orderChannelType]; ok && t == nil {
o.Details().ChannelType = order.ChannelType(channelType)
}
return nil
}
@ -713,6 +723,11 @@ func serializeOrderTlvData(w io.Writer, o order.Order) error {
}
}
channelType := uint8(o.Details().ChannelType)
tlvRecords = append(tlvRecords, tlv.MakePrimitiveRecord(
orderChannelType, &channelType,
))
tlvStream, err := tlv.NewStream(tlvRecords...)
if err != nil {
return err

View file

@ -42,6 +42,7 @@ func TestSubmitOrder(t *testing.T) {
},
}
o.Details().MinUnitsMatch = 10
o.Details().ChannelType = order.ChannelTypeScriptEnforced
err := store.SubmitOrder(o)
if err != nil {
t.Fatalf("unable to store order: %v", err)
@ -100,6 +101,7 @@ func TestUpdateOrders(t *testing.T) {
MinNodeTier: 3,
}
o1.Details().MinUnitsMatch = 10
o1.Details().ChannelType = order.ChannelTypeScriptEnforced
err := store.SubmitOrder(o1)
if err != nil {
t.Fatalf("unable to store order: %v", err)
@ -107,6 +109,7 @@ func TestUpdateOrders(t *testing.T) {
o2 := &order.Ask{
Kit: *dummyOrder(500000, 1337),
}
o2.Details().ChannelType = order.ChannelTypeScriptEnforced
err = store.SubmitOrder(o2)
if err != nil {
t.Fatalf("unable to store order: %v", err)

View file

@ -10,6 +10,7 @@ import (
"github.com/btcsuite/btcutil"
"github.com/lightninglabs/pool/auctioneer"
"github.com/lightninglabs/pool/auctioneerrpc"
"github.com/lightninglabs/pool/order"
"github.com/lightninglabs/pool/poolrpc"
"github.com/lightninglabs/pool/sidecar"
@ -20,6 +21,9 @@ import (
const (
defaultAskMaxDuration = 2016
defaultBidMinDuration = 2016
channelTypePeerDependent = "legacy"
channelTypeScriptEnforced = "script-enforced"
)
// Default max batch fee rate to 100 sat/vByte.
@ -105,6 +109,17 @@ var sharedFlags = []cli.Flag{
"the batch transaction",
Value: defaultMaxBatchFeeRateSatPerVByte,
},
cli.StringFlag{
Name: "channel_type",
Usage: fmt.Sprintf("the type of channel resulting from the "+
"order being matched (%q, %q)",
channelTypePeerDependent,
channelTypeScriptEnforced),
// TODO: Switch to script enforcement by default once we can
// enforce the lnd release supporting script enforced channels
// as the minimalCompatibleVersion.
Value: channelTypePeerDependent,
},
}
// promptForConfirmation continuously prompts the user for the message until
@ -229,6 +244,20 @@ func parseCommonParams(ctx *cli.Context, blockDuration uint32) (*poolrpc.Order,
params.RateFixed = rateFixed
// Determine the appropriate channel type that should be opened upon an
// order match.
channelType := ctx.String("channel_type")
switch channelType {
case "":
break
case channelTypePeerDependent:
params.ChannelType = auctioneerrpc.OrderChannelType_ORDER_CHANNEL_TYPE_PEER_DEPENDENT
case channelTypeScriptEnforced:
params.ChannelType = auctioneerrpc.OrderChannelType_ORDER_CHANNEL_TYPE_SCRIPT_ENFORCED
default:
return nil, fmt.Errorf("unknown channel type %q", channelType)
}
return params, nil
}
@ -306,7 +335,7 @@ func ordersSubmitAsk(ctx *cli.Context) error { // nolint: dupl
ask := &poolrpc.Ask{
LeaseDurationBlocks: uint32(ctx.Uint64("lease_duration_blocks")),
Version: uint32(order.VersionSelfChanBalance),
Version: uint32(order.VersionChannelType),
}
params, err := parseCommonParams(ctx, ask.LeaseDurationBlocks)
@ -462,7 +491,7 @@ func parseBaseBid(ctx *cli.Context) (*poolrpc.Bid, *sidecar.Ticket, error) {
bid := &poolrpc.Bid{
LeaseDurationBlocks: uint32(ctx.Uint64("lease_duration_blocks")),
Version: uint32(order.VersionSidecarChannel),
Version: uint32(order.VersionChannelType),
MinNodeTier: nodeTier,
}

View file

@ -283,8 +283,8 @@ func (m *Manager) SubscribePendingOpenChan() (*subscribe.Client, error) {
// the maker or taker to properly make a channel that stems off the main batch
// funding transaction.
func (m *Manager) deriveFundingShim(ourOrder order.Order,
matchedOrder *order.MatchedOrder,
batchTx *wire.MsgTx) (*lnrpc.FundingShim, [32]byte, error) {
matchedOrder *order.MatchedOrder, batchTx *wire.MsgTx,
batchHeightHint uint32) (*lnrpc.FundingShim, [32]byte, error) {
log.Infof("Registering funding shim for Order(type=%v, amt=%v, "+
"nonce=%v", ourOrder.Type(),
@ -315,6 +315,15 @@ func (m *Manager) deriveFundingShim(ourOrder order.Order,
selfChanBalance = matchedOrder.Order.(*order.Bid).SelfChanBalance
}
// If either order requires script enforcement, our thaw height needs to
// be an absolute height instead of a relative one.
switch {
case ourOrder.Details().ChannelType == order.ChannelTypeScriptEnforced:
fallthrough
case matchedOrder.Order.Details().ChannelType == order.ChannelTypeScriptEnforced:
thawHeight += batchHeightHint
}
pendingChanID := order.PendingChanKey(
askNonce, bidNonce,
)
@ -398,12 +407,13 @@ func (m *Manager) deriveFundingShim(ourOrder order.Order,
// side of a new matched order. To prepare ourselves for their incoming funding
// request, we'll register a shim with all the expected parameters.
func (m *Manager) registerFundingShim(ourBid *order.Bid,
matchedOrder *order.MatchedOrder, batchTx *wire.MsgTx) error {
matchedOrder *order.MatchedOrder, batchTx *wire.MsgTx,
batchHeightHint uint32) error {
ctxb := context.Background()
fundingShim, pendingChanID, err := m.deriveFundingShim(
ourBid, matchedOrder, batchTx,
ourBid, matchedOrder, batchTx, batchHeightHint,
)
if err != nil {
return err
@ -556,6 +566,7 @@ func (m *Manager) PrepChannelFunding(batch *order.Batch,
// from the asker.
err := m.registerFundingShim(
ourOrderBid, matchedOrder, batch.BatchTX,
batch.HeightHint,
)
if err != nil {
return fmt.Errorf("unable to register funding "+
@ -639,6 +650,7 @@ func (m *Manager) BatchChannelSetup(
for _, matchedOrder := range matchedOrders {
fundingShim, _, err := m.deriveFundingShim(
ourOrder, matchedOrder, batch.BatchTX,
batch.HeightHint,
)
if err != nil {
return nil, err
@ -678,6 +690,13 @@ func (m *Manager) BatchChannelSetup(
// * also other params to set as well
chanAmt := matchedOrder.UnitsFilled.ToSatoshis()
chanAmt += matchedOrderBid.SelfChanBalance
var commitmentType lnrpc.CommitmentType
switch {
case ourOrder.Details().ChannelType == order.ChannelTypeScriptEnforced:
fallthrough
case matchedOrderBid.Details().ChannelType == order.ChannelTypeScriptEnforced:
commitmentType = lnrpc.CommitmentType_SCRIPT_ENFORCED_LEASE
}
fundingReq := &lnrpc.OpenChannelRequest{
NodePubkey: matchedOrder.NodeKey[:],
LocalFundingAmount: int64(chanAmt),
@ -685,6 +704,7 @@ func (m *Manager) BatchChannelSetup(
PushSat: int64(
matchedOrderBid.SelfChanBalance,
),
CommitmentType: commitmentType,
}
chanStream, err := m.cfg.BaseClient.OpenChannel(
setupCtx, fundingReq,
@ -827,6 +847,7 @@ func (m *Manager) SidecarBatchChannelSetup(batch *order.Batch,
for _, matchedOrder := range matchedOrders {
fundingShim, _, err := m.deriveFundingShim(
ourOrder, matchedOrder, batch.BatchTX,
batch.HeightHint,
)
if err != nil {
return nil, err

View file

@ -578,6 +578,7 @@ func TestDeriveFundingShim(t *testing.T) {
batchTx = &wire.MsgTx{
TxOut: []*wire.TxOut{{}},
}
batchHeightHint uint32 = 1337
)
askKit := order.NewKit(askNonce)
@ -601,7 +602,7 @@ func TestDeriveFundingShim(t *testing.T) {
matchedAsk.MultiSigKey[:], int64(4*order.BaseSupplyUnit),
)
shim, pendingChanID, err := h.mgr.deriveFundingShim(
bid, matchedAsk, batchTx,
bid, matchedAsk, batchTx, batchHeightHint,
)
require.NoError(t, err)
require.Equal(t, order.PendingChanKey(askNonce, bidNonce), pendingChanID)
@ -639,7 +640,7 @@ func TestDeriveFundingShim(t *testing.T) {
matchedAsk.MultiSigKey[:], int64(4*order.BaseSupplyUnit),
)
shim, pendingChanID, err = h.mgr.deriveFundingShim(
bid, matchedAsk, batchTx,
bid, matchedAsk, batchTx, batchHeightHint,
)
require.NoError(t, err)
require.Equal(t, order.PendingChanKey(askNonce, bidNonce), pendingChanID)

View file

@ -202,6 +202,11 @@ type Batch struct {
// FeeRebate is the rebate that was offered to the trader if another
// batch participant wanted to pay more fees for a faster confirmation.
FeeRebate btcutil.Amount
// HeightHint represents the earliest absolute height in the chain in
// which the batch transaction can be found within. This will be used by
// traders to base off their absolute channel lease maturity height.
HeightHint uint32
}
// Fetcher describes a function that's able to fetch the latest version of an
@ -304,7 +309,7 @@ type BatchSignature map[[33]byte]*btcec.Signature
type BatchVerifier interface {
// Verify makes sure the batch prepared by the server is correct and
// can be accepted by the trader.
Verify(*Batch) error
Verify(_ *Batch, bestHeight uint32) error
}
// BatchSigner is an interface that can sign for a trader's account inputs in
@ -320,7 +325,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, bestHeight uint32) error
StorePendingBatch(_ *Batch) error
// MarkBatchComplete marks a pending batch as complete, allowing a
// trader to participate in a new batch.

View file

@ -9,13 +9,6 @@ import (
"github.com/lightninglabs/pool/auctioneerrpc"
)
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 {
@ -30,7 +23,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, bestHeight uint32) error {
func (s *batchStorer) StorePendingBatch(batch *Batch) error {
// Prepare the order modifications first.
orders := make([]Nonce, len(batch.MatchedOrders))
orderModifiers := make([][]Modifier, len(orders))
@ -80,13 +73,6 @@ func (s *batchStorer) StorePendingBatch(batch *Batch, bestHeight uint32) 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.
@ -135,7 +121,7 @@ func (s *batchStorer) StorePendingBatch(batch *Batch, bestHeight uint32) error {
modifiers, account.ValueModifier(diff.EndingBalance),
)
modifiers = append(
modifiers, account.HeightHintModifier(uint32(heightHint)),
modifiers, account.HeightHintModifier(batch.HeightHint),
)
modifiers = append(
modifiers, account.LatestTxModifier(batch.BatchTX),

View file

@ -15,7 +15,6 @@ import (
func TestBatchStorer(t *testing.T) {
t.Parallel()
const bestHeight = 1337
var (
storeMock = newMockStore()
storer = &batchStorer{
@ -106,6 +105,7 @@ func TestBatchStorer(t *testing.T) {
AccountDiffs: accountDiffs,
BatchTX: batchTx,
BatchTxFeeRate: chainfee.FeePerKwFloor,
HeightHint: 1337,
}
// Create the starting database state now.
@ -120,7 +120,7 @@ func TestBatchStorer(t *testing.T) {
}
// Pass the assembled batch to the storer now.
err := storer.StorePendingBatch(batch, bestHeight)
err := storer.StorePendingBatch(batch)
if err != nil {
t.Fatalf("error storing batch: %v", err)
}
@ -166,10 +166,9 @@ 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 {
if smallAcct.HeightHint != batch.HeightHint {
t.Fatalf("invalid account height hint, got %d wanted %d",
smallAcct.Value, heightHint)
smallAcct.HeightHint, batch.HeightHint)
}
if bigAcct.State != account.StatePendingBatch {
@ -184,9 +183,9 @@ func TestBatchStorer(t *testing.T) {
t.Fatalf("invalid account expiry, got %d wanted %d",
bigAcct.Value, 144)
}
if bigAcct.HeightHint != heightHint {
if bigAcct.HeightHint != batch.HeightHint {
t.Fatalf("invalid account height hint, got %d wanted %d",
bigAcct.Value, heightHint)
bigAcct.HeightHint, batch.HeightHint)
}
}

View file

@ -15,6 +15,11 @@ const (
// deriveKeyTimeout is the number of seconds we allow the wallet to take
// to derive a key.
deriveKeyTimeout = 10 * time.Second
// heightHintPadding is the padding we subtract/add to our best known
// height to avoid any discrepancies in block propagation between us and
// the auctioneer.
heightHintPadding = 3
)
var (
@ -68,7 +73,7 @@ type batchVerifier struct {
// accepted by the trader.
//
// NOTE: This method is part of the BatchVerifier interface.
func (v *batchVerifier) Verify(batch *Batch) error {
func (v *batchVerifier) Verify(batch *Batch, bestHeight uint32) error {
// First of all, make sure we're using the same batch validation version
// as the server. Otherwise we bail out of the batch. This should
// already be handled when the client connects/authenticates. But
@ -77,6 +82,13 @@ func (v *batchVerifier) Verify(batch *Batch) error {
return ErrVersionMismatch
}
// Reject the batch if we're too far in the past or future compared to
// the auctioneer.
if bestHeight < batch.HeightHint-heightHintPadding ||
bestHeight > batch.HeightHint+heightHintPadding {
return ErrInvalidBatchHeightHint
}
// First go through all orders that were matched for us. We'll make sure
// we know of the order and that the numbers check out on a high level.
tallies := make(map[[33]byte]*AccountTally)

View file

@ -36,6 +36,7 @@ var (
func TestBatchVerifier(t *testing.T) {
t.Parallel()
const bestHeight = 1337
var (
walletKit = test.NewMockWalletKit()
batchID BatchID
@ -61,7 +62,7 @@ func TestBatchVerifier(t *testing.T) {
doVerify: func(v BatchVerifier, a *Ask, b1, b2 *Bid,
b *Batch) error {
return v.Verify(&Batch{Version: 999})
return v.Verify(&Batch{Version: 999}, bestHeight)
},
},
{
@ -72,7 +73,7 @@ func TestBatchVerifier(t *testing.T) {
arr := make([]*MatchedOrder, 0)
b.MatchedOrders[Nonce{99, 99}] = arr
return v.Verify(b)
return v.Verify(b, bestHeight)
},
},
{
@ -87,7 +88,7 @@ func TestBatchVerifier(t *testing.T) {
Order: a,
},
)
return v.Verify(b)
return v.Verify(b, bestHeight)
},
},
{
@ -97,7 +98,7 @@ func TestBatchVerifier(t *testing.T) {
b *Batch) error {
b.MatchedOrders[a.nonce][0].NodeKey = nodePubkey
return v.Verify(b)
return v.Verify(b, bestHeight)
},
},
{
@ -107,7 +108,7 @@ func TestBatchVerifier(t *testing.T) {
b *Batch) error {
a.LeaseDuration = 100
return v.Verify(b)
return v.Verify(b, bestHeight)
},
},
{
@ -117,7 +118,7 @@ func TestBatchVerifier(t *testing.T) {
b *Batch) error {
a.FixedRate = 20000
return v.Verify(b)
return v.Verify(b, bestHeight)
},
},
{
@ -128,7 +129,7 @@ func TestBatchVerifier(t *testing.T) {
delete(b.MatchedOrders, a.nonce)
b2.LeaseDuration = 5000
return v.Verify(b)
return v.Verify(b, bestHeight)
},
},
{
@ -139,7 +140,7 @@ func TestBatchVerifier(t *testing.T) {
delete(b.MatchedOrders, a.nonce)
a.FixedRate = b1.FixedRate + 1
return v.Verify(b)
return v.Verify(b, bestHeight)
},
},
{
@ -149,7 +150,7 @@ func TestBatchVerifier(t *testing.T) {
b *Batch) error {
b.BatchTX.TxOut[0].Value = 123
return v.Verify(b)
return v.Verify(b, bestHeight)
},
},
{
@ -159,7 +160,7 @@ func TestBatchVerifier(t *testing.T) {
b *Batch) error {
b.BatchTX.TxOut[0].PkScript = []byte{99, 88}
return v.Verify(b)
return v.Verify(b, bestHeight)
},
},
{
@ -171,7 +172,7 @@ func TestBatchVerifier(t *testing.T) {
b.BatchTX.TxOut[0].Value = 900_000
b.MatchedOrders[a.nonce][0].UnitsFilled = 9
b.MatchedOrders[b1.nonce][0].UnitsFilled = 9
return v.Verify(b)
return v.Verify(b, bestHeight)
},
},
{
@ -181,7 +182,7 @@ func TestBatchVerifier(t *testing.T) {
b *Batch) error {
a.MinUnitsMatch = b1.MinUnitsMatch * 100
return v.Verify(b)
return v.Verify(b, bestHeight)
},
},
{
@ -191,7 +192,7 @@ func TestBatchVerifier(t *testing.T) {
b *Batch) error {
b.BatchTxFeeRate *= 2
return v.Verify(b)
return v.Verify(b, bestHeight)
},
},
{
@ -202,7 +203,7 @@ func TestBatchVerifier(t *testing.T) {
delete(b.MatchedOrders, a.nonce)
b1.FixedRate = uint32(clearingPrice) - 1
return v.Verify(b)
return v.Verify(b, bestHeight)
},
},
{
@ -257,7 +258,7 @@ func TestBatchVerifier(t *testing.T) {
// Verification should fail as the first match
// has an ask with a price greater than the
// clearing price.
return v.Verify(b)
return v.Verify(b, bestHeight)
},
},
{
@ -267,7 +268,7 @@ func TestBatchVerifier(t *testing.T) {
b *Batch) error {
b.ExecutionFee = terms.NewLinearFeeSchedule(1, 1)
return v.Verify(b)
return v.Verify(b, bestHeight)
},
},
{
@ -280,7 +281,7 @@ func TestBatchVerifier(t *testing.T) {
b.BatchTX.TxOut[2].Value += 2220
b.AccountDiffs[0].EndingBalance += 2220
b.AccountDiffs[1].EndingBalance += 2220
return v.Verify(b)
return v.Verify(b, bestHeight)
},
},
{
@ -294,7 +295,7 @@ func TestBatchVerifier(t *testing.T) {
b.AccountDiffs[0].EndingBalance += 2220
b.AccountDiffs[1].EndingBalance += 2220
b.AccountDiffs[1].EndingState = stateRecreated
return v.Verify(b)
return v.Verify(b, bestHeight)
},
},
{
@ -306,7 +307,7 @@ func TestBatchVerifier(t *testing.T) {
b *Batch) error {
b1.SelfChanBalance = 100
return v.Verify(b)
return v.Verify(b, bestHeight)
},
},
{
@ -317,7 +318,7 @@ func TestBatchVerifier(t *testing.T) {
b.BatchTX.TxOut[0].Value += 100
b1.SelfChanBalance = 100
return v.Verify(b)
return v.Verify(b, bestHeight)
},
},
{
@ -326,7 +327,7 @@ func TestBatchVerifier(t *testing.T) {
doVerify: func(v BatchVerifier, a *Ask, b1, b2 *Bid,
b *Batch) error {
return v.Verify(b)
return v.Verify(b, bestHeight)
},
},
}
@ -505,6 +506,7 @@ func TestBatchVerifier(t *testing.T) {
},
BatchTX: batchTx,
BatchTxFeeRate: chainfee.FeePerKwFloor,
HeightHint: bestHeight,
}
// Create the starting database state now.

View file

@ -59,6 +59,11 @@ const (
// accounting or whatever) can opt out by explicitly submitting their
// ask orders with a version previous to this one.
VersionSidecarChannel Version = 4
// VersionChannelType is the order version that added use of the channel
// type field. Only orders with this version are allowed to use the
// channel type field.
VersionChannelType Version = 5
)
// Type is the type of an order. We don't use iota for the constants due to the
@ -216,6 +221,24 @@ func (s MatchState) String() string {
}
}
// ChannelType is a numerical type that represents all possible channel types
// that are supported to be opened through the auction process.
type ChannelType uint8
// NOTE: We avoid the use of iota as this type is stored on disk.
const (
// ChannelTypePeerDependent denotes that the resulting channel type from
// an order match will depend on the shared features between its
// participants.
ChannelTypePeerDependent ChannelType = 0
// ChannelTypeScriptEnforced represents a new channel type that builds
// upon the anchors commitment format to enforce the maturity of a
// leased channel in the commitment and HTLC outputs that pay directly
// to the channel initiator.
ChannelTypeScriptEnforced ChannelType = 1
)
var (
// ErrInsufficientBalance is the error that is returned if an account
// has insufficient balance to perform a requested action.
@ -305,6 +328,10 @@ type Kit struct {
// MinUnitsMatch signals the minimum number of units that must be
// matched against an order.
MinUnitsMatch SupplyUnit
// ChannelType denotes the channel type that must be used for the
// resulting matched channels.
ChannelType ChannelType
}
// Nonce is the unique identifier of each order and MUST be created by hashing a
@ -391,6 +418,16 @@ func (a *Ask) Digest() ([sha256.Size]byte, error) {
return result, err
}
case VersionChannelType:
err := lnwire.WriteElements(
&msg, a.nonce[:], uint32(a.Version), a.FixedRate,
a.Amt, a.LeaseDuration, uint64(a.MaxBatchFeeRate),
uint32(a.MinUnitsMatch), uint8(a.ChannelType),
)
if err != nil {
return result, err
}
default:
return result, fmt.Errorf("unknown version %d", a.Kit.Version)
}
@ -605,6 +642,23 @@ func (b *Bid) Digest() ([sha256.Size]byte, error) {
return result, err
}
case VersionChannelType:
var isSidecar uint8
if b.SidecarTicket != nil {
isSidecar = 1
}
err := lnwire.WriteElements(
&msg, b.nonce[:], uint32(b.Version), b.FixedRate,
b.Amt, b.LeaseDuration, uint64(b.MaxBatchFeeRate),
uint32(b.MinNodeTier), uint32(b.MinUnitsMatch),
uint64(b.SelfChanBalance), isSidecar,
uint8(b.ChannelType),
)
if err != nil {
return result, err
}
default:
return result, fmt.Errorf("unknown version %d", b.Kit.Version)
}

View file

@ -2,6 +2,7 @@ package order
import (
"context"
"errors"
"fmt"
"net"
"strconv"
@ -35,6 +36,12 @@ var (
// implement the same batch verification version as the server.
ErrVersionMismatch = fmt.Errorf("version %d mismatches server version",
CurrentBatchVersion)
// ErrInvalidBatchHeightHint is an error returned by a trader upon
// verifying a batch when its proposed height hint is outside of the
// trader's acceptable range.
ErrInvalidBatchHeightHint = errors.New("proposed batch height hint is " +
"outside of acceptable range")
)
// ManagerConfig contains all of the required dependencies for the Manager to
@ -299,10 +306,10 @@ func (m *Manager) validateOrder(order Order, acct *account.Account,
}
// OrderMatchValidate verifies an incoming batch is sane before accepting it.
func (m *Manager) OrderMatchValidate(batch *Batch) error {
func (m *Manager) OrderMatchValidate(batch *Batch, bestHeight uint32) error {
// Make sure we have no objection to the current batch. Then store
// it in case it ends up being the final version.
err := m.batchVerifier.Verify(batch)
err := m.batchVerifier.Verify(batch, bestHeight)
if err != nil {
// This error will lead to us sending an OrderMatchReject
// message and canceling all funding shims we might already have
@ -330,13 +337,13 @@ 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(bestHeight uint32) (BatchSignature, error) {
func (m *Manager) BatchSign() (BatchSignature, error) {
sig, err := m.batchSigner.Sign(m.pendingBatch)
if err != nil {
return nil, err
}
err = m.batchStorer.StorePendingBatch(m.pendingBatch, bestHeight)
err = m.batchStorer.StorePendingBatch(m.pendingBatch)
if err != nil {
return nil, fmt.Errorf("unable to store batch: %v", err)
}

View file

@ -62,6 +62,25 @@ func ParseRPCOrder(version, leaseDuration uint32,
}
kit.MinUnitsMatch = SupplyUnit(details.MinUnitsMatch)
switch details.ChannelType {
// Default value, trader didn't specify a channel type.
case auctioneerrpc.OrderChannelType_ORDER_CHANNEL_TYPE_UNKNOWN:
// TODO: Switch to script enforcement by default once we can
// enforce the lnd release supporting script enforced channels
// as the minimalCompatibleVersion.
kit.ChannelType = ChannelTypePeerDependent
case auctioneerrpc.OrderChannelType_ORDER_CHANNEL_TYPE_PEER_DEPENDENT:
kit.ChannelType = ChannelTypePeerDependent
case auctioneerrpc.OrderChannelType_ORDER_CHANNEL_TYPE_SCRIPT_ENFORCED:
kit.ChannelType = ChannelTypeScriptEnforced
default:
return nil, fmt.Errorf("unhandled channel type %v",
details.ChannelType)
}
return kit, nil
}
@ -169,6 +188,22 @@ func ParseRPCServerOrder(version uint32, details *auctioneerrpc.ServerOrder,
}
copy(multiSigKey[:], multiSigPubkey.SerializeCompressed())
switch details.ChannelType {
// Default value, trader didn't specify a channel type.
case auctioneerrpc.OrderChannelType_ORDER_CHANNEL_TYPE_UNKNOWN:
kit.ChannelType = ChannelTypePeerDependent
case auctioneerrpc.OrderChannelType_ORDER_CHANNEL_TYPE_PEER_DEPENDENT:
kit.ChannelType = ChannelTypePeerDependent
case auctioneerrpc.OrderChannelType_ORDER_CHANNEL_TYPE_SCRIPT_ENFORCED:
kit.ChannelType = ChannelTypeScriptEnforced
default:
return nil, nodeKey, nil, multiSigKey,
fmt.Errorf("unhandled channel type %v", details.ChannelType)
}
return kit, nodeKey, nodeAddrs, multiSigKey, nil
}
@ -231,6 +266,7 @@ func ParseRPCBatch(prepareMsg *auctioneerrpc.OrderMatchPrepare) (*Batch,
MatchedOrders: make(map[Nonce][]*MatchedOrder),
BatchTX: &wire.MsgTx{},
ClearingPrices: make(map[uint32]FixedRatePremium),
HeightHint: prepareMsg.BatchHeightHint,
}
// Parse matched orders market by market.

File diff suppressed because it is too large Load diff

View file

@ -601,6 +601,9 @@ message Order {
// The minimum number of order units that must be matched per order pair.
uint32 min_units_match = 12;
// The channel type to use for the resulting matched channels.
OrderChannelType channel_type = 13;
}
message Bid {

View file

@ -1067,8 +1067,7 @@
"description": "The true bid price of the order in parts per billion."
},
"chan_type": {
"type": "integer",
"format": "int64",
"$ref": "#/definitions/poolrpcOrderChannelType",
"description": "The channel type to be created."
}
}
@ -1203,8 +1202,7 @@
"description": "The true bid price of the order in parts per billion."
},
"chan_type": {
"type": "integer",
"format": "int64",
"$ref": "#/definitions/poolrpcOrderChannelType",
"description": "The channel type to be created."
}
}
@ -1983,9 +1981,23 @@
"type": "integer",
"format": "int64",
"description": "The minimum number of order units that must be matched per order pair."
},
"channel_type": {
"$ref": "#/definitions/poolrpcOrderChannelType",
"description": "The channel type to use for the resulting matched channels."
}
}
},
"poolrpcOrderChannelType": {
"type": "string",
"enum": [
"ORDER_CHANNEL_TYPE_UNKNOWN",
"ORDER_CHANNEL_TYPE_PEER_DEPENDENT",
"ORDER_CHANNEL_TYPE_SCRIPT_ENFORCED"
],
"default": "ORDER_CHANNEL_TYPE_UNKNOWN",
"description": " - ORDER_CHANNEL_TYPE_UNKNOWN: Used to set defaults when a trader doesn't specify a channel type.\n - ORDER_CHANNEL_TYPE_PEER_DEPENDENT: The channel type will vary per matched channel based on the features shared\nbetween its participants.\n - ORDER_CHANNEL_TYPE_SCRIPT_ENFORCED: A channel type that builds upon the anchors commitment format to enforce\nchannel lease maturities in the commitment and HTLC outputs that pay to the\nchannel initiator/seller."
},
"poolrpcOrderEvent": {
"type": "object",
"properties": {

View file

@ -350,7 +350,8 @@ func (s *rpcServer) handleServerMessage(
}
// Do an in-depth verification of the batch.
err = s.orderManager.OrderMatchValidate(batch)
bestHeight := atomic.LoadUint32(&s.bestHeight)
err = s.orderManager.OrderMatchValidate(batch, bestHeight)
if err != nil {
// We can't accept the batch, something went wrong.
rpcLog.Errorf("Error validating batch: %v", err)
@ -393,8 +394,7 @@ func (s *rpcServer) handleServerMessage(
"num_orders=%v", batch.ID[:], len(batch.MatchedOrders))
// Sign for the accounts in the batch.
bestHeight := atomic.LoadUint32(&s.bestHeight)
sigs, err := s.orderManager.BatchSign(bestHeight)
sigs, err := s.orderManager.BatchSign()
if err != nil {
rpcLog.Errorf("Error signing batch: %v", err)
return s.sendRejectBatch(batch, err)
@ -2886,8 +2886,11 @@ func marshallChannelInfo(chanInfos map[wire.OutPoint]*chaninfo.ChannelInfo) (
// between them for our purpose.
case chanbackup.AnchorsCommitVersion,
chanbackup.AnchorsZeroFeeHtlcTxCommitVersion:
channelType = auctioneerrpc.ChannelType_ANCHORS
case chanbackup.ScriptEnforcedLeaseVersion:
channelType = auctioneerrpc.ChannelType_SCRIPT_ENFORCED_LEASE
default:
return nil, fmt.Errorf("unknown channel type: %v",
chanInfo.Version)