pool/sidecar/interface.go
Olaoluwa Osuntokun 2a83726c21
sidecar: switch sidecar encoding from base68 to base58
In this commit, we change the encoding of the sidecar ticket from base68
to base58 as it doesn't contain any special characters which makes copy
pasting a string easier.

An example encoding of the new string looks something like:
```
sidecar13HtPsD1UiB2EA4ZfUbnVejG1Pu3p1GW2ydq65NZBuQgTaD9LRESHtKasXwNrQPYqkwkMyJNFiNoBig4PBQQKoKeXz5jHWXZ3pMZNX5JW2FXyf6zKTPWthRQczNQuf2pM8ppjTqHK6tYTEReZk7pFynYJoHnAeBtix28k2PiRSz5qVVmHpWHWFgVjxihEmcD1T1V1g5LwzVSRUyCJ11ajaE4nHdnU1ZR3HdduxXnyyefacfXWAts3tytr7RnWzRbnVPFSx7eVgeinknYvQ3njtiStpQpSacoBH4y1KckVpRAo8N8KuWWmAgYBNpxZfK8RsgViJ26kTpbfuyyHy1uPkAsGtv25iz5pNcopXyaXMnZGyDDWrDf9ZJEgU6wzm4VxwrPfNN4ASR6wLGWAKy1SsCMVFi5Bx6khN4ReSmJVrALWXrbgM52j7evHVRYXa1LZCJTBccCvaRXNYQqxJvnUnLnHW4N4THv4j1y94wmo2SafRfKapxppVW
```
2021-05-19 19:55:56 -07:00

291 lines
8.9 KiB
Go

package sidecar
import (
"bytes"
"crypto/rand"
"crypto/sha256"
"fmt"
"github.com/btcsuite/btcd/btcec"
"github.com/btcsuite/btcutil"
"github.com/lightningnetwork/lnd/lnwire"
)
// Version is the version of the sidecar ticket format.
type Version uint8
const (
// VersionDefault is the initial version of the ticket format.
VersionDefault Version = 0
)
// State is the state a sidecar ticket currently is in. Each updater of the
// ticket is responsible for also setting the state correctly after adding their
// data according to their role.
type State uint8
const (
// StateCreated is the state a ticket is in after it's been first
// created and the offer information was added but not signed yet.
StateCreated State = 0
// StateOffered is the state a ticket is in after the offer data was
// signed by the sidecar provider.
StateOffered State = 1
// StateRegistered is the state a ticket is in after the recipient has
// registered it in their system and added their information.
StateRegistered State = 2
// StateOrdered is the state a ticket is in after the bid order for the
// sidecar channel was submitted and the order information was added to
// the ticket. The ticket now also contains a signature of the provider
// over the order digest.
StateOrdered State = 3
// StateExpectingChannel is the state a ticket is in after it's been
// returned to the channel recipient and their node was instructed to
// start listening for batch events for it.
StateExpectingChannel State = 4
// StateCompleted is the state a ticket is in after the sidecar channel
// was successfully opened and the bid order completed.
StateCompleted State = 5
// StateCanceled is the state a ticket is in after the sidecar channel
// bid order was canceled by the taker.
StateCanceled State = 6
)
// String returns the string representation of a sidecar ticket state.
func (s State) String() string {
switch s {
case StateCreated:
return "created"
case StateOffered:
return "offered"
case StateRegistered:
return "registered"
case StateOrdered:
return "ordered"
case StateExpectingChannel:
return "expecting channel"
case StateCompleted:
return "completed"
case StateCanceled:
return "canceled"
default:
return fmt.Sprintf("unknown <%d>", s)
}
}
// Offer is a struct holding the information that a sidecar channel provider is
// committing to when offering to buy a channel for the recipient. The sidecar
// channel flow is initiated by the provider creating a ticket and adding its
// signed offer to it.
type Offer struct {
// Capacity is the channel capacity of the sidecar channel in satoshis.
Capacity btcutil.Amount
// PushAmt is the amount in satoshis that will be pushed to the
// recipient of the channel to kick start them with inbound capacity.
// If this is non-zero then the provider must pay for the push amount as
// well as all other costs from their Pool account. Makers can opt out
// of supporting push amounts when submitting ask orders so this will
// reduce the matching chances somewhat.
PushAmt btcutil.Amount
// LeaseDurationBlocks is the number of blocks the offered channel in
// this offer would be leased for.
LeaseDurationBlocks uint32
// SignPubKey is the public key for corresponding to the private key
// that signed the SigOfferDigest below and, in a later state, the
// SigOrderDigest of the Order struct.
SignPubKey *btcec.PublicKey
// SigOfferDigest is a signature over the offer digest, signed with the
// private key that corresponds to the SignPubKey above.
SigOfferDigest *btcec.Signature
// Auto determines if the provider requires that the ticket be
// completed using an automated negotiation sequence.
Auto bool
}
// Recipient is a struct holding the information about the recipient of the
// sidecar channel in question.
type Recipient struct {
// NodePubKey is the recipient nodes' identity public key that is
// advertised in the bid order.
NodePubKey *btcec.PublicKey
// MultiSigPubKey is a public key to which the recipient node has the
// private key to. It is the key that will be used as one of the 2-of-2
// multisig keys of the channel funding transaction output and is
// advertised in the bid order.
MultiSigPubKey *btcec.PublicKey
// MultiSigKeyIndex is the derivation index of the MultiSigPubKey.
MultiSigKeyIndex uint32
}
// Order is a struct holding the information about the sidecar bid order after
// it's been submitted by the provider.
type Order struct {
// BidNonce is the order nonce of the bid order that was submitted for
// purchasing the sidecar channel.
BidNonce [32]byte
// SigOrderDigest is a signature over the order digest, signed with the
// private key that corresponds to the SignPubKey in the Offer struct.
SigOrderDigest *btcec.Signature
}
// Execution is a struct holding information about the sidecar bid order during
// its execution.
type Execution struct {
// PendingChannelID is the pending channel ID of the currently
// registered funding shim that was added by the recipient node to
// receive the channel.
PendingChannelID [32]byte
}
// Ticket is a struct holding all the information for establishing/buying a
// sidecar channel. It is meant to be used in a PSBT like manner where each
// participant incrementally adds their information according to their role and
// the current step.
type Ticket struct {
// ID is a pseudorandom identifier of the ticket.
ID [8]byte
// Version is the version of the ticket serialization format.
Version Version
// State is the current state of the ticket. The state is updated by
// each participant and is therefore not covered in any of the signature
// digests.
State State
// Offer contains the initial conditions offered by the sidecar channel
// provider. Every ticket must start with an offer and therefore this
// member can never be empty or nil.
Offer Offer
// Recipient contains the information about the receiver node of the
// sidecar channel. This field must be set for all states greater or
// equal to StateRegistered.
Recipient *Recipient
// Order contains the information about the order that was submitted for
// leasing the sidecar channel represented by this ticket. This field
// must be set for all states greater or equal to StateOrdered.
Order *Order
// Execution contains the information about the execution part of the
// sidecar channel. This information is not meant to be exchanged
// between the participating parties but is included in the ticket to
// make it easy to serialize/deserialize a ticket state within the local
// database.
Execution *Execution
}
// NewTicket creates a new sidecar ticket with the given version and offer
// information.
func NewTicket(version Version, capacity, pushAmt btcutil.Amount,
duration uint32, offerPubKey *btcec.PublicKey,
auto bool) (*Ticket, error) {
t := &Ticket{
Version: version,
State: StateOffered,
Offer: Offer{
Capacity: capacity,
PushAmt: pushAmt,
LeaseDurationBlocks: duration,
SignPubKey: offerPubKey,
Auto: auto,
},
}
// The linter flags this, even though crypto/rand is being used...
if _, err := rand.Read(t.ID[:]); err != nil { // nolint:gosec
return nil, err
}
return t, nil
}
// OfferDigest returns a message digest over the offer fields.
func (t *Ticket) OfferDigest() ([32]byte, error) {
var (
msg bytes.Buffer
result [sha256.Size]byte
)
switch t.Version {
case VersionDefault:
err := lnwire.WriteElements(
&msg, t.ID[:], uint8(t.Version), t.Offer.Capacity,
t.Offer.PushAmt, t.Offer.Auto,
)
if err != nil {
return result, err
}
default:
return result, fmt.Errorf("unknown version %d", t.Version)
}
return sha256.Sum256(msg.Bytes()), nil
}
// OrderDigest returns a message digest over the order fields.
func (t *Ticket) OrderDigest() ([32]byte, error) {
var (
msg bytes.Buffer
result [sha256.Size]byte
)
if t.State < StateOrdered || t.Order == nil {
return result, fmt.Errorf("invalid state for order digest")
}
switch t.Version {
case VersionDefault:
err := lnwire.WriteElements(
&msg, t.ID[:], uint8(t.Version), t.Offer.Capacity,
t.Offer.PushAmt, t.Order.BidNonce[:],
)
if err != nil {
return result, err
}
default:
return result, fmt.Errorf("unknown version %d", t.Version)
}
return sha256.Sum256(msg.Bytes()), nil
}
// Store is the interface a persistent storage must implement for storing and
// retrieving sidecar tickets.
type Store interface {
// AddSidecar adds a record for the sidecar order to the database.
AddSidecar(sidecar *Ticket) error
// UpdateSidecar updates a sidecar order in the database.
UpdateSidecar(sidecar *Ticket) error
// Sidecar retrieves a specific sidecar by its ID and provider signing
// key (offer signature pubkey) or returns ErrNoSidecar if it's not
// found.
Sidecar(id [8]byte, offerSignPubKey *btcec.PublicKey) (*Ticket, error)
// Sidecars retrieves all known sidecar orders from the database.
Sidecars() ([]*Ticket, error)
}