mirror of
https://github.com/lightninglabs/pool.git
synced 2026-08-17 13:06:52 +02:00
Merge pull request #250 from lightninglabs/auto-sidecar
multi: implement automated negotiation for sidecar channels
This commit is contained in:
commit
a43019f246
25 changed files with 3077 additions and 649 deletions
|
|
@ -21,6 +21,7 @@ import (
|
|||
"github.com/lightninglabs/pool/auctioneerrpc"
|
||||
"github.com/lightninglabs/pool/order"
|
||||
"github.com/lightninglabs/pool/poolrpc"
|
||||
"github.com/lightninglabs/pool/sidecar"
|
||||
"github.com/lightninglabs/pool/terms"
|
||||
"github.com/lightningnetwork/lnd/keychain"
|
||||
"github.com/lightningnetwork/lnd/lntypes"
|
||||
|
|
@ -122,8 +123,9 @@ type Client struct {
|
|||
errChanSwitch *ErrChanSwitch
|
||||
FromServerChan chan *auctioneerrpc.ServerAuctionMessage
|
||||
|
||||
serverConn *grpc.ClientConn
|
||||
client auctioneerrpc.ChannelAuctioneerClient
|
||||
serverConn *grpc.ClientConn
|
||||
client auctioneerrpc.ChannelAuctioneerClient
|
||||
hashMailClient auctioneerrpc.HashMailClient
|
||||
|
||||
quit chan struct{}
|
||||
wg sync.WaitGroup
|
||||
|
|
@ -171,6 +173,7 @@ func (c *Client) Start() error {
|
|||
|
||||
c.serverConn = serverConn
|
||||
c.client = auctioneerrpc.NewChannelAuctioneerClient(serverConn)
|
||||
c.hashMailClient = auctioneerrpc.NewHashMailClient(serverConn)
|
||||
|
||||
c.errChanSwitch.Start()
|
||||
|
||||
|
|
@ -1318,6 +1321,110 @@ func (c *Client) MarketInfo(ctx context.Context) (
|
|||
return c.client.MarketInfo(ctx, &auctioneerrpc.MarketInfoRequest{})
|
||||
}
|
||||
|
||||
// InitAccountCipherBox attempts to initialize a new CipherBox using the
|
||||
// sidecar ticket as the authentication method.
|
||||
func (c *Client) InitTicketCipherBox(ctx context.Context, sid [64]byte,
|
||||
ticket *sidecar.Ticket) error {
|
||||
|
||||
// TODO(roasbeef): add error to catch deupliacte stream
|
||||
// existence/creation, also need to allow stream deletion as well
|
||||
|
||||
strTicket, err := sidecar.EncodeToString(ticket)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
streamInit := &auctioneerrpc.CipherBoxAuth{
|
||||
Desc: &auctioneerrpc.CipherBoxDesc{
|
||||
StreamId: sid[:],
|
||||
},
|
||||
Auth: &auctioneerrpc.CipherBoxAuth_SidecarAuth{
|
||||
SidecarAuth: &auctioneerrpc.SidecarAuth{
|
||||
Ticket: strTicket,
|
||||
},
|
||||
},
|
||||
}
|
||||
_, err = c.hashMailClient.NewCipherBox(ctx, streamInit)
|
||||
return err
|
||||
}
|
||||
|
||||
// InitAccountCipherBox attempts to initialize a new CipherBox using the
|
||||
// account key as an authentication mechanism.
|
||||
func (c *Client) InitAccountCipherBox(ctx context.Context, sid [64]byte,
|
||||
acctKey *keychain.KeyDescriptor) error {
|
||||
|
||||
streamSig, err := c.cfg.Signer.SignMessage(
|
||||
ctx, sid[:], acctKey.KeyLocator,
|
||||
)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to sign cipher box auth: %w", err)
|
||||
}
|
||||
|
||||
acctKeyBytes := acctKey.PubKey.SerializeCompressed()
|
||||
streamInit := &auctioneerrpc.CipherBoxAuth{
|
||||
Desc: &auctioneerrpc.CipherBoxDesc{
|
||||
StreamId: sid[:],
|
||||
},
|
||||
Auth: &auctioneerrpc.CipherBoxAuth_AcctAuth{
|
||||
AcctAuth: &auctioneerrpc.PoolAccountAuth{
|
||||
AcctKey: acctKeyBytes,
|
||||
StreamSig: streamSig,
|
||||
},
|
||||
},
|
||||
}
|
||||
_, err = c.hashMailClient.NewCipherBox(ctx, streamInit)
|
||||
return err
|
||||
}
|
||||
|
||||
// SendCipherBoxMsg attempts to the passed message into the cipher box
|
||||
// identified by the passed stream ID. This message will be on-blocking as long
|
||||
// as the buffer size of the stream is not exceed.
|
||||
//
|
||||
// TODO(roasbeef): option to expose a streaming interface?
|
||||
func (c *Client) SendCipherBoxMsg(ctx context.Context, sid [64]byte,
|
||||
msg []byte) error {
|
||||
|
||||
writeStream, err := c.hashMailClient.SendStream(ctx)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to create send stream: %w", err)
|
||||
}
|
||||
|
||||
err = writeStream.Send(&auctioneerrpc.CipherBox{
|
||||
Desc: &auctioneerrpc.CipherBoxDesc{
|
||||
StreamId: sid[:],
|
||||
},
|
||||
Msg: msg,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return writeStream.CloseSend()
|
||||
}
|
||||
|
||||
// RecvCipherBoxMsg attempts to read a message from the cipher box identified
|
||||
// by the passed stream ID.
|
||||
func (c *Client) RecvCipherBoxMsg(ctx context.Context,
|
||||
sid [64]byte) ([]byte, error) {
|
||||
|
||||
streamDesc := &auctioneerrpc.CipherBoxDesc{
|
||||
StreamId: sid[:],
|
||||
}
|
||||
readStream, err := c.hashMailClient.RecvStream(ctx, streamDesc)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to create read stream: %w", err)
|
||||
}
|
||||
|
||||
// TODO(roasbeef): need to cancel context?
|
||||
|
||||
msg, err := readStream.Recv()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return msg.Msg, nil
|
||||
}
|
||||
|
||||
// MarshallNodeTier maps the node tier integer into the enum used on the RPC
|
||||
// interface.
|
||||
func MarshallNodeTier(nodeTier order.NodeTier) (auctioneerrpc.NodeTier, error) {
|
||||
|
|
|
|||
858
auctioneerrpc/hashmail.pb.go
Normal file
858
auctioneerrpc/hashmail.pb.go
Normal file
|
|
@ -0,0 +1,858 @@
|
|||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||
// source: hashmail.proto
|
||||
|
||||
// We can't rename this to auctioneerrpc, otherwise it would be a breaking
|
||||
// change since the package name is also contained in the HTTP URIs and old
|
||||
// clients would call the wrong endpoints. Luckily with the go_package option we
|
||||
// can have different golang and RPC package names.
|
||||
|
||||
package auctioneerrpc
|
||||
|
||||
import (
|
||||
context "context"
|
||||
fmt "fmt"
|
||||
proto "github.com/golang/protobuf/proto"
|
||||
grpc "google.golang.org/grpc"
|
||||
codes "google.golang.org/grpc/codes"
|
||||
status "google.golang.org/grpc/status"
|
||||
math "math"
|
||||
)
|
||||
|
||||
// Reference imports to suppress errors if they are not otherwise used.
|
||||
var _ = proto.Marshal
|
||||
var _ = fmt.Errorf
|
||||
var _ = math.Inf
|
||||
|
||||
// This is a compile-time assertion to ensure that this generated file
|
||||
// is compatible with the proto package it is being compiled against.
|
||||
// A compilation error at this line likely means your copy of the
|
||||
// proto package needs to be updated.
|
||||
const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package
|
||||
|
||||
type PoolAccountAuth struct {
|
||||
// The account key being used to authenticate.
|
||||
AcctKey []byte `protobuf:"bytes,1,opt,name=acct_key,json=acctKey,proto3" json:"acct_key,omitempty"`
|
||||
// A valid signature over the stream ID being used.
|
||||
StreamSig []byte `protobuf:"bytes,2,opt,name=stream_sig,json=streamSig,proto3" json:"stream_sig,omitempty"`
|
||||
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
||||
XXX_unrecognized []byte `json:"-"`
|
||||
XXX_sizecache int32 `json:"-"`
|
||||
}
|
||||
|
||||
func (m *PoolAccountAuth) Reset() { *m = PoolAccountAuth{} }
|
||||
func (m *PoolAccountAuth) String() string { return proto.CompactTextString(m) }
|
||||
func (*PoolAccountAuth) ProtoMessage() {}
|
||||
func (*PoolAccountAuth) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_165b784e4d2471a2, []int{0}
|
||||
}
|
||||
|
||||
func (m *PoolAccountAuth) XXX_Unmarshal(b []byte) error {
|
||||
return xxx_messageInfo_PoolAccountAuth.Unmarshal(m, b)
|
||||
}
|
||||
func (m *PoolAccountAuth) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
|
||||
return xxx_messageInfo_PoolAccountAuth.Marshal(b, m, deterministic)
|
||||
}
|
||||
func (m *PoolAccountAuth) XXX_Merge(src proto.Message) {
|
||||
xxx_messageInfo_PoolAccountAuth.Merge(m, src)
|
||||
}
|
||||
func (m *PoolAccountAuth) XXX_Size() int {
|
||||
return xxx_messageInfo_PoolAccountAuth.Size(m)
|
||||
}
|
||||
func (m *PoolAccountAuth) XXX_DiscardUnknown() {
|
||||
xxx_messageInfo_PoolAccountAuth.DiscardUnknown(m)
|
||||
}
|
||||
|
||||
var xxx_messageInfo_PoolAccountAuth proto.InternalMessageInfo
|
||||
|
||||
func (m *PoolAccountAuth) GetAcctKey() []byte {
|
||||
if m != nil {
|
||||
return m.AcctKey
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *PoolAccountAuth) GetStreamSig() []byte {
|
||||
if m != nil {
|
||||
return m.StreamSig
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type SidecarAuth struct {
|
||||
//
|
||||
//A valid sidecar ticket that has been signed (offered) by a Pool account in
|
||||
//the active state.
|
||||
Ticket string `protobuf:"bytes,1,opt,name=ticket,proto3" json:"ticket,omitempty"`
|
||||
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
||||
XXX_unrecognized []byte `json:"-"`
|
||||
XXX_sizecache int32 `json:"-"`
|
||||
}
|
||||
|
||||
func (m *SidecarAuth) Reset() { *m = SidecarAuth{} }
|
||||
func (m *SidecarAuth) String() string { return proto.CompactTextString(m) }
|
||||
func (*SidecarAuth) ProtoMessage() {}
|
||||
func (*SidecarAuth) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_165b784e4d2471a2, []int{1}
|
||||
}
|
||||
|
||||
func (m *SidecarAuth) XXX_Unmarshal(b []byte) error {
|
||||
return xxx_messageInfo_SidecarAuth.Unmarshal(m, b)
|
||||
}
|
||||
func (m *SidecarAuth) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
|
||||
return xxx_messageInfo_SidecarAuth.Marshal(b, m, deterministic)
|
||||
}
|
||||
func (m *SidecarAuth) XXX_Merge(src proto.Message) {
|
||||
xxx_messageInfo_SidecarAuth.Merge(m, src)
|
||||
}
|
||||
func (m *SidecarAuth) XXX_Size() int {
|
||||
return xxx_messageInfo_SidecarAuth.Size(m)
|
||||
}
|
||||
func (m *SidecarAuth) XXX_DiscardUnknown() {
|
||||
xxx_messageInfo_SidecarAuth.DiscardUnknown(m)
|
||||
}
|
||||
|
||||
var xxx_messageInfo_SidecarAuth proto.InternalMessageInfo
|
||||
|
||||
func (m *SidecarAuth) GetTicket() string {
|
||||
if m != nil {
|
||||
return m.Ticket
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
type CipherBoxAuth struct {
|
||||
// A description of the stream one is attempting to initialize.
|
||||
Desc *CipherBoxDesc `protobuf:"bytes,1,opt,name=desc,proto3" json:"desc,omitempty"`
|
||||
// Types that are valid to be assigned to Auth:
|
||||
// *CipherBoxAuth_AcctAuth
|
||||
// *CipherBoxAuth_SidecarAuth
|
||||
Auth isCipherBoxAuth_Auth `protobuf_oneof:"auth"`
|
||||
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
||||
XXX_unrecognized []byte `json:"-"`
|
||||
XXX_sizecache int32 `json:"-"`
|
||||
}
|
||||
|
||||
func (m *CipherBoxAuth) Reset() { *m = CipherBoxAuth{} }
|
||||
func (m *CipherBoxAuth) String() string { return proto.CompactTextString(m) }
|
||||
func (*CipherBoxAuth) ProtoMessage() {}
|
||||
func (*CipherBoxAuth) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_165b784e4d2471a2, []int{2}
|
||||
}
|
||||
|
||||
func (m *CipherBoxAuth) XXX_Unmarshal(b []byte) error {
|
||||
return xxx_messageInfo_CipherBoxAuth.Unmarshal(m, b)
|
||||
}
|
||||
func (m *CipherBoxAuth) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
|
||||
return xxx_messageInfo_CipherBoxAuth.Marshal(b, m, deterministic)
|
||||
}
|
||||
func (m *CipherBoxAuth) XXX_Merge(src proto.Message) {
|
||||
xxx_messageInfo_CipherBoxAuth.Merge(m, src)
|
||||
}
|
||||
func (m *CipherBoxAuth) XXX_Size() int {
|
||||
return xxx_messageInfo_CipherBoxAuth.Size(m)
|
||||
}
|
||||
func (m *CipherBoxAuth) XXX_DiscardUnknown() {
|
||||
xxx_messageInfo_CipherBoxAuth.DiscardUnknown(m)
|
||||
}
|
||||
|
||||
var xxx_messageInfo_CipherBoxAuth proto.InternalMessageInfo
|
||||
|
||||
func (m *CipherBoxAuth) GetDesc() *CipherBoxDesc {
|
||||
if m != nil {
|
||||
return m.Desc
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type isCipherBoxAuth_Auth interface {
|
||||
isCipherBoxAuth_Auth()
|
||||
}
|
||||
|
||||
type CipherBoxAuth_AcctAuth struct {
|
||||
AcctAuth *PoolAccountAuth `protobuf:"bytes,2,opt,name=acct_auth,json=acctAuth,proto3,oneof"`
|
||||
}
|
||||
|
||||
type CipherBoxAuth_SidecarAuth struct {
|
||||
SidecarAuth *SidecarAuth `protobuf:"bytes,3,opt,name=sidecar_auth,json=sidecarAuth,proto3,oneof"`
|
||||
}
|
||||
|
||||
func (*CipherBoxAuth_AcctAuth) isCipherBoxAuth_Auth() {}
|
||||
|
||||
func (*CipherBoxAuth_SidecarAuth) isCipherBoxAuth_Auth() {}
|
||||
|
||||
func (m *CipherBoxAuth) GetAuth() isCipherBoxAuth_Auth {
|
||||
if m != nil {
|
||||
return m.Auth
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *CipherBoxAuth) GetAcctAuth() *PoolAccountAuth {
|
||||
if x, ok := m.GetAuth().(*CipherBoxAuth_AcctAuth); ok {
|
||||
return x.AcctAuth
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *CipherBoxAuth) GetSidecarAuth() *SidecarAuth {
|
||||
if x, ok := m.GetAuth().(*CipherBoxAuth_SidecarAuth); ok {
|
||||
return x.SidecarAuth
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// XXX_OneofWrappers is for the internal use of the proto package.
|
||||
func (*CipherBoxAuth) XXX_OneofWrappers() []interface{} {
|
||||
return []interface{}{
|
||||
(*CipherBoxAuth_AcctAuth)(nil),
|
||||
(*CipherBoxAuth_SidecarAuth)(nil),
|
||||
}
|
||||
}
|
||||
|
||||
type DelCipherBoxResp struct {
|
||||
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
||||
XXX_unrecognized []byte `json:"-"`
|
||||
XXX_sizecache int32 `json:"-"`
|
||||
}
|
||||
|
||||
func (m *DelCipherBoxResp) Reset() { *m = DelCipherBoxResp{} }
|
||||
func (m *DelCipherBoxResp) String() string { return proto.CompactTextString(m) }
|
||||
func (*DelCipherBoxResp) ProtoMessage() {}
|
||||
func (*DelCipherBoxResp) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_165b784e4d2471a2, []int{3}
|
||||
}
|
||||
|
||||
func (m *DelCipherBoxResp) XXX_Unmarshal(b []byte) error {
|
||||
return xxx_messageInfo_DelCipherBoxResp.Unmarshal(m, b)
|
||||
}
|
||||
func (m *DelCipherBoxResp) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
|
||||
return xxx_messageInfo_DelCipherBoxResp.Marshal(b, m, deterministic)
|
||||
}
|
||||
func (m *DelCipherBoxResp) XXX_Merge(src proto.Message) {
|
||||
xxx_messageInfo_DelCipherBoxResp.Merge(m, src)
|
||||
}
|
||||
func (m *DelCipherBoxResp) XXX_Size() int {
|
||||
return xxx_messageInfo_DelCipherBoxResp.Size(m)
|
||||
}
|
||||
func (m *DelCipherBoxResp) XXX_DiscardUnknown() {
|
||||
xxx_messageInfo_DelCipherBoxResp.DiscardUnknown(m)
|
||||
}
|
||||
|
||||
var xxx_messageInfo_DelCipherBoxResp proto.InternalMessageInfo
|
||||
|
||||
type CipherChallenge struct {
|
||||
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
||||
XXX_unrecognized []byte `json:"-"`
|
||||
XXX_sizecache int32 `json:"-"`
|
||||
}
|
||||
|
||||
func (m *CipherChallenge) Reset() { *m = CipherChallenge{} }
|
||||
func (m *CipherChallenge) String() string { return proto.CompactTextString(m) }
|
||||
func (*CipherChallenge) ProtoMessage() {}
|
||||
func (*CipherChallenge) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_165b784e4d2471a2, []int{4}
|
||||
}
|
||||
|
||||
func (m *CipherChallenge) XXX_Unmarshal(b []byte) error {
|
||||
return xxx_messageInfo_CipherChallenge.Unmarshal(m, b)
|
||||
}
|
||||
func (m *CipherChallenge) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
|
||||
return xxx_messageInfo_CipherChallenge.Marshal(b, m, deterministic)
|
||||
}
|
||||
func (m *CipherChallenge) XXX_Merge(src proto.Message) {
|
||||
xxx_messageInfo_CipherChallenge.Merge(m, src)
|
||||
}
|
||||
func (m *CipherChallenge) XXX_Size() int {
|
||||
return xxx_messageInfo_CipherChallenge.Size(m)
|
||||
}
|
||||
func (m *CipherChallenge) XXX_DiscardUnknown() {
|
||||
xxx_messageInfo_CipherChallenge.DiscardUnknown(m)
|
||||
}
|
||||
|
||||
var xxx_messageInfo_CipherChallenge proto.InternalMessageInfo
|
||||
|
||||
type CipherError struct {
|
||||
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
||||
XXX_unrecognized []byte `json:"-"`
|
||||
XXX_sizecache int32 `json:"-"`
|
||||
}
|
||||
|
||||
func (m *CipherError) Reset() { *m = CipherError{} }
|
||||
func (m *CipherError) String() string { return proto.CompactTextString(m) }
|
||||
func (*CipherError) ProtoMessage() {}
|
||||
func (*CipherError) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_165b784e4d2471a2, []int{5}
|
||||
}
|
||||
|
||||
func (m *CipherError) XXX_Unmarshal(b []byte) error {
|
||||
return xxx_messageInfo_CipherError.Unmarshal(m, b)
|
||||
}
|
||||
func (m *CipherError) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
|
||||
return xxx_messageInfo_CipherError.Marshal(b, m, deterministic)
|
||||
}
|
||||
func (m *CipherError) XXX_Merge(src proto.Message) {
|
||||
xxx_messageInfo_CipherError.Merge(m, src)
|
||||
}
|
||||
func (m *CipherError) XXX_Size() int {
|
||||
return xxx_messageInfo_CipherError.Size(m)
|
||||
}
|
||||
func (m *CipherError) XXX_DiscardUnknown() {
|
||||
xxx_messageInfo_CipherError.DiscardUnknown(m)
|
||||
}
|
||||
|
||||
var xxx_messageInfo_CipherError proto.InternalMessageInfo
|
||||
|
||||
type CipherSuccess struct {
|
||||
Desc *CipherBoxDesc `protobuf:"bytes,1,opt,name=desc,proto3" json:"desc,omitempty"`
|
||||
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
||||
XXX_unrecognized []byte `json:"-"`
|
||||
XXX_sizecache int32 `json:"-"`
|
||||
}
|
||||
|
||||
func (m *CipherSuccess) Reset() { *m = CipherSuccess{} }
|
||||
func (m *CipherSuccess) String() string { return proto.CompactTextString(m) }
|
||||
func (*CipherSuccess) ProtoMessage() {}
|
||||
func (*CipherSuccess) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_165b784e4d2471a2, []int{6}
|
||||
}
|
||||
|
||||
func (m *CipherSuccess) XXX_Unmarshal(b []byte) error {
|
||||
return xxx_messageInfo_CipherSuccess.Unmarshal(m, b)
|
||||
}
|
||||
func (m *CipherSuccess) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
|
||||
return xxx_messageInfo_CipherSuccess.Marshal(b, m, deterministic)
|
||||
}
|
||||
func (m *CipherSuccess) XXX_Merge(src proto.Message) {
|
||||
xxx_messageInfo_CipherSuccess.Merge(m, src)
|
||||
}
|
||||
func (m *CipherSuccess) XXX_Size() int {
|
||||
return xxx_messageInfo_CipherSuccess.Size(m)
|
||||
}
|
||||
func (m *CipherSuccess) XXX_DiscardUnknown() {
|
||||
xxx_messageInfo_CipherSuccess.DiscardUnknown(m)
|
||||
}
|
||||
|
||||
var xxx_messageInfo_CipherSuccess proto.InternalMessageInfo
|
||||
|
||||
func (m *CipherSuccess) GetDesc() *CipherBoxDesc {
|
||||
if m != nil {
|
||||
return m.Desc
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type CipherInitResp struct {
|
||||
// Types that are valid to be assigned to Resp:
|
||||
// *CipherInitResp_Success
|
||||
// *CipherInitResp_Challenge
|
||||
// *CipherInitResp_Error
|
||||
Resp isCipherInitResp_Resp `protobuf_oneof:"resp"`
|
||||
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
||||
XXX_unrecognized []byte `json:"-"`
|
||||
XXX_sizecache int32 `json:"-"`
|
||||
}
|
||||
|
||||
func (m *CipherInitResp) Reset() { *m = CipherInitResp{} }
|
||||
func (m *CipherInitResp) String() string { return proto.CompactTextString(m) }
|
||||
func (*CipherInitResp) ProtoMessage() {}
|
||||
func (*CipherInitResp) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_165b784e4d2471a2, []int{7}
|
||||
}
|
||||
|
||||
func (m *CipherInitResp) XXX_Unmarshal(b []byte) error {
|
||||
return xxx_messageInfo_CipherInitResp.Unmarshal(m, b)
|
||||
}
|
||||
func (m *CipherInitResp) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
|
||||
return xxx_messageInfo_CipherInitResp.Marshal(b, m, deterministic)
|
||||
}
|
||||
func (m *CipherInitResp) XXX_Merge(src proto.Message) {
|
||||
xxx_messageInfo_CipherInitResp.Merge(m, src)
|
||||
}
|
||||
func (m *CipherInitResp) XXX_Size() int {
|
||||
return xxx_messageInfo_CipherInitResp.Size(m)
|
||||
}
|
||||
func (m *CipherInitResp) XXX_DiscardUnknown() {
|
||||
xxx_messageInfo_CipherInitResp.DiscardUnknown(m)
|
||||
}
|
||||
|
||||
var xxx_messageInfo_CipherInitResp proto.InternalMessageInfo
|
||||
|
||||
type isCipherInitResp_Resp interface {
|
||||
isCipherInitResp_Resp()
|
||||
}
|
||||
|
||||
type CipherInitResp_Success struct {
|
||||
Success *CipherSuccess `protobuf:"bytes,1,opt,name=success,proto3,oneof"`
|
||||
}
|
||||
|
||||
type CipherInitResp_Challenge struct {
|
||||
Challenge *CipherChallenge `protobuf:"bytes,2,opt,name=challenge,proto3,oneof"`
|
||||
}
|
||||
|
||||
type CipherInitResp_Error struct {
|
||||
Error *CipherError `protobuf:"bytes,3,opt,name=error,proto3,oneof"`
|
||||
}
|
||||
|
||||
func (*CipherInitResp_Success) isCipherInitResp_Resp() {}
|
||||
|
||||
func (*CipherInitResp_Challenge) isCipherInitResp_Resp() {}
|
||||
|
||||
func (*CipherInitResp_Error) isCipherInitResp_Resp() {}
|
||||
|
||||
func (m *CipherInitResp) GetResp() isCipherInitResp_Resp {
|
||||
if m != nil {
|
||||
return m.Resp
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *CipherInitResp) GetSuccess() *CipherSuccess {
|
||||
if x, ok := m.GetResp().(*CipherInitResp_Success); ok {
|
||||
return x.Success
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *CipherInitResp) GetChallenge() *CipherChallenge {
|
||||
if x, ok := m.GetResp().(*CipherInitResp_Challenge); ok {
|
||||
return x.Challenge
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *CipherInitResp) GetError() *CipherError {
|
||||
if x, ok := m.GetResp().(*CipherInitResp_Error); ok {
|
||||
return x.Error
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// XXX_OneofWrappers is for the internal use of the proto package.
|
||||
func (*CipherInitResp) XXX_OneofWrappers() []interface{} {
|
||||
return []interface{}{
|
||||
(*CipherInitResp_Success)(nil),
|
||||
(*CipherInitResp_Challenge)(nil),
|
||||
(*CipherInitResp_Error)(nil),
|
||||
}
|
||||
}
|
||||
|
||||
type CipherBoxDesc struct {
|
||||
StreamId []byte `protobuf:"bytes,1,opt,name=stream_id,json=streamId,proto3" json:"stream_id,omitempty"`
|
||||
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
||||
XXX_unrecognized []byte `json:"-"`
|
||||
XXX_sizecache int32 `json:"-"`
|
||||
}
|
||||
|
||||
func (m *CipherBoxDesc) Reset() { *m = CipherBoxDesc{} }
|
||||
func (m *CipherBoxDesc) String() string { return proto.CompactTextString(m) }
|
||||
func (*CipherBoxDesc) ProtoMessage() {}
|
||||
func (*CipherBoxDesc) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_165b784e4d2471a2, []int{8}
|
||||
}
|
||||
|
||||
func (m *CipherBoxDesc) XXX_Unmarshal(b []byte) error {
|
||||
return xxx_messageInfo_CipherBoxDesc.Unmarshal(m, b)
|
||||
}
|
||||
func (m *CipherBoxDesc) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
|
||||
return xxx_messageInfo_CipherBoxDesc.Marshal(b, m, deterministic)
|
||||
}
|
||||
func (m *CipherBoxDesc) XXX_Merge(src proto.Message) {
|
||||
xxx_messageInfo_CipherBoxDesc.Merge(m, src)
|
||||
}
|
||||
func (m *CipherBoxDesc) XXX_Size() int {
|
||||
return xxx_messageInfo_CipherBoxDesc.Size(m)
|
||||
}
|
||||
func (m *CipherBoxDesc) XXX_DiscardUnknown() {
|
||||
xxx_messageInfo_CipherBoxDesc.DiscardUnknown(m)
|
||||
}
|
||||
|
||||
var xxx_messageInfo_CipherBoxDesc proto.InternalMessageInfo
|
||||
|
||||
func (m *CipherBoxDesc) GetStreamId() []byte {
|
||||
if m != nil {
|
||||
return m.StreamId
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type CipherBox struct {
|
||||
Desc *CipherBoxDesc `protobuf:"bytes,1,opt,name=desc,proto3" json:"desc,omitempty"`
|
||||
Msg []byte `protobuf:"bytes,2,opt,name=msg,proto3" json:"msg,omitempty"`
|
||||
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
||||
XXX_unrecognized []byte `json:"-"`
|
||||
XXX_sizecache int32 `json:"-"`
|
||||
}
|
||||
|
||||
func (m *CipherBox) Reset() { *m = CipherBox{} }
|
||||
func (m *CipherBox) String() string { return proto.CompactTextString(m) }
|
||||
func (*CipherBox) ProtoMessage() {}
|
||||
func (*CipherBox) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_165b784e4d2471a2, []int{9}
|
||||
}
|
||||
|
||||
func (m *CipherBox) XXX_Unmarshal(b []byte) error {
|
||||
return xxx_messageInfo_CipherBox.Unmarshal(m, b)
|
||||
}
|
||||
func (m *CipherBox) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
|
||||
return xxx_messageInfo_CipherBox.Marshal(b, m, deterministic)
|
||||
}
|
||||
func (m *CipherBox) XXX_Merge(src proto.Message) {
|
||||
xxx_messageInfo_CipherBox.Merge(m, src)
|
||||
}
|
||||
func (m *CipherBox) XXX_Size() int {
|
||||
return xxx_messageInfo_CipherBox.Size(m)
|
||||
}
|
||||
func (m *CipherBox) XXX_DiscardUnknown() {
|
||||
xxx_messageInfo_CipherBox.DiscardUnknown(m)
|
||||
}
|
||||
|
||||
var xxx_messageInfo_CipherBox proto.InternalMessageInfo
|
||||
|
||||
func (m *CipherBox) GetDesc() *CipherBoxDesc {
|
||||
if m != nil {
|
||||
return m.Desc
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *CipherBox) GetMsg() []byte {
|
||||
if m != nil {
|
||||
return m.Msg
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func init() {
|
||||
proto.RegisterType((*PoolAccountAuth)(nil), "poolrpc.PoolAccountAuth")
|
||||
proto.RegisterType((*SidecarAuth)(nil), "poolrpc.SidecarAuth")
|
||||
proto.RegisterType((*CipherBoxAuth)(nil), "poolrpc.CipherBoxAuth")
|
||||
proto.RegisterType((*DelCipherBoxResp)(nil), "poolrpc.DelCipherBoxResp")
|
||||
proto.RegisterType((*CipherChallenge)(nil), "poolrpc.CipherChallenge")
|
||||
proto.RegisterType((*CipherError)(nil), "poolrpc.CipherError")
|
||||
proto.RegisterType((*CipherSuccess)(nil), "poolrpc.CipherSuccess")
|
||||
proto.RegisterType((*CipherInitResp)(nil), "poolrpc.CipherInitResp")
|
||||
proto.RegisterType((*CipherBoxDesc)(nil), "poolrpc.CipherBoxDesc")
|
||||
proto.RegisterType((*CipherBox)(nil), "poolrpc.CipherBox")
|
||||
}
|
||||
|
||||
func init() { proto.RegisterFile("hashmail.proto", fileDescriptor_165b784e4d2471a2) }
|
||||
|
||||
var fileDescriptor_165b784e4d2471a2 = []byte{
|
||||
// 500 bytes of a gzipped FileDescriptorProto
|
||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x94, 0x54, 0xdd, 0x6a, 0xdb, 0x30,
|
||||
0x18, 0xb5, 0xdb, 0x2e, 0x89, 0x3f, 0xa7, 0x3f, 0x13, 0xa3, 0x4b, 0x33, 0x06, 0xc3, 0x30, 0x28,
|
||||
0x5b, 0x97, 0x8c, 0xec, 0x62, 0x7f, 0x17, 0x23, 0x69, 0x07, 0x0e, 0x65, 0x63, 0xc8, 0x77, 0xbb,
|
||||
0x09, 0x8a, 0x2c, 0x6c, 0x51, 0xc7, 0x32, 0x92, 0xbc, 0xad, 0x2f, 0xb0, 0x27, 0x1a, 0xec, 0xf5,
|
||||
0x8a, 0x64, 0xc7, 0x09, 0x49, 0x73, 0xd1, 0xbb, 0xe8, 0xe4, 0x9c, 0xa3, 0x73, 0x3e, 0x7d, 0x18,
|
||||
0x8e, 0x52, 0xa2, 0xd2, 0x05, 0xe1, 0xd9, 0xa0, 0x90, 0x42, 0x0b, 0xd4, 0x2e, 0x84, 0xc8, 0x64,
|
||||
0x41, 0x83, 0x6b, 0x38, 0xfe, 0x21, 0x44, 0x36, 0xa6, 0x54, 0x94, 0xb9, 0x1e, 0x97, 0x3a, 0x45,
|
||||
0x67, 0xd0, 0x21, 0x94, 0xea, 0xd9, 0x0d, 0xbb, 0xed, 0xb9, 0x2f, 0xdc, 0xf3, 0x2e, 0x6e, 0x9b,
|
||||
0xf3, 0x35, 0xbb, 0x45, 0xcf, 0x01, 0x94, 0x96, 0x8c, 0x2c, 0x66, 0x8a, 0x27, 0xbd, 0x3d, 0xfb,
|
||||
0xa7, 0x57, 0x21, 0x11, 0x4f, 0x82, 0x97, 0xe0, 0x47, 0x3c, 0x66, 0x94, 0x48, 0x6b, 0x74, 0x0a,
|
||||
0x2d, 0xcd, 0xe9, 0x0d, 0xd3, 0xd6, 0xc6, 0xc3, 0xf5, 0x29, 0xf8, 0xef, 0xc2, 0xe1, 0x25, 0x2f,
|
||||
0x52, 0x26, 0x27, 0xe2, 0x8f, 0x65, 0xbe, 0x82, 0x83, 0x98, 0x29, 0x6a, 0x79, 0xfe, 0xe8, 0x74,
|
||||
0x50, 0xa7, 0x1b, 0x34, 0xac, 0x2b, 0xa6, 0x28, 0xb6, 0x1c, 0xf4, 0x1e, 0x3c, 0x1b, 0x8f, 0x94,
|
||||
0x3a, 0xb5, 0x11, 0xfc, 0x51, 0xaf, 0x11, 0x6c, 0x74, 0x09, 0x1d, 0x6c, 0xbb, 0xd8, 0x4b, 0x3e,
|
||||
0x42, 0x57, 0x55, 0xe9, 0x2a, 0xed, 0xbe, 0xd5, 0x3e, 0x69, 0xb4, 0x6b, 0xd1, 0x43, 0x07, 0xfb,
|
||||
0x6a, 0x75, 0x9c, 0xb4, 0xe0, 0xc0, 0x48, 0x02, 0x04, 0x27, 0x57, 0x2c, 0x6b, 0x52, 0x61, 0xa6,
|
||||
0x8a, 0xe0, 0x31, 0x1c, 0x57, 0xc0, 0x65, 0x4a, 0xb2, 0x8c, 0xe5, 0x09, 0x0b, 0x0e, 0xc1, 0xaf,
|
||||
0xa0, 0xaf, 0x52, 0x0a, 0x19, 0x7c, 0x5e, 0xd6, 0x8d, 0x4a, 0x4a, 0x99, 0x52, 0x0f, 0xa9, 0x1b,
|
||||
0xfc, 0x73, 0xe1, 0xa8, 0xc2, 0xa7, 0x39, 0xd7, 0xe6, 0x46, 0x34, 0x82, 0xb6, 0xaa, 0x9c, 0x76,
|
||||
0x38, 0xd4, 0xf7, 0x84, 0x0e, 0x5e, 0x12, 0xd1, 0x07, 0xf0, 0xe8, 0x32, 0xdf, 0xd6, 0xd4, 0x36,
|
||||
0xf2, 0x87, 0x0e, 0x5e, 0x91, 0xd1, 0x05, 0x3c, 0x62, 0xa6, 0xc6, 0xd6, 0xbc, 0xd6, 0x2a, 0x86,
|
||||
0x0e, 0xae, 0x48, 0x66, 0x52, 0xd2, 0x4c, 0xe5, 0x62, 0xed, 0x89, 0x4d, 0x1b, 0xf4, 0x0c, 0xea,
|
||||
0x45, 0x99, 0xf1, 0xb8, 0x5e, 0xab, 0x4e, 0x05, 0x4c, 0xe3, 0x60, 0x0a, 0x5e, 0xc3, 0x7e, 0xd0,
|
||||
0x32, 0x9c, 0xc0, 0xfe, 0x42, 0x2d, 0x37, 0xd1, 0xfc, 0x1c, 0xfd, 0xdd, 0x83, 0x4e, 0x48, 0x54,
|
||||
0xfa, 0x8d, 0xf0, 0x0c, 0x7d, 0x81, 0xee, 0x77, 0xf6, 0x7b, 0x65, 0x7d, 0x8f, 0x99, 0x79, 0xdf,
|
||||
0xfe, 0xd3, 0x0d, 0xbc, 0x19, 0xf5, 0x18, 0xba, 0xeb, 0x0f, 0xbe, 0xd3, 0xe0, 0xac, 0xc1, 0x37,
|
||||
0xf7, 0x03, 0x7d, 0x02, 0x88, 0x58, 0x1e, 0x47, 0xb6, 0x2b, 0x42, 0xdb, 0x06, 0xfd, 0x1d, 0x15,
|
||||
0xcf, 0x5d, 0xa3, 0xc5, 0x8c, 0xfe, 0xaa, 0xb5, 0x3b, 0x78, 0xfd, 0x7b, 0x3c, 0xdf, 0xba, 0x93,
|
||||
0x37, 0x3f, 0x5f, 0x27, 0x5c, 0xa7, 0xe5, 0x7c, 0x40, 0xc5, 0x62, 0x98, 0xf1, 0x24, 0xd5, 0x39,
|
||||
0xcf, 0x93, 0x8c, 0xcc, 0xd5, 0xd0, 0xf0, 0x87, 0xa4, 0xa4, 0x9a, 0x8b, 0x9c, 0x31, 0x29, 0x0b,
|
||||
0x3a, 0x6f, 0xd9, 0x0f, 0xc3, 0xbb, 0xbb, 0x00, 0x00, 0x00, 0xff, 0xff, 0xea, 0x35, 0xf5, 0xfe,
|
||||
0x2a, 0x04, 0x00, 0x00,
|
||||
}
|
||||
|
||||
// Reference imports to suppress errors if they are not otherwise used.
|
||||
var _ context.Context
|
||||
var _ grpc.ClientConn
|
||||
|
||||
// This is a compile-time assertion to ensure that this generated file
|
||||
// is compatible with the grpc package it is being compiled against.
|
||||
const _ = grpc.SupportPackageIsVersion4
|
||||
|
||||
// HashMailClient is the client API for HashMail service.
|
||||
//
|
||||
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream.
|
||||
type HashMailClient interface {
|
||||
//
|
||||
//NewCipherBox creates a new cipher box pipe/stream given a valid
|
||||
//authentication mechanism. If the authentication mechanism has been revoked,
|
||||
//or needs to be changed, then a CipherChallenge message is returned.
|
||||
//Otherwise the method will either be accepted or rejected.
|
||||
NewCipherBox(ctx context.Context, in *CipherBoxAuth, opts ...grpc.CallOption) (*CipherInitResp, error)
|
||||
//
|
||||
//DelCipherBox attempts to tear down an existing cipher box pipe. The same
|
||||
//authentication mechanism used to initially create the stream MUST be
|
||||
//specified.
|
||||
DelCipherBox(ctx context.Context, in *CipherBoxAuth, opts ...grpc.CallOption) (*DelCipherBoxResp, error)
|
||||
//
|
||||
//SendStream opens up the write side of the passed CipherBox pipe. Writes
|
||||
//will be non-blocking up to the buffer size of the pipe. Beyond that writes
|
||||
//will block until completed.
|
||||
SendStream(ctx context.Context, opts ...grpc.CallOption) (HashMail_SendStreamClient, error)
|
||||
//
|
||||
//RecvStream opens up the read side of the passed CipherBox pipe. This method
|
||||
//will block until a full message has been read as this is a message based
|
||||
//pipe/stream abstraction.
|
||||
RecvStream(ctx context.Context, in *CipherBoxDesc, opts ...grpc.CallOption) (HashMail_RecvStreamClient, error)
|
||||
}
|
||||
|
||||
type hashMailClient struct {
|
||||
cc *grpc.ClientConn
|
||||
}
|
||||
|
||||
func NewHashMailClient(cc *grpc.ClientConn) HashMailClient {
|
||||
return &hashMailClient{cc}
|
||||
}
|
||||
|
||||
func (c *hashMailClient) NewCipherBox(ctx context.Context, in *CipherBoxAuth, opts ...grpc.CallOption) (*CipherInitResp, error) {
|
||||
out := new(CipherInitResp)
|
||||
err := c.cc.Invoke(ctx, "/poolrpc.HashMail/NewCipherBox", in, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *hashMailClient) DelCipherBox(ctx context.Context, in *CipherBoxAuth, opts ...grpc.CallOption) (*DelCipherBoxResp, error) {
|
||||
out := new(DelCipherBoxResp)
|
||||
err := c.cc.Invoke(ctx, "/poolrpc.HashMail/DelCipherBox", in, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *hashMailClient) SendStream(ctx context.Context, opts ...grpc.CallOption) (HashMail_SendStreamClient, error) {
|
||||
stream, err := c.cc.NewStream(ctx, &_HashMail_serviceDesc.Streams[0], "/poolrpc.HashMail/SendStream", opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
x := &hashMailSendStreamClient{stream}
|
||||
return x, nil
|
||||
}
|
||||
|
||||
type HashMail_SendStreamClient interface {
|
||||
Send(*CipherBox) error
|
||||
CloseAndRecv() (*CipherBoxDesc, error)
|
||||
grpc.ClientStream
|
||||
}
|
||||
|
||||
type hashMailSendStreamClient struct {
|
||||
grpc.ClientStream
|
||||
}
|
||||
|
||||
func (x *hashMailSendStreamClient) Send(m *CipherBox) error {
|
||||
return x.ClientStream.SendMsg(m)
|
||||
}
|
||||
|
||||
func (x *hashMailSendStreamClient) CloseAndRecv() (*CipherBoxDesc, error) {
|
||||
if err := x.ClientStream.CloseSend(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
m := new(CipherBoxDesc)
|
||||
if err := x.ClientStream.RecvMsg(m); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return m, nil
|
||||
}
|
||||
|
||||
func (c *hashMailClient) RecvStream(ctx context.Context, in *CipherBoxDesc, opts ...grpc.CallOption) (HashMail_RecvStreamClient, error) {
|
||||
stream, err := c.cc.NewStream(ctx, &_HashMail_serviceDesc.Streams[1], "/poolrpc.HashMail/RecvStream", opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
x := &hashMailRecvStreamClient{stream}
|
||||
if err := x.ClientStream.SendMsg(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := x.ClientStream.CloseSend(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return x, nil
|
||||
}
|
||||
|
||||
type HashMail_RecvStreamClient interface {
|
||||
Recv() (*CipherBox, error)
|
||||
grpc.ClientStream
|
||||
}
|
||||
|
||||
type hashMailRecvStreamClient struct {
|
||||
grpc.ClientStream
|
||||
}
|
||||
|
||||
func (x *hashMailRecvStreamClient) Recv() (*CipherBox, error) {
|
||||
m := new(CipherBox)
|
||||
if err := x.ClientStream.RecvMsg(m); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return m, nil
|
||||
}
|
||||
|
||||
// HashMailServer is the server API for HashMail service.
|
||||
type HashMailServer interface {
|
||||
//
|
||||
//NewCipherBox creates a new cipher box pipe/stream given a valid
|
||||
//authentication mechanism. If the authentication mechanism has been revoked,
|
||||
//or needs to be changed, then a CipherChallenge message is returned.
|
||||
//Otherwise the method will either be accepted or rejected.
|
||||
NewCipherBox(context.Context, *CipherBoxAuth) (*CipherInitResp, error)
|
||||
//
|
||||
//DelCipherBox attempts to tear down an existing cipher box pipe. The same
|
||||
//authentication mechanism used to initially create the stream MUST be
|
||||
//specified.
|
||||
DelCipherBox(context.Context, *CipherBoxAuth) (*DelCipherBoxResp, error)
|
||||
//
|
||||
//SendStream opens up the write side of the passed CipherBox pipe. Writes
|
||||
//will be non-blocking up to the buffer size of the pipe. Beyond that writes
|
||||
//will block until completed.
|
||||
SendStream(HashMail_SendStreamServer) error
|
||||
//
|
||||
//RecvStream opens up the read side of the passed CipherBox pipe. This method
|
||||
//will block until a full message has been read as this is a message based
|
||||
//pipe/stream abstraction.
|
||||
RecvStream(*CipherBoxDesc, HashMail_RecvStreamServer) error
|
||||
}
|
||||
|
||||
// UnimplementedHashMailServer can be embedded to have forward compatible implementations.
|
||||
type UnimplementedHashMailServer struct {
|
||||
}
|
||||
|
||||
func (*UnimplementedHashMailServer) NewCipherBox(ctx context.Context, req *CipherBoxAuth) (*CipherInitResp, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method NewCipherBox not implemented")
|
||||
}
|
||||
func (*UnimplementedHashMailServer) DelCipherBox(ctx context.Context, req *CipherBoxAuth) (*DelCipherBoxResp, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method DelCipherBox not implemented")
|
||||
}
|
||||
func (*UnimplementedHashMailServer) SendStream(srv HashMail_SendStreamServer) error {
|
||||
return status.Errorf(codes.Unimplemented, "method SendStream not implemented")
|
||||
}
|
||||
func (*UnimplementedHashMailServer) RecvStream(req *CipherBoxDesc, srv HashMail_RecvStreamServer) error {
|
||||
return status.Errorf(codes.Unimplemented, "method RecvStream not implemented")
|
||||
}
|
||||
|
||||
func RegisterHashMailServer(s *grpc.Server, srv HashMailServer) {
|
||||
s.RegisterService(&_HashMail_serviceDesc, srv)
|
||||
}
|
||||
|
||||
func _HashMail_NewCipherBox_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(CipherBoxAuth)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(HashMailServer).NewCipherBox(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: "/poolrpc.HashMail/NewCipherBox",
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(HashMailServer).NewCipherBox(ctx, req.(*CipherBoxAuth))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _HashMail_DelCipherBox_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(CipherBoxAuth)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(HashMailServer).DelCipherBox(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: "/poolrpc.HashMail/DelCipherBox",
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(HashMailServer).DelCipherBox(ctx, req.(*CipherBoxAuth))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _HashMail_SendStream_Handler(srv interface{}, stream grpc.ServerStream) error {
|
||||
return srv.(HashMailServer).SendStream(&hashMailSendStreamServer{stream})
|
||||
}
|
||||
|
||||
type HashMail_SendStreamServer interface {
|
||||
SendAndClose(*CipherBoxDesc) error
|
||||
Recv() (*CipherBox, error)
|
||||
grpc.ServerStream
|
||||
}
|
||||
|
||||
type hashMailSendStreamServer struct {
|
||||
grpc.ServerStream
|
||||
}
|
||||
|
||||
func (x *hashMailSendStreamServer) SendAndClose(m *CipherBoxDesc) error {
|
||||
return x.ServerStream.SendMsg(m)
|
||||
}
|
||||
|
||||
func (x *hashMailSendStreamServer) Recv() (*CipherBox, error) {
|
||||
m := new(CipherBox)
|
||||
if err := x.ServerStream.RecvMsg(m); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return m, nil
|
||||
}
|
||||
|
||||
func _HashMail_RecvStream_Handler(srv interface{}, stream grpc.ServerStream) error {
|
||||
m := new(CipherBoxDesc)
|
||||
if err := stream.RecvMsg(m); err != nil {
|
||||
return err
|
||||
}
|
||||
return srv.(HashMailServer).RecvStream(m, &hashMailRecvStreamServer{stream})
|
||||
}
|
||||
|
||||
type HashMail_RecvStreamServer interface {
|
||||
Send(*CipherBox) error
|
||||
grpc.ServerStream
|
||||
}
|
||||
|
||||
type hashMailRecvStreamServer struct {
|
||||
grpc.ServerStream
|
||||
}
|
||||
|
||||
func (x *hashMailRecvStreamServer) Send(m *CipherBox) error {
|
||||
return x.ServerStream.SendMsg(m)
|
||||
}
|
||||
|
||||
var _HashMail_serviceDesc = grpc.ServiceDesc{
|
||||
ServiceName: "poolrpc.HashMail",
|
||||
HandlerType: (*HashMailServer)(nil),
|
||||
Methods: []grpc.MethodDesc{
|
||||
{
|
||||
MethodName: "NewCipherBox",
|
||||
Handler: _HashMail_NewCipherBox_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "DelCipherBox",
|
||||
Handler: _HashMail_DelCipherBox_Handler,
|
||||
},
|
||||
},
|
||||
Streams: []grpc.StreamDesc{
|
||||
{
|
||||
StreamName: "SendStream",
|
||||
Handler: _HashMail_SendStream_Handler,
|
||||
ClientStreams: true,
|
||||
},
|
||||
{
|
||||
StreamName: "RecvStream",
|
||||
Handler: _HashMail_RecvStream_Handler,
|
||||
ServerStreams: true,
|
||||
},
|
||||
},
|
||||
Metadata: "hashmail.proto",
|
||||
}
|
||||
118
auctioneerrpc/hashmail.proto
Normal file
118
auctioneerrpc/hashmail.proto
Normal file
|
|
@ -0,0 +1,118 @@
|
|||
syntax = "proto3";
|
||||
|
||||
// We can't rename this to auctioneerrpc, otherwise it would be a breaking
|
||||
// change since the package name is also contained in the HTTP URIs and old
|
||||
// clients would call the wrong endpoints. Luckily with the go_package option we
|
||||
// can have different golang and RPC package names.
|
||||
package poolrpc;
|
||||
|
||||
option go_package = "github.com/lightninglabs/pool/auctioneerrpc";
|
||||
|
||||
// HashMail exposes a simple synchronous network stream that can be used for
|
||||
// various types of synchronization and coordination. The service allows
|
||||
// authenticated users to create a simplex stream call a cipher box. Once the
|
||||
// stream is created, any user that knows of the stream ID can read/write from
|
||||
// the stream, but only a single user can be on either side at a time.
|
||||
service HashMail {
|
||||
/*
|
||||
NewCipherBox creates a new cipher box pipe/stream given a valid
|
||||
authentication mechanism. If the authentication mechanism has been revoked,
|
||||
or needs to be changed, then a CipherChallenge message is returned.
|
||||
Otherwise the method will either be accepted or rejected.
|
||||
*/
|
||||
rpc NewCipherBox (CipherBoxAuth) returns (CipherInitResp);
|
||||
|
||||
/*
|
||||
DelCipherBox attempts to tear down an existing cipher box pipe. The same
|
||||
authentication mechanism used to initially create the stream MUST be
|
||||
specified.
|
||||
*/
|
||||
rpc DelCipherBox (CipherBoxAuth) returns (DelCipherBoxResp);
|
||||
|
||||
/*
|
||||
SendStream opens up the write side of the passed CipherBox pipe. Writes
|
||||
will be non-blocking up to the buffer size of the pipe. Beyond that writes
|
||||
will block until completed.
|
||||
*/
|
||||
rpc SendStream (stream CipherBox) returns (CipherBoxDesc);
|
||||
|
||||
/*
|
||||
RecvStream opens up the read side of the passed CipherBox pipe. This method
|
||||
will block until a full message has been read as this is a message based
|
||||
pipe/stream abstraction.
|
||||
*/
|
||||
rpc RecvStream (CipherBoxDesc) returns (stream CipherBox);
|
||||
}
|
||||
|
||||
message PoolAccountAuth {
|
||||
// The account key being used to authenticate.
|
||||
bytes acct_key = 1;
|
||||
|
||||
// A valid signature over the stream ID being used.
|
||||
bytes stream_sig = 2;
|
||||
}
|
||||
|
||||
message SidecarAuth {
|
||||
/*
|
||||
A valid sidecar ticket that has been signed (offered) by a Pool account in
|
||||
the active state.
|
||||
*/
|
||||
string ticket = 1;
|
||||
}
|
||||
|
||||
message CipherBoxAuth {
|
||||
// A description of the stream one is attempting to initialize.
|
||||
CipherBoxDesc desc = 1;
|
||||
|
||||
oneof auth {
|
||||
PoolAccountAuth acct_auth = 2;
|
||||
|
||||
SidecarAuth sidecar_auth = 3;
|
||||
}
|
||||
}
|
||||
|
||||
message DelCipherBoxResp {
|
||||
}
|
||||
|
||||
message CipherChallenge {
|
||||
// TODO(roasbeef): payment request, node key, etc, etc
|
||||
}
|
||||
|
||||
message CipherError {
|
||||
}
|
||||
|
||||
message CipherSuccess {
|
||||
CipherBoxDesc desc = 1;
|
||||
}
|
||||
|
||||
message CipherInitResp {
|
||||
oneof resp {
|
||||
/*
|
||||
CipherSuccess is returned if the initialization of the cipher box was
|
||||
successful.
|
||||
*/
|
||||
CipherSuccess success = 1;
|
||||
|
||||
/*
|
||||
CipherChallenge is returned if the authentication mechanism was revoked
|
||||
or needs to be refreshed.
|
||||
*/
|
||||
CipherChallenge challenge = 2;
|
||||
|
||||
/*
|
||||
CipherError is returned if the authentication mechanism failed to
|
||||
validate.
|
||||
*/
|
||||
CipherError error = 3;
|
||||
}
|
||||
}
|
||||
|
||||
message CipherBoxDesc {
|
||||
bytes stream_id = 1;
|
||||
}
|
||||
|
||||
message CipherBox {
|
||||
CipherBoxDesc desc = 1;
|
||||
|
||||
bytes msg = 2;
|
||||
}
|
||||
607
auto_sidecar.go
Normal file
607
auto_sidecar.go
Normal file
|
|
@ -0,0 +1,607 @@
|
|||
package pool
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/lightninglabs/pool/account"
|
||||
"github.com/lightninglabs/pool/clientdb"
|
||||
"github.com/lightninglabs/pool/order"
|
||||
"github.com/lightninglabs/pool/sidecar"
|
||||
"github.com/lightningnetwork/lnd/lnwire"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/status"
|
||||
)
|
||||
|
||||
// SidecarPacket encapsulates the current state of an auto sidecar negotiator.
|
||||
// Note that the state of the negotiator, and the ticket may differ, this is
|
||||
// what will trigger a state transition.
|
||||
type SidecarPacket struct {
|
||||
// CurrentState is the current state of the negotiator.
|
||||
//
|
||||
// TODO(roasbeef): remove??
|
||||
CurrentState sidecar.State
|
||||
|
||||
// ReceiverTicket is the current ticket of the receiver.
|
||||
ReceiverTicket *sidecar.Ticket
|
||||
|
||||
// ProviderTicket is the current ticket of the provider.
|
||||
ProviderTicket *sidecar.Ticket
|
||||
}
|
||||
|
||||
// deriveProviderStreamID derives the stream ID of the provider's cipher box,
|
||||
// we'll use this to allow the recipient to send messages to the provider.
|
||||
func deriveProviderStreamID(ticket *sidecar.Ticket) ([64]byte, error) {
|
||||
|
||||
var streamID [64]byte
|
||||
|
||||
// This stream ID will simply be the fixed 64-byte signature of our
|
||||
// sidecar ticket offer.
|
||||
wireSig, err := lnwire.NewSigFromRawSignature(
|
||||
ticket.Offer.SigOfferDigest.Serialize(),
|
||||
)
|
||||
if err != nil {
|
||||
return streamID, err
|
||||
}
|
||||
|
||||
copy(streamID[:], wireSig[:])
|
||||
|
||||
return streamID, nil
|
||||
}
|
||||
|
||||
// deriveRecipientStreamID derives the stream ID of the cipher box that the
|
||||
// provider of the sidecar ticket will use to send messages to the receiver.
|
||||
func deriveRecipientStreamID(ticket *sidecar.Ticket) [64]byte {
|
||||
receiverMultisig := ticket.Recipient.NodePubKey.SerializeCompressed()
|
||||
receiverNode := ticket.Recipient.MultiSigPubKey.SerializeCompressed()
|
||||
|
||||
// The stream ID will be the concentration of the receiver's multi-sig
|
||||
// and node keys, ignoring the first byte of each key that essentially
|
||||
// communicates parity information.
|
||||
var (
|
||||
streamID [64]byte
|
||||
n int
|
||||
)
|
||||
n += copy(streamID[:], receiverMultisig[1:])
|
||||
copy(streamID[n:], receiverNode[1:])
|
||||
|
||||
return streamID
|
||||
}
|
||||
|
||||
// deriveStreamID derives corresponding stream ID for the provider of the
|
||||
// receiver based on the passed sidecar ticket.
|
||||
func deriveStreamID(ticket *sidecar.Ticket, provider bool) ([64]byte, error) {
|
||||
if provider {
|
||||
return deriveProviderStreamID(ticket)
|
||||
}
|
||||
|
||||
return deriveRecipientStreamID(ticket), nil
|
||||
}
|
||||
|
||||
// sendSidecarPkt attempts to send a sidecar packet to the opposite party using
|
||||
// their registered cipherbox stream.
|
||||
func (a *SidecarAcceptor) sendSidecarPkt(pkt *sidecar.Ticket,
|
||||
provider bool) error {
|
||||
|
||||
var ticketBuf bytes.Buffer
|
||||
err := sidecar.SerializeTicket(&ticketBuf, pkt)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
streamID, err := deriveStreamID(pkt, provider)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
target := "receiver"
|
||||
if provider {
|
||||
target = "provider"
|
||||
}
|
||||
|
||||
log.Infof("Sending ticket(state=%v, id=%x) to %v stream_id=%x",
|
||||
pkt.State, pkt.ID[:], target, streamID[:])
|
||||
|
||||
return a.client.SendCipherBoxMsg(
|
||||
context.Background(), streamID, ticketBuf.Bytes(),
|
||||
)
|
||||
}
|
||||
|
||||
// recvSidecarPkt attempts to receive a new sidecar packet from the opposite
|
||||
// party using their registered cipherbox stream.
|
||||
func (a *SidecarAcceptor) recvSidecarPkt(ticket *sidecar.Ticket,
|
||||
provider bool) (*sidecar.Ticket, error) {
|
||||
|
||||
streamID, err := deriveStreamID(ticket, provider)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
log.Infof("Waiting for ticket (id=%x) using stream_id=%x, provider=%v",
|
||||
ticket.ID[:], streamID[:], provider)
|
||||
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
|
||||
msg, err := a.client.RecvCipherBoxMsg(ctx, streamID)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to recv cipher box "+
|
||||
"msg: %w", err)
|
||||
}
|
||||
|
||||
log.Infof("Receive new message for ticket (id=%x) "+
|
||||
"via stream_id=%x, provider=%v", ticket.ID[:], streamID,
|
||||
provider)
|
||||
|
||||
return sidecar.DeserializeTicket(bytes.NewReader(msg))
|
||||
}
|
||||
|
||||
// isErrAlreadyExists returns true if the passed error is the "already exists"
|
||||
// error within the error wrapped error which is returned by the hash mail
|
||||
// server when a stream we're attempting to create already exists.
|
||||
func isErrAlreadyExists(err error) bool {
|
||||
statusCode, ok := status.FromError(err)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
|
||||
return statusCode.Code() == codes.AlreadyExists
|
||||
}
|
||||
|
||||
// autoSidecarReceiver is a goroutine that will attempt to advance a new
|
||||
// sidecar ticket through the process until it reaches its final state.
|
||||
func (a *SidecarAcceptor) autoSidecarReceiver(startingPkt *SidecarPacket) {
|
||||
defer a.wg.Done()
|
||||
|
||||
packetChan := make(chan *sidecar.Ticket, 1)
|
||||
cancelChan := make(chan struct{})
|
||||
|
||||
currentState := startingPkt.CurrentState
|
||||
localTicket := startingPkt.ReceiverTicket
|
||||
|
||||
// We'll start with a simulated starting message from the sidecar
|
||||
// provider.
|
||||
packetChan <- startingPkt.ProviderTicket
|
||||
|
||||
// Before we enter our main read loop below, we'll attempt to re-create
|
||||
// out mailbox as the recipient.
|
||||
recipientStreamID := deriveRecipientStreamID(
|
||||
localTicket,
|
||||
)
|
||||
|
||||
log.Infof("Creating receiver reply mailbox for ticket=%x, "+
|
||||
"stream_id=%x", startingPkt.ReceiverTicket.ID[:],
|
||||
recipientStreamID[:])
|
||||
|
||||
err := a.client.InitTicketCipherBox(
|
||||
context.Background(), recipientStreamID,
|
||||
startingPkt.ReceiverTicket,
|
||||
)
|
||||
if err != nil && !isErrAlreadyExists(err) {
|
||||
log.Errorf("unable to init cipher box: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
// Launch a goroutine to continually read new packets off the wire and
|
||||
// send them to our state step routine. We'll always read packets until
|
||||
// things are finished, as the other side may retransmit messages until
|
||||
// the process has been finalized.
|
||||
a.wg.Add(1)
|
||||
go func() {
|
||||
defer a.wg.Done()
|
||||
|
||||
// We'll continue to read out new messages from the cipherbox
|
||||
// stream and deliver them to the main gorotuine until we
|
||||
// receive a message over the cancel channel.
|
||||
for {
|
||||
newTicket, err := a.recvSidecarPkt(
|
||||
startingPkt.ReceiverTicket, false,
|
||||
)
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
return
|
||||
}
|
||||
|
||||
select {
|
||||
case packetChan <- newTicket:
|
||||
|
||||
case <-cancelChan:
|
||||
return
|
||||
case <-a.quit:
|
||||
return
|
||||
}
|
||||
|
||||
}
|
||||
}()
|
||||
|
||||
for {
|
||||
select {
|
||||
|
||||
case newTicket := <-packetChan:
|
||||
newPktState, err := a.stateStepRecipient(&SidecarPacket{
|
||||
CurrentState: currentState,
|
||||
ProviderTicket: newTicket,
|
||||
ReceiverTicket: localTicket,
|
||||
})
|
||||
if err != nil {
|
||||
log.Errorf("unable to transition state: %v", err)
|
||||
continue
|
||||
}
|
||||
|
||||
currentState = newPktState.CurrentState
|
||||
localTicket = newPktState.ReceiverTicket
|
||||
|
||||
// If our next target state is the completion state,
|
||||
// then our job here is done, and we can safely exit
|
||||
// this main goroutine.
|
||||
if newPktState.CurrentState == sidecar.StateCompleted {
|
||||
log.Infof("Receiver negotiation for " +
|
||||
"SidecarTicket(%x) complete!")
|
||||
|
||||
close(cancelChan)
|
||||
return
|
||||
}
|
||||
|
||||
case <-a.quit:
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// stateStepRecipient is a state transition function that will walk the
|
||||
// receiver through the sidecar negotiation process. It takes the current state
|
||||
// (the state of the goroutine, and the incoming ticket) and maps that into a
|
||||
// new state, with a possibly modified ticket.
|
||||
func (a *SidecarAcceptor) stateStepRecipient(pkt *SidecarPacket,
|
||||
) (*SidecarPacket, error) {
|
||||
|
||||
switch {
|
||||
|
||||
// If the state of the ticket shows up as offered, then this is the
|
||||
// remote party restarting and requesting we re-send our registered
|
||||
// ticket. So we'll fall through to our "starting" state below to
|
||||
// re-send them the packet.
|
||||
case pkt.ProviderTicket.State == sidecar.StateOffered:
|
||||
log.Infof("Provider retransmitted initial offer, re-sending "+
|
||||
"registered ticket=%x", pkt.ProviderTicket.ID[:])
|
||||
|
||||
fallthrough
|
||||
|
||||
// In this state, they've just sent us their version of the ticket w/o
|
||||
// our node information (and processed it adding our information),
|
||||
// we'll populate it then send it to them over the cipherbox they've
|
||||
// created for this purpose.
|
||||
case pkt.CurrentState == sidecar.StateRegistered &&
|
||||
pkt.ReceiverTicket.State == sidecar.StateRegistered &&
|
||||
pkt.ProviderTicket.State == sidecar.StateRegistered:
|
||||
|
||||
log.Infof("Transmitting registered ticket=%x to provider",
|
||||
pkt.ProviderTicket.ID[:])
|
||||
|
||||
err := a.sendSidecarPkt(pkt.ReceiverTicket, true)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to send pkt: %w", err)
|
||||
}
|
||||
|
||||
// We'll return a new packet that should reflect our state
|
||||
// after the above message is sent: both parties have the
|
||||
// ticket in the registered state.
|
||||
return &SidecarPacket{
|
||||
CurrentState: sidecar.StateRegistered,
|
||||
ReceiverTicket: pkt.ReceiverTicket,
|
||||
ProviderTicket: pkt.ReceiverTicket,
|
||||
}, nil
|
||||
|
||||
// This is effectively our final state transition: we're waiting with a
|
||||
// local registered ticket and receive a ticket in the ordered state.
|
||||
// We'll validate the ticket and start expecting the channel and
|
||||
// transition to our final state.
|
||||
case pkt.CurrentState == sidecar.StateRegistered &&
|
||||
pkt.ProviderTicket.State == sidecar.StateOrdered:
|
||||
|
||||
// At this point, we'll finish validating the ticket, then
|
||||
// await the ticket on the side lines if it's valid.
|
||||
ctx := context.Background()
|
||||
err := validateOrderedTicket(
|
||||
ctx, pkt.ProviderTicket, a.cfg.Signer, a.cfg.SidecarDB,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to verify ticket: "+
|
||||
"%w", err)
|
||||
}
|
||||
|
||||
log.Infof("Auto negotiation for ticket=%x complete! Expecting "+
|
||||
"channel...", pkt.ProviderTicket.ID[:])
|
||||
|
||||
// Now that we know the channel is valid, we'll wait for the
|
||||
// channel to show up at our node, and allow things to advance
|
||||
// to the completion state.
|
||||
err = a.ExpectChannel(ctx, pkt.ProviderTicket)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to expect "+
|
||||
"channel: %w", err)
|
||||
}
|
||||
|
||||
// TODO(roasbeef): set state to expecting channel?
|
||||
|
||||
return &SidecarPacket{
|
||||
CurrentState: sidecar.StateExpectingChannel,
|
||||
ReceiverTicket: pkt.ProviderTicket,
|
||||
ProviderTicket: pkt.ProviderTicket,
|
||||
}, nil
|
||||
|
||||
// If we fall through here, then either we read a buffered message or
|
||||
// the remote party isn't following the protocol, so we'll just ignore
|
||||
// it.
|
||||
default:
|
||||
return nil, fmt.Errorf("unhandled receiver state transition "+
|
||||
"for ticket=%v, state=%v", pkt.ProviderTicket.ID[:],
|
||||
pkt.ProviderTicket.State)
|
||||
}
|
||||
}
|
||||
|
||||
// autoSidecarProvider is a goroutine that will attempt to advance a new
|
||||
// sidecar ticket through the negotiation process until it reaches its final
|
||||
// state.
|
||||
func (a *SidecarAcceptor) autoSidecarProvider(startingPkt *SidecarPacket,
|
||||
bid *order.Bid, acct *account.Account) {
|
||||
|
||||
defer a.wg.Done()
|
||||
|
||||
// TODO(roasbeef): subscribe to order state so know when things are
|
||||
// done, use that to send the extra msg
|
||||
|
||||
packetChan := make(chan *sidecar.Ticket, 1)
|
||||
cancelChan := make(chan struct{})
|
||||
|
||||
currentState := startingPkt.CurrentState
|
||||
localTicket := startingPkt.ProviderTicket
|
||||
|
||||
// We'll start with a simulated starting message from the sidecar
|
||||
// receiver.
|
||||
packetChan <- startingPkt.ReceiverTicket
|
||||
|
||||
// First, we'll need to derive the stream ID that we'll use to receive
|
||||
// new messages from the recipient.
|
||||
streamID, err := deriveProviderStreamID(localTicket)
|
||||
if err != nil {
|
||||
log.Errorf("unable to derive stream_id for ticket=%x",
|
||||
localTicket.ID[:])
|
||||
return
|
||||
}
|
||||
|
||||
log.Infof("Creating provider mailbox for ticket=%x, w/ stream_id=%x",
|
||||
localTicket.ID[:], streamID[:])
|
||||
|
||||
err = a.client.InitAccountCipherBox(
|
||||
context.Background(), streamID, acct.TraderKey,
|
||||
)
|
||||
if err != nil && !isErrAlreadyExists(err) {
|
||||
log.Errorf("unable to init cipher box: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
a.wg.Add(1)
|
||||
go func() {
|
||||
defer a.wg.Done()
|
||||
|
||||
// We'll continue to read out new messages from the cipherbox
|
||||
// stream and deliver them to the main gorotuine until we
|
||||
// receive a message over the cancel channel.
|
||||
for {
|
||||
newTicket, err := a.recvSidecarPkt(
|
||||
startingPkt.ProviderTicket, true,
|
||||
)
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
return
|
||||
}
|
||||
|
||||
select {
|
||||
case packetChan <- newTicket:
|
||||
|
||||
case <-cancelChan:
|
||||
return
|
||||
case <-a.quit:
|
||||
return
|
||||
}
|
||||
|
||||
}
|
||||
}()
|
||||
|
||||
for {
|
||||
select {
|
||||
case newTicket := <-packetChan:
|
||||
// The provider has more states it needs to transition
|
||||
// through, so we'll continue until we end up at the
|
||||
// same state (a noop)
|
||||
for {
|
||||
priorState := currentState
|
||||
|
||||
log.Infof("step=%v", currentState)
|
||||
|
||||
newPktState, err := a.stateStepProvider(&SidecarPacket{
|
||||
CurrentState: currentState,
|
||||
ReceiverTicket: newTicket,
|
||||
ProviderTicket: localTicket,
|
||||
}, bid, acct)
|
||||
if err != nil {
|
||||
log.Errorf("unable to transition state: %v", err)
|
||||
break
|
||||
}
|
||||
|
||||
currentState = newPktState.CurrentState
|
||||
localTicket = newPktState.ProviderTicket
|
||||
|
||||
switch {
|
||||
case priorState == currentState:
|
||||
fallthrough
|
||||
case currentState == sidecar.StateExpectingChannel:
|
||||
break
|
||||
|
||||
// If our next target state is the completion
|
||||
// state, then our job here is done, and we can
|
||||
// safely exit this main goroutine.
|
||||
case newPktState.CurrentState ==
|
||||
sidecar.StateCompleted:
|
||||
|
||||
log.Infof("Receiver negotiation for " +
|
||||
"SidecarTicket(%x) complete!")
|
||||
|
||||
close(cancelChan)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
case <-a.quit:
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// stateStepProvider is the state transition function for the provider of a
|
||||
// sidecar ticket. It takes the current transcript state, the provider's
|
||||
// account, and canned bid and returns a new transition to a new ticket state.
|
||||
func (a *SidecarAcceptor) stateStepProvider(pkt *SidecarPacket, bid *order.Bid,
|
||||
acct *account.Account) (*SidecarPacket, error) {
|
||||
|
||||
switch {
|
||||
// In this case, we've just restarted, so we'll attempt to start from
|
||||
// scratch by sending the recipient a packet that has our ticket in the
|
||||
// offered state. This signals to them we never wrote the registered
|
||||
// ticket and need it again.
|
||||
case pkt.CurrentState == sidecar.StateCreated &&
|
||||
pkt.ProviderTicket.State == sidecar.StateOffered:
|
||||
|
||||
log.Infof("Resuming negotiation for ticket=%x, requesting "+
|
||||
"registered ticket", pkt.ProviderTicket.ID[:])
|
||||
|
||||
err := a.sendSidecarPkt(pkt.ProviderTicket, false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &SidecarPacket{
|
||||
CurrentState: sidecar.StateOffered,
|
||||
ReceiverTicket: pkt.ProviderTicket,
|
||||
ProviderTicket: pkt.ReceiverTicket,
|
||||
}, nil
|
||||
|
||||
// In this state, we've just started anew, and have received a ticket
|
||||
// from the receiver with their node information. We'll write this to
|
||||
// disk, then transition to the next state.
|
||||
//
|
||||
// Transition: -> StateRegistered
|
||||
case pkt.CurrentState == sidecar.StateOffered &&
|
||||
pkt.ReceiverTicket.State == sidecar.StateRegistered:
|
||||
|
||||
log.Infof("Received registered ticket=%x from recipient",
|
||||
pkt.ReceiverTicket.ID[:])
|
||||
|
||||
// Now that we have the ticket, we'll update the state on disk
|
||||
// to checkpoint the new state.
|
||||
err := a.cfg.SidecarDB.UpdateSidecar(pkt.ReceiverTicket)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to update ticket: %w",
|
||||
err)
|
||||
}
|
||||
|
||||
return &SidecarPacket{
|
||||
CurrentState: sidecar.StateRegistered,
|
||||
ReceiverTicket: pkt.ReceiverTicket,
|
||||
ProviderTicket: pkt.ReceiverTicket,
|
||||
}, nil
|
||||
|
||||
// If we're in this state (possibly after a restart), we have all the
|
||||
// information we need to submit the order, so we'll do that, then send
|
||||
// the finalized ticket back to the recipient.
|
||||
//
|
||||
// Transition: -> StateOrdered
|
||||
case pkt.CurrentState == sidecar.StateRegistered:
|
||||
|
||||
log.Infof("Submitting bid order for ticket=%x",
|
||||
pkt.ProviderTicket.ID[:])
|
||||
|
||||
// Now we have the recipient's information, we can attach it to
|
||||
// our bid, and submit it as normal.
|
||||
updatedTicket, err := a.submitSidecarOrder(
|
||||
context.Background(), pkt.ProviderTicket, bid, acct,
|
||||
)
|
||||
switch {
|
||||
// If the order has already been submitted, then we'll catch
|
||||
// this error and go to the next state. Submitting the order
|
||||
// doesn't persist the state update to the ticket, so we don't
|
||||
// risk a split brain state.
|
||||
case err == nil:
|
||||
case errors.Is(err, clientdb.ErrOrderExists):
|
||||
|
||||
default:
|
||||
return nil, fmt.Errorf("unable to submit sidecar "+
|
||||
"order: %v", err)
|
||||
}
|
||||
|
||||
return &SidecarPacket{
|
||||
CurrentState: sidecar.StateOrdered,
|
||||
ReceiverTicket: updatedTicket,
|
||||
ProviderTicket: updatedTicket,
|
||||
}, nil
|
||||
|
||||
// In this state, we've already sent over the final ticket, but the
|
||||
// other party is requesting a re-transmission.
|
||||
case pkt.CurrentState == sidecar.StateExpectingChannel &&
|
||||
pkt.ReceiverTicket.State == sidecar.StateRegistered:
|
||||
|
||||
fallthrough
|
||||
|
||||
// In this state, we've submitted the order and now need to send back
|
||||
// the completed order to the recipient so they can expect the ultimate
|
||||
// sidecar channel. Notice that we don't persist this state, as upon
|
||||
// restart we'll always re-send the ticket to the other party until
|
||||
// things are finalized.
|
||||
//
|
||||
// Transition: -> StateExpectingChannel
|
||||
case pkt.CurrentState == sidecar.StateOrdered:
|
||||
|
||||
log.Infof("Sending finalize ticket=%x to receiver, entering "+
|
||||
"final stage", pkt.ProviderTicket.ID[:])
|
||||
|
||||
// We might be retransmitting here, so ensure that the ticket
|
||||
// we send over is in the state they expect.
|
||||
pkt.ProviderTicket.State = sidecar.StateOrdered
|
||||
|
||||
err := a.sendSidecarPkt(pkt.ProviderTicket, false)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to send sidecar "+
|
||||
"pkt: %v", err)
|
||||
}
|
||||
|
||||
updatedTicket := *pkt.ProviderTicket
|
||||
updatedTicket.State = sidecar.StateExpectingChannel
|
||||
|
||||
// Now that we have the final ticket, we'll update the state on
|
||||
// disk to checkpoint the new state. If the remote party ends
|
||||
// us any messages after we persist this state, then we'll
|
||||
// simply re-send the latest ticket.
|
||||
err = a.cfg.SidecarDB.UpdateSidecar(&updatedTicket)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to update ticket: %w",
|
||||
err)
|
||||
}
|
||||
|
||||
log.Infof("Negotiation for ticket=%x has been "+
|
||||
"completed!", pkt.ProviderTicket.ID[:])
|
||||
|
||||
return &SidecarPacket{
|
||||
CurrentState: sidecar.StateExpectingChannel,
|
||||
ReceiverTicket: &updatedTicket,
|
||||
ProviderTicket: &updatedTicket,
|
||||
}, nil
|
||||
|
||||
default:
|
||||
return nil, fmt.Errorf("unhandled provider state "+
|
||||
"transition ticket=%x, state=%v",
|
||||
pkt.ReceiverTicket.ID[:], pkt.ReceiverTicket.State)
|
||||
}
|
||||
}
|
||||
|
|
@ -6,6 +6,7 @@ import (
|
|||
"fmt"
|
||||
|
||||
"github.com/btcsuite/btcd/btcec"
|
||||
"github.com/lightninglabs/pool/order"
|
||||
"github.com/lightninglabs/pool/sidecar"
|
||||
"go.etcd.io/bbolt"
|
||||
)
|
||||
|
|
@ -19,6 +20,11 @@ var (
|
|||
// currently pending or completed. This bucket is keyed by the ticket ID
|
||||
// and offer signing pubkey of a sidecar.
|
||||
sidecarsBucketKey = []byte("sidecars")
|
||||
|
||||
// bidTemplateBucket is a bucket that's used to store the order
|
||||
// template of a sidecar ticket for the provider to be able to execute
|
||||
// automated negotiation of the order.
|
||||
bidTemplateBucket = []byte("sidecar-bids")
|
||||
)
|
||||
|
||||
const (
|
||||
|
|
@ -70,6 +76,49 @@ func (db *DB) AddSidecar(ticket *sidecar.Ticket) error {
|
|||
})
|
||||
}
|
||||
|
||||
// AddSidecarWithBid is identical to the AddSidecar method, but it also inserts
|
||||
// a bid template in a special bucket to facilitate automated negotiation of
|
||||
// sidecar channels.
|
||||
func (db *DB) AddSidecarWithBid(ticket *sidecar.Ticket, bid *order.Bid) error {
|
||||
sidecarKey, err := getSidecarKey(ticket.ID, ticket.Offer.SignPubKey)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Although the order hasn't fully advanced set to the state where we
|
||||
// sign+commit to the order nonce itself, since we already know it at
|
||||
// this point, we can just apply it directly to the ticket.
|
||||
ticket.Order = new(sidecar.Order)
|
||||
bidNonce := bid.Nonce()
|
||||
copy(ticket.Order.BidNonce[:], bidNonce[:])
|
||||
|
||||
return db.Update(func(tx *bbolt.Tx) error {
|
||||
sidecarBucket, err := getBucket(tx, sidecarsBucketKey)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
sidecarValue := sidecarBucket.Get(sidecarKey)
|
||||
if len(sidecarValue) != 0 {
|
||||
return fmt.Errorf("sidecar for key %x already exists",
|
||||
sidecarKey)
|
||||
}
|
||||
|
||||
err = storeSidecar(sidecarBucket, sidecarKey, ticket)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
bidBucket, err := sidecarBucket.CreateBucketIfNotExists(
|
||||
bidTemplateBucket,
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return storeBidTemplate(bidBucket, bid, bidNonce)
|
||||
})
|
||||
}
|
||||
|
||||
// UpdateSidecar updates a sidecar in the database.
|
||||
func (db *DB) UpdateSidecar(ticket *sidecar.Ticket) error {
|
||||
sidecarKey, err := getSidecarKey(ticket.ID, ticket.Offer.SignPubKey)
|
||||
|
|
@ -88,6 +137,8 @@ func (db *DB) UpdateSidecar(ticket *sidecar.Ticket) error {
|
|||
return ErrNoSidecar
|
||||
}
|
||||
|
||||
// TODO(roasbeef): remove the bid if in the final state now/
|
||||
|
||||
return storeSidecar(sidecarBucket, sidecarKey, ticket)
|
||||
})
|
||||
}
|
||||
|
|
@ -119,6 +170,32 @@ func (db *DB) Sidecar(id [8]byte,
|
|||
return s, nil
|
||||
}
|
||||
|
||||
// SidecarBidTemplate attempts to retrieve a bid template associated with the
|
||||
// passed sidecar ticket.
|
||||
func (db *DB) SidecarBidTemplate(ticket *sidecar.Ticket) (*order.Bid, error) {
|
||||
var bid *order.Bid
|
||||
|
||||
err := db.View(func(tx *bbolt.Tx) error {
|
||||
sidecarBucket, err := getBucket(tx, sidecarsBucketKey)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
bidBucket := sidecarBucket.Bucket(bidTemplateBucket)
|
||||
if bidBucket == nil {
|
||||
return err
|
||||
}
|
||||
|
||||
bid, err = readBidTemplate(bidBucket, ticket.Order.BidNonce)
|
||||
return err
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return bid, nil
|
||||
}
|
||||
|
||||
// Sidecars retrieves all known sidecars from the database.
|
||||
func (db *DB) Sidecars() ([]*sidecar.Ticket, error) {
|
||||
var res []*sidecar.Ticket
|
||||
|
|
@ -158,6 +235,8 @@ func storeSidecar(targetBucket *bbolt.Bucket, key []byte,
|
|||
return err
|
||||
}
|
||||
|
||||
// TODO(roasbeef): store bid along side in new key?
|
||||
|
||||
return targetBucket.Put(key, sidecarBuf.Bytes())
|
||||
}
|
||||
|
||||
|
|
@ -171,3 +250,64 @@ func readSidecar(sourceBucket *bbolt.Bucket, id []byte) (*sidecar.Ticket,
|
|||
|
||||
return sidecar.DeserializeTicket(bytes.NewReader(sidecarBytes))
|
||||
}
|
||||
|
||||
func storeBidTemplate(bidBucket *bbolt.Bucket, bid *order.Bid, ticketNonce order.Nonce) error {
|
||||
var w bytes.Buffer
|
||||
if err := SerializeOrder(bid, &w); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err := storeOrderTX(bidBucket, ticketNonce, w.Bytes(), nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = storeOrderMinUnitsMatchTX(
|
||||
bidBucket, ticketNonce, bid.Details().MinUnitsMatch,
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := storeOrderTlvTX(bidBucket, ticketNonce, bid); err != nil {
|
||||
return err
|
||||
}
|
||||
return storeOrderMinNoderTierTX(bidBucket, ticketNonce, bid.MinNodeTier)
|
||||
}
|
||||
|
||||
func readBidTemplate(bidBucket *bbolt.Bucket,
|
||||
ticketNonce order.Nonce) (*order.Bid, error) {
|
||||
|
||||
var (
|
||||
o order.Order
|
||||
err error
|
||||
)
|
||||
|
||||
callback := func(nonce order.Nonce, rawOrder []byte,
|
||||
extraData *extraOrderData) error {
|
||||
|
||||
r := bytes.NewReader(rawOrder)
|
||||
o, err = DeserializeOrder(nonce, r)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
tlvReader := bytes.NewReader(extraData.tlvData)
|
||||
err := deserializeOrderTlvData(tlvReader, o)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if bidOrder, ok := o.(*order.Bid); ok {
|
||||
bidOrder.MinNodeTier = extraData.minNodeTier
|
||||
}
|
||||
o.Details().MinUnitsMatch = extraData.minUnitsMatch
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
err = fetchOrderTX(bidBucket, ticketNonce, callback)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return o.(*order.Bid), nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -70,3 +70,64 @@ func TestSidecars(t *testing.T) {
|
|||
require.NoError(t, err)
|
||||
require.Equal(t, s, updatedTicket)
|
||||
}
|
||||
|
||||
// TestSidecarsWithOrder tests that we're able to properly insert a new order
|
||||
// into a sidecar sub-bucket along with the ticket, as well as retrieve it again
|
||||
// in the future.
|
||||
func TestSidecarsWithOrder(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
db, cleanup := newTestDB(t)
|
||||
defer cleanup()
|
||||
|
||||
// First, we'll make a new order that'll be matched along with a ticket
|
||||
// we'll create below.
|
||||
bid := &order.Bid{
|
||||
Kit: *dummyOrder(500000, 1337),
|
||||
MinNodeTier: 2,
|
||||
SelfChanBalance: 123,
|
||||
SidecarTicket: &sidecar.Ticket{
|
||||
ID: [8]byte{11, 22, 33, 44, 55, 66, 77},
|
||||
State: sidecar.StateRegistered,
|
||||
Offer: sidecar.Offer{
|
||||
Capacity: 1000000,
|
||||
PushAmt: 200000,
|
||||
LeaseDurationBlocks: 2016,
|
||||
},
|
||||
Recipient: &sidecar.Recipient{
|
||||
MultiSigPubKey: testTraderKey,
|
||||
MultiSigKeyIndex: 7,
|
||||
},
|
||||
},
|
||||
}
|
||||
bid.Details().MinUnitsMatch = 10
|
||||
|
||||
// Next we'll craft a new ticket that we'll use to bind to the base
|
||||
// order.
|
||||
ticket := &sidecar.Ticket{
|
||||
ID: [8]byte{12, 34, 56},
|
||||
State: sidecar.StateRegistered,
|
||||
Offer: sidecar.Offer{
|
||||
Capacity: 1000000,
|
||||
PushAmt: 200000,
|
||||
SignPubKey: testTraderKey,
|
||||
LeaseDurationBlocks: 2016,
|
||||
},
|
||||
Recipient: &sidecar.Recipient{
|
||||
MultiSigPubKey: testTraderKey,
|
||||
MultiSigKeyIndex: 7,
|
||||
},
|
||||
}
|
||||
|
||||
err := db.AddSidecarWithBid(ticket, bid)
|
||||
require.NoError(t, err)
|
||||
assertSidecarExists(t, db, ticket)
|
||||
|
||||
// We should be able to retrieve the bid again given the original
|
||||
// ticket.
|
||||
diskBid, err := db.SidecarBidTemplate(ticket)
|
||||
require.NoError(t, err)
|
||||
|
||||
// This bid should match the one we inserted earlier exactly.
|
||||
require.Equal(t, diskBid, bid)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -48,6 +48,57 @@ var ordersCommands = []cli.Command{
|
|||
},
|
||||
}
|
||||
|
||||
// baseBidFlags is the set of flags that are common to any command that may
|
||||
// need to accept a bid such as the main bid submission method as when a user
|
||||
// attempts to offer a sidecar ticket.
|
||||
var baseBidFlags = []cli.Flag{
|
||||
cli.Float64Flag{
|
||||
Name: "interest_rate_percent",
|
||||
Usage: "the total percent one is willing to pay or " +
|
||||
"accept as yield for the specified interval",
|
||||
},
|
||||
cli.Uint64Flag{
|
||||
Name: "amt",
|
||||
Usage: "the amount of inbound liquidity in satoshis " +
|
||||
"to request",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "acct_key",
|
||||
Usage: "the account key to use to pay the order " +
|
||||
"fees with",
|
||||
},
|
||||
cli.Uint64Flag{
|
||||
Name: "lease_duration_blocks",
|
||||
Usage: "the number of blocks that the " +
|
||||
"liquidity should be provided for",
|
||||
Value: defaultBidMinDuration,
|
||||
},
|
||||
cli.Uint64Flag{
|
||||
Name: "min_node_tier",
|
||||
Usage: "the min node tier this bid should be matched " +
|
||||
"with, tier 1 nodes are considered 'good', if " +
|
||||
"set to tier 0, then all nodes will be " +
|
||||
"considered regardless of 'quality'",
|
||||
Value: uint64(order.NodeTierDefault),
|
||||
},
|
||||
cli.Uint64Flag{
|
||||
Name: "min_chan_amt",
|
||||
Usage: "the minimum amount of satoshis that a " +
|
||||
"resulting channel from this order must have",
|
||||
},
|
||||
cli.BoolFlag{
|
||||
Name: "force",
|
||||
Usage: "skip order placement confirmation",
|
||||
},
|
||||
cli.Uint64Flag{
|
||||
Name: "self_chan_balance",
|
||||
Usage: "give the channel leased by this bid order an " +
|
||||
"initial balance by adding additional funds " +
|
||||
"from our account into the channel; can be " +
|
||||
"used to create up to 50/50 balanced channels",
|
||||
},
|
||||
}
|
||||
|
||||
var sharedFlags = []cli.Flag{
|
||||
cli.Uint64Flag{
|
||||
Name: "max_batch_fee_rate",
|
||||
|
|
@ -372,74 +423,26 @@ var ordersSubmitBidCommand = cli.Command{
|
|||
Description: `
|
||||
Place an offer for acquiring inbound liquidity by lending
|
||||
funding capacity from another participant in the order book.`,
|
||||
Flags: append([]cli.Flag{
|
||||
cli.Float64Flag{
|
||||
Name: "interest_rate_percent",
|
||||
Usage: "the total percent one is willing to pay or " +
|
||||
"accept as yield for the specified interval",
|
||||
},
|
||||
cli.Uint64Flag{
|
||||
Name: "amt",
|
||||
Usage: "the amount of inbound liquidity in satoshis " +
|
||||
"to request",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "acct_key",
|
||||
Usage: "the account key to use to pay the order " +
|
||||
"fees with",
|
||||
},
|
||||
cli.Uint64Flag{
|
||||
Name: "lease_duration_blocks",
|
||||
Usage: "the number of blocks that the " +
|
||||
"liquidity should be provided for",
|
||||
Value: defaultBidMinDuration,
|
||||
},
|
||||
cli.Uint64Flag{
|
||||
Name: "min_node_tier",
|
||||
Usage: "the min node tier this bid should be matched " +
|
||||
"with, tier 1 nodes are considered 'good', if " +
|
||||
"set to tier 0, then all nodes will be " +
|
||||
"considered regardless of 'quality'",
|
||||
Value: uint64(order.NodeTierDefault),
|
||||
},
|
||||
cli.Uint64Flag{
|
||||
Name: "min_chan_amt",
|
||||
Usage: "the minimum amount of satoshis that a " +
|
||||
"resulting channel from this order must have",
|
||||
},
|
||||
cli.BoolFlag{
|
||||
Name: "force",
|
||||
Usage: "skip order placement confirmation",
|
||||
},
|
||||
cli.Uint64Flag{
|
||||
Name: "self_chan_balance",
|
||||
Usage: "give the channel leased by this bid order an " +
|
||||
"initial balance by adding additional funds " +
|
||||
"from our account into the channel; can be " +
|
||||
"used to create up to 50/50 balanced channels",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "sidecar_ticket",
|
||||
Usage: "instead of leasing a channel for the node " +
|
||||
"connected to this pool instance, lease a " +
|
||||
"channel for another node; use the " +
|
||||
"information within the ticket to identify " +
|
||||
"the receiver of the sidecar channel; using " +
|
||||
"a sidecar ticket will also overwrite the " +
|
||||
"amt, min_chan_amt, lease_duration_blocks " +
|
||||
"and self_chan_balance fields",
|
||||
},
|
||||
}, sharedFlags...),
|
||||
Flags: append(
|
||||
append(
|
||||
baseBidFlags,
|
||||
cli.StringFlag{
|
||||
Name: "sidecar_ticket",
|
||||
Usage: "instead of leasing a channel for the node " +
|
||||
"connected to this pool instance, lease a " +
|
||||
"channel for another node; use the " +
|
||||
"information within the ticket to identify " +
|
||||
"the receiver of the sidecar channel; using " +
|
||||
"a sidecar ticket will also overwrite the " +
|
||||
"amt, min_chan_amt, lease_duration_blocks " +
|
||||
"and self_chan_balance fields",
|
||||
},
|
||||
), sharedFlags...,
|
||||
),
|
||||
Action: ordersSubmitBid,
|
||||
}
|
||||
|
||||
func ordersSubmitBid(ctx *cli.Context) error { // nolint: dupl
|
||||
// Show help if no arguments or flags are provided.
|
||||
if ctx.NArg() == 0 && ctx.NumFlags() == 0 {
|
||||
_ = cli.ShowCommandHelp(ctx, "bid")
|
||||
return nil
|
||||
}
|
||||
|
||||
func parseBaseBid(ctx *cli.Context) (*poolrpc.Bid, *sidecar.Ticket, error) {
|
||||
// The node tier values are a bit un-intuitive. We need to convert
|
||||
// between the human interpretation of "tier 1" (value 1) to the
|
||||
// internal representation of "tier 1" (value order.NodeTier1=2).
|
||||
|
|
@ -455,7 +458,7 @@ func ordersSubmitBid(ctx *cli.Context) error { // nolint: dupl
|
|||
|
||||
nodeTier, err := auctioneer.MarshallNodeTier(cliNodeTier)
|
||||
if err != nil {
|
||||
return nil
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
bid := &poolrpc.Bid{
|
||||
|
|
@ -475,8 +478,8 @@ func ordersSubmitBid(ctx *cli.Context) error { // nolint: dupl
|
|||
// check it in the process.
|
||||
ticket, err = sidecar.DecodeString(ctx.String("sidecar_ticket"))
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to parse sidecar ticket: %v",
|
||||
err)
|
||||
return nil, nil, fmt.Errorf("unable to parse sidecar "+
|
||||
"ticket: %v", err)
|
||||
}
|
||||
|
||||
// Let's make sure the ticket is in the correct state. This will
|
||||
|
|
@ -486,9 +489,9 @@ func ordersSubmitBid(ctx *cli.Context) error { // nolint: dupl
|
|||
if ticket.State != sidecar.StateRegistered ||
|
||||
ticket.Recipient == nil {
|
||||
|
||||
return fmt.Errorf("unexpected sidecar ticket state "+
|
||||
"%d, possibly not registered with recipient "+
|
||||
"node yet", ticket.State)
|
||||
return nil, nil, fmt.Errorf("unexpected sidecar "+
|
||||
"ticket state %d, possibly not registered "+
|
||||
"with recipient node yet", ticket.State)
|
||||
}
|
||||
|
||||
// With the ticket parsed and formally checked, we can now pre-
|
||||
|
|
@ -510,7 +513,8 @@ func ordersSubmitBid(ctx *cli.Context) error { // nolint: dupl
|
|||
|
||||
params, err := parseCommonParams(ctx, bid.LeaseDurationBlocks)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to parse order params: %v", err)
|
||||
return nil, nil, fmt.Errorf("unable to parse order "+
|
||||
"params: %v", err)
|
||||
}
|
||||
|
||||
bid.Details = params
|
||||
|
|
@ -526,17 +530,32 @@ func ordersSubmitBid(ctx *cli.Context) error { // nolint: dupl
|
|||
order.BaseSupplyUnit,
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
bidUnits := order.NewSupplyFromSats(bidAmt)
|
||||
if bid.Details.MinUnitsMatch != uint32(bidUnits) {
|
||||
return fmt.Errorf("when using self_chan_balance the " +
|
||||
return nil, nil, fmt.Errorf("when using self_chan_balance the " +
|
||||
"min_chan_amt must be set to the same value " +
|
||||
"as amt")
|
||||
}
|
||||
}
|
||||
|
||||
return bid, ticket, nil
|
||||
}
|
||||
|
||||
func ordersSubmitBid(ctx *cli.Context) error { // nolint: dupl
|
||||
// Show help if no arguments or flags are provided.
|
||||
if ctx.NArg() == 0 && ctx.NumFlags() == 0 {
|
||||
_ = cli.ShowCommandHelp(ctx, "bid")
|
||||
return nil
|
||||
}
|
||||
|
||||
bid, ticket, err := parseBaseBid(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
client, cleanup, err := getClient(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
|
|
|
|||
|
|
@ -6,9 +6,11 @@ import (
|
|||
"fmt"
|
||||
"strconv"
|
||||
|
||||
"github.com/btcsuite/btcutil"
|
||||
"github.com/lightninglabs/pool/order"
|
||||
"github.com/lightninglabs/pool/poolrpc"
|
||||
"github.com/lightninglabs/pool/sidecar"
|
||||
"github.com/lightningnetwork/lnd/lnwallet/chainfee"
|
||||
"github.com/urfave/cli"
|
||||
)
|
||||
|
||||
|
|
@ -31,31 +33,21 @@ var sidecarOfferCommand = cli.Command{
|
|||
Name: "offer",
|
||||
Aliases: []string{"o"},
|
||||
Usage: "offer a sidecar channel",
|
||||
ArgsUsage: "capacity self_chan_balance lease_duration_blocks",
|
||||
ArgsUsage: "[<full bid args> --auto] | capacity self_chan_balance lease_duration_blocks",
|
||||
Description: `
|
||||
Creates an offer for providing a sidecar channel to another node.`,
|
||||
Flags: []cli.Flag{
|
||||
cli.Uint64Flag{
|
||||
Name: "capacity",
|
||||
Usage: "the total channel capacity of the sidecar " +
|
||||
"channel to offer",
|
||||
Creates an offer for providing a sidecar channel to another node.
|
||||
If the auto flag is specified, then all bid information needs to be
|
||||
specified as normal. If the auto flag isn't specified, then only
|
||||
capacity, self_chan_balance and lease_duration_blocks needs to set.`,
|
||||
Flags: append(
|
||||
append(baseBidFlags, sharedFlags...),
|
||||
cli.BoolFlag{
|
||||
Name: "auto",
|
||||
Usage: "if true, then the full bid information needs to " +
|
||||
"be specified as automated negotiation will be " +
|
||||
"attempted",
|
||||
},
|
||||
cli.Uint64Flag{
|
||||
Name: "self_chan_balance",
|
||||
Usage: "the number of satoshis that should be pushed " +
|
||||
"to the recipient of the sidecar channel as " +
|
||||
"initial outbound channel balance; amount " +
|
||||
"will be deducted from account that submits " +
|
||||
"bid order, reimbursement must happen out of " +
|
||||
"band, not part of the sidecar protocol",
|
||||
},
|
||||
cli.Uint64Flag{
|
||||
Name: "lease_duration_blocks",
|
||||
Usage: "the number of blocks the resulting leased " +
|
||||
"channel should be open for",
|
||||
Value: uint64(order.LegacyLeaseDurationBucket),
|
||||
},
|
||||
},
|
||||
),
|
||||
Action: sidecarOffer,
|
||||
}
|
||||
|
||||
|
|
@ -67,60 +59,107 @@ func sidecarOffer(ctx *cli.Context) error {
|
|||
}
|
||||
|
||||
var (
|
||||
args = ctx.Args()
|
||||
capacity, pushAmt uint64
|
||||
duration uint32
|
||||
bid *poolrpc.Bid
|
||||
err error
|
||||
)
|
||||
|
||||
switch {
|
||||
case ctx.IsSet("capacity"):
|
||||
capacity = ctx.Uint64("capacity")
|
||||
case args.Present():
|
||||
parsed, err := parseAmt(args.First())
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to decode capacity: %v", err)
|
||||
}
|
||||
capacity = uint64(parsed)
|
||||
args = args.Tail()
|
||||
}
|
||||
// If auto isn't set, then we'll only need to parse out a hand full of
|
||||
// fields to submit a valid ticket.
|
||||
if !ctx.Bool("auto") {
|
||||
var (
|
||||
args = ctx.Args()
|
||||
capacity, pushAmt uint64
|
||||
duration uint32
|
||||
)
|
||||
|
||||
switch {
|
||||
case ctx.IsSet("self_chan_balance"):
|
||||
pushAmt = ctx.Uint64("self_chan_balance")
|
||||
case args.Present():
|
||||
parsed, err := parseAmt(args.First())
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to decode self channel "+
|
||||
"balance: %v", err)
|
||||
switch {
|
||||
case ctx.IsSet("capacity"):
|
||||
capacity = ctx.Uint64("capacity")
|
||||
case args.Present():
|
||||
parsed, err := parseAmt(args.First())
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to decode capacity: %v", err)
|
||||
}
|
||||
capacity = uint64(parsed)
|
||||
args = args.Tail()
|
||||
}
|
||||
pushAmt = uint64(parsed)
|
||||
args = args.Tail()
|
||||
}
|
||||
|
||||
switch {
|
||||
case ctx.IsSet("lease_duration_blocks"):
|
||||
duration = uint32(ctx.Uint64("lease_duration_blocks"))
|
||||
case args.Present():
|
||||
duration64, err := strconv.ParseInt(args.First(), 10, 32)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to parse lease duration "+
|
||||
"blocks: %v", err)
|
||||
switch {
|
||||
case ctx.IsSet("self_chan_balance"):
|
||||
pushAmt = ctx.Uint64("self_chan_balance")
|
||||
case args.Present():
|
||||
parsed, err := parseAmt(args.First())
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to decode self channel "+
|
||||
"balance: %v", err)
|
||||
}
|
||||
pushAmt = uint64(parsed)
|
||||
args = args.Tail()
|
||||
}
|
||||
|
||||
switch {
|
||||
case ctx.IsSet("lease_duration_blocks"):
|
||||
duration = uint32(ctx.Uint64("lease_duration_blocks"))
|
||||
case args.Present():
|
||||
duration64, err := strconv.ParseInt(args.First(), 10, 32)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to parse lease duration "+
|
||||
"blocks: %v", err)
|
||||
}
|
||||
duration = uint32(duration64)
|
||||
args = args.Tail()
|
||||
}
|
||||
|
||||
bid = &poolrpc.Bid{
|
||||
Details: &poolrpc.Order{
|
||||
Amt: capacity,
|
||||
},
|
||||
SelfChanBalance: pushAmt,
|
||||
LeaseDurationBlocks: duration,
|
||||
}
|
||||
|
||||
} else {
|
||||
// Otherwise, will parse out the full bid as normal.
|
||||
bid, _, err = parseBaseBid(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
|
||||
}
|
||||
duration = uint32(duration64)
|
||||
args = args.Tail()
|
||||
}
|
||||
|
||||
client, cleanup, err := getClient(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
defer cleanup()
|
||||
|
||||
// Give the user a chance to confirm the order details as this is
|
||||
// binding once submitted, but only if the entire bid was specified.
|
||||
if !ctx.Bool("force") && ctx.Bool("auto") {
|
||||
if err := printOrderDetails(
|
||||
client, btcutil.Amount(bid.Details.Amt),
|
||||
order.SupplyUnit(bid.Details.MinUnitsMatch),
|
||||
btcutil.Amount(bid.SelfChanBalance),
|
||||
order.FixedRatePremium(bid.Details.RateFixed),
|
||||
bid.LeaseDurationBlocks,
|
||||
chainfee.SatPerKWeight(
|
||||
bid.Details.MaxBatchFeeRateSatPerKw,
|
||||
), false, nil,
|
||||
); err != nil {
|
||||
return fmt.Errorf("unable to print order details: %v", err)
|
||||
}
|
||||
|
||||
if !promptForConfirmation("Confirm order (yes/no): ") {
|
||||
fmt.Println("Cancelling order...")
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
resp, err := client.OfferSidecar(
|
||||
context.Background(), &poolrpc.OfferSidecarRequest{
|
||||
ChannelCapacitySat: capacity,
|
||||
SelfChanBalance: pushAmt,
|
||||
LeaseDurationBlocks: duration,
|
||||
AutoNegotiate: ctx.Bool("auto"),
|
||||
Bid: bid,
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
|
|
|
|||
|
|
@ -34,13 +34,6 @@ import (
|
|||
)
|
||||
|
||||
var (
|
||||
// nodeIdentityKeyLoc is the key locator from which the identity key of
|
||||
// an lnd node is derived.
|
||||
nodeIdentityKeyLoc = keychain.KeyLocator{
|
||||
Family: keychain.KeyFamilyNodeKey,
|
||||
Index: 0,
|
||||
}
|
||||
|
||||
// rpcCodeFundingFailed is the error code we send if the channel funding
|
||||
// fails because of a timeout or another problem.
|
||||
rpcCodeFundingFailed = auctioneerrpc.OrderReject_CHANNEL_FUNDING_FAILED
|
||||
|
|
@ -1000,9 +993,13 @@ func (m *Manager) RemovePendingBatchArtifacts(
|
|||
}
|
||||
|
||||
// OfferSidecar creates a sidecar channel offer and embeds it in a new sidecar
|
||||
// ticket. The offer is signed with the local lnd's node public key.
|
||||
// ticket. The offer is signed with the local lnd's node public key. If a bid
|
||||
// is passed along, then this indicates that the ticket is intended to be used
|
||||
// for autonated sidecar negotiation.
|
||||
func (m *Manager) OfferSidecar(ctx context.Context, capacity,
|
||||
pushAmt btcutil.Amount, duration uint32) (*sidecar.Ticket, error) {
|
||||
pushAmt btcutil.Amount, duration uint32,
|
||||
acctPubKey *keychain.KeyDescriptor,
|
||||
bid *order.Bid, auto bool) (*sidecar.Ticket, error) {
|
||||
|
||||
// Make sure the capacity and push amounts are sane.
|
||||
err := sidecar.CheckOfferParams(capacity, pushAmt, order.BaseSupplyUnit)
|
||||
|
|
@ -1014,7 +1011,7 @@ func (m *Manager) OfferSidecar(ctx context.Context, capacity,
|
|||
// now.
|
||||
ticket, err := sidecar.NewTicket(
|
||||
sidecar.VersionDefault, capacity, pushAmt, duration,
|
||||
m.cfg.NodePubKey,
|
||||
acctPubKey.PubKey, auto,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error creating sidecar ticket: %v", err)
|
||||
|
|
@ -1023,13 +1020,19 @@ func (m *Manager) OfferSidecar(ctx context.Context, capacity,
|
|||
// Let's sign the offer part of the ticket with our node's identity key
|
||||
// now.
|
||||
if err := sidecar.SignOffer(
|
||||
ctx, ticket, nodeIdentityKeyLoc, m.cfg.SignerClient,
|
||||
ctx, ticket, acctPubKey.KeyLocator, m.cfg.SignerClient,
|
||||
); err != nil {
|
||||
return nil, fmt.Errorf("error signing offer: %v", err)
|
||||
}
|
||||
|
||||
// Let's now store and return the ticket with the signed offer.
|
||||
err = m.cfg.DB.AddSidecar(ticket)
|
||||
// Let's now store and return the ticket with the signed offer. If a
|
||||
// bid was specified, then we'll commit that as well so we can submit
|
||||
// it to the auctioneer later once we communicate with the recipient.
|
||||
if bid == nil {
|
||||
err = m.cfg.DB.AddSidecar(ticket)
|
||||
} else {
|
||||
err = m.cfg.DB.AddSidecarWithBid(ticket, bid)
|
||||
}
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error storing sidecar ticket: %v", err)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -619,7 +619,7 @@ func TestDeriveFundingShim(t *testing.T) {
|
|||
|
||||
// And the second test is with a sidecar channel bid.
|
||||
ticket, err := sidecar.NewTicket(
|
||||
sidecar.VersionDefault, 400_000, 0, 12345, pubKeyBid,
|
||||
sidecar.VersionDefault, 400_000, 0, 12345, pubKeyBid, false,
|
||||
)
|
||||
require.NoError(t, err)
|
||||
ticket.Recipient = &sidecar.Recipient{
|
||||
|
|
@ -823,7 +823,7 @@ func TestOfferSidecarValidation(t *testing.T) {
|
|||
for _, testCase := range negativeCases {
|
||||
_, err := h.mgr.OfferSidecar(
|
||||
context.Background(), testCase.capacity,
|
||||
testCase.pushAmt, 2016,
|
||||
testCase.pushAmt, 2016, nil, nil, false,
|
||||
)
|
||||
require.Error(t, err)
|
||||
require.Contains(t, err.Error(), testCase.expectedErr)
|
||||
|
|
@ -855,6 +855,9 @@ func TestOfferSidecar(t *testing.T) {
|
|||
capacity, pushAmt := btcutil.Amount(100_000), btcutil.Amount(40_000)
|
||||
ticket, err := h.mgr.OfferSidecar(
|
||||
context.Background(), capacity, pushAmt, 2016,
|
||||
&keychain.KeyDescriptor{
|
||||
PubKey: privKey.PubKey(),
|
||||
}, nil, false,
|
||||
)
|
||||
require.NoError(t, err)
|
||||
|
||||
|
|
|
|||
|
|
@ -98,7 +98,7 @@ func TestChannelOutput(t *testing.T) {
|
|||
|
||||
// And the second test is with a sidecar channel bid.
|
||||
ticket, err := sidecar.NewTicket(
|
||||
sidecar.VersionDefault, 400_000, 0, 12345, pubKeyBid,
|
||||
sidecar.VersionDefault, 400_000, 0, 12345, pubKeyBid, false,
|
||||
)
|
||||
require.NoError(t, err)
|
||||
ticket.Recipient = &sidecar.Recipient{
|
||||
|
|
|
|||
|
|
@ -10,7 +10,6 @@ import (
|
|||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"github.com/btcsuite/btcd/btcec"
|
||||
"github.com/btcsuite/btcutil"
|
||||
"github.com/lightninglabs/lndclient"
|
||||
"github.com/lightninglabs/pool/account"
|
||||
|
|
@ -36,13 +35,6 @@ var (
|
|||
// implement the same batch verification version as the server.
|
||||
ErrVersionMismatch = fmt.Errorf("version %d mismatches server version",
|
||||
CurrentBatchVersion)
|
||||
|
||||
// nodeIdentityKeyLoc is the key locator from which the identity key of
|
||||
// an lnd node is derived.
|
||||
nodeIdentityKeyLoc = keychain.KeyLocator{
|
||||
Family: keychain.KeyFamilyNodeKey,
|
||||
Index: 0,
|
||||
}
|
||||
)
|
||||
|
||||
// ManagerConfig contains all of the required dependencies for the Manager to
|
||||
|
|
@ -168,7 +160,7 @@ func (m *Manager) PrepareOrder(ctx context.Context, order Order,
|
|||
// that node's information must be present. If everything checks
|
||||
// out, we add our signature over it since we are now sure that
|
||||
// we have an order nonce set.
|
||||
err := m.validateAndSignTicketForOrder(ctx, ticket, bid)
|
||||
err := m.validateAndSignTicketForOrder(ctx, ticket, bid, acct)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error validating sidecar "+
|
||||
"ticket: %v", err)
|
||||
|
|
@ -390,7 +382,7 @@ func (m *Manager) OurNodePubkey() ([33]byte, error) {
|
|||
// channel by checking the embedded signature. If everything checks out, we add
|
||||
// our signature over the order part to the ticket.
|
||||
func (m *Manager) validateAndSignTicketForOrder(ctx context.Context,
|
||||
t *sidecar.Ticket, bid *Bid) error {
|
||||
t *sidecar.Ticket, bid *Bid, acct *account.Account) error {
|
||||
|
||||
if t.State != sidecar.StateRegistered {
|
||||
return fmt.Errorf("invalid sidecar ticket state: %d", t.State)
|
||||
|
|
@ -410,21 +402,13 @@ func (m *Manager) validateAndSignTicketForOrder(ctx context.Context,
|
|||
return fmt.Errorf("error verifying sidecar offer: %v", err)
|
||||
}
|
||||
|
||||
ourNodeKeyRaw, err := m.OurNodePubkey()
|
||||
if err != nil {
|
||||
return fmt.Errorf("error getting own node public key: %v", err)
|
||||
}
|
||||
ourNodeKey, err := btcec.ParsePubKey(ourNodeKeyRaw[:], btcec.S256())
|
||||
if err != nil {
|
||||
return fmt.Errorf("error parsing own node public key: %v", err)
|
||||
}
|
||||
if !ourNodeKey.IsEqual(o.SignPubKey) {
|
||||
if !acct.TraderKey.PubKey.IsEqual(o.SignPubKey) {
|
||||
return fmt.Errorf("invalid sidecar ticket, not offered by us")
|
||||
}
|
||||
|
||||
// The signature is valid! Let's now make sure the offer and the order
|
||||
// parameters actually match.
|
||||
err = sidecar.CheckOfferParamsForOrder(
|
||||
err := sidecar.CheckOfferParamsForOrder(
|
||||
o, bid.Amt, btcutil.Amount(bid.MinUnitsMatch), BaseSupplyUnit,
|
||||
)
|
||||
if err != nil {
|
||||
|
|
@ -433,7 +417,8 @@ func (m *Manager) validateAndSignTicketForOrder(ctx context.Context,
|
|||
|
||||
// Everything checks out, let's add our signature to the ticket now.
|
||||
return sidecar.SignOrder(
|
||||
ctx, t, bid.nonce, nodeIdentityKeyLoc, m.cfg.Signer,
|
||||
ctx, t, bid.nonce, acct.TraderKey.KeyLocator,
|
||||
m.cfg.Signer,
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@ package order
|
|||
|
||||
import (
|
||||
"context"
|
||||
"encoding/hex"
|
||||
"math/big"
|
||||
"testing"
|
||||
|
||||
|
|
@ -307,10 +306,6 @@ func TestPrepareOrderSidecarTicket(t *testing.T) {
|
|||
S: new(big.Int).SetInt64(22),
|
||||
}
|
||||
mockSigner.Signature = testSig.Serialize()
|
||||
ourPubKeyRaw, err := hex.DecodeString(mockLightning.NodePubkey)
|
||||
require.NoError(t, err)
|
||||
ourPubKey, err := btcec.ParsePubKey(ourPubKeyRaw, btcec.S256())
|
||||
require.NoError(t, err)
|
||||
|
||||
testCases := []struct {
|
||||
name string
|
||||
|
|
@ -345,7 +340,7 @@ func TestPrepareOrderSidecarTicket(t *testing.T) {
|
|||
State: sidecar.StateRegistered,
|
||||
Offer: sidecar.Offer{
|
||||
SigOfferDigest: testSig,
|
||||
SignPubKey: ourPubKey,
|
||||
SignPubKey: acctKeySmall,
|
||||
},
|
||||
Recipient: &sidecar.Recipient{
|
||||
NodePubKey: acctKeySmall,
|
||||
|
|
@ -359,7 +354,7 @@ func TestPrepareOrderSidecarTicket(t *testing.T) {
|
|||
State: sidecar.StateRegistered,
|
||||
Offer: sidecar.Offer{
|
||||
SigOfferDigest: testSig,
|
||||
SignPubKey: ourPubKey,
|
||||
SignPubKey: acctKeySmall,
|
||||
Capacity: 5_000_000,
|
||||
},
|
||||
Recipient: &sidecar.Recipient{
|
||||
|
|
|
|||
|
|
@ -3593,24 +3593,15 @@ var xxx_messageInfo_StopDaemonResponse proto.InternalMessageInfo
|
|||
|
||||
type OfferSidecarRequest struct {
|
||||
//
|
||||
//The total channel capacity in satoshis. This will be used for the bid
|
||||
//order's amount and min channel/match size values.
|
||||
ChannelCapacitySat uint64 `protobuf:"varint,1,opt,name=channel_capacity_sat,json=channelCapacitySat,proto3" json:"channel_capacity_sat,omitempty"`
|
||||
//If false, then only the trader_key, unit, self_chan_balance, and
|
||||
//lease_duration_blocks need to be set in the bid below. Otherwise, the
|
||||
//fields as they're set when submitting a bid need to be filled in.
|
||||
AutoNegotiate bool `protobuf:"varint,1,opt,name=auto_negotiate,json=autoNegotiate,proto3" json:"auto_negotiate,omitempty"`
|
||||
//
|
||||
//The number of satoshis that will be pushed to the recipient in the sidecar
|
||||
//channel resulting from the bid order submitted by the offering trader
|
||||
//(=provider). The initial outbound channel balance will be transferred from
|
||||
//the provider's pool account (=taker account) to the maker's account to
|
||||
//reimburse them for the balance they'll effectively be giving away. The
|
||||
//reimbursement between the recipient of the sidecar channel and the provider
|
||||
//(=taker) is _not_ part of the protocol and must happen out of band. The
|
||||
//sidecar protocol simply deducts all fees for the sidecar channel (execution
|
||||
//fee, lease premium, chain fees, push amount) from the taker's trading
|
||||
//account.
|
||||
SelfChanBalance uint64 `protobuf:"varint,2,opt,name=self_chan_balance,json=selfChanBalance,proto3" json:"self_chan_balance,omitempty"`
|
||||
//
|
||||
//The number of blocks the resulting leased channel should be open for.
|
||||
LeaseDurationBlocks uint32 `protobuf:"varint,3,opt,name=lease_duration_blocks,json=leaseDurationBlocks,proto3" json:"lease_duration_blocks,omitempty"`
|
||||
//The bid template that will be used to populate the initial sidecar ticket
|
||||
//as well as auto negotiate the remainig steps of the sidecar channel if
|
||||
//needed.
|
||||
Bid *Bid `protobuf:"bytes,2,opt,name=bid,proto3" json:"bid,omitempty"`
|
||||
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
||||
XXX_unrecognized []byte `json:"-"`
|
||||
XXX_sizecache int32 `json:"-"`
|
||||
|
|
@ -3641,25 +3632,18 @@ func (m *OfferSidecarRequest) XXX_DiscardUnknown() {
|
|||
|
||||
var xxx_messageInfo_OfferSidecarRequest proto.InternalMessageInfo
|
||||
|
||||
func (m *OfferSidecarRequest) GetChannelCapacitySat() uint64 {
|
||||
func (m *OfferSidecarRequest) GetAutoNegotiate() bool {
|
||||
if m != nil {
|
||||
return m.ChannelCapacitySat
|
||||
return m.AutoNegotiate
|
||||
}
|
||||
return 0
|
||||
return false
|
||||
}
|
||||
|
||||
func (m *OfferSidecarRequest) GetSelfChanBalance() uint64 {
|
||||
func (m *OfferSidecarRequest) GetBid() *Bid {
|
||||
if m != nil {
|
||||
return m.SelfChanBalance
|
||||
return m.Bid
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (m *OfferSidecarRequest) GetLeaseDurationBlocks() uint32 {
|
||||
if m != nil {
|
||||
return m.LeaseDurationBlocks
|
||||
}
|
||||
return 0
|
||||
return nil
|
||||
}
|
||||
|
||||
type SidecarTicket struct {
|
||||
|
|
@ -3711,7 +3695,14 @@ type RegisterSidecarRequest struct {
|
|||
//
|
||||
//The sidecar ticket to register and add the node and channel funding
|
||||
//information to. The ticket must be in the state "offered".
|
||||
Ticket string `protobuf:"bytes,1,opt,name=ticket,proto3" json:"ticket,omitempty"`
|
||||
Ticket string `protobuf:"bytes,1,opt,name=ticket,proto3" json:"ticket,omitempty"`
|
||||
//
|
||||
//If this value is True, then the daemon will attempt to finish negotiating
|
||||
//the details of the sidecar channel automatically in the background. The
|
||||
//progress of the ticket can be monitored using the SidecarState RPC. In
|
||||
//addition, if this flag is set, then this method will _block_ until the
|
||||
//sidecar negotiation either finishes or breaks down.
|
||||
AutoNegotiate bool `protobuf:"varint,2,opt,name=auto_negotiate,json=autoNegotiate,proto3" json:"auto_negotiate,omitempty"`
|
||||
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
||||
XXX_unrecognized []byte `json:"-"`
|
||||
XXX_sizecache int32 `json:"-"`
|
||||
|
|
@ -3749,6 +3740,13 @@ func (m *RegisterSidecarRequest) GetTicket() string {
|
|||
return ""
|
||||
}
|
||||
|
||||
func (m *RegisterSidecarRequest) GetAutoNegotiate() bool {
|
||||
if m != nil {
|
||||
return m.AutoNegotiate
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
type ExpectSidecarChannelRequest struct {
|
||||
//
|
||||
//The sidecar ticket to expect an incoming channel for. The ticket must be in
|
||||
|
|
@ -3892,240 +3890,240 @@ func init() {
|
|||
func init() { proto.RegisterFile("trader.proto", fileDescriptor_b8f61804588c75fe) }
|
||||
|
||||
var fileDescriptor_b8f61804588c75fe = []byte{
|
||||
// 3726 bytes of a gzipped FileDescriptorProto
|
||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xc4, 0x5a, 0xcd, 0x73, 0x23, 0x49,
|
||||
0x56, 0x77, 0x49, 0xfe, 0xd2, 0xd3, 0xa7, 0x53, 0xb6, 0x5b, 0xa3, 0x9e, 0x9e, 0xee, 0xae, 0xdd,
|
||||
0xde, 0xe9, 0xee, 0x19, 0xba, 0x07, 0xb3, 0xdd, 0x0c, 0xc3, 0xc0, 0xae, 0x2c, 0xcb, 0x63, 0xed,
|
||||
0xb8, 0x65, 0x4d, 0xda, 0xdd, 0x2c, 0x7b, 0xa9, 0x48, 0x95, 0xd2, 0x76, 0xad, 0xa5, 0x2a, 0x51,
|
||||
0x95, 0xf2, 0xc7, 0x81, 0x03, 0xc1, 0x46, 0x70, 0x84, 0x58, 0x02, 0xae, 0xdc, 0xe0, 0x7f, 0x80,
|
||||
0x08, 0x0e, 0x9c, 0x88, 0x80, 0x7f, 0x81, 0x08, 0xce, 0xfc, 0x05, 0x1c, 0x89, 0xfc, 0xaa, 0xca,
|
||||
0x2a, 0x95, 0xa6, 0x77, 0x86, 0xc3, 0xde, 0x54, 0xef, 0x23, 0xf3, 0xbd, 0xcc, 0x7c, 0x2f, 0x7f,
|
||||
0xef, 0xa5, 0xa0, 0xc2, 0x42, 0x32, 0xa6, 0xe1, 0x8b, 0x59, 0x18, 0xb0, 0x00, 0x6d, 0xcc, 0x82,
|
||||
0x60, 0x12, 0xce, 0xdc, 0xf6, 0x47, 0x64, 0xee, 0x32, 0x2f, 0xf0, 0x29, 0x0d, 0xc3, 0x99, 0xfb,
|
||||
0x32, 0xf9, 0x92, 0x82, 0xf6, 0xff, 0x58, 0x80, 0xfa, 0xbe, 0xc7, 0x3a, 0xae, 0x1b, 0xcc, 0x7d,
|
||||
0x86, 0xe9, 0x9f, 0xcd, 0x69, 0xc4, 0xd0, 0x0f, 0xa0, 0x4a, 0x24, 0xc5, 0xb9, 0x26, 0x93, 0x39,
|
||||
0x6d, 0x59, 0x8f, 0xac, 0xa7, 0xab, 0xb8, 0xa2, 0x88, 0xef, 0x38, 0x0d, 0x3d, 0x83, 0x3a, 0x19,
|
||||
0x45, 0xc1, 0x64, 0xce, 0xa8, 0x73, 0x49, 0xbd, 0x8b, 0x4b, 0xd6, 0x2a, 0x3c, 0xb2, 0x9e, 0x56,
|
||||
0x8f, 0x56, 0x70, 0x4d, 0x33, 0x8e, 0x04, 0x9d, 0x8b, 0x86, 0x74, 0x42, 0x98, 0x77, 0x1d, 0x8b,
|
||||
0x16, 0xb5, 0xa8, 0x66, 0x28, 0xd1, 0xc7, 0x50, 0x76, 0x03, 0xff, 0xdc, 0x61, 0x24, 0xbc, 0xa0,
|
||||
0xac, 0xb5, 0x2a, 0xc4, 0x2c, 0x0c, 0x9c, 0x78, 0x26, 0x68, 0xe8, 0x43, 0x28, 0x79, 0xbe, 0xc7,
|
||||
0x3c, 0xc2, 0x82, 0xb0, 0xb5, 0xf6, 0xc8, 0x7a, 0x5a, 0xc2, 0x09, 0x61, 0xbf, 0x01, 0x35, 0x6d,
|
||||
0x3b, 0xbd, 0x9d, 0x79, 0xe1, 0xdd, 0xfe, 0x3a, 0xac, 0x9e, 0x53, 0x1a, 0xd9, 0x14, 0x9a, 0xdf,
|
||||
0xcc, 0x03, 0x46, 0xbf, 0x8f, 0xb3, 0x19, 0xb3, 0xb4, 0xa3, 0x86, 0x59, 0xf1, 0x34, 0x37, 0xb0,
|
||||
0x9d, 0x9e, 0x26, 0x9a, 0x05, 0x7e, 0x44, 0xd1, 0xef, 0xc3, 0x07, 0x53, 0xcf, 0xa7, 0xa1, 0x73,
|
||||
0x4e, 0xa9, 0x13, 0x12, 0x46, 0x9d, 0x88, 0x30, 0x67, 0x46, 0x43, 0xe7, 0xea, 0x46, 0xcd, 0xb9,
|
||||
0x2d, 0x04, 0x0e, 0x29, 0xc5, 0x84, 0xd1, 0x53, 0xc2, 0x86, 0x34, 0xfc, 0xfa, 0x06, 0xfd, 0x08,
|
||||
0xea, 0x89, 0x22, 0x0b, 0x18, 0x99, 0x88, 0xf9, 0x57, 0x71, 0x55, 0x8b, 0x9f, 0x71, 0xa2, 0xfd,
|
||||
0x1a, 0x9a, 0xc7, 0x5e, 0xa4, 0xf7, 0x32, 0xd2, 0xfe, 0x3d, 0x84, 0x32, 0x71, 0xc5, 0xd2, 0x07,
|
||||
0xfe, 0xe4, 0x4e, 0xcc, 0xb4, 0x89, 0x41, 0x92, 0x4e, 0xfc, 0xc9, 0x9d, 0x7d, 0x00, 0xdb, 0x69,
|
||||
0x3d, 0x65, 0xf0, 0xa7, 0xb0, 0xa9, 0xd6, 0x20, 0x6a, 0x59, 0x8f, 0x8a, 0x4f, 0xcb, 0x7b, 0x8d,
|
||||
0x17, 0xea, 0x60, 0xbd, 0xd0, 0xce, 0xc5, 0x12, 0xf6, 0x4f, 0x60, 0xfd, 0x64, 0xce, 0x66, 0x73,
|
||||
0x86, 0xee, 0x43, 0x49, 0x2c, 0x24, 0xf7, 0x4f, 0x39, 0xb6, 0x29, 0x08, 0xa7, 0x84, 0xa1, 0x16,
|
||||
0x6c, 0x90, 0xf1, 0x38, 0xa4, 0x51, 0x24, 0x9c, 0x28, 0x61, 0xfd, 0x69, 0xff, 0xca, 0x82, 0xaa,
|
||||
0x1c, 0xe1, 0x4f, 0x3c, 0x76, 0x79, 0x48, 0xa9, 0x29, 0x6b, 0xa5, 0x64, 0x7f, 0x83, 0xed, 0x40,
|
||||
0x2f, 0xa0, 0x99, 0xb7, 0xd0, 0xfc, 0xdc, 0xad, 0x1e, 0xad, 0xe0, 0xfa, 0x79, 0x7a, 0x95, 0xe3,
|
||||
0xed, 0xeb, 0xc2, 0xae, 0xb4, 0x22, 0xe2, 0x66, 0xf4, 0xa7, 0xb3, 0x89, 0xe7, 0x7a, 0x8c, 0x9b,
|
||||
0xf3, 0x0c, 0x36, 0x02, 0xc9, 0x51, 0xcb, 0x51, 0x8f, 0x97, 0x43, 0x6a, 0x60, 0xcd, 0xb7, 0xff,
|
||||
0xdd, 0x82, 0x66, 0x77, 0x12, 0x44, 0xd9, 0xb3, 0xf6, 0x00, 0x40, 0x06, 0xaa, 0x73, 0x45, 0xe5,
|
||||
0x56, 0x54, 0x70, 0x49, 0x52, 0xbe, 0xa6, 0x77, 0xe8, 0xa7, 0x50, 0x97, 0x23, 0x38, 0x37, 0x1e,
|
||||
0xbb, 0xe4, 0xfb, 0x2d, 0x5c, 0x2b, 0xef, 0xed, 0x66, 0x66, 0x52, 0x2b, 0x74, 0xb4, 0x82, 0xab,
|
||||
0x41, 0x6a, 0xc9, 0xfe, 0x30, 0xb1, 0xb1, 0x28, 0x34, 0x1f, 0x66, 0x34, 0xb3, 0x5e, 0x1d, 0xad,
|
||||
0xc4, 0x56, 0xef, 0x37, 0x61, 0xeb, 0x7c, 0xee, 0x8f, 0x23, 0x67, 0x4c, 0x23, 0xe6, 0xf9, 0x84,
|
||||
0xe7, 0x0a, 0xfb, 0x15, 0x6c, 0xa7, 0x3d, 0x51, 0xa7, 0xe3, 0x01, 0x80, 0xcb, 0xe9, 0x0e, 0xbb,
|
||||
0xf5, 0xc6, 0xda, 0x15, 0x41, 0x39, 0xbb, 0xf5, 0xc6, 0xf6, 0xdf, 0x58, 0xb0, 0xcb, 0xa7, 0x1a,
|
||||
0x87, 0xe4, 0xe6, 0xbb, 0x2d, 0x82, 0xb1, 0xcc, 0x85, 0x6f, 0x5f, 0x66, 0xf4, 0xe9, 0xb7, 0xec,
|
||||
0xf1, 0xc2, 0x0e, 0xdb, 0xbf, 0x84, 0x7b, 0x0b, 0x16, 0x29, 0x67, 0x9e, 0xc3, 0x86, 0x3a, 0xc8,
|
||||
0xc2, 0x9e, 0xbc, 0x93, 0xae, 0x05, 0x78, 0xbe, 0xb8, 0x51, 0xc3, 0x48, 0xdf, 0x0b, 0xc2, 0x83,
|
||||
0x8a, 0x26, 0x0a, 0xf7, 0xff, 0xd2, 0x82, 0x9d, 0x03, 0x3a, 0x0b, 0xa2, 0x85, 0xdc, 0xfa, 0x1e,
|
||||
0xef, 0x1f, 0x00, 0x90, 0xa9, 0x48, 0x46, 0x3c, 0x7a, 0x64, 0x9c, 0x97, 0x24, 0x85, 0x87, 0xcf,
|
||||
0x77, 0xf3, 0xf8, 0x02, 0x76, 0xb3, 0x46, 0x7c, 0x0f, 0x87, 0x1f, 0x43, 0x65, 0x2c, 0x47, 0x31,
|
||||
0xfd, 0x2d, 0x2b, 0x9a, 0x70, 0xf7, 0x3f, 0x2d, 0x68, 0x62, 0xea, 0xd3, 0xec, 0x56, 0x8b, 0xdc,
|
||||
0x23, 0x73, 0x6b, 0xe2, 0x2d, 0x28, 0x92, 0xdc, 0xec, 0xe4, 0x12, 0x91, 0xe9, 0x7a, 0xf1, 0x12,
|
||||
0xe9, 0x09, 0x7a, 0xea, 0x12, 0x51, 0xa2, 0x0b, 0x97, 0x88, 0x12, 0x5d, 0xb2, 0x4a, 0xab, 0xb9,
|
||||
0xab, 0xb4, 0x78, 0x63, 0xd8, 0x14, 0xb6, 0xd3, 0xde, 0x7c, 0xbf, 0x55, 0x0b, 0xf9, 0x18, 0x64,
|
||||
0x92, 0x5a, 0x35, 0x45, 0x13, 0xab, 0x36, 0x86, 0x9d, 0xfd, 0xf9, 0x74, 0xa6, 0x54, 0x79, 0xda,
|
||||
0xff, 0xcd, 0xce, 0xc8, 0x12, 0xf7, 0x0a, 0xf9, 0x87, 0xa0, 0x05, 0xbb, 0xd9, 0x59, 0xa4, 0x3b,
|
||||
0xf6, 0xdf, 0x15, 0x60, 0x43, 0x91, 0xdf, 0x37, 0xe5, 0xef, 0xc0, 0x26, 0x0f, 0xba, 0xc0, 0xf3,
|
||||
0x99, 0x4a, 0x49, 0x5b, 0x66, 0x54, 0x0e, 0x39, 0x03, 0xc7, 0x22, 0x68, 0x1b, 0xd6, 0xe4, 0x5d,
|
||||
0x2a, 0x0f, 0xa6, 0xfc, 0x40, 0x9f, 0xc0, 0x16, 0xb9, 0x26, 0xde, 0x84, 0x8c, 0x26, 0xd4, 0x19,
|
||||
0x91, 0x09, 0xf1, 0x5d, 0xaa, 0x36, 0xa5, 0x11, 0x33, 0xf6, 0x25, 0x9d, 0x0b, 0x8b, 0xdd, 0x10,
|
||||
0x59, 0x48, 0xa3, 0x06, 0x7e, 0xdb, 0x57, 0x71, 0x23, 0x61, 0x28, 0xd4, 0xf0, 0x09, 0xac, 0x45,
|
||||
0x8c, 0x30, 0xda, 0x5a, 0x7f, 0x64, 0x3d, 0xad, 0xed, 0xed, 0x64, 0xb7, 0xe5, 0x94, 0x33, 0xb1,
|
||||
0x94, 0xe1, 0x87, 0x72, 0x42, 0x18, 0x8d, 0xd4, 0x71, 0xde, 0x90, 0x87, 0x52, 0x92, 0xc4, 0xbe,
|
||||
0xfc, 0x85, 0x05, 0xe8, 0x74, 0x3e, 0x9a, 0x7a, 0xec, 0x24, 0x1c, 0xd3, 0x50, 0xef, 0xca, 0x23,
|
||||
0x28, 0x92, 0xe8, 0x4a, 0xed, 0x7c, 0x25, 0x99, 0x22, 0xba, 0x3a, 0x5a, 0xc1, 0x9c, 0xc5, 0x25,
|
||||
0x46, 0x6a, 0xab, 0x4d, 0x89, 0x7d, 0x6f, 0xcc, 0x25, 0x46, 0xde, 0x38, 0x8d, 0x5d, 0x8a, 0x59,
|
||||
0xec, 0x52, 0x82, 0x8d, 0x31, 0x65, 0xc4, 0x9b, 0x44, 0xf6, 0xaf, 0x2d, 0x68, 0xa6, 0x6c, 0x50,
|
||||
0x47, 0xf0, 0x4b, 0xa8, 0x7a, 0xfe, 0x35, 0x99, 0x78, 0x63, 0x27, 0xe0, 0x0c, 0x65, 0x4e, 0xe2,
|
||||
0x71, 0x5f, 0x72, 0x85, 0xd6, 0xd1, 0x0a, 0xae, 0x78, 0xc6, 0x37, 0xda, 0x83, 0x6d, 0xe2, 0xba,
|
||||
0x74, 0xc6, 0xa8, 0x52, 0x77, 0xfc, 0x80, 0x6f, 0x82, 0x38, 0x9c, 0x47, 0x2b, 0x18, 0x69, 0xae,
|
||||
0x10, 0x1f, 0x70, 0x9e, 0x69, 0xd4, 0x00, 0xb6, 0x38, 0x52, 0x10, 0xcc, 0x18, 0x5f, 0xb4, 0x60,
|
||||
0xe3, 0x9a, 0x86, 0xa3, 0x20, 0xa2, 0x0a, 0x5b, 0xe8, 0xcf, 0x2c, 0xf2, 0x28, 0x2c, 0x20, 0x8f,
|
||||
0x9f, 0x03, 0x32, 0xc7, 0x53, 0x2e, 0x3e, 0x82, 0x55, 0x12, 0x5d, 0xe9, 0x4b, 0x36, 0xb5, 0xd0,
|
||||
0x58, 0x70, 0xb8, 0xc4, 0xc8, 0x1b, 0xeb, 0xfb, 0x21, 0xb5, 0xd0, 0x58, 0x70, 0xec, 0x57, 0x80,
|
||||
0xba, 0xfc, 0x18, 0x4d, 0x52, 0x3b, 0xf8, 0x10, 0xca, 0xa6, 0xd7, 0x2a, 0x1d, 0x05, 0xb1, 0xaf,
|
||||
0xf6, 0x0e, 0x34, 0x53, 0x6a, 0x2a, 0x50, 0xfe, 0xab, 0x08, 0x6b, 0x72, 0x01, 0xdf, 0x9f, 0xbd,
|
||||
0x45, 0x54, 0x9e, 0x7b, 0xb7, 0x54, 0x9e, 0x83, 0x2a, 0x2e, 0x71, 0xca, 0x21, 0x27, 0xa0, 0x06,
|
||||
0x14, 0xc9, 0x94, 0xa9, 0xa0, 0xe0, 0x3f, 0xd1, 0x1f, 0xc3, 0x83, 0x29, 0xb9, 0x75, 0x46, 0x84,
|
||||
0xb9, 0x97, 0xce, 0xf2, 0x9c, 0x75, 0x6f, 0x4a, 0x6e, 0xf7, 0xb9, 0x4c, 0x16, 0x1b, 0x66, 0x3c,
|
||||
0x5a, 0xcb, 0x7a, 0x84, 0x9e, 0xa5, 0x23, 0xa3, 0x99, 0x44, 0x2d, 0x97, 0x49, 0xc5, 0xc5, 0x36,
|
||||
0xac, 0xcd, 0x7d, 0x8f, 0x45, 0x22, 0x22, 0xaa, 0x58, 0x7e, 0xf0, 0x38, 0x14, 0x3f, 0x9c, 0xb9,
|
||||
0x7f, 0x3e, 0x9f, 0x9c, 0x7b, 0x93, 0x09, 0x1d, 0xb7, 0x36, 0x65, 0x1c, 0x0a, 0xc6, 0xdb, 0x84,
|
||||
0x8e, 0x3e, 0x05, 0x14, 0xd2, 0x88, 0x86, 0xd7, 0x74, 0xec, 0x24, 0x18, 0xb0, 0x24, 0x43, 0x5c,
|
||||
0x73, 0xde, 0x69, 0x2c, 0xb8, 0x07, 0x3b, 0x6e, 0x48, 0x65, 0x80, 0x33, 0x6f, 0x4a, 0x23, 0x46,
|
||||
0xa6, 0x33, 0xc7, 0x8f, 0x5a, 0x20, 0x14, 0x9a, 0x9a, 0x79, 0xa6, 0x79, 0x03, 0x6e, 0xce, 0x3a,
|
||||
0xbd, 0xa6, 0x1c, 0x92, 0x96, 0xc5, 0xe6, 0x67, 0x1c, 0xea, 0x71, 0x1e, 0x56, 0x22, 0x0a, 0x39,
|
||||
0x3b, 0xd2, 0xfe, 0x29, 0x5f, 0xbf, 0x56, 0x45, 0x58, 0xce, 0x91, 0xf3, 0x5b, 0x4e, 0x7d, 0xc3,
|
||||
0x89, 0xf6, 0x5f, 0x15, 0xa0, 0xb8, 0xef, 0x8d, 0xd1, 0xd3, 0xf8, 0xa8, 0xab, 0xb0, 0xaa, 0xa5,
|
||||
0x47, 0xc7, 0x9a, 0xcd, 0x4d, 0x9f, 0x50, 0x12, 0x51, 0x67, 0x3c, 0x57, 0x19, 0x6a, 0x34, 0x09,
|
||||
0xdc, 0xab, 0x48, 0xed, 0x79, 0x53, 0x30, 0x0f, 0x14, 0x6f, 0x5f, 0xb0, 0x54, 0xa0, 0x44, 0x5e,
|
||||
0xe0, 0xcb, 0x8b, 0x0b, 0xeb, 0x4f, 0xf4, 0x0a, 0xb8, 0x41, 0x8e, 0x1f, 0x8c, 0xa9, 0xc3, 0x3c,
|
||||
0x1a, 0x8a, 0x5d, 0xaf, 0x19, 0x29, 0x76, 0x10, 0x8c, 0xe9, 0x99, 0x47, 0x43, 0x5c, 0x9e, 0x7a,
|
||||
0xbe, 0xfe, 0x40, 0xcf, 0x61, 0x2b, 0xa2, 0x93, 0x73, 0xc7, 0xbd, 0x24, 0x7e, 0x9c, 0x4f, 0xd7,
|
||||
0xe4, 0x2d, 0xc0, 0x19, 0xdd, 0x4b, 0xe2, 0xeb, 0x74, 0xfa, 0x04, 0x6a, 0x91, 0x37, 0xa6, 0x2e,
|
||||
0x09, 0x1d, 0xe6, 0xb9, 0x57, 0x94, 0x89, 0x03, 0x51, 0xc2, 0x55, 0x45, 0x3d, 0x13, 0x44, 0xfb,
|
||||
0xcf, 0xa1, 0xd8, 0x89, 0xae, 0x7e, 0x5b, 0x0b, 0x61, 0xff, 0xb7, 0x05, 0x5b, 0xa2, 0x78, 0x4a,
|
||||
0x85, 0xad, 0x0a, 0x1b, 0x2b, 0x09, 0x9b, 0xf7, 0xc4, 0xd9, 0x52, 0xa3, 0x8a, 0xcb, 0x8d, 0xfa,
|
||||
0xff, 0x46, 0x62, 0xce, 0x59, 0x5b, 0xcb, 0x3b, 0x6b, 0xff, 0x6b, 0x01, 0x32, 0x5d, 0x8c, 0xa1,
|
||||
0xc5, 0x96, 0x28, 0xed, 0x9c, 0x59, 0x48, 0xa7, 0xde, 0x7c, 0x6a, 0x14, 0x4f, 0x75, 0xc1, 0x18,
|
||||
0x4a, 0x3a, 0x8f, 0x9b, 0x1f, 0x42, 0x4d, 0x18, 0xc7, 0x0d, 0x13, 0x8e, 0x89, 0x15, 0xb0, 0x70,
|
||||
0x85, 0x53, 0x87, 0x34, 0x14, 0x1e, 0x09, 0x00, 0xa2, 0xa4, 0x5c, 0xea, 0xcb, 0xac, 0x63, 0xe1,
|
||||
0xb2, 0x92, 0xe1, 0x24, 0xf4, 0x0a, 0xee, 0xc9, 0x49, 0xe9, 0x2d, 0x75, 0xe7, 0x62, 0xa1, 0xb8,
|
||||
0xe7, 0x7c, 0x6a, 0xe9, 0xed, 0xb6, 0x60, 0xf7, 0x34, 0xf7, 0x90, 0x8a, 0xb8, 0x7d, 0x0d, 0xad,
|
||||
0x9b, 0x20, 0x8c, 0x98, 0xe3, 0xf2, 0x35, 0x76, 0x2f, 0x89, 0x97, 0xe8, 0xc9, 0xe3, 0xb7, 0x2d,
|
||||
0xf8, 0x5d, 0x12, 0xd1, 0x2e, 0xe7, 0x4a, 0x3d, 0xfb, 0xdf, 0x2c, 0x80, 0x24, 0x4a, 0xb9, 0x81,
|
||||
0xa9, 0xa8, 0xe7, 0xde, 0x16, 0x71, 0x99, 0x19, 0xd1, 0x7e, 0x1f, 0x4a, 0x22, 0x94, 0x9d, 0x88,
|
||||
0x85, 0xaa, 0x5e, 0xdc, 0x14, 0x84, 0x53, 0x16, 0xa2, 0x2f, 0xa0, 0x22, 0x12, 0x97, 0x38, 0xff,
|
||||
0x17, 0x54, 0x15, 0x3c, 0xc9, 0x4d, 0xf8, 0x76, 0x36, 0x26, 0x8c, 0x8e, 0xc5, 0x64, 0x47, 0x2b,
|
||||
0xb8, 0x2c, 0x84, 0xbb, 0x42, 0x16, 0xbd, 0x84, 0x0d, 0xb1, 0x47, 0x74, 0x2c, 0x3c, 0x35, 0xf3,
|
||||
0x88, 0xd8, 0x26, 0xad, 0xa4, 0xa5, 0xf6, 0x37, 0x60, 0x4d, 0x4c, 0x6c, 0xff, 0x83, 0x05, 0x15,
|
||||
0x73, 0x64, 0xf4, 0x05, 0xd4, 0x66, 0x21, 0xbd, 0xf6, 0x82, 0x79, 0xe4, 0xc8, 0x54, 0x6b, 0x2d,
|
||||
0x4f, 0xb5, 0x55, 0x2d, 0x2a, 0x3e, 0xd1, 0x67, 0x50, 0xf2, 0xe9, 0x8d, 0x52, 0x2b, 0x2c, 0x57,
|
||||
0xdb, 0xf4, 0xe9, 0x8d, 0xd4, 0x78, 0x0c, 0x15, 0x79, 0xc4, 0x54, 0x26, 0x96, 0x27, 0xba, 0x2c,
|
||||
0x68, 0x87, 0x82, 0x64, 0xff, 0x87, 0x05, 0x90, 0x38, 0x81, 0x7e, 0x0c, 0x65, 0xe1, 0xc4, 0x12,
|
||||
0xe3, 0x84, 0xa4, 0x9c, 0x05, 0xa6, 0xf1, 0xef, 0x85, 0x79, 0x0a, 0x0b, 0xf3, 0xf0, 0x42, 0x48,
|
||||
0xad, 0x8e, 0x82, 0x22, 0x45, 0x59, 0x08, 0x29, 0xa2, 0xbc, 0x30, 0x7f, 0x02, 0xd5, 0x90, 0xfe,
|
||||
0x92, 0xba, 0xcc, 0x09, 0x29, 0x89, 0x02, 0x5f, 0xa5, 0xb6, 0x76, 0x7a, 0x7e, 0x2c, 0x44, 0xb0,
|
||||
0x90, 0xc0, 0x95, 0xd0, 0xf8, 0xe2, 0xf0, 0x15, 0x53, 0x37, 0xb8, 0xa6, 0x61, 0xa6, 0xb1, 0x61,
|
||||
0x9f, 0xc0, 0xbd, 0x05, 0x8e, 0x8a, 0xa6, 0x1f, 0xc3, 0xae, 0x3f, 0x9f, 0x3a, 0xa1, 0x64, 0xd3,
|
||||
0xb1, 0x63, 0x34, 0x32, 0xb8, 0x1f, 0xdb, 0xfe, 0x7c, 0x8a, 0x35, 0x53, 0x6b, 0xdb, 0x4d, 0xd8,
|
||||
0xea, 0xc8, 0x0e, 0x59, 0x82, 0xc5, 0xed, 0x21, 0x20, 0x93, 0xa8, 0x26, 0xf8, 0x02, 0xaa, 0xa9,
|
||||
0x98, 0x59, 0x80, 0x61, 0x66, 0xcc, 0xe0, 0x0a, 0x35, 0xbe, 0xec, 0x7f, 0x5c, 0x83, 0xb5, 0x63,
|
||||
0x9e, 0x81, 0xd0, 0x6b, 0xa8, 0xf2, 0xb3, 0xeb, 0xd3, 0x89, 0x23, 0xa1, 0xb5, 0xb5, 0x0c, 0x5a,
|
||||
0x57, 0x94, 0x9c, 0xf8, 0xe2, 0xb9, 0x46, 0xeb, 0x91, 0xa9, 0x59, 0x29, 0xea, 0xe1, 0x3a, 0x53,
|
||||
0x26, 0x03, 0xf5, 0x9e, 0x96, 0xcb, 0xcf, 0x84, 0x3b, 0x8a, 0x9d, 0xc9, 0x85, 0x9f, 0xc1, 0xb6,
|
||||
0xd6, 0x93, 0x79, 0x54, 0xd5, 0x5b, 0xa2, 0x1b, 0x87, 0x91, 0xe2, 0x09, 0x1f, 0x54, 0xc5, 0xf5,
|
||||
0x10, 0xca, 0x66, 0xe2, 0x92, 0x59, 0x00, 0x66, 0x49, 0xce, 0x7a, 0xce, 0xe1, 0x7c, 0x36, 0xc9,
|
||||
0xac, 0xcb, 0xfc, 0x46, 0x33, 0xf9, 0xc5, 0x16, 0xcb, 0x62, 0x24, 0x95, 0x0d, 0x21, 0x57, 0x76,
|
||||
0x93, 0x5c, 0x82, 0x5e, 0x40, 0xd3, 0x9d, 0x50, 0x12, 0x7a, 0xfe, 0x85, 0xcc, 0xd4, 0xb3, 0xd0,
|
||||
0x73, 0xa9, 0x00, 0x26, 0xab, 0x78, 0x4b, 0xb3, 0x78, 0x86, 0x1e, 0x72, 0x06, 0x7a, 0x0a, 0x0d,
|
||||
0x09, 0x94, 0xc4, 0x95, 0x21, 0x54, 0x14, 0x2e, 0xa9, 0x09, 0xba, 0xb8, 0x38, 0xb0, 0x2a, 0x0f,
|
||||
0x4c, 0x48, 0x05, 0x0b, 0x90, 0xea, 0x43, 0x28, 0xcd, 0xe6, 0xa1, 0x7b, 0x49, 0x22, 0x3a, 0x6e,
|
||||
0x95, 0x05, 0xa8, 0x4d, 0x08, 0x3c, 0xa7, 0xea, 0xb5, 0x0b, 0xe9, 0x34, 0x60, 0x54, 0x5e, 0xeb,
|
||||
0x1c, 0x2e, 0x56, 0xc4, 0x50, 0x7a, 0x69, 0xb1, 0xe0, 0xf2, 0xcb, 0x9c, 0x23, 0xc7, 0x3f, 0x82,
|
||||
0x2d, 0xad, 0x96, 0xc0, 0x80, 0xea, 0x32, 0x18, 0xa0, 0xb7, 0xff, 0xdb, 0xa1, 0x40, 0x2d, 0x1f,
|
||||
0x0a, 0x7c, 0x0c, 0x75, 0x0d, 0x05, 0xd4, 0x30, 0xad, 0xba, 0xf0, 0x42, 0x23, 0x84, 0xae, 0xa4,
|
||||
0xda, 0x47, 0x50, 0x15, 0x7b, 0x1c, 0x43, 0xfd, 0xfb, 0x50, 0x92, 0xf7, 0x23, 0x07, 0xdf, 0x1c,
|
||||
0x9e, 0x57, 0xf0, 0xa6, 0x20, 0xf4, 0xc7, 0x11, 0x6a, 0x1b, 0xed, 0xc2, 0x82, 0xe4, 0xc5, 0xcd,
|
||||
0xc1, 0xbf, 0xb7, 0xa0, 0xa6, 0x87, 0x52, 0x11, 0xf4, 0x23, 0x58, 0x17, 0x67, 0x4b, 0xe3, 0xfc,
|
||||
0x04, 0x61, 0x08, 0x41, 0xac, 0xb8, 0xe8, 0x25, 0xc8, 0x4b, 0x48, 0x9c, 0x74, 0x4a, 0x42, 0x9f,
|
||||
0x8e, 0x8d, 0x03, 0x2f, 0x2f, 0xcd, 0xce, 0x94, 0xf5, 0x04, 0x87, 0x9f, 0x8c, 0x4f, 0x00, 0x25,
|
||||
0x0a, 0x33, 0xe2, 0x49, 0xf1, 0xa2, 0x71, 0x95, 0x76, 0xa6, 0x6c, 0x48, 0x3c, 0x2e, 0x6c, 0xd7,
|
||||
0xa1, 0x7a, 0x16, 0x5c, 0x51, 0x3f, 0x4e, 0x2a, 0x5f, 0x42, 0x4d, 0x13, 0xe2, 0x9b, 0x79, 0x9d,
|
||||
0x09, 0x8a, 0x32, 0x14, 0x25, 0x86, 0x46, 0x84, 0x09, 0x61, 0xac, 0x24, 0xec, 0x7f, 0x29, 0x40,
|
||||
0x29, 0xa6, 0xf2, 0x04, 0x39, 0xe2, 0xd1, 0x33, 0x25, 0x2e, 0x09, 0x83, 0xc0, 0x57, 0xf5, 0x42,
|
||||
0x85, 0x13, 0xdf, 0x28, 0x1a, 0x4f, 0xb4, 0x33, 0x72, 0x37, 0xe5, 0x97, 0xdc, 0x25, 0x89, 0x2e,
|
||||
0x75, 0x9f, 0x40, 0xd1, 0x8e, 0x48, 0x74, 0x89, 0x9e, 0x41, 0x43, 0x8b, 0xcc, 0x42, 0xea, 0x4d,
|
||||
0x89, 0xba, 0xec, 0x2a, 0xb8, 0xae, 0xe8, 0x43, 0x45, 0xe6, 0xc7, 0x5c, 0xb5, 0x8f, 0x84, 0xe7,
|
||||
0x53, 0x7d, 0x95, 0x17, 0x71, 0x4d, 0xd2, 0xb9, 0xe3, 0x6f, 0x22, 0xc2, 0xd0, 0xef, 0xc2, 0x4e,
|
||||
0x18, 0xcc, 0x19, 0x8f, 0x1f, 0x1e, 0x66, 0x89, 0xf8, 0x9a, 0x10, 0x47, 0x8a, 0x79, 0x48, 0x69,
|
||||
0xac, 0xa2, 0x2e, 0x6c, 0x47, 0xe0, 0x72, 0x3a, 0x16, 0xe1, 0xab, 0x2e, 0xec, 0xae, 0x24, 0x71,
|
||||
0x68, 0x27, 0x72, 0x05, 0x95, 0x75, 0xf5, 0x26, 0xd6, 0x9f, 0x5c, 0x39, 0x62, 0x41, 0x48, 0x2e,
|
||||
0xa8, 0xe3, 0x93, 0xa9, 0x8c, 0xd4, 0x12, 0xbf, 0x94, 0x05, 0x6d, 0x40, 0xa6, 0xd4, 0xde, 0x85,
|
||||
0xed, 0x63, 0x13, 0x99, 0xe9, 0x3d, 0xf9, 0x75, 0x11, 0x76, 0x32, 0x0c, 0xb5, 0x37, 0x0e, 0xd4,
|
||||
0xd3, 0x40, 0x4f, 0x6f, 0xd2, 0x5e, 0xfa, 0x34, 0x65, 0x15, 0xd3, 0xd4, 0xa8, 0xe7, 0xb3, 0xf0,
|
||||
0x6e, 0xbf, 0xd0, 0xb2, 0x70, 0x2d, 0x05, 0x0d, 0x23, 0xe4, 0xc3, 0x6e, 0x16, 0x49, 0xce, 0x39,
|
||||
0x50, 0xd6, 0xb5, 0xe7, 0xe7, 0xdf, 0x65, 0x9e, 0x7d, 0xa9, 0x2a, 0x66, 0xc3, 0xdb, 0x93, 0x1c,
|
||||
0x56, 0xbb, 0x03, 0xcd, 0x1c, 0xd3, 0x38, 0x02, 0xd6, 0xf5, 0x66, 0x15, 0xf3, 0x9f, 0x49, 0x87,
|
||||
0x45, 0x56, 0xd5, 0xf2, 0xe3, 0x8b, 0xc2, 0xe7, 0x56, 0x9b, 0xc2, 0x07, 0x4b, 0x67, 0xcd, 0x19,
|
||||
0x68, 0xcf, 0x1c, 0xa8, 0xb6, 0xf7, 0x61, 0xec, 0x50, 0x5a, 0x5f, 0x55, 0x8a, 0xf1, 0x34, 0x7c,
|
||||
0xb3, 0x06, 0xf4, 0x96, 0x09, 0x2c, 0xdc, 0xf7, 0xcf, 0x03, 0xbd, 0x59, 0x7f, 0x6d, 0xc1, 0x4e,
|
||||
0x86, 0xa1, 0x36, 0xeb, 0x61, 0xba, 0x69, 0x2f, 0xd1, 0xb1, 0xd9, 0xb2, 0x5f, 0xd2, 0xd7, 0x5a,
|
||||
0xcf, 0xed, 0x6b, 0xf1, 0x34, 0x26, 0xd2, 0x7c, 0x52, 0x3a, 0xaa, 0x7b, 0xa2, 0x26, 0xc8, 0x71,
|
||||
0xd1, 0x68, 0xbf, 0x86, 0x2d, 0x9e, 0x27, 0x31, 0xe1, 0xe7, 0x59, 0xa7, 0xb2, 0xc7, 0x50, 0x11,
|
||||
0x79, 0x76, 0x36, 0x1f, 0x5d, 0xd1, 0x3b, 0x9d, 0xcd, 0xca, 0x9c, 0x36, 0x94, 0x24, 0xfb, 0x18,
|
||||
0x90, 0xa9, 0xa7, 0xbc, 0x78, 0xad, 0x14, 0x43, 0x41, 0xd6, 0xe7, 0xad, 0x99, 0xca, 0xd1, 0x4a,
|
||||
0x45, 0x8c, 0x26, 0x7f, 0x47, 0x76, 0x03, 0x6a, 0x5f, 0x51, 0x66, 0xae, 0xd4, 0xaf, 0xd6, 0xa1,
|
||||
0x1e, 0x93, 0xd4, 0xe8, 0x46, 0x69, 0xa4, 0x9e, 0x3c, 0x74, 0x8d, 0xf8, 0x24, 0xee, 0x52, 0x46,
|
||||
0xc6, 0x23, 0x50, 0x15, 0xeb, 0xc7, 0xab, 0x48, 0x3c, 0x02, 0xf1, 0x55, 0x89, 0xc5, 0x64, 0xa7,
|
||||
0x45, 0x5d, 0xf5, 0xb1, 0x76, 0x47, 0x50, 0x39, 0x36, 0xc8, 0x08, 0x3a, 0x3a, 0x72, 0xe5, 0x35,
|
||||
0xbf, 0x93, 0x56, 0xe8, 0xa9, 0x38, 0xfe, 0x04, 0xb6, 0x12, 0xbd, 0xd0, 0xbd, 0xf4, 0xae, 0xe9,
|
||||
0x58, 0xf7, 0xe5, 0x62, 0x0d, 0x45, 0xe7, 0xab, 0x2c, 0x2e, 0x4e, 0x6d, 0xf2, 0xba, 0x44, 0x91,
|
||||
0x92, 0x26, 0x0d, 0xfe, 0x01, 0x54, 0x95, 0x88, 0x32, 0x57, 0x76, 0x1f, 0x94, 0x9e, 0x32, 0xf6,
|
||||
0x63, 0xa8, 0x6b, 0x21, 0x3d, 0xa5, 0x6c, 0x41, 0xd4, 0x94, 0x98, 0x9e, 0x90, 0x23, 0x97, 0x79,
|
||||
0x18, 0xf2, 0x54, 0x29, 0x80, 0x8e, 0x6e, 0x1c, 0x96, 0x14, 0x72, 0x91, 0x3c, 0x01, 0x73, 0xe2,
|
||||
0xb7, 0xc9, 0x86, 0xb8, 0xc2, 0x68, 0xe4, 0x78, 0xfe, 0x75, 0x30, 0xe1, 0x63, 0x83, 0x90, 0xae,
|
||||
0x2b, 0x7a, 0x5f, 0x91, 0x39, 0x92, 0x36, 0xb6, 0x5e, 0x5c, 0xfd, 0x4b, 0x76, 0x1e, 0x92, 0x9d,
|
||||
0x17, 0xed, 0x46, 0x7e, 0x9a, 0xd5, 0x25, 0x22, 0x1b, 0x10, 0x30, 0xd1, 0xb7, 0x44, 0x84, 0x3e,
|
||||
0x87, 0x56, 0x34, 0x1f, 0x45, 0x6e, 0xe8, 0x8d, 0xe8, 0xd8, 0x61, 0x81, 0x93, 0x3c, 0xd3, 0x0a,
|
||||
0x04, 0xb0, 0x89, 0x77, 0x13, 0xfe, 0x59, 0xd0, 0x89, 0xb9, 0xbc, 0x10, 0xe4, 0xe5, 0x03, 0x9f,
|
||||
0x2c, 0x92, 0x3d, 0xb6, 0x9a, 0x90, 0xaf, 0xf8, 0xf4, 0x86, 0x5b, 0x13, 0x9d, 0xf8, 0x93, 0x3b,
|
||||
0xd4, 0xe7, 0x05, 0x40, 0x78, 0x45, 0x99, 0xe3, 0xf9, 0xe7, 0x41, 0xab, 0x2e, 0x0e, 0xec, 0xd3,
|
||||
0xd8, 0xec, 0xcc, 0x11, 0x7c, 0xf1, 0x46, 0xc8, 0x72, 0x92, 0x4c, 0x54, 0x30, 0x8d, 0x09, 0x6d,
|
||||
0x0c, 0xf5, 0x0c, 0x3b, 0x27, 0xa3, 0x3c, 0x33, 0x33, 0x4a, 0xba, 0xb2, 0xd2, 0xaa, 0x66, 0x22,
|
||||
0x69, 0xc2, 0xd6, 0x29, 0x0b, 0x66, 0x07, 0x84, 0x4e, 0x93, 0x94, 0xbf, 0x0d, 0xc8, 0x24, 0xaa,
|
||||
0x3e, 0xdc, 0x3f, 0x59, 0xd0, 0x3c, 0x39, 0x3f, 0xa7, 0xe1, 0xa9, 0x04, 0x2a, 0x3a, 0x98, 0x0d,
|
||||
0xbc, 0xea, 0x92, 0x19, 0x71, 0x3d, 0x76, 0x67, 0xd4, 0xcf, 0x1a, 0xaf, 0x76, 0x15, 0x4b, 0xc1,
|
||||
0xd1, 0x45, 0xbc, 0x54, 0xc8, 0xc7, 0x4b, 0xdf, 0xa3, 0x9b, 0x60, 0x7f, 0x0c, 0xd5, 0x53, 0xb3,
|
||||
0xb1, 0x82, 0x76, 0x61, 0x5d, 0xf5, 0x5d, 0x64, 0x5c, 0xab, 0x2f, 0xfb, 0x33, 0x5e, 0xde, 0x5c,
|
||||
0x78, 0x11, 0x5b, 0x70, 0x6a, 0x99, 0xc6, 0x2b, 0xb8, 0xdf, 0xbb, 0x9d, 0x51, 0x97, 0x9d, 0xa6,
|
||||
0xd0, 0xda, 0xfb, 0xd4, 0x3e, 0x82, 0x0f, 0xf3, 0xd5, 0xe4, 0xda, 0x3e, 0xff, 0x5b, 0x0b, 0x2a,
|
||||
0x66, 0xb7, 0x1c, 0x35, 0xa0, 0x32, 0xec, 0x0d, 0x0e, 0xfa, 0x83, 0xaf, 0x9c, 0x93, 0x61, 0x6f,
|
||||
0xd0, 0x58, 0x41, 0x08, 0x6a, 0x9a, 0xf2, 0x76, 0x78, 0xd0, 0x39, 0xeb, 0x35, 0x2c, 0xb4, 0x09,
|
||||
0xab, 0x82, 0x5b, 0x40, 0x65, 0xd8, 0xe8, 0xfd, 0x7c, 0xd8, 0xc7, 0xbd, 0x83, 0x46, 0xd1, 0x14,
|
||||
0xed, 0x1e, 0x9f, 0x9c, 0xf6, 0x0e, 0x1a, 0xab, 0x08, 0x60, 0x5d, 0xfd, 0x5e, 0x43, 0x4d, 0xa8,
|
||||
0xe3, 0x5e, 0xf7, 0xe4, 0x5d, 0x0f, 0xff, 0xa9, 0x73, 0xd8, 0xe9, 0x1f, 0xf7, 0x0e, 0x1a, 0xeb,
|
||||
0x68, 0x0b, 0xaa, 0x5a, 0x69, 0xbf, 0x73, 0xd6, 0x3d, 0x6a, 0x6c, 0x3c, 0x1f, 0xaa, 0x52, 0x56,
|
||||
0x9a, 0x54, 0x86, 0x8d, 0x21, 0xee, 0x0d, 0x3b, 0xb8, 0xd7, 0x58, 0x41, 0x15, 0xd8, 0xec, 0x74,
|
||||
0xbb, 0xbd, 0xe1, 0x59, 0xef, 0xa0, 0x61, 0xf1, 0x2f, 0xdc, 0xfb, 0x59, 0xaf, 0xcb, 0xbf, 0x0a,
|
||||
0x7c, 0xaa, 0xd3, 0xfe, 0x57, 0x03, 0x61, 0x4a, 0x15, 0x4a, 0x87, 0xfd, 0x41, 0xe7, 0xb8, 0xff,
|
||||
0x0b, 0x6e, 0xc5, 0xf3, 0x7f, 0xb5, 0x60, 0x6b, 0xa1, 0xe6, 0xe4, 0x6e, 0x0c, 0x4e, 0x06, 0x7c,
|
||||
0xd8, 0x5d, 0x40, 0xa7, 0x3d, 0xfc, 0xae, 0x87, 0x9d, 0x37, 0xfd, 0xd3, 0xfd, 0xde, 0x51, 0xe7,
|
||||
0x5d, 0xff, 0x04, 0x37, 0x2c, 0xd4, 0x86, 0x5d, 0x61, 0x94, 0xf3, 0xae, 0x87, 0x4f, 0xfb, 0x27,
|
||||
0x03, 0xce, 0x7e, 0x23, 0xac, 0x2c, 0xa0, 0x07, 0xf0, 0xc1, 0xb0, 0x83, 0xcf, 0xfa, 0x9d, 0x63,
|
||||
0x47, 0x1a, 0xe1, 0x74, 0x4f, 0x8e, 0x8f, 0x3b, 0x67, 0x3d, 0xdc, 0x39, 0x6e, 0x14, 0xd1, 0x63,
|
||||
0x78, 0x90, 0x61, 0x1f, 0xbc, 0x1d, 0x1e, 0xf7, 0xbb, 0x9d, 0xb3, 0x9e, 0x33, 0xec, 0xf5, 0x70,
|
||||
0x63, 0x15, 0x3d, 0x83, 0x27, 0xd9, 0x11, 0x8e, 0x3a, 0x83, 0x41, 0xef, 0xd8, 0x39, 0x7c, 0x2b,
|
||||
0x57, 0x44, 0xad, 0xd2, 0xda, 0xde, 0x3f, 0xd7, 0x61, 0xfd, 0x4c, 0x74, 0x9c, 0xd1, 0x97, 0xb0,
|
||||
0xa1, 0xa2, 0x17, 0xdd, 0x5b, 0x8c, 0x67, 0x71, 0x1e, 0xda, 0xad, 0x65, 0x81, 0x8e, 0x7a, 0x00,
|
||||
0x49, 0x8c, 0xa1, 0xa4, 0x22, 0x5f, 0x88, 0xc6, 0xf6, 0xfd, 0x5c, 0x9e, 0x1a, 0xe6, 0x6b, 0xa8,
|
||||
0x98, 0xff, 0x77, 0x40, 0x09, 0x82, 0xc8, 0xf9, 0xb7, 0x45, 0xfb, 0xc1, 0x12, 0x6e, 0xfc, 0xbc,
|
||||
0x51, 0x36, 0xfe, 0x8f, 0x82, 0xee, 0x1b, 0xcf, 0x1a, 0xd9, 0x97, 0xd4, 0xf6, 0xc2, 0xe3, 0x1b,
|
||||
0x37, 0xc5, 0xfc, 0x27, 0x83, 0x61, 0x4a, 0xce, 0x1f, 0x23, 0x0c, 0x53, 0x72, 0xff, 0xfe, 0xf0,
|
||||
0x35, 0x54, 0xcc, 0x87, 0x6f, 0x63, 0xb0, 0x9c, 0x97, 0x7d, 0x63, 0xb0, 0xdc, 0xd7, 0xf2, 0x33,
|
||||
0xa8, 0x67, 0xde, 0x9e, 0x51, 0xf2, 0x32, 0x9f, 0xff, 0x4e, 0xde, 0x7e, 0xb4, 0x5c, 0x40, 0x8d,
|
||||
0xfa, 0x0d, 0xd4, 0xd2, 0xef, 0xbb, 0xe8, 0xa3, 0x04, 0xbe, 0xe5, 0xbd, 0x3e, 0xb7, 0x1f, 0x2e,
|
||||
0xe5, 0x27, 0x5e, 0x9b, 0x4f, 0x9f, 0x86, 0xd7, 0x39, 0xef, 0xbb, 0x86, 0xd7, 0xb9, 0xef, 0xa5,
|
||||
0xdf, 0x40, 0x2d, 0xfd, 0xf4, 0x68, 0xd8, 0x97, 0xfb, 0xf2, 0x69, 0xd8, 0x97, 0xff, 0x66, 0xc9,
|
||||
0x17, 0x32, 0xd3, 0xf4, 0x31, 0x16, 0x32, 0xbf, 0x51, 0x64, 0x2c, 0xe4, 0xb2, 0x7e, 0xd1, 0x11,
|
||||
0x94, 0x8d, 0xc7, 0x36, 0xe3, 0xd8, 0x2d, 0x3e, 0x03, 0xb6, 0x3f, 0xcc, 0x67, 0x26, 0x41, 0x95,
|
||||
0x3c, 0x69, 0x19, 0x41, 0xb5, 0xf0, 0x6e, 0x66, 0x04, 0x55, 0xce, 0x1b, 0xd8, 0x11, 0x94, 0x8d,
|
||||
0x87, 0x28, 0xc3, 0xa0, 0xc5, 0x57, 0x2d, 0xc3, 0xa0, 0x9c, 0xb7, 0x2b, 0x6e, 0x50, 0xd2, 0x6e,
|
||||
0x36, 0x0c, 0x5a, 0x68, 0xb3, 0x1b, 0x06, 0xe5, 0xf4, 0xa7, 0x7b, 0x00, 0x49, 0x1b, 0xcc, 0x18,
|
||||
0x66, 0xa1, 0x61, 0x66, 0x0c, 0x93, 0xd3, 0x37, 0x3b, 0x51, 0x7d, 0x80, 0xa4, 0xc2, 0x7a, 0xb0,
|
||||
0xac, 0x82, 0x92, 0xa3, 0x7d, 0xf4, 0xed, 0x05, 0x16, 0x1a, 0x40, 0x35, 0x55, 0x6d, 0x18, 0xe3,
|
||||
0xe5, 0x95, 0x27, 0xc6, 0x78, 0xf9, 0x45, 0xca, 0x00, 0xaa, 0x82, 0x78, 0xea, 0x93, 0x59, 0x74,
|
||||
0x19, 0x30, 0x63, 0xbc, 0x14, 0x7d, 0x71, 0xbc, 0x0c, 0x5b, 0x8d, 0xf7, 0x53, 0xa8, 0x7e, 0x45,
|
||||
0xd9, 0x71, 0x82, 0xf6, 0x92, 0xbf, 0xf2, 0xa4, 0x1a, 0x0f, 0xed, 0x7b, 0x0b, 0x74, 0x35, 0xc2,
|
||||
0x1f, 0xc0, 0xba, 0x6c, 0x9d, 0x18, 0xaa, 0xa9, 0xb6, 0x8c, 0xa1, 0x9a, 0xe9, 0xb1, 0x1c, 0x42,
|
||||
0x39, 0x01, 0xa5, 0xe6, 0x69, 0x5c, 0xa8, 0x87, 0x8c, 0x5d, 0xcb, 0xa9, 0x79, 0x78, 0x1c, 0x9b,
|
||||
0xde, 0x45, 0x68, 0x89, 0xdb, 0x51, 0x4e, 0x1c, 0x67, 0xf8, 0x6a, 0xc8, 0x03, 0xa8, 0x98, 0x48,
|
||||
0xce, 0xc8, 0x33, 0x39, 0x00, 0xaf, 0x9d, 0x78, 0x9e, 0x46, 0x55, 0x3f, 0xe3, 0xd9, 0x20, 0x85,
|
||||
0x9e, 0x52, 0xd9, 0x20, 0x0f, 0x57, 0x2d, 0x1d, 0xcb, 0x85, 0xed, 0x3c, 0x80, 0x84, 0x7e, 0x68,
|
||||
0xf4, 0x74, 0x97, 0xc2, 0xae, 0xf6, 0x93, 0xf7, 0x48, 0x49, 0xb7, 0xf7, 0x3f, 0xfe, 0xc5, 0x93,
|
||||
0x0b, 0x8f, 0x5d, 0xce, 0x47, 0x2f, 0xdc, 0x60, 0xfa, 0x72, 0xc2, 0x2b, 0x10, 0xdf, 0xf3, 0x2f,
|
||||
0x26, 0x64, 0x14, 0xbd, 0xe4, 0x03, 0xbc, 0x54, 0xa3, 0x8c, 0xd6, 0xc5, 0x1f, 0x34, 0x7f, 0xef,
|
||||
0xff, 0x02, 0x00, 0x00, 0xff, 0xff, 0xc1, 0x8e, 0xa8, 0x20, 0xd9, 0x29, 0x00, 0x00,
|
||||
// 3728 bytes of a gzipped FileDescriptorProto
|
||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xc4, 0x3a, 0x4d, 0x73, 0x23, 0x49,
|
||||
0x56, 0x2e, 0xc9, 0x5f, 0x7a, 0xfa, 0x74, 0xca, 0x76, 0x6b, 0xd4, 0xdf, 0xb5, 0xdb, 0x3b, 0xdd,
|
||||
0x3d, 0x43, 0xf7, 0x62, 0xb6, 0x9b, 0x61, 0x18, 0xd8, 0x95, 0x65, 0x79, 0xac, 0x1d, 0xb7, 0xac,
|
||||
0x49, 0xbb, 0x7b, 0x97, 0x0d, 0x22, 0x2a, 0x52, 0xa5, 0xb4, 0x5d, 0x6b, 0xa9, 0x4a, 0x54, 0xa5,
|
||||
0x6c, 0xf7, 0x81, 0x03, 0xc1, 0x46, 0x70, 0x84, 0x58, 0x02, 0xae, 0xdc, 0xf8, 0x11, 0x10, 0xc1,
|
||||
0x81, 0x13, 0x11, 0xf0, 0x17, 0x88, 0xe0, 0xcc, 0x2f, 0xe0, 0x48, 0xe4, 0x57, 0x55, 0x56, 0xa9,
|
||||
0x34, 0x3d, 0x33, 0x1c, 0xb8, 0xa9, 0xde, 0x47, 0xe6, 0x7b, 0x2f, 0xdf, 0x7b, 0xf9, 0xde, 0x4b,
|
||||
0x41, 0x85, 0x85, 0x64, 0x4c, 0xc3, 0x17, 0xb3, 0x30, 0x60, 0x01, 0xda, 0x98, 0x05, 0xc1, 0x24,
|
||||
0x9c, 0xb9, 0xed, 0x07, 0x64, 0xee, 0x32, 0x2f, 0xf0, 0x29, 0x0d, 0xc3, 0x99, 0xfb, 0x32, 0xf9,
|
||||
0x92, 0x84, 0xf6, 0x7f, 0x5b, 0x80, 0xfa, 0xbe, 0xc7, 0x3a, 0xae, 0x1b, 0xcc, 0x7d, 0x86, 0xe9,
|
||||
0x9f, 0xcd, 0x69, 0xc4, 0xd0, 0x0f, 0xa0, 0x4a, 0x24, 0xc4, 0xb9, 0x26, 0x93, 0x39, 0x6d, 0x59,
|
||||
0x8f, 0xac, 0xa7, 0xab, 0xb8, 0xa2, 0x80, 0xef, 0x38, 0x0c, 0x3d, 0x83, 0x3a, 0x19, 0x45, 0xc1,
|
||||
0x64, 0xce, 0xa8, 0x73, 0x49, 0xbd, 0x8b, 0x4b, 0xd6, 0x2a, 0x3c, 0xb2, 0x9e, 0x56, 0x8f, 0x56,
|
||||
0x70, 0x4d, 0x23, 0x8e, 0x04, 0x9c, 0x93, 0x86, 0x74, 0x42, 0x98, 0x77, 0x1d, 0x93, 0x16, 0x35,
|
||||
0xa9, 0x46, 0x28, 0xd2, 0xc7, 0x50, 0x76, 0x03, 0xff, 0xdc, 0x61, 0x24, 0xbc, 0xa0, 0xac, 0xb5,
|
||||
0x2a, 0xc8, 0x2c, 0x0c, 0x1c, 0x78, 0x26, 0x60, 0xe8, 0x1e, 0x94, 0x3c, 0xdf, 0x63, 0x1e, 0x61,
|
||||
0x41, 0xd8, 0x5a, 0x7b, 0x64, 0x3d, 0x2d, 0xe1, 0x04, 0xb0, 0xdf, 0x80, 0x9a, 0x96, 0x9d, 0xde,
|
||||
0xce, 0xbc, 0xf0, 0xfd, 0xfe, 0x3a, 0xac, 0x9e, 0x53, 0x1a, 0xd9, 0x14, 0x9a, 0x5f, 0xcf, 0x03,
|
||||
0x46, 0xbf, 0x8f, 0xb2, 0x19, 0xb1, 0xb4, 0xa2, 0x86, 0x58, 0xf1, 0x36, 0x37, 0xb0, 0x9d, 0xde,
|
||||
0x26, 0x9a, 0x05, 0x7e, 0x44, 0xd1, 0xef, 0xc3, 0x47, 0x53, 0xcf, 0xa7, 0xa1, 0x73, 0x4e, 0xa9,
|
||||
0x13, 0x12, 0x46, 0x9d, 0x88, 0x30, 0x67, 0x46, 0x43, 0xe7, 0xea, 0x46, 0xed, 0xb9, 0x2d, 0x08,
|
||||
0x0e, 0x29, 0xc5, 0x84, 0xd1, 0x53, 0xc2, 0x86, 0x34, 0xfc, 0xea, 0x06, 0xfd, 0x08, 0xea, 0x09,
|
||||
0x23, 0x0b, 0x18, 0x99, 0x88, 0xfd, 0x57, 0x71, 0x55, 0x93, 0x9f, 0x71, 0xa0, 0xfd, 0x1a, 0x9a,
|
||||
0xc7, 0x5e, 0xa4, 0xcf, 0x32, 0xd2, 0xfa, 0x3d, 0x84, 0x32, 0x71, 0x85, 0xe9, 0x03, 0x7f, 0xf2,
|
||||
0x5e, 0xec, 0xb4, 0x89, 0x41, 0x82, 0x4e, 0xfc, 0xc9, 0x7b, 0xfb, 0x00, 0xb6, 0xd3, 0x7c, 0x4a,
|
||||
0xe0, 0x4f, 0x61, 0x53, 0xd9, 0x20, 0x6a, 0x59, 0x8f, 0x8a, 0x4f, 0xcb, 0x7b, 0x8d, 0x17, 0xca,
|
||||
0xb1, 0x5e, 0x68, 0xe5, 0x62, 0x0a, 0xfb, 0xa7, 0xb0, 0x7e, 0x32, 0x67, 0xb3, 0x39, 0x43, 0x77,
|
||||
0xa1, 0x24, 0x0c, 0xc9, 0xf5, 0x53, 0x8a, 0x6d, 0x0a, 0xc0, 0x29, 0x61, 0xa8, 0x05, 0x1b, 0x64,
|
||||
0x3c, 0x0e, 0x69, 0x14, 0x09, 0x25, 0x4a, 0x58, 0x7f, 0xda, 0xbf, 0xb1, 0xa0, 0x2a, 0x57, 0xf8,
|
||||
0x85, 0xc7, 0x2e, 0x0f, 0x29, 0x35, 0x69, 0xad, 0x14, 0xed, 0xb7, 0x38, 0x0e, 0xf4, 0x02, 0x9a,
|
||||
0x79, 0x86, 0xe6, 0x7e, 0xb7, 0x7a, 0xb4, 0x82, 0xeb, 0xe7, 0x69, 0x2b, 0xc7, 0xc7, 0xd7, 0x85,
|
||||
0x5d, 0x29, 0x45, 0xc4, 0xc5, 0xe8, 0x4f, 0x67, 0x13, 0xcf, 0xf5, 0x18, 0x17, 0xe7, 0x19, 0x6c,
|
||||
0x04, 0x12, 0xa3, 0xcc, 0x51, 0x8f, 0xcd, 0x21, 0x39, 0xb0, 0xc6, 0xdb, 0xff, 0x66, 0x41, 0xb3,
|
||||
0x3b, 0x09, 0xa2, 0xac, 0xaf, 0xdd, 0x07, 0x90, 0x81, 0xea, 0x5c, 0x51, 0x79, 0x14, 0x15, 0x5c,
|
||||
0x92, 0x90, 0xaf, 0xe8, 0x7b, 0xf4, 0x33, 0xa8, 0xcb, 0x15, 0x9c, 0x1b, 0x8f, 0x5d, 0xf2, 0xf3,
|
||||
0x16, 0xaa, 0x95, 0xf7, 0x76, 0x33, 0x3b, 0x29, 0x0b, 0x1d, 0xad, 0xe0, 0x6a, 0x90, 0x32, 0xd9,
|
||||
0x1f, 0x26, 0x32, 0x16, 0x05, 0xe7, 0xc3, 0x0c, 0x67, 0x56, 0xab, 0xa3, 0x95, 0x58, 0xea, 0xfd,
|
||||
0x26, 0x6c, 0x9d, 0xcf, 0xfd, 0x71, 0xe4, 0x8c, 0x69, 0xc4, 0x3c, 0x9f, 0xf0, 0x5c, 0x61, 0xbf,
|
||||
0x82, 0xed, 0xb4, 0x26, 0xca, 0x3b, 0xee, 0x03, 0xb8, 0x1c, 0xee, 0xb0, 0x5b, 0x6f, 0xac, 0x55,
|
||||
0x11, 0x90, 0xb3, 0x5b, 0x6f, 0x6c, 0xff, 0x8d, 0x05, 0xbb, 0x7c, 0xab, 0x71, 0x48, 0x6e, 0xbe,
|
||||
0x9b, 0x11, 0x0c, 0x33, 0x17, 0xbe, 0xd9, 0xcc, 0xe8, 0xd3, 0x6f, 0x38, 0xe3, 0x85, 0x13, 0xb6,
|
||||
0x7f, 0x0d, 0x77, 0x16, 0x24, 0x52, 0xca, 0x3c, 0x87, 0x0d, 0xe5, 0xc8, 0x42, 0x9e, 0x3c, 0x4f,
|
||||
0xd7, 0x04, 0x3c, 0x5f, 0xdc, 0xa8, 0x65, 0xa4, 0xee, 0x05, 0xa1, 0x41, 0x45, 0x03, 0x85, 0xfa,
|
||||
0x7f, 0x69, 0xc1, 0xce, 0x01, 0x9d, 0x05, 0xd1, 0x42, 0x6e, 0xfd, 0x80, 0xf6, 0xf7, 0x01, 0xc8,
|
||||
0x54, 0x24, 0x23, 0x1e, 0x3d, 0x32, 0xce, 0x4b, 0x12, 0xc2, 0xc3, 0xe7, 0xbb, 0x69, 0x7c, 0x01,
|
||||
0xbb, 0x59, 0x21, 0xbe, 0x87, 0xc2, 0x8f, 0xa1, 0x32, 0x96, 0xab, 0x98, 0xfa, 0x96, 0x15, 0x4c,
|
||||
0xa8, 0xfb, 0x1f, 0x16, 0x34, 0x31, 0xf5, 0x69, 0xf6, 0xa8, 0x45, 0xee, 0x91, 0xb9, 0x35, 0xd1,
|
||||
0x16, 0x14, 0x48, 0x1e, 0x76, 0x72, 0x89, 0xc8, 0x74, 0xbd, 0x78, 0x89, 0xf4, 0x04, 0x3c, 0x75,
|
||||
0x89, 0x28, 0xd2, 0x85, 0x4b, 0x44, 0x91, 0x2e, 0xb1, 0xd2, 0x6a, 0xae, 0x95, 0x16, 0x6f, 0x0c,
|
||||
0x9b, 0xc2, 0x76, 0x5a, 0x9b, 0xef, 0x67, 0xb5, 0x90, 0xaf, 0x41, 0x26, 0x29, 0xab, 0x29, 0x98,
|
||||
0xb0, 0xda, 0x18, 0x76, 0xf6, 0xe7, 0xd3, 0x99, 0x62, 0xe5, 0x69, 0xff, 0xdb, 0xf9, 0xc8, 0x12,
|
||||
0xf5, 0x0a, 0xf9, 0x4e, 0xd0, 0x82, 0xdd, 0xec, 0x2e, 0x52, 0x1d, 0xfb, 0xef, 0x0a, 0xb0, 0xa1,
|
||||
0xc0, 0x1f, 0xda, 0xf2, 0x77, 0x60, 0x93, 0x07, 0x5d, 0xe0, 0xf9, 0x4c, 0xa5, 0xa4, 0x2d, 0x33,
|
||||
0x2a, 0x87, 0x1c, 0x81, 0x63, 0x12, 0xb4, 0x0d, 0x6b, 0xf2, 0x2e, 0x95, 0x8e, 0x29, 0x3f, 0xd0,
|
||||
0x27, 0xb0, 0x45, 0xae, 0x89, 0x37, 0x21, 0xa3, 0x09, 0x75, 0x46, 0x64, 0x42, 0x7c, 0x97, 0xaa,
|
||||
0x43, 0x69, 0xc4, 0x88, 0x7d, 0x09, 0xe7, 0xc4, 0xe2, 0x34, 0x44, 0x16, 0xd2, 0x55, 0x03, 0xbf,
|
||||
0xed, 0xab, 0xb8, 0x91, 0x20, 0x54, 0xd5, 0xf0, 0x09, 0xac, 0x45, 0x8c, 0x30, 0xda, 0x5a, 0x7f,
|
||||
0x64, 0x3d, 0xad, 0xed, 0xed, 0x64, 0x8f, 0xe5, 0x94, 0x23, 0xb1, 0xa4, 0xe1, 0x4e, 0x39, 0x21,
|
||||
0x8c, 0x46, 0xca, 0x9d, 0x37, 0xa4, 0x53, 0x4a, 0x90, 0x38, 0x97, 0xbf, 0xb0, 0x00, 0x9d, 0xce,
|
||||
0x47, 0x53, 0x8f, 0x9d, 0x84, 0x63, 0x1a, 0xea, 0x53, 0x79, 0x04, 0x45, 0x12, 0x5d, 0xa9, 0x93,
|
||||
0xaf, 0x24, 0x5b, 0x44, 0x57, 0x47, 0x2b, 0x98, 0xa3, 0x38, 0xc5, 0x48, 0x1d, 0xb5, 0x49, 0xb1,
|
||||
0xef, 0x8d, 0x39, 0xc5, 0xc8, 0x1b, 0xa7, 0x6b, 0x97, 0x62, 0xb6, 0x76, 0x29, 0xc1, 0xc6, 0x98,
|
||||
0x32, 0xe2, 0x4d, 0x22, 0xfb, 0xb7, 0x16, 0x34, 0x53, 0x32, 0x28, 0x17, 0xfc, 0x02, 0xaa, 0x9e,
|
||||
0x7f, 0x4d, 0x26, 0xde, 0xd8, 0x09, 0x38, 0x42, 0x89, 0x93, 0x68, 0xdc, 0x97, 0x58, 0xc1, 0x75,
|
||||
0xb4, 0x82, 0x2b, 0x9e, 0xf1, 0x8d, 0xf6, 0x60, 0x9b, 0xb8, 0x2e, 0x9d, 0x31, 0xaa, 0xd8, 0x1d,
|
||||
0x3f, 0xe0, 0x87, 0x20, 0x9c, 0xf3, 0x68, 0x05, 0x23, 0x8d, 0x15, 0xe4, 0x03, 0x8e, 0x33, 0x85,
|
||||
0x1a, 0xc0, 0x16, 0xaf, 0x14, 0x04, 0x32, 0xae, 0x2f, 0x5a, 0xb0, 0x71, 0x4d, 0xc3, 0x51, 0x10,
|
||||
0x51, 0x55, 0x5b, 0xe8, 0xcf, 0x6c, 0xe5, 0x51, 0x58, 0xa8, 0x3c, 0x7e, 0x09, 0xc8, 0x5c, 0x4f,
|
||||
0xa9, 0xf8, 0x08, 0x56, 0x49, 0x74, 0xa5, 0x2f, 0xd9, 0x94, 0xa1, 0xb1, 0xc0, 0x70, 0x8a, 0x91,
|
||||
0x37, 0xd6, 0xf7, 0x43, 0xca, 0xd0, 0x58, 0x60, 0xec, 0x57, 0x80, 0xba, 0xdc, 0x8d, 0x26, 0xa9,
|
||||
0x13, 0x7c, 0x08, 0x65, 0x53, 0x6b, 0x95, 0x8e, 0x82, 0x58, 0x57, 0x7b, 0x07, 0x9a, 0x29, 0x36,
|
||||
0x15, 0x28, 0xff, 0x59, 0x84, 0x35, 0x69, 0xc0, 0x0f, 0x67, 0x6f, 0x11, 0x95, 0xe7, 0xde, 0x2d,
|
||||
0x95, 0x7e, 0x50, 0xc5, 0x25, 0x0e, 0x39, 0xe4, 0x00, 0xd4, 0x80, 0x22, 0x99, 0x32, 0x15, 0x14,
|
||||
0xfc, 0x27, 0xfa, 0x63, 0xb8, 0x3f, 0x25, 0xb7, 0xce, 0x88, 0x30, 0xf7, 0xd2, 0x59, 0x9e, 0xb3,
|
||||
0xee, 0x4c, 0xc9, 0xed, 0x3e, 0xa7, 0xc9, 0xd6, 0x86, 0x19, 0x8d, 0xd6, 0xb2, 0x1a, 0xa1, 0x67,
|
||||
0xe9, 0xc8, 0x68, 0x26, 0x51, 0xcb, 0x69, 0x52, 0x71, 0xb1, 0x0d, 0x6b, 0x73, 0xdf, 0x63, 0x91,
|
||||
0x88, 0x88, 0x2a, 0x96, 0x1f, 0x3c, 0x0e, 0xc5, 0x0f, 0x67, 0xee, 0x9f, 0xcf, 0x27, 0xe7, 0xde,
|
||||
0x64, 0x42, 0xc7, 0xad, 0x4d, 0x19, 0x87, 0x02, 0xf1, 0x36, 0x81, 0xa3, 0x4f, 0x01, 0x85, 0x34,
|
||||
0xa2, 0xe1, 0x35, 0x1d, 0x3b, 0x49, 0x0d, 0x58, 0x92, 0x21, 0xae, 0x31, 0xef, 0x74, 0x2d, 0xb8,
|
||||
0x07, 0x3b, 0x6e, 0x48, 0x65, 0x80, 0x33, 0x6f, 0x4a, 0x23, 0x46, 0xa6, 0x33, 0xc7, 0x8f, 0x5a,
|
||||
0x20, 0x18, 0x9a, 0x1a, 0x79, 0xa6, 0x71, 0x03, 0x2e, 0xce, 0x3a, 0xbd, 0xa6, 0xbc, 0x24, 0x2d,
|
||||
0x8b, 0xc3, 0xcf, 0x28, 0xd4, 0xe3, 0x38, 0xac, 0x48, 0x54, 0xe5, 0xec, 0x48, 0xf9, 0xa7, 0xdc,
|
||||
0x7e, 0xad, 0x8a, 0x90, 0x9c, 0x57, 0xce, 0x6f, 0x39, 0xf4, 0x0d, 0x07, 0xda, 0x7f, 0x55, 0x80,
|
||||
0xe2, 0xbe, 0x37, 0x46, 0x4f, 0x63, 0x57, 0x57, 0x61, 0x55, 0x4b, 0xaf, 0x8e, 0x35, 0x9a, 0x8b,
|
||||
0x3e, 0xa1, 0x24, 0xa2, 0xce, 0x78, 0xae, 0x32, 0xd4, 0x68, 0x12, 0xb8, 0x57, 0x91, 0x3a, 0xf3,
|
||||
0xa6, 0x40, 0x1e, 0x28, 0xdc, 0xbe, 0x40, 0xa9, 0x40, 0x89, 0xbc, 0xc0, 0x97, 0x17, 0x17, 0xd6,
|
||||
0x9f, 0xe8, 0x15, 0x70, 0x81, 0x1c, 0x3f, 0x18, 0x53, 0x87, 0x79, 0x34, 0x14, 0xa7, 0x5e, 0x33,
|
||||
0x52, 0xec, 0x20, 0x18, 0xd3, 0x33, 0x8f, 0x86, 0xb8, 0x3c, 0xf5, 0x7c, 0xfd, 0x81, 0x9e, 0xc3,
|
||||
0x56, 0x44, 0x27, 0xe7, 0x8e, 0x7b, 0x49, 0xfc, 0x38, 0x9f, 0xae, 0xc9, 0x5b, 0x80, 0x23, 0xba,
|
||||
0x97, 0xc4, 0xd7, 0xe9, 0xf4, 0x09, 0xd4, 0x22, 0x6f, 0x4c, 0x5d, 0x12, 0x3a, 0xcc, 0x73, 0xaf,
|
||||
0x28, 0x13, 0x0e, 0x51, 0xc2, 0x55, 0x05, 0x3d, 0x13, 0x40, 0xfb, 0xcf, 0xa1, 0xd8, 0x89, 0xae,
|
||||
0xfe, 0xbf, 0x0c, 0x61, 0xff, 0x97, 0x05, 0x5b, 0xa2, 0x79, 0x4a, 0x85, 0xad, 0x0a, 0x1b, 0x2b,
|
||||
0x09, 0x9b, 0x0f, 0xc4, 0xd9, 0x52, 0xa1, 0x8a, 0xcb, 0x85, 0xfa, 0xbf, 0x46, 0x62, 0x8e, 0xaf,
|
||||
0xad, 0xe5, 0xf9, 0xda, 0xff, 0x58, 0x80, 0x4c, 0x15, 0xe3, 0xd2, 0x62, 0x4b, 0xb4, 0x76, 0xce,
|
||||
0x2c, 0xa4, 0x53, 0x6f, 0x3e, 0x35, 0x9a, 0xa7, 0xba, 0x40, 0x0c, 0x25, 0x9c, 0xc7, 0xcd, 0x0f,
|
||||
0xa1, 0x26, 0x84, 0xe3, 0x82, 0x09, 0xc5, 0x84, 0x05, 0x2c, 0x5c, 0xe1, 0xd0, 0x21, 0x0d, 0x85,
|
||||
0x46, 0xa2, 0x00, 0x51, 0x54, 0x2e, 0xf5, 0x65, 0xd6, 0xb1, 0x70, 0x59, 0xd1, 0x70, 0x10, 0x7a,
|
||||
0x05, 0x77, 0xe4, 0xa6, 0xf4, 0x96, 0xba, 0x73, 0x61, 0x28, 0xae, 0x39, 0xdf, 0x5a, 0x6a, 0xbb,
|
||||
0x2d, 0xd0, 0x3d, 0x8d, 0x3d, 0xa4, 0x22, 0x6e, 0x5f, 0x43, 0xeb, 0x26, 0x08, 0x23, 0xe6, 0xb8,
|
||||
0xdc, 0xc6, 0xee, 0x25, 0xf1, 0x12, 0x3e, 0xe9, 0x7e, 0xdb, 0x02, 0xdf, 0x25, 0x11, 0xed, 0x72,
|
||||
0xac, 0xe4, 0xb3, 0xff, 0xd5, 0x02, 0x48, 0xa2, 0x94, 0x0b, 0x98, 0x8a, 0x7a, 0xae, 0x6d, 0x11,
|
||||
0x97, 0x99, 0x11, 0xed, 0x77, 0xa1, 0x24, 0x42, 0xd9, 0x89, 0x58, 0xa8, 0xfa, 0xc5, 0x4d, 0x01,
|
||||
0x38, 0x65, 0x21, 0xfa, 0x1c, 0x2a, 0x22, 0x71, 0x09, 0xff, 0xbf, 0xa0, 0xaa, 0xe1, 0x49, 0x6e,
|
||||
0xc2, 0xb7, 0xb3, 0x31, 0x61, 0x74, 0x2c, 0x36, 0x3b, 0x5a, 0xc1, 0x65, 0x41, 0xdc, 0x15, 0xb4,
|
||||
0xe8, 0x25, 0x6c, 0x88, 0x33, 0xa2, 0x63, 0xa1, 0xa9, 0x99, 0x47, 0xc4, 0x31, 0x69, 0x26, 0x4d,
|
||||
0xb5, 0xbf, 0x01, 0x6b, 0x62, 0x63, 0xfb, 0x1f, 0x2c, 0xa8, 0x98, 0x2b, 0xa3, 0xcf, 0xa1, 0x36,
|
||||
0x0b, 0xe9, 0xb5, 0x17, 0xcc, 0x23, 0x47, 0xa6, 0x5a, 0x6b, 0x79, 0xaa, 0xad, 0x6a, 0x52, 0xf1,
|
||||
0x89, 0x7e, 0x0c, 0x25, 0x9f, 0xde, 0x28, 0xb6, 0xc2, 0x72, 0xb6, 0x4d, 0x9f, 0xde, 0x48, 0x8e,
|
||||
0xc7, 0x50, 0x91, 0x2e, 0xa6, 0x32, 0xb1, 0xf4, 0xe8, 0xb2, 0x80, 0x1d, 0x0a, 0x90, 0xfd, 0xef,
|
||||
0x16, 0x40, 0xa2, 0x04, 0xfa, 0x09, 0x94, 0x85, 0x12, 0x4b, 0x84, 0x13, 0x94, 0x72, 0x17, 0x98,
|
||||
0xc6, 0xbf, 0x17, 0xf6, 0x29, 0x2c, 0xec, 0xc3, 0x1b, 0x21, 0x65, 0x1d, 0x55, 0x8a, 0x14, 0x65,
|
||||
0x23, 0xa4, 0x80, 0xf2, 0xc2, 0xfc, 0x29, 0x54, 0x43, 0xfa, 0x6b, 0xea, 0x32, 0x27, 0xa4, 0x24,
|
||||
0x0a, 0x7c, 0x95, 0xda, 0xda, 0xe9, 0xfd, 0xb1, 0x20, 0xc1, 0x82, 0x02, 0x57, 0x42, 0xe3, 0x8b,
|
||||
0x97, 0xaf, 0x98, 0xba, 0xc1, 0x35, 0x0d, 0x33, 0x83, 0x0d, 0xfb, 0x04, 0xee, 0x2c, 0x60, 0x54,
|
||||
0x34, 0xfd, 0x04, 0x76, 0xfd, 0xf9, 0xd4, 0x09, 0x25, 0x9a, 0x8e, 0x1d, 0x63, 0x90, 0xc1, 0xf5,
|
||||
0xd8, 0xf6, 0xe7, 0x53, 0xac, 0x91, 0x9a, 0xdb, 0x6e, 0xc2, 0x56, 0x47, 0x4e, 0xc8, 0x92, 0x5a,
|
||||
0xdc, 0x1e, 0x02, 0x32, 0x81, 0x6a, 0x83, 0xcf, 0xa1, 0x9a, 0x8a, 0x99, 0x85, 0x32, 0xcc, 0x8c,
|
||||
0x19, 0x5c, 0xa1, 0xc6, 0x97, 0xfd, 0x8f, 0x6b, 0xb0, 0x76, 0xcc, 0x33, 0x10, 0x7a, 0x0d, 0x55,
|
||||
0xee, 0xbb, 0x3e, 0x9d, 0x38, 0xb2, 0xb4, 0xb6, 0x96, 0x95, 0xd6, 0x15, 0x45, 0x27, 0xbe, 0x78,
|
||||
0xae, 0xd1, 0x7c, 0x64, 0x6a, 0x76, 0x8a, 0x7a, 0xb9, 0xce, 0x94, 0xc9, 0x40, 0xbd, 0xa3, 0xe9,
|
||||
0xf2, 0x33, 0xe1, 0x8e, 0x42, 0x67, 0x72, 0xe1, 0x8f, 0x61, 0x5b, 0xf3, 0xc9, 0x3c, 0xaa, 0xfa,
|
||||
0x2d, 0x31, 0x8d, 0xc3, 0x48, 0xe1, 0x84, 0x0e, 0xaa, 0xe3, 0x7a, 0x08, 0x65, 0x33, 0x71, 0xc9,
|
||||
0x2c, 0x00, 0xb3, 0x24, 0x67, 0x3d, 0xe7, 0xe5, 0x7c, 0x36, 0xc9, 0xac, 0xcb, 0xfc, 0x46, 0x33,
|
||||
0xf9, 0xc5, 0x16, 0x66, 0x31, 0x92, 0xca, 0x86, 0xa0, 0x2b, 0xbb, 0x49, 0x2e, 0x41, 0x2f, 0xa0,
|
||||
0xe9, 0x4e, 0x28, 0x09, 0x3d, 0xff, 0x42, 0x66, 0xea, 0x59, 0xe8, 0xb9, 0x54, 0x14, 0x26, 0xab,
|
||||
0x78, 0x4b, 0xa3, 0x78, 0x86, 0x1e, 0x72, 0x04, 0x7a, 0x0a, 0x0d, 0x59, 0x28, 0x89, 0x2b, 0x43,
|
||||
0xb0, 0xa8, 0xba, 0xa4, 0x26, 0xe0, 0xe2, 0xe2, 0xc0, 0xaa, 0x3d, 0x30, 0x4b, 0x2a, 0x58, 0x28,
|
||||
0xa9, 0xee, 0x41, 0x69, 0x36, 0x0f, 0xdd, 0x4b, 0x12, 0xd1, 0x71, 0xab, 0x2c, 0x8a, 0xda, 0x04,
|
||||
0xc0, 0x73, 0xaa, 0xb6, 0x5d, 0x48, 0xa7, 0x01, 0xa3, 0xf2, 0x5a, 0xe7, 0xe5, 0x62, 0x45, 0x2c,
|
||||
0xa5, 0x4d, 0x8b, 0x05, 0x96, 0x5f, 0xe6, 0xbc, 0x72, 0xfc, 0x23, 0xd8, 0xd2, 0x6c, 0x49, 0x19,
|
||||
0x50, 0x5d, 0x56, 0x06, 0xe8, 0xe3, 0xff, 0xe6, 0x52, 0xa0, 0x96, 0x5f, 0x0a, 0x7c, 0x0c, 0x75,
|
||||
0x5d, 0x0a, 0xa8, 0x65, 0x5a, 0x75, 0xa1, 0x85, 0xae, 0x10, 0xba, 0x12, 0x6a, 0x1f, 0x41, 0x55,
|
||||
0x9c, 0x71, 0x5c, 0xea, 0xdf, 0x85, 0x92, 0xbc, 0x1f, 0x79, 0xf1, 0xcd, 0xcb, 0xf3, 0x0a, 0xde,
|
||||
0x14, 0x80, 0xfe, 0x38, 0x42, 0x6d, 0x63, 0x5c, 0x58, 0x90, 0xb8, 0x78, 0x38, 0xf8, 0xf7, 0x16,
|
||||
0xd4, 0xf4, 0x52, 0x2a, 0x82, 0x7e, 0x04, 0xeb, 0xc2, 0xb7, 0x74, 0x9d, 0x9f, 0x54, 0x18, 0x82,
|
||||
0x10, 0x2b, 0x2c, 0x7a, 0x09, 0xf2, 0x12, 0x12, 0x9e, 0x4e, 0x49, 0xe8, 0xd3, 0xb1, 0xe1, 0xf0,
|
||||
0xf2, 0xd2, 0xec, 0x4c, 0x59, 0x4f, 0x60, 0xb8, 0x67, 0x7c, 0x02, 0x28, 0x61, 0x98, 0x11, 0x4f,
|
||||
0x92, 0x17, 0x8d, 0xab, 0xb4, 0x33, 0x65, 0x43, 0xe2, 0x71, 0x62, 0xbb, 0x0e, 0xd5, 0xb3, 0xe0,
|
||||
0x8a, 0xfa, 0x71, 0x52, 0xf9, 0x02, 0x6a, 0x1a, 0x10, 0xdf, 0xcc, 0xeb, 0x4c, 0x40, 0x94, 0xa0,
|
||||
0x28, 0x11, 0x34, 0x22, 0x4c, 0x10, 0x63, 0x45, 0x61, 0xff, 0x73, 0x01, 0x4a, 0x31, 0x94, 0x27,
|
||||
0xc8, 0x11, 0x8f, 0x9e, 0x29, 0x71, 0x49, 0x18, 0x04, 0xbe, 0xea, 0x17, 0x2a, 0x1c, 0xf8, 0x46,
|
||||
0xc1, 0x78, 0xa2, 0x9d, 0x91, 0xf7, 0x53, 0x7e, 0xc9, 0x5d, 0x92, 0xe8, 0x52, 0xcf, 0x09, 0x14,
|
||||
0xec, 0x88, 0x44, 0x97, 0xe8, 0x19, 0x34, 0x34, 0xc9, 0x2c, 0xa4, 0xde, 0x94, 0xa8, 0xcb, 0xae,
|
||||
0x82, 0xeb, 0x0a, 0x3e, 0x54, 0x60, 0xee, 0xe6, 0x6a, 0x7c, 0x24, 0x34, 0x9f, 0xea, 0xab, 0xbc,
|
||||
0x88, 0x6b, 0x12, 0xce, 0x15, 0x7f, 0x13, 0x11, 0x86, 0x7e, 0x17, 0x76, 0xc2, 0x60, 0xce, 0x78,
|
||||
0xfc, 0xf0, 0x30, 0x4b, 0xc8, 0xd7, 0x04, 0x39, 0x52, 0xc8, 0x43, 0x4a, 0x63, 0x16, 0x75, 0x61,
|
||||
0x3b, 0xa2, 0x2e, 0xa7, 0x63, 0x11, 0xbe, 0xea, 0xc2, 0xee, 0x4a, 0x10, 0x2f, 0xed, 0x44, 0xae,
|
||||
0xa0, 0xb2, 0xaf, 0xde, 0xc4, 0xfa, 0x93, 0x33, 0x47, 0x2c, 0x08, 0xc9, 0x05, 0x75, 0x7c, 0x32,
|
||||
0x95, 0x91, 0x5a, 0xe2, 0x97, 0xb2, 0x80, 0x0d, 0xc8, 0x94, 0xda, 0xbb, 0xb0, 0x7d, 0x6c, 0x56,
|
||||
0x66, 0xfa, 0x4c, 0x7e, 0x5b, 0x84, 0x9d, 0x0c, 0x42, 0x9d, 0x8d, 0x03, 0xf5, 0x74, 0xa1, 0xa7,
|
||||
0x0f, 0x69, 0x2f, 0xed, 0x4d, 0x59, 0xc6, 0x34, 0x34, 0xea, 0xf9, 0x2c, 0x7c, 0xbf, 0x5f, 0x68,
|
||||
0x59, 0xb8, 0x96, 0x2a, 0x0d, 0x23, 0xe4, 0xc3, 0x6e, 0xb6, 0x92, 0x9c, 0xf3, 0x42, 0x59, 0xf7,
|
||||
0x9e, 0x9f, 0x7d, 0x97, 0x7d, 0xf6, 0x25, 0xab, 0xd8, 0x0d, 0x6f, 0x4f, 0x72, 0x50, 0xed, 0x0e,
|
||||
0x34, 0x73, 0x44, 0xe3, 0x15, 0xb0, 0xee, 0x37, 0xab, 0x98, 0xff, 0x4c, 0x26, 0x2c, 0xb2, 0xab,
|
||||
0x96, 0x1f, 0x9f, 0x17, 0x3e, 0xb3, 0xda, 0x14, 0x3e, 0x5a, 0xba, 0x6b, 0xce, 0x42, 0x7b, 0xe6,
|
||||
0x42, 0xb5, 0xbd, 0x7b, 0xb1, 0x42, 0x69, 0x7e, 0xd5, 0x29, 0xc6, 0xdb, 0xf0, 0xc3, 0x1a, 0xd0,
|
||||
0x5b, 0x26, 0x6a, 0xe1, 0xbe, 0x7f, 0x1e, 0xe8, 0xc3, 0xfa, 0x6b, 0x0b, 0x76, 0x32, 0x08, 0x75,
|
||||
0x58, 0x0f, 0xd3, 0x43, 0x7b, 0x59, 0x1d, 0x9b, 0x23, 0xfb, 0x25, 0x73, 0xad, 0xf5, 0xdc, 0xb9,
|
||||
0x16, 0x4f, 0x63, 0x22, 0xcd, 0x27, 0xad, 0xa3, 0xba, 0x27, 0x6a, 0x02, 0x1c, 0x37, 0x8d, 0xf6,
|
||||
0x6b, 0xd8, 0xe2, 0x79, 0x12, 0x13, 0xee, 0xcf, 0x3a, 0x95, 0x3d, 0x86, 0x8a, 0xc8, 0xb3, 0xb3,
|
||||
0xf9, 0xe8, 0x8a, 0xbe, 0xd7, 0xd9, 0xac, 0xcc, 0x61, 0x43, 0x09, 0xb2, 0x8f, 0x01, 0x99, 0x7c,
|
||||
0x4a, 0x8b, 0xd7, 0x8a, 0x31, 0x14, 0x60, 0xed, 0x6f, 0xcd, 0x54, 0x8e, 0x56, 0x2c, 0x62, 0x35,
|
||||
0xf9, 0x3b, 0xb2, 0x1b, 0x50, 0xfb, 0x92, 0x32, 0xd3, 0x52, 0xbf, 0x59, 0x87, 0x7a, 0x0c, 0x52,
|
||||
0xab, 0x1b, 0xad, 0x91, 0x7a, 0xf2, 0xd0, 0x3d, 0xe2, 0x93, 0x78, 0x4a, 0x19, 0x19, 0x8f, 0x40,
|
||||
0x55, 0xac, 0x1f, 0xaf, 0x22, 0xf1, 0x08, 0xc4, 0xad, 0x12, 0x93, 0xc9, 0x49, 0x8b, 0xba, 0xea,
|
||||
0x63, 0xee, 0x8e, 0x80, 0xf2, 0xda, 0x20, 0x43, 0xe8, 0xe8, 0xc8, 0x95, 0xd7, 0xfc, 0x4e, 0x9a,
|
||||
0xa1, 0xa7, 0xe2, 0xf8, 0x13, 0xd8, 0x4a, 0xf8, 0x42, 0xf7, 0xd2, 0xbb, 0xa6, 0x63, 0x3d, 0x97,
|
||||
0x8b, 0x39, 0x14, 0x9c, 0x5b, 0x59, 0x5c, 0x9c, 0x5a, 0xe4, 0x75, 0x59, 0x45, 0x4a, 0x98, 0x14,
|
||||
0xf8, 0x07, 0x50, 0x55, 0x24, 0x4a, 0x5c, 0x39, 0x7d, 0x50, 0x7c, 0x4a, 0xd8, 0x8f, 0xa1, 0xae,
|
||||
0x89, 0xf4, 0x96, 0x72, 0x04, 0x51, 0x53, 0x64, 0x7a, 0x43, 0x5e, 0xb9, 0xcc, 0xc3, 0x90, 0xa7,
|
||||
0x4a, 0x51, 0xe8, 0xe8, 0xc1, 0x61, 0x49, 0x55, 0x2e, 0x12, 0x27, 0xca, 0x9c, 0xf8, 0x6d, 0xb2,
|
||||
0x21, 0xae, 0x30, 0x1a, 0x39, 0x9e, 0x7f, 0x1d, 0x4c, 0xf8, 0xda, 0x20, 0xa8, 0xeb, 0x0a, 0xde,
|
||||
0x57, 0x60, 0x5e, 0x49, 0x1b, 0x47, 0x2f, 0xae, 0xfe, 0x25, 0x27, 0x0f, 0xc9, 0xc9, 0x8b, 0x71,
|
||||
0x23, 0xf7, 0x66, 0x75, 0x89, 0xc8, 0x01, 0x04, 0x4c, 0xf4, 0x2d, 0x11, 0xa1, 0xcf, 0xa0, 0x15,
|
||||
0xcd, 0x47, 0x91, 0x1b, 0x7a, 0x23, 0x3a, 0x76, 0x58, 0xe0, 0x24, 0xcf, 0xb4, 0xa2, 0x02, 0xd8,
|
||||
0xc4, 0xbb, 0x09, 0xfe, 0x2c, 0xe8, 0xc4, 0x58, 0xde, 0x08, 0xf2, 0xf6, 0x81, 0x6f, 0x16, 0xc9,
|
||||
0x19, 0x5b, 0x4d, 0xd0, 0x57, 0x7c, 0x7a, 0xc3, 0xa5, 0x89, 0x4e, 0xfc, 0xc9, 0x7b, 0xd4, 0xe7,
|
||||
0x0d, 0x40, 0x78, 0x45, 0x99, 0xe3, 0xf9, 0xe7, 0x41, 0xab, 0x2e, 0x1c, 0xf6, 0x69, 0x2c, 0x76,
|
||||
0xc6, 0x05, 0x5f, 0xbc, 0x11, 0xb4, 0x1c, 0x24, 0x13, 0x15, 0x4c, 0x63, 0x40, 0x1b, 0x43, 0x3d,
|
||||
0x83, 0xce, 0xc9, 0x28, 0xcf, 0xcc, 0x8c, 0x92, 0xee, 0xac, 0x34, 0xab, 0x99, 0x48, 0x9a, 0xb0,
|
||||
0x75, 0xca, 0x82, 0xd9, 0x01, 0xa1, 0xd3, 0x24, 0xe5, 0x6f, 0x03, 0x32, 0x81, 0x6a, 0x0e, 0xf7,
|
||||
0xa7, 0xd0, 0x3c, 0x39, 0x3f, 0xa7, 0xe1, 0xa9, 0xac, 0x53, 0x74, 0x2c, 0xf3, 0xd0, 0x98, 0xb3,
|
||||
0xc0, 0xf1, 0xe9, 0x45, 0xc0, 0x3c, 0xdd, 0xe4, 0x6c, 0xe2, 0x2a, 0x87, 0x0e, 0x34, 0x10, 0x3d,
|
||||
0x58, 0x3a, 0x9d, 0x15, 0xb3, 0x59, 0xfb, 0x63, 0xa8, 0x9e, 0x9a, 0xc3, 0x10, 0xb4, 0x0b, 0xeb,
|
||||
0x6a, 0x56, 0x22, 0x63, 0x51, 0x7d, 0xd9, 0xbf, 0xe0, 0x2d, 0xc9, 0x85, 0x17, 0xb1, 0x05, 0x49,
|
||||
0x96, 0x70, 0xe4, 0x48, 0x58, 0xc8, 0x91, 0xd0, 0x7e, 0x05, 0x77, 0x7b, 0xb7, 0x33, 0xea, 0xb2,
|
||||
0xd3, 0x54, 0x21, 0xf6, 0x81, 0xd5, 0xed, 0x07, 0x70, 0x2f, 0x9f, 0x4d, 0x9a, 0xed, 0xf9, 0xdf,
|
||||
0x5a, 0x50, 0x31, 0x07, 0xe1, 0xa8, 0x01, 0x95, 0x61, 0x6f, 0x70, 0xd0, 0x1f, 0x7c, 0xe9, 0x9c,
|
||||
0x0c, 0x7b, 0x83, 0xc6, 0x0a, 0x42, 0x50, 0xd3, 0x90, 0xb7, 0xc3, 0x83, 0xce, 0x59, 0xaf, 0x61,
|
||||
0xa1, 0x4d, 0x58, 0x15, 0xd8, 0x02, 0x2a, 0xc3, 0x46, 0xef, 0x97, 0xc3, 0x3e, 0xee, 0x1d, 0x34,
|
||||
0x8a, 0x26, 0x69, 0xf7, 0xf8, 0xe4, 0xb4, 0x77, 0xd0, 0x58, 0x45, 0x00, 0xeb, 0xea, 0xf7, 0x1a,
|
||||
0x6a, 0x42, 0x1d, 0xf7, 0xba, 0x27, 0xef, 0x7a, 0xf8, 0x4f, 0x9c, 0xc3, 0x4e, 0xff, 0xb8, 0x77,
|
||||
0xd0, 0x58, 0x47, 0x5b, 0x50, 0xd5, 0x4c, 0xfb, 0x9d, 0xb3, 0xee, 0x51, 0x63, 0xe3, 0xf9, 0x50,
|
||||
0x75, 0xa9, 0x52, 0xa4, 0x32, 0x6c, 0x0c, 0x71, 0x6f, 0xd8, 0xc1, 0xbd, 0xc6, 0x0a, 0xaa, 0xc0,
|
||||
0x66, 0xa7, 0xdb, 0xed, 0x0d, 0xcf, 0x7a, 0x07, 0x0d, 0x8b, 0x7f, 0xe1, 0xde, 0xcf, 0x7b, 0x5d,
|
||||
0xfe, 0x55, 0xe0, 0x5b, 0x9d, 0xf6, 0xbf, 0x1c, 0x08, 0x51, 0xaa, 0x50, 0x3a, 0xec, 0x0f, 0x3a,
|
||||
0xc7, 0xfd, 0x5f, 0x71, 0x29, 0x9e, 0xff, 0x8b, 0x05, 0x5b, 0x0b, 0xed, 0x24, 0x57, 0x63, 0x70,
|
||||
0x32, 0xe0, 0xcb, 0xee, 0x02, 0x3a, 0xed, 0xe1, 0x77, 0x3d, 0xec, 0xbc, 0xe9, 0x9f, 0xee, 0xf7,
|
||||
0x8e, 0x3a, 0xef, 0xfa, 0x27, 0xb8, 0x61, 0xa1, 0x36, 0xec, 0x0a, 0xa1, 0x9c, 0x77, 0x3d, 0x7c,
|
||||
0xda, 0x3f, 0x19, 0x70, 0xf4, 0x1b, 0x21, 0x65, 0x01, 0xdd, 0x87, 0x8f, 0x86, 0x1d, 0x7c, 0xd6,
|
||||
0xef, 0x1c, 0x3b, 0x52, 0x08, 0xa7, 0x7b, 0x72, 0x7c, 0xdc, 0x39, 0xeb, 0xe1, 0xce, 0x71, 0xa3,
|
||||
0x88, 0x1e, 0xc3, 0xfd, 0x0c, 0xfa, 0xe0, 0xed, 0xf0, 0xb8, 0xdf, 0xed, 0x9c, 0xf5, 0x9c, 0x61,
|
||||
0xaf, 0x87, 0x1b, 0xab, 0xe8, 0x19, 0x3c, 0xc9, 0xae, 0x70, 0xd4, 0x19, 0x0c, 0x7a, 0xc7, 0xce,
|
||||
0xe1, 0x5b, 0x69, 0x11, 0x65, 0xa5, 0xb5, 0xbd, 0x7f, 0xaa, 0xc3, 0xfa, 0x99, 0x18, 0x26, 0xa3,
|
||||
0x2f, 0x60, 0x43, 0x05, 0x26, 0xba, 0xb3, 0x18, 0xaa, 0xc2, 0x1f, 0xda, 0xad, 0x65, 0x31, 0x8c,
|
||||
0x7a, 0x00, 0x49, 0xf8, 0xa0, 0xa4, 0xd9, 0x5e, 0x08, 0xb4, 0xf6, 0xdd, 0x5c, 0x9c, 0x5a, 0xe6,
|
||||
0x2b, 0xa8, 0x98, 0x7f, 0x65, 0x40, 0x49, 0x71, 0x90, 0xf3, 0x47, 0x8a, 0xf6, 0xfd, 0x25, 0xd8,
|
||||
0xf8, 0xe5, 0xa2, 0x6c, 0xfc, 0xd5, 0x04, 0xdd, 0x35, 0x5e, 0x2c, 0xb2, 0x8f, 0xa4, 0xed, 0x85,
|
||||
0x77, 0x35, 0x2e, 0x8a, 0xf9, 0x27, 0x05, 0x43, 0x94, 0x9c, 0xff, 0x3c, 0x18, 0xa2, 0xe4, 0xfe,
|
||||
0xb3, 0xe1, 0x2b, 0xa8, 0x98, 0x6f, 0xda, 0xc6, 0x62, 0x39, 0x8f, 0xf6, 0xc6, 0x62, 0xb9, 0x0f,
|
||||
0xe1, 0x67, 0x50, 0xcf, 0x3c, 0x2b, 0xa3, 0xe4, 0xd1, 0x3d, 0xff, 0x09, 0xbc, 0xfd, 0x68, 0x39,
|
||||
0x81, 0x5a, 0xf5, 0x6b, 0xa8, 0xa5, 0x9f, 0x6e, 0xd1, 0x83, 0xa4, 0x32, 0xcb, 0x7b, 0x58, 0x6e,
|
||||
0x3f, 0x5c, 0x8a, 0x4f, 0xb4, 0x36, 0x5f, 0x35, 0x0d, 0xad, 0x73, 0x9e, 0x6e, 0x0d, 0xad, 0x73,
|
||||
0x9f, 0x42, 0xbf, 0x86, 0x5a, 0xfa, 0x55, 0xd1, 0x90, 0x2f, 0xf7, 0x51, 0xd3, 0x90, 0x2f, 0xff,
|
||||
0x39, 0x92, 0x1b, 0x32, 0x33, 0xcf, 0x31, 0x0c, 0x99, 0x3f, 0x03, 0x32, 0x0c, 0xb9, 0x6c, 0x14,
|
||||
0x74, 0x04, 0x65, 0xe3, 0x1d, 0xcd, 0x70, 0xbb, 0xc5, 0x17, 0xbe, 0xf6, 0xbd, 0x7c, 0x64, 0x12,
|
||||
0x54, 0xc9, 0x6b, 0x95, 0x11, 0x54, 0x0b, 0x4f, 0x62, 0x46, 0x50, 0xe5, 0x3c, 0x6f, 0x1d, 0x41,
|
||||
0xd9, 0x78, 0x63, 0x32, 0x04, 0x5a, 0x7c, 0xb0, 0x32, 0x04, 0xca, 0x79, 0x96, 0xe2, 0x02, 0x25,
|
||||
0x93, 0x64, 0x43, 0xa0, 0x85, 0x09, 0xba, 0x21, 0x50, 0xce, 0xe8, 0xb9, 0x07, 0x90, 0x4c, 0xb8,
|
||||
0x8c, 0x65, 0x16, 0x66, 0x61, 0xc6, 0x32, 0x39, 0x23, 0xb1, 0x13, 0xd5, 0xe2, 0x27, 0xcd, 0xd3,
|
||||
0xfd, 0x65, 0xcd, 0x91, 0x5c, 0xed, 0xc1, 0x37, 0xf7, 0x4e, 0x68, 0x00, 0xd5, 0x54, 0x23, 0x61,
|
||||
0xac, 0x97, 0xd7, 0x79, 0x18, 0xeb, 0xe5, 0xf7, 0x1f, 0x03, 0xa8, 0x0a, 0xe0, 0xa9, 0x4f, 0x66,
|
||||
0xd1, 0x65, 0xc0, 0x8c, 0xf5, 0x52, 0xf0, 0xc5, 0xf5, 0x32, 0x68, 0xb5, 0xde, 0xcf, 0xa0, 0xfa,
|
||||
0x25, 0x65, 0xc7, 0x49, 0x21, 0x97, 0xfc, 0x4b, 0x27, 0x35, 0x53, 0x68, 0xdf, 0x59, 0x80, 0xab,
|
||||
0x15, 0xfe, 0x00, 0xd6, 0xe5, 0x54, 0xc4, 0x60, 0x4d, 0x4d, 0x5c, 0x0c, 0xd6, 0xcc, 0xf8, 0xe4,
|
||||
0x10, 0xca, 0x49, 0xbd, 0x69, 0x7a, 0xe3, 0x42, 0xab, 0x63, 0x9c, 0x5a, 0x4e, 0x3b, 0xc3, 0xe3,
|
||||
0xd8, 0xd4, 0x2e, 0x42, 0x4b, 0xd4, 0x8e, 0x72, 0xe2, 0x38, 0x83, 0x57, 0x4b, 0x1e, 0x40, 0xc5,
|
||||
0xac, 0xd2, 0x8c, 0x3c, 0x93, 0x53, 0xbc, 0xb5, 0x13, 0xcd, 0xd3, 0xc5, 0xd7, 0xcf, 0x79, 0x36,
|
||||
0x48, 0x15, 0x59, 0xa9, 0x6c, 0x90, 0x57, 0x7e, 0x2d, 0x5d, 0xcb, 0x85, 0xed, 0xbc, 0x02, 0x09,
|
||||
0xfd, 0xd0, 0x18, 0xd7, 0x2e, 0x2d, 0xbb, 0xda, 0x4f, 0x3e, 0x40, 0x25, 0xd5, 0xde, 0xff, 0xf8,
|
||||
0x57, 0x4f, 0x2e, 0x3c, 0x76, 0x39, 0x1f, 0xbd, 0x70, 0x83, 0xe9, 0xcb, 0x09, 0x6f, 0x2e, 0x7c,
|
||||
0xcf, 0xbf, 0x98, 0x90, 0x51, 0xf4, 0x92, 0x2f, 0xf0, 0x52, 0xad, 0x32, 0x5a, 0x17, 0xff, 0xbd,
|
||||
0xfc, 0xbd, 0xff, 0x0d, 0x00, 0x00, 0xff, 0xff, 0x67, 0x7e, 0x6a, 0x1b, 0xb4, 0x29, 0x00, 0x00,
|
||||
}
|
||||
|
||||
// Reference imports to suppress errors if they are not otherwise used.
|
||||
|
|
|
|||
|
|
@ -509,6 +509,40 @@ func local_request_Trader_CancelOrder_0(ctx context.Context, marshaler runtime.M
|
|||
|
||||
}
|
||||
|
||||
func request_Trader_QuoteOrder_0(ctx context.Context, marshaler runtime.Marshaler, client TraderClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
|
||||
var protoReq QuoteOrderRequest
|
||||
var metadata runtime.ServerMetadata
|
||||
|
||||
newReader, berr := utilities.IOReaderFactory(req.Body)
|
||||
if berr != nil {
|
||||
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr)
|
||||
}
|
||||
if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF {
|
||||
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
|
||||
}
|
||||
|
||||
msg, err := client.QuoteOrder(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
|
||||
return msg, metadata, err
|
||||
|
||||
}
|
||||
|
||||
func local_request_Trader_QuoteOrder_0(ctx context.Context, marshaler runtime.Marshaler, server TraderServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
|
||||
var protoReq QuoteOrderRequest
|
||||
var metadata runtime.ServerMetadata
|
||||
|
||||
newReader, berr := utilities.IOReaderFactory(req.Body)
|
||||
if berr != nil {
|
||||
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr)
|
||||
}
|
||||
if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF {
|
||||
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
|
||||
}
|
||||
|
||||
msg, err := server.QuoteOrder(ctx, &protoReq)
|
||||
return msg, metadata, err
|
||||
|
||||
}
|
||||
|
||||
func request_Trader_AuctionFee_0(ctx context.Context, marshaler runtime.Marshaler, client TraderClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
|
||||
var protoReq AuctionFeeRequest
|
||||
var metadata runtime.ServerMetadata
|
||||
|
|
@ -1245,6 +1279,26 @@ func RegisterTraderHandlerServer(ctx context.Context, mux *runtime.ServeMux, ser
|
|||
|
||||
})
|
||||
|
||||
mux.Handle("POST", pattern_Trader_QuoteOrder_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
|
||||
ctx, cancel := context.WithCancel(req.Context())
|
||||
defer cancel()
|
||||
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
|
||||
rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req)
|
||||
if err != nil {
|
||||
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
|
||||
return
|
||||
}
|
||||
resp, md, err := local_request_Trader_QuoteOrder_0(rctx, inboundMarshaler, server, req, pathParams)
|
||||
ctx = runtime.NewServerMetadataContext(ctx, md)
|
||||
if err != nil {
|
||||
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
|
||||
return
|
||||
}
|
||||
|
||||
forward_Trader_QuoteOrder_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
|
||||
|
||||
})
|
||||
|
||||
mux.Handle("GET", pattern_Trader_AuctionFee_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
|
||||
ctx, cancel := context.WithCancel(req.Context())
|
||||
defer cancel()
|
||||
|
|
@ -1826,6 +1880,26 @@ func RegisterTraderHandlerClient(ctx context.Context, mux *runtime.ServeMux, cli
|
|||
|
||||
})
|
||||
|
||||
mux.Handle("POST", pattern_Trader_QuoteOrder_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
|
||||
ctx, cancel := context.WithCancel(req.Context())
|
||||
defer cancel()
|
||||
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
|
||||
rctx, err := runtime.AnnotateContext(ctx, mux, req)
|
||||
if err != nil {
|
||||
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
|
||||
return
|
||||
}
|
||||
resp, md, err := request_Trader_QuoteOrder_0(rctx, inboundMarshaler, client, req, pathParams)
|
||||
ctx = runtime.NewServerMetadataContext(ctx, md)
|
||||
if err != nil {
|
||||
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
|
||||
return
|
||||
}
|
||||
|
||||
forward_Trader_QuoteOrder_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
|
||||
|
||||
})
|
||||
|
||||
mux.Handle("GET", pattern_Trader_AuctionFee_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
|
||||
ctx, cancel := context.WithCancel(req.Context())
|
||||
defer cancel()
|
||||
|
|
@ -2118,6 +2192,8 @@ var (
|
|||
|
||||
pattern_Trader_CancelOrder_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 4, 1, 5, 3}, []string{"v1", "pool", "orders", "order_nonce"}, "", runtime.AssumeColonVerbOpt(true)))
|
||||
|
||||
pattern_Trader_QuoteOrder_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"v1", "pool", "orders", "quote"}, "", runtime.AssumeColonVerbOpt(true)))
|
||||
|
||||
pattern_Trader_AuctionFee_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"v1", "pool", "fee"}, "", runtime.AssumeColonVerbOpt(true)))
|
||||
|
||||
pattern_Trader_LeaseDurations_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"v1", "pool", "lease_durations"}, "", runtime.AssumeColonVerbOpt(true)))
|
||||
|
|
@ -2126,7 +2202,7 @@ var (
|
|||
|
||||
pattern_Trader_BatchSnapshot_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"v1", "pool", "batch", "snapshot"}, "", runtime.AssumeColonVerbOpt(true)))
|
||||
|
||||
pattern_Trader_GetLsatTokens_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"v1", "lsat", "tokens"}, "", runtime.AssumeColonVerbOpt(true)))
|
||||
pattern_Trader_GetLsatTokens_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"v1", "pool", "lsat", "tokens"}, "", runtime.AssumeColonVerbOpt(true)))
|
||||
|
||||
pattern_Trader_Leases_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"v1", "pool", "leases"}, "", runtime.AssumeColonVerbOpt(true)))
|
||||
|
||||
|
|
@ -2174,6 +2250,8 @@ var (
|
|||
|
||||
forward_Trader_CancelOrder_0 = runtime.ForwardResponseMessage
|
||||
|
||||
forward_Trader_QuoteOrder_0 = runtime.ForwardResponseMessage
|
||||
|
||||
forward_Trader_AuctionFee_0 = runtime.ForwardResponseMessage
|
||||
|
||||
forward_Trader_LeaseDurations_0 = runtime.ForwardResponseMessage
|
||||
|
|
|
|||
|
|
@ -1105,30 +1105,19 @@ message StopDaemonResponse {
|
|||
}
|
||||
|
||||
message OfferSidecarRequest {
|
||||
/*
|
||||
The total channel capacity in satoshis. This will be used for the bid
|
||||
order's amount and min channel/match size values.
|
||||
/*
|
||||
If false, then only the trader_key, unit, self_chan_balance, and
|
||||
lease_duration_blocks need to be set in the bid below. Otherwise, the
|
||||
fields as they're set when submitting a bid need to be filled in.
|
||||
*/
|
||||
uint64 channel_capacity_sat = 1;
|
||||
bool auto_negotiate = 1;
|
||||
|
||||
/*
|
||||
The number of satoshis that will be pushed to the recipient in the sidecar
|
||||
channel resulting from the bid order submitted by the offering trader
|
||||
(=provider). The initial outbound channel balance will be transferred from
|
||||
the provider's pool account (=taker account) to the maker's account to
|
||||
reimburse them for the balance they'll effectively be giving away. The
|
||||
reimbursement between the recipient of the sidecar channel and the provider
|
||||
(=taker) is _not_ part of the protocol and must happen out of band. The
|
||||
sidecar protocol simply deducts all fees for the sidecar channel (execution
|
||||
fee, lease premium, chain fees, push amount) from the taker's trading
|
||||
account.
|
||||
The bid template that will be used to populate the initial sidecar ticket
|
||||
as well as auto negotiate the remainig steps of the sidecar channel if
|
||||
needed.
|
||||
*/
|
||||
uint64 self_chan_balance = 2;
|
||||
|
||||
/*
|
||||
The number of blocks the resulting leased channel should be open for.
|
||||
*/
|
||||
uint32 lease_duration_blocks = 3;
|
||||
Bid bid = 2;
|
||||
}
|
||||
|
||||
message SidecarTicket {
|
||||
|
|
@ -1148,6 +1137,15 @@ message RegisterSidecarRequest {
|
|||
information to. The ticket must be in the state "offered".
|
||||
*/
|
||||
string ticket = 1;
|
||||
|
||||
/*
|
||||
If this value is True, then the daemon will attempt to finish negotiating
|
||||
the details of the sidecar channel automatically in the background. The
|
||||
progress of the ticket can be monitored using the SidecarState RPC. In
|
||||
addition, if this flag is set, then this method will _block_ until the
|
||||
sidecar negotiation either finishes or breaks down.
|
||||
*/
|
||||
bool auto_negotiate = 2;
|
||||
}
|
||||
|
||||
message ExpectSidecarChannelRequest {
|
||||
|
|
|
|||
|
|
@ -11,29 +11,6 @@
|
|||
"application/json"
|
||||
],
|
||||
"paths": {
|
||||
"/v1/lsat/tokens": {
|
||||
"get": {
|
||||
"summary": "pool: `listauth`\nGetLsatTokens returns all LSAT tokens the daemon ever paid for.",
|
||||
"operationId": "GetLsatTokens",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "A successful response.",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/poolrpcTokensResponse"
|
||||
}
|
||||
},
|
||||
"default": {
|
||||
"description": "An unexpected error response",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/gatewayruntimeError"
|
||||
}
|
||||
}
|
||||
},
|
||||
"tags": [
|
||||
"Trader"
|
||||
]
|
||||
}
|
||||
},
|
||||
"/v1/pool/accounts": {
|
||||
"get": {
|
||||
"summary": "pool: `accounts list`\nListAccounts returns a list of all accounts known to the trader daemon and\ntheir current state.",
|
||||
|
|
@ -647,6 +624,29 @@
|
|||
]
|
||||
}
|
||||
},
|
||||
"/v1/pool/lsat/tokens": {
|
||||
"get": {
|
||||
"summary": "pool: `listauth`\nGetLsatTokens returns all LSAT tokens the daemon ever paid for.",
|
||||
"operationId": "GetLsatTokens",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "A successful response.",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/poolrpcTokensResponse"
|
||||
}
|
||||
},
|
||||
"default": {
|
||||
"description": "An unexpected error response",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/gatewayruntimeError"
|
||||
}
|
||||
}
|
||||
},
|
||||
"tags": [
|
||||
"Trader"
|
||||
]
|
||||
}
|
||||
},
|
||||
"/v1/pool/node_ratings": {
|
||||
"get": {
|
||||
"summary": "pool: `auction ratings`\nReturns the Node Tier information for this target Lightning node, and other\nrelated ranking information.",
|
||||
|
|
@ -756,6 +756,39 @@
|
|||
]
|
||||
}
|
||||
},
|
||||
"/v1/pool/orders/quote": {
|
||||
"post": {
|
||||
"summary": "QuoteOrder calculates the premium, execution fees and max batch fee rate for\nan order based on the given order parameters.",
|
||||
"operationId": "QuoteOrder",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "A successful response.",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/poolrpcQuoteOrderResponse"
|
||||
}
|
||||
},
|
||||
"default": {
|
||||
"description": "An unexpected error response",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/gatewayruntimeError"
|
||||
}
|
||||
}
|
||||
},
|
||||
"parameters": [
|
||||
{
|
||||
"name": "body",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/poolrpcQuoteOrderRequest"
|
||||
}
|
||||
}
|
||||
],
|
||||
"tags": [
|
||||
"Trader"
|
||||
]
|
||||
}
|
||||
},
|
||||
"/v1/pool/orders/{order_nonce}": {
|
||||
"delete": {
|
||||
"summary": "pool: `orders cancel`\nCancelOrder cancels an active order with the auction server to remove it\nfrom future matching.",
|
||||
|
|
@ -1796,20 +1829,14 @@
|
|||
"poolrpcOfferSidecarRequest": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"channel_capacity_sat": {
|
||||
"type": "string",
|
||||
"format": "uint64",
|
||||
"description": "The total channel capacity in satoshis. This will be used for the bid\norder's amount and min channel/match size values."
|
||||
"auto_negotiate": {
|
||||
"type": "boolean",
|
||||
"format": "boolean",
|
||||
"description": "If false, then only the trader_key, unit, self_chan_balance, and\nlease_duration_blocks need to be set in the bid below. Otherwise, the\nfields as they're set when submitting a bid need to be filled in."
|
||||
},
|
||||
"self_chan_balance": {
|
||||
"type": "string",
|
||||
"format": "uint64",
|
||||
"description": "The number of satoshis that will be pushed to the recipient in the sidecar\nchannel resulting from the bid order submitted by the offering trader\n(=provider). The initial outbound channel balance will be transferred from\nthe provider's pool account (=taker account) to the maker's account to\nreimburse them for the balance they'll effectively be giving away. The\nreimbursement between the recipient of the sidecar channel and the provider\n(=taker) is _not_ part of the protocol and must happen out of band. The\nsidecar protocol simply deducts all fees for the sidecar channel (execution\nfee, lease premium, chain fees, push amount) from the taker's trading\naccount."
|
||||
},
|
||||
"lease_duration_blocks": {
|
||||
"type": "integer",
|
||||
"format": "int64",
|
||||
"description": "The number of blocks the resulting leased channel should be open for."
|
||||
"bid": {
|
||||
"$ref": "#/definitions/poolrpcBid",
|
||||
"description": "The bid template that will be used to populate the initial sidecar ticket\nas well as auto negotiate the remainig steps of the sidecar channel if\nneeded."
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
@ -2000,6 +2027,36 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"poolrpcQuoteOrderRequest": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"amt": {
|
||||
"type": "string",
|
||||
"format": "uint64",
|
||||
"description": "Order amount in satoshis."
|
||||
},
|
||||
"rate_fixed": {
|
||||
"type": "integer",
|
||||
"format": "int64",
|
||||
"description": "Fixed order rate in parts per billion."
|
||||
},
|
||||
"lease_duration_blocks": {
|
||||
"type": "integer",
|
||||
"format": "int64",
|
||||
"description": "Required number of blocks that a channel opened as a result of this bid\nshould be kept open."
|
||||
},
|
||||
"max_batch_fee_rate_sat_per_kw": {
|
||||
"type": "string",
|
||||
"format": "uint64",
|
||||
"description": "Maximum fee rate the trader is willing to pay for the batch transaction,\nexpressed in satoshis per 1000 weight units (sat/KW)."
|
||||
},
|
||||
"min_units_match": {
|
||||
"type": "integer",
|
||||
"format": "int64",
|
||||
"description": "The minimum number of order units that must be matched per order pair."
|
||||
}
|
||||
}
|
||||
},
|
||||
"poolrpcQuoteOrderResponse": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
|
|
@ -2049,6 +2106,11 @@
|
|||
"ticket": {
|
||||
"type": "string",
|
||||
"description": "The sidecar ticket to register and add the node and channel funding\ninformation to. The ticket must be in the state \"offered\"."
|
||||
},
|
||||
"auto_negotiate": {
|
||||
"type": "boolean",
|
||||
"format": "boolean",
|
||||
"description": "If this value is True, then the daemon will attempt to finish negotiating\nthe details of the sidecar channel automatically in the background. The\nprogress of the ticket can be monitored using the SidecarState RPC. In\naddition, if this flag is set, then this method will _block_ until the\nsidecar negotiation either finishes or breaks down."
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
|
|||
295
rpcserver.go
295
rpcserver.go
|
|
@ -1067,6 +1067,89 @@ func (s *rpcServer) RecoverAccounts(ctx context.Context,
|
|||
}, nil
|
||||
}
|
||||
|
||||
// assertAccountReady enrues that an account is in the "ready" state that
|
||||
// allows it to submit orders.We'll only allow orders for accounts that present
|
||||
// in an open state, or have a pending update or batch. On the server-side if
|
||||
// we have a pending update we won't be matched, but this lets us place our
|
||||
// orders early so we can join the earliest available batch.
|
||||
func assertAccountReady(acct *account.Account) error {
|
||||
switch acct.State {
|
||||
case account.StateOpen, account.StatePendingUpdate,
|
||||
account.StatePendingBatch:
|
||||
|
||||
return nil
|
||||
|
||||
default:
|
||||
return fmt.Errorf("acct=%x is in state %v, cannot "+
|
||||
"make order",
|
||||
acct.TraderKey.PubKey.SerializeCompressed(), acct.State)
|
||||
}
|
||||
}
|
||||
|
||||
// validateOrder validates the order to ensure that all fields are consistent,
|
||||
// and the order is likely to be accepted by the auctioneer. If this method
|
||||
// returns nil, then the order is safe to submit to the auctioneer.
|
||||
func (s *rpcServer) validateOrder(order order.Order, acct *account.Account,
|
||||
auctionTerms *terms.AuctioneerTerms) error {
|
||||
|
||||
// Now that we now how large the order is, ensure that if it's a
|
||||
// wumbo-sized order, then the backing lnd node is advertising wumbo
|
||||
// support.
|
||||
if order.Details().Amt > lndFunding.MaxBtcFundingAmount && !s.wumboSupported {
|
||||
return fmt.Errorf("%v is wumbo sized, but "+
|
||||
"lnd node isn't signalling wumbo", order.Details().Amt)
|
||||
}
|
||||
|
||||
// Ensure that the account can actually submit orders in its present
|
||||
// state.
|
||||
if err := assertAccountReady(acct); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// If the market isn't currently accepting orders for this particular
|
||||
// lease duration, then we'll exit here as the order will be rejected.
|
||||
leaseDuration := order.Details().LeaseDuration
|
||||
if _, ok := auctionTerms.LeaseDurationBuckets[leaseDuration]; !ok {
|
||||
return fmt.Errorf("invalid channel lease duration %v "+
|
||||
"blocks, active durations are: %v",
|
||||
leaseDuration, auctionTerms.LeaseDurationBuckets)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// orderPreparer represents a type of function that inserts the order into the
|
||||
// local database, and returns the params needed to submit it to the
|
||||
// auctioneer.
|
||||
type orderPreparer func(context.Context, order.Order,
|
||||
*account.Account, *terms.AuctioneerTerms) (*order.ServerOrderParams, error)
|
||||
|
||||
// prepareAndSubmitOrder performs a series of final checks locally to ensure
|
||||
// the order is valid, before submitting it to the auctioneer.
|
||||
func prepareAndSubmitOrder(ctx context.Context, o order.Order,
|
||||
auctionTerms *terms.AuctioneerTerms, acct *account.Account,
|
||||
auction *auctioneer.Client, prepareOrder orderPreparer) error {
|
||||
|
||||
// Collect all the order data and sign it before sending it to the
|
||||
// auction server.
|
||||
serverParams, err := prepareOrder(
|
||||
ctx, o, acct, auctionTerms,
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Send the order to the server. If this fails, then the order is
|
||||
// certain to never get into the order book. We don't need to keep it
|
||||
// around in that case.
|
||||
//
|
||||
// TODO(roasbeef): commit initiator to disk so don't lose when
|
||||
// submitting orders for sidecar channels?
|
||||
return auction.SubmitOrder(
|
||||
ctx, o, serverParams,
|
||||
)
|
||||
}
|
||||
|
||||
// SubmitOrder assembles all the information that is required to submit an order
|
||||
// from the trader's lnd node, signs it and then sends the order to the server
|
||||
// to be included in the auctioneer's order book.
|
||||
|
|
@ -1116,12 +1199,11 @@ func (s *rpcServer) SubmitOrder(ctx context.Context,
|
|||
return nil, fmt.Errorf("invalid order request")
|
||||
}
|
||||
|
||||
// Now that we now how large the order is, ensure that if it's a
|
||||
// wumbo-sized order, then the backing lnd node is advertising wumbo
|
||||
// support.
|
||||
if o.Details().Amt > lndFunding.MaxBtcFundingAmount && !s.wumboSupported {
|
||||
return nil, fmt.Errorf("order of %v is wumbo sized, but "+
|
||||
"lnd node isn't signalling wumbo", o.Details().Amt)
|
||||
// We also need to know the current maximum order duration.
|
||||
auctionTerms, err := s.auctioneer.Terms(ctx)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not query auctioneer terms: %v",
|
||||
err)
|
||||
}
|
||||
|
||||
// Verify that the account exists.
|
||||
|
|
@ -1136,49 +1218,17 @@ func (s *rpcServer) SubmitOrder(ctx context.Context,
|
|||
return nil, fmt.Errorf("cannot accept order: %v", err)
|
||||
}
|
||||
|
||||
// We'll only allow orders for accounts that present in an open state,
|
||||
// or have a pending update or batch. On the server-side if we have a
|
||||
// pending update we won't be matched, but this lets us place our orders
|
||||
// early so we can join the earliest available batch.
|
||||
switch acct.State {
|
||||
case account.StateOpen, account.StatePendingUpdate,
|
||||
account.StatePendingBatch:
|
||||
|
||||
break
|
||||
|
||||
default:
|
||||
return nil, fmt.Errorf("acct=%x is in state %v, cannot "+
|
||||
"make order", o.Details().AcctKey[:], acct.State)
|
||||
// Validate the order to ensure the account is in a live state, and the
|
||||
// target lease duration period actually exists.
|
||||
if err := s.validateOrder(o, acct, auctionTerms); err != nil {
|
||||
return nil, fmt.Errorf("order valid validation: %w", err)
|
||||
}
|
||||
|
||||
// We also need to know the current maximum order duration.
|
||||
auctionTerms, err := s.auctioneer.Terms(ctx)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not query auctioneer terms: %v",
|
||||
err)
|
||||
}
|
||||
|
||||
// If the market isn't currently accepting orders for this particular
|
||||
// lease duration, then we'll exit here as the order will be rejected.
|
||||
leaseDuration := o.Details().LeaseDuration
|
||||
if _, ok := auctionTerms.LeaseDurationBuckets[leaseDuration]; !ok {
|
||||
return nil, fmt.Errorf("invalid channel lease duration %v "+
|
||||
"blocks, active durations are: %v",
|
||||
leaseDuration, auctionTerms.LeaseDurationBuckets)
|
||||
}
|
||||
|
||||
// Collect all the order data and sign it before sending it to the
|
||||
// auction server.
|
||||
serverParams, err := s.orderManager.PrepareOrder(ctx, o, acct, auctionTerms)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Send the order to the server. If this fails, then the order is
|
||||
// certain to never get into the order book. We don't need to keep it
|
||||
// around in that case.
|
||||
err = s.auctioneer.SubmitOrder(
|
||||
ContextWithInitiator(ctx, req.Initiator), o, serverParams,
|
||||
// Finally add the order to the local order database, and submit it to
|
||||
// the auctioneer server.
|
||||
err = prepareAndSubmitOrder(
|
||||
ContextWithInitiator(ctx, req.Initiator), o, auctionTerms,
|
||||
acct, s.auctioneer, s.orderManager.PrepareOrder,
|
||||
)
|
||||
if err != nil {
|
||||
// The server rejected the order. We keep it around for now,
|
||||
|
|
@ -2202,26 +2252,108 @@ func (s *rpcServer) OfferSidecar(ctx context.Context,
|
|||
req *poolrpc.OfferSidecarRequest) (*poolrpc.SidecarTicket, error) {
|
||||
|
||||
// Do some basic sanity checks first.
|
||||
if req.ChannelCapacitySat == 0 {
|
||||
switch {
|
||||
case req.Bid == nil:
|
||||
return nil, fmt.Errorf("bid must be set")
|
||||
|
||||
case req.Bid.Details.Amt == 0:
|
||||
return nil, fmt.Errorf("channel capacity missing")
|
||||
}
|
||||
|
||||
// We'll need to look up the account state in the database to make sure
|
||||
// the account is actually still open (able to submit bids), and also
|
||||
// to grab the KeyDescriptor that we'll need for signing later.
|
||||
acctKey, err := btcec.ParsePubKey(
|
||||
req.Bid.Details.TraderKey, btcec.S256(),
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
acct, err := s.server.db.Account(acctKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// If automated negotiation was set, then we'll parse out the rest of
|
||||
// the bid now so we can validate that it'll pass all checks when we
|
||||
// eventually need to submit it.
|
||||
var bid *order.Bid
|
||||
if req.AutoNegotiate {
|
||||
kit, err := order.ParseRPCOrder(
|
||||
req.Bid.Version, req.Bid.LeaseDurationBlocks,
|
||||
req.Bid.Details,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
nodeTier, err := unmarshallNodeTier(req.Bid.MinNodeTier)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// We don't add the ticket here yet as we'll only add it at the
|
||||
// very end once the ticket has advanced to the final stage.
|
||||
bid = &order.Bid{
|
||||
Kit: *kit,
|
||||
MinNodeTier: nodeTier,
|
||||
SelfChanBalance: btcutil.Amount(req.Bid.SelfChanBalance),
|
||||
}
|
||||
|
||||
// Perform some initial validation on the order to ensure that
|
||||
// we'll be able to eventually submit it.
|
||||
auctionTerms, err := s.auctioneer.Terms(ctx)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to query auctioneer "+
|
||||
"terms: %v", err)
|
||||
}
|
||||
err = s.validateOrder(bid, acct, auctionTerms)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
// The funding manager does all the work, including signing and storing
|
||||
// the new ticket.
|
||||
ticket, err := s.server.fundingManager.OfferSidecar(
|
||||
ctx, btcutil.Amount(req.ChannelCapacitySat),
|
||||
btcutil.Amount(req.SelfChanBalance), req.LeaseDurationBlocks,
|
||||
ctx, btcutil.Amount(req.Bid.Details.Amt),
|
||||
btcutil.Amount(req.Bid.SelfChanBalance),
|
||||
req.Bid.LeaseDurationBlocks, acct.TraderKey, bid,
|
||||
req.AutoNegotiate,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var nonce order.Nonce
|
||||
if bid != nil {
|
||||
nonce = bid.Nonce()
|
||||
}
|
||||
|
||||
// If the bid has already been specified, then we can go ahead and set
|
||||
// it within the ticket.
|
||||
ticket.Order = &sidecar.Order{
|
||||
BidNonce: nonce,
|
||||
}
|
||||
|
||||
// If the ticket has requested automated negotiation, then we'll hand
|
||||
// it off to the coordinate tor now.
|
||||
if ticket.Offer.Auto {
|
||||
err := s.server.sidecarAcceptor.CoordinateSidecar(
|
||||
ticket, bid, acct,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
// We'll return a nice string encoded version of the ticket to the user.
|
||||
ticketStr, err := sidecar.EncodeToString(ticket)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &poolrpc.SidecarTicket{Ticket: ticketStr}, nil
|
||||
return &poolrpc.SidecarTicket{
|
||||
Ticket: ticketStr,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// RegisterSidecar is step 2/4 of the sidecar negotiation between the provider
|
||||
|
|
@ -2241,19 +2373,47 @@ func (s *rpcServer) RegisterSidecar(ctx context.Context,
|
|||
|
||||
// The sidecar acceptor will add all required information and add the
|
||||
// ticket to our DB.
|
||||
err = s.server.sidecarAcceptor.RegisterSidecar(ctx, ticket)
|
||||
registeredTicket, err := s.server.sidecarAcceptor.RegisterSidecar(
|
||||
ctx, *ticket,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// We'll return a nice string encoded version of the ticket to the user.
|
||||
ticketStr, err := sidecar.EncodeToString(ticket)
|
||||
// At this point, we'll now check if the ticket specifies that
|
||||
// automated negotiation is to be sued, if so then we'll hand things
|
||||
// off to the sidecar acceptor to finish the process.
|
||||
if registeredTicket.Offer.Auto {
|
||||
err := s.server.sidecarAcceptor.AutoAcceptSidecar(registeredTicket)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to start ticket auto "+
|
||||
"negotiation: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
ticketStr, err := sidecar.EncodeToString(registeredTicket)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &poolrpc.SidecarTicket{Ticket: ticketStr}, nil
|
||||
}
|
||||
|
||||
// expectSidecarChannel is a private version of ExpectSidecarChannel that may
|
||||
// be used in earlier steps if automated negotiation is requested.
|
||||
func (s *rpcServer) expectSidecarChannel(ctx context.Context,
|
||||
t *sidecar.Ticket) error {
|
||||
|
||||
err := validateOrderedTicket(ctx, t, s.lndServices.Signer, s.server.db)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Formally everything looks good so far. We can now pass the sidecar
|
||||
// order with the verified information to the acceptor and let it do its
|
||||
// job.
|
||||
return s.server.sidecarAcceptor.ExpectChannel(ctx, t)
|
||||
}
|
||||
|
||||
// ExpectSidecarChannel is step 4/4 of the sidecar negotiation between the
|
||||
// provider (the trader submitting the bid order) and the recipient (the trader
|
||||
// receiving the sidecar channel).
|
||||
|
|
@ -2271,31 +2431,8 @@ func (s *rpcServer) ExpectSidecarChannel(ctx context.Context,
|
|||
return nil, fmt.Errorf("error decoding ticket: %v", err)
|
||||
}
|
||||
|
||||
// Let's make sure the ticket itself and the offer is valid.
|
||||
if err := sidecar.VerifyOffer(ctx, t, s.lndServices.Signer); err != nil {
|
||||
return nil, fmt.Errorf("error validating order in sidecar "+
|
||||
"ticket: %v", err)
|
||||
}
|
||||
|
||||
// Make sure the order signature is valid and the ticket actually exists
|
||||
// in our database. We need to have it stored already since must've done
|
||||
// the register part before.
|
||||
if err := sidecar.VerifyOrder(ctx, t, s.lndServices.Signer); err != nil {
|
||||
return nil, fmt.Errorf("error validating order in sidecar "+
|
||||
"ticket: %v", err)
|
||||
}
|
||||
if _, err = s.server.db.Sidecar(t.ID, t.Offer.SignPubKey); err != nil {
|
||||
return nil, fmt.Errorf("error looking up sidecar order for "+
|
||||
"ticket with ID %x: %v", t.ID[:], err)
|
||||
}
|
||||
|
||||
// Formally everything looks good so far. We can now pass the sidecar
|
||||
// order with the verified information to the acceptor and let it do its
|
||||
// job.
|
||||
err = s.server.sidecarAcceptor.ExpectChannel(ctx, t)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error managing sidecar channel: %v",
|
||||
err)
|
||||
if err := s.expectSidecarChannel(ctx, t); err != nil {
|
||||
return nil, fmt.Errorf("unable to expect sidecar chan: %v", err)
|
||||
}
|
||||
|
||||
return &poolrpc.ExpectSidecarChannelResponse{}, nil
|
||||
|
|
|
|||
32
server.go
32
server.go
|
|
@ -18,11 +18,13 @@ import (
|
|||
proxy "github.com/grpc-ecosystem/grpc-gateway/runtime"
|
||||
"github.com/lightninglabs/aperture/lsat"
|
||||
"github.com/lightninglabs/lndclient"
|
||||
"github.com/lightninglabs/pool/account"
|
||||
"github.com/lightninglabs/pool/auctioneer"
|
||||
"github.com/lightninglabs/pool/clientdb"
|
||||
"github.com/lightninglabs/pool/funding"
|
||||
"github.com/lightninglabs/pool/order"
|
||||
"github.com/lightninglabs/pool/poolrpc"
|
||||
"github.com/lightninglabs/pool/terms"
|
||||
"github.com/lightningnetwork/lnd/lnrpc"
|
||||
"github.com/lightningnetwork/lnd/lnrpc/verrpc"
|
||||
"github.com/lightningnetwork/lnd/macaroons"
|
||||
|
|
@ -503,11 +505,31 @@ func (s *Server) setupClient() error {
|
|||
// create a copy of the auctioneer client configuration because the
|
||||
// acceptor is going to overwrite some of its values.
|
||||
clientCfgCopy := *clientCfg
|
||||
s.sidecarAcceptor = NewSidecarAcceptor(
|
||||
s.db, s.lndServices.Signer, s.lndServices.WalletKit,
|
||||
s.lndClient, channelAcceptor, nodePubKey, clientCfgCopy,
|
||||
s.fundingManager,
|
||||
)
|
||||
s.sidecarAcceptor = NewSidecarAcceptor(&SidecarAcceptorConfig{
|
||||
SidecarDB: s.db,
|
||||
AcctDB: &accountStore{DB: s.db},
|
||||
Signer: s.lndServices.Signer,
|
||||
Wallet: s.lndServices.WalletKit,
|
||||
BaseClient: s.lndClient,
|
||||
Acceptor: channelAcceptor,
|
||||
NodePubKey: nodePubKey,
|
||||
ClientCfg: clientCfgCopy,
|
||||
FundingManager: s.fundingManager,
|
||||
PrepareOrder: func(ctx context.Context,
|
||||
order order.Order,
|
||||
acct *account.Account,
|
||||
terms *terms.AuctioneerTerms) (*order.ServerOrderParams, error) {
|
||||
|
||||
// Rather than passing in the function directly, we use
|
||||
// an intermediate closure as this pointer won't
|
||||
// existing when we initialize this config, as the rpc
|
||||
// server is created _after_ we set up the client.
|
||||
return s.rpcServer.orderManager.PrepareOrder(
|
||||
ctx, order, acct, terms,
|
||||
)
|
||||
},
|
||||
FetchSidecarBid: s.db.SidecarBidTemplate,
|
||||
})
|
||||
|
||||
// Create an instance of the auctioneer client library.
|
||||
s.AuctioneerClient, err = auctioneer.NewClient(clientCfg)
|
||||
|
|
|
|||
|
|
@ -114,6 +114,10 @@ type Offer struct {
|
|||
// 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
|
||||
|
|
@ -196,7 +200,8 @@ type Ticket struct {
|
|||
// 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) (*Ticket, error) {
|
||||
duration uint32, offerPubKey *btcec.PublicKey,
|
||||
auto bool) (*Ticket, error) {
|
||||
|
||||
t := &Ticket{
|
||||
Version: version,
|
||||
|
|
@ -206,6 +211,7 @@ func NewTicket(version Version, capacity, pushAmt btcutil.Amount,
|
|||
PushAmt: pushAmt,
|
||||
LeaseDurationBlocks: duration,
|
||||
SignPubKey: offerPubKey,
|
||||
Auto: auto,
|
||||
},
|
||||
}
|
||||
|
||||
|
|
@ -226,7 +232,7 @@ func (t *Ticket) OfferDigest() ([32]byte, error) {
|
|||
case VersionDefault:
|
||||
err := lnwire.WriteElements(
|
||||
&msg, t.ID[:], uint8(t.Version), t.Offer.Capacity,
|
||||
t.Offer.PushAmt,
|
||||
t.Offer.PushAmt, t.Offer.Auto,
|
||||
)
|
||||
if err != nil {
|
||||
return result, err
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ const (
|
|||
leaseDurationType tlv.Type = 13
|
||||
signPubKeyType tlv.Type = 14
|
||||
sigOfferDigestType tlv.Type = 15
|
||||
offerAutoType tlv.Type = 16
|
||||
|
||||
recipientType tlv.Type = 20
|
||||
nodePubKeyType tlv.Type = 21
|
||||
|
|
@ -176,12 +177,18 @@ func serializeOffer(o Offer) ([]byte, error) {
|
|||
capacity := uint64(o.Capacity)
|
||||
pushAmt := uint64(o.PushAmt)
|
||||
|
||||
var autoAsInt uint8
|
||||
if o.Auto {
|
||||
autoAsInt = 1
|
||||
}
|
||||
|
||||
tlvRecords := []tlv.Record{
|
||||
tlv.MakePrimitiveRecord(capacityType, &capacity),
|
||||
tlv.MakePrimitiveRecord(pushAmtType, &pushAmt),
|
||||
tlv.MakePrimitiveRecord(
|
||||
leaseDurationType, &o.LeaseDurationBlocks,
|
||||
),
|
||||
tlv.MakePrimitiveRecord(offerAutoType, &autoAsInt),
|
||||
}
|
||||
|
||||
if o.SignPubKey != nil {
|
||||
|
|
@ -205,6 +212,7 @@ func deserializeOffer(offerBytes []byte) (Offer, error) {
|
|||
var (
|
||||
o = Offer{}
|
||||
capacity, pushAmt uint64
|
||||
autoAsInt uint8
|
||||
)
|
||||
|
||||
if err := decodeBytes(
|
||||
|
|
@ -218,10 +226,12 @@ func deserializeOffer(offerBytes []byte) (Offer, error) {
|
|||
tlv.MakeStaticRecord(
|
||||
sigOfferDigestType, &o.SigOfferDigest, 64, ESig, DSig,
|
||||
),
|
||||
tlv.MakePrimitiveRecord(offerAutoType, &autoAsInt),
|
||||
); err != nil {
|
||||
return o, err
|
||||
}
|
||||
|
||||
o.Auto = autoAsInt == 1
|
||||
o.Capacity = btcutil.Amount(capacity)
|
||||
o.PushAmt = btcutil.Amount(pushAmt)
|
||||
|
||||
|
|
@ -376,6 +386,8 @@ func DBytes8(r io.Reader, val interface{}, _ *[8]byte, l uint64) error {
|
|||
|
||||
// encodeBytes encodes the given tlv records into a byte slice.
|
||||
func encodeBytes(tlvRecords ...tlv.Record) ([]byte, error) {
|
||||
tlv.SortRecords(tlvRecords)
|
||||
|
||||
tlvStream, err := tlv.NewStream(tlvRecords...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
|
@ -392,6 +404,8 @@ func encodeBytes(tlvRecords ...tlv.Record) ([]byte, error) {
|
|||
// decodeBytes decodes the given byte slice interpreting the data as a tlv
|
||||
// stream containing the given records.
|
||||
func decodeBytes(tlvBytes []byte, tlvRecords ...tlv.Record) error {
|
||||
tlv.SortRecords(tlvRecords)
|
||||
|
||||
tlvStream, err := tlv.NewStream(tlvRecords...)
|
||||
if err != nil {
|
||||
return err
|
||||
|
|
|
|||
|
|
@ -77,6 +77,7 @@ func TestSerializeTicket(t *testing.T) {
|
|||
R: new(big.Int).SetInt64(22),
|
||||
S: new(big.Int).SetInt64(55),
|
||||
},
|
||||
Auto: true,
|
||||
},
|
||||
Recipient: &Recipient{
|
||||
NodePubKey: testPubKey,
|
||||
|
|
|
|||
|
|
@ -146,6 +146,7 @@ func TestVerifyOffer(t *testing.T) {
|
|||
Offer: Offer{
|
||||
SignPubKey: providerPubKey,
|
||||
SigOfferDigest: testOfferSig,
|
||||
Auto: true,
|
||||
},
|
||||
},
|
||||
expectedErr: "",
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ import (
|
|||
"github.com/btcsuite/btcd/btcec"
|
||||
"github.com/davecgh/go-spew/spew"
|
||||
"github.com/lightninglabs/lndclient"
|
||||
"github.com/lightninglabs/pool/account"
|
||||
"github.com/lightninglabs/pool/auctioneer"
|
||||
"github.com/lightninglabs/pool/auctioneerrpc"
|
||||
"github.com/lightninglabs/pool/clientdb"
|
||||
|
|
@ -33,16 +34,9 @@ import (
|
|||
// later on. It also makes it easier to see what code would need to be re-
|
||||
// implemented in another language to integrate just the acceptor part.
|
||||
type SidecarAcceptor struct {
|
||||
store sidecar.Store
|
||||
signer lndclient.SignerClient
|
||||
wallet lndclient.WalletKitClient
|
||||
baseClient funding.BaseClient
|
||||
nodePubKey *btcec.PublicKey
|
||||
acceptor *ChannelAcceptor
|
||||
cfg *SidecarAcceptorConfig
|
||||
|
||||
clientCfg *auctioneer.Config
|
||||
client *auctioneer.Client
|
||||
fundingManager *funding.Manager
|
||||
pendingOpenChanClient *subscribe.Client
|
||||
|
||||
pendingSidecarOrders map[order.Nonce]*sidecar.Ticket
|
||||
|
|
@ -55,24 +49,39 @@ type SidecarAcceptor struct {
|
|||
wg sync.WaitGroup
|
||||
}
|
||||
|
||||
// NewSidecarAcceptor creates a new sidecar acceptor.
|
||||
func NewSidecarAcceptor(store sidecar.Store, signer lndclient.SignerClient,
|
||||
wallet lndclient.WalletKitClient, baseClient funding.BaseClient,
|
||||
acceptor *ChannelAcceptor, nodePubKey *btcec.PublicKey,
|
||||
clientCfg auctioneer.Config,
|
||||
fundingManager *funding.Manager) *SidecarAcceptor {
|
||||
// SidecarAcceptorConfig holds all the configuration information that sidecar
|
||||
// acceptor needs in order to carry out its dutes.
|
||||
type SidecarAcceptorConfig struct {
|
||||
SidecarDB sidecar.Store
|
||||
|
||||
clientCfg.ConnectSidecar = true
|
||||
AcctDB account.Store
|
||||
|
||||
Signer lndclient.SignerClient
|
||||
|
||||
Wallet lndclient.WalletKitClient
|
||||
|
||||
BaseClient funding.BaseClient
|
||||
|
||||
Acceptor *ChannelAcceptor
|
||||
|
||||
NodePubKey *btcec.PublicKey
|
||||
|
||||
ClientCfg auctioneer.Config
|
||||
|
||||
PrepareOrder orderPreparer
|
||||
|
||||
FundingManager *funding.Manager
|
||||
|
||||
FetchSidecarBid func(*sidecar.Ticket) (*order.Bid, error)
|
||||
}
|
||||
|
||||
// NewSidecarAcceptor creates a new sidecar acceptor.
|
||||
func NewSidecarAcceptor(cfg *SidecarAcceptorConfig) *SidecarAcceptor {
|
||||
|
||||
cfg.ClientCfg.ConnectSidecar = true
|
||||
|
||||
return &SidecarAcceptor{
|
||||
store: store,
|
||||
signer: signer,
|
||||
wallet: wallet,
|
||||
baseClient: baseClient,
|
||||
nodePubKey: nodePubKey,
|
||||
acceptor: acceptor,
|
||||
clientCfg: &clientCfg,
|
||||
fundingManager: fundingManager,
|
||||
cfg: cfg,
|
||||
pendingSidecarOrders: make(map[order.Nonce]*sidecar.Ticket),
|
||||
quit: make(chan struct{}),
|
||||
}
|
||||
|
|
@ -81,20 +90,20 @@ func NewSidecarAcceptor(store sidecar.Store, signer lndclient.SignerClient,
|
|||
// Start starts the sidecar acceptor.
|
||||
func (a *SidecarAcceptor) Start(errChan chan error) error {
|
||||
var err error
|
||||
a.client, err = auctioneer.NewClient(a.clientCfg)
|
||||
a.client, err = auctioneer.NewClient(&a.cfg.ClientCfg)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error creating auctioneer client: %v", err)
|
||||
}
|
||||
if err := a.client.Start(); err != nil {
|
||||
return fmt.Errorf("error starting auctioneer client: %v", err)
|
||||
}
|
||||
if err := a.acceptor.Start(errChan); err != nil {
|
||||
if err := a.cfg.Acceptor.Start(errChan); err != nil {
|
||||
return fmt.Errorf("error starting channel acceptor: %v", err)
|
||||
}
|
||||
|
||||
// We want to make sure we don't miss any channel updates as long as we
|
||||
// are running.
|
||||
a.pendingOpenChanClient, err = a.fundingManager.SubscribePendingOpenChan()
|
||||
a.pendingOpenChanClient, err = a.cfg.FundingManager.SubscribePendingOpenChan()
|
||||
if err != nil {
|
||||
return fmt.Errorf("error subscribing to pending open channel "+
|
||||
"events: %v", err)
|
||||
|
|
@ -102,21 +111,79 @@ func (a *SidecarAcceptor) Start(errChan chan error) error {
|
|||
|
||||
// If we weren't able to complete all expected sidecar channels, we want
|
||||
// to resume them now.
|
||||
tickets, err := a.store.Sidecars()
|
||||
tickets, err := a.cfg.SidecarDB.Sidecars()
|
||||
if err != nil {
|
||||
return fmt.Errorf("error reading sidecar tickets: %v", err)
|
||||
}
|
||||
for _, ticket := range tickets {
|
||||
if ticket.State != sidecar.StateExpectingChannel {
|
||||
continue
|
||||
}
|
||||
switch {
|
||||
// If this ticket was intended to be negotiated in an automated
|
||||
// manner, then we'll launch a goroutine to manage the
|
||||
// remaining state transitions depending on if we're the
|
||||
// provider of responder.
|
||||
case ticket.Offer.Auto:
|
||||
// In order to determine our role, we'll first need to see
|
||||
// if the account for the offer exists in our database. If
|
||||
// not, then we're the recipient.
|
||||
acct, err := a.cfg.AcctDB.Account(ticket.Offer.SignPubKey)
|
||||
switch {
|
||||
// If we can't find the account, then we assume that
|
||||
// we're the recipient, so we'll attempt to accept the
|
||||
// sidecar ticket.
|
||||
case err == clientdb.ErrAccountNotFound:
|
||||
|
||||
if ticket.Recipient == nil {
|
||||
go a.autoSidecarReceiver(&SidecarPacket{
|
||||
CurrentState: ticket.State,
|
||||
ReceiverTicket: ticket,
|
||||
ProviderTicket: ticket,
|
||||
})
|
||||
|
||||
// Otherwise, we're on the other end of things, so
|
||||
// we'll assume the role of the provider.
|
||||
case err == nil:
|
||||
// As we're the provider of this ticket, we'll
|
||||
// need to fetch the bid that goes along with
|
||||
// it so we can submit it to the auctioneer
|
||||
// once we've gathered all the necessary
|
||||
// materials.
|
||||
ticketBid, err := a.cfg.FetchSidecarBid(ticket)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to fetch "+
|
||||
"sidecar bid: %w", err)
|
||||
}
|
||||
|
||||
// If we're resuming the ticket, and it's still
|
||||
// in the offered state, then we'll reset our
|
||||
// state so wer send a message to the other
|
||||
// party to have them re-send their registered
|
||||
// ticket.
|
||||
state := ticket.State
|
||||
if state == sidecar.StateOffered {
|
||||
state = sidecar.StateCreated
|
||||
}
|
||||
|
||||
// TODO(roasbeef): state to cause to re-send?
|
||||
go a.autoSidecarProvider(&SidecarPacket{
|
||||
CurrentState: state,
|
||||
ReceiverTicket: ticket,
|
||||
ProviderTicket: ticket,
|
||||
}, ticketBid, acct)
|
||||
|
||||
default:
|
||||
return fmt.Errorf("unable to fetch account "+
|
||||
"for sidecar: %w", err)
|
||||
}
|
||||
|
||||
// If the ticket has no recipient or isn't in the expecting
|
||||
// state, then we can safely skip it.
|
||||
case ticket.State != sidecar.StateExpectingChannel:
|
||||
continue
|
||||
case ticket.Recipient == nil:
|
||||
continue
|
||||
}
|
||||
|
||||
r := ticket.Recipient
|
||||
if !r.NodePubKey.IsEqual(a.nodePubKey) {
|
||||
if !r.NodePubKey.IsEqual(a.cfg.NodePubKey) {
|
||||
continue
|
||||
}
|
||||
|
||||
|
|
@ -172,7 +239,7 @@ func (a *SidecarAcceptor) Stop() error {
|
|||
}
|
||||
|
||||
a.pendingOpenChanClient.Cancel()
|
||||
a.acceptor.Stop()
|
||||
a.cfg.Acceptor.Stop()
|
||||
close(a.quit)
|
||||
|
||||
a.wg.Wait()
|
||||
|
|
@ -184,40 +251,40 @@ func (a *SidecarAcceptor) Stop() error {
|
|||
// bought over a sidecar order and adds that to the offered ticket. If
|
||||
// successful, the updated ticket is added to the local database.
|
||||
func (a *SidecarAcceptor) RegisterSidecar(ctx context.Context,
|
||||
ticket *sidecar.Ticket) error {
|
||||
ticket sidecar.Ticket) (*sidecar.Ticket, error) {
|
||||
|
||||
// The ticket needs to be in the correct state for us to register it.
|
||||
if err := sidecar.VerifyOffer(ctx, ticket, a.signer); err != nil {
|
||||
return fmt.Errorf("error verifying sidecar offer: %v", err)
|
||||
if err := sidecar.VerifyOffer(ctx, &ticket, a.cfg.Signer); err != nil {
|
||||
return nil, fmt.Errorf("error verifying sidecar offer: %v", err)
|
||||
}
|
||||
|
||||
// Do we already have a ticket with that ID?
|
||||
_, err := a.store.Sidecar(ticket.ID, ticket.Offer.SignPubKey)
|
||||
_, err := a.cfg.SidecarDB.Sidecar(ticket.ID, ticket.Offer.SignPubKey)
|
||||
if err != clientdb.ErrNoSidecar {
|
||||
return fmt.Errorf("ticket with ID %x already exists",
|
||||
return nil, fmt.Errorf("ticket with ID %x already exists",
|
||||
ticket.ID[:])
|
||||
}
|
||||
|
||||
// First we'll need a new multisig key for the channel that will be
|
||||
// opened through this sidecar order.
|
||||
keyDesc, err := a.wallet.DeriveNextKey(
|
||||
keyDesc, err := a.cfg.Wallet.DeriveNextKey(
|
||||
ctx, int32(keychain.KeyFamilyMultiSig),
|
||||
)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error deriving multisig key: %v", err)
|
||||
return nil, fmt.Errorf("error deriving multisig key: %v", err)
|
||||
}
|
||||
|
||||
ticket.State = sidecar.StateRegistered
|
||||
ticket.Recipient = &sidecar.Recipient{
|
||||
NodePubKey: a.nodePubKey,
|
||||
NodePubKey: a.cfg.NodePubKey,
|
||||
MultiSigPubKey: keyDesc.PubKey,
|
||||
MultiSigKeyIndex: keyDesc.Index,
|
||||
}
|
||||
if err := a.store.AddSidecar(ticket); err != nil {
|
||||
return fmt.Errorf("error storing sidecar: %v", err)
|
||||
if err := a.cfg.SidecarDB.AddSidecar(&ticket); err != nil {
|
||||
return nil, fmt.Errorf("error storing sidecar: %v", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
return &ticket, nil
|
||||
}
|
||||
|
||||
// ExpectChannel informs the acceptor that a new bid order was submitted for the
|
||||
|
|
@ -246,7 +313,7 @@ func (a *SidecarAcceptor) ExpectChannel(ctx context.Context,
|
|||
// update its state in the database and start expecting a channel for it
|
||||
// now.
|
||||
t.State = sidecar.StateExpectingChannel
|
||||
if err := a.store.UpdateSidecar(t); err != nil {
|
||||
if err := a.cfg.SidecarDB.UpdateSidecar(t); err != nil {
|
||||
return fmt.Errorf("error updating sidecar: %v", err)
|
||||
}
|
||||
|
||||
|
|
@ -265,6 +332,106 @@ func (a *SidecarAcceptor) ExpectChannel(ctx context.Context,
|
|||
})
|
||||
}
|
||||
|
||||
// validateOrderedTicket validates a ticket in the ordered state to ensure all
|
||||
// the details are in place, and signed properly.
|
||||
func validateOrderedTicket(ctx context.Context, t *sidecar.Ticket,
|
||||
signer lndclient.SignerClient, db sidecar.Store) error {
|
||||
|
||||
// The ticket should be in the ordered state at this point (has the bid
|
||||
// information).
|
||||
if t.State != sidecar.StateOrdered {
|
||||
return fmt.Errorf("sidecar ticket in state %v, expected %v",
|
||||
t.State, sidecar.StateOrdered)
|
||||
}
|
||||
|
||||
// Let's make sure the ticket itself and the offer is valid.
|
||||
if err := sidecar.VerifyOffer(ctx, t, signer); err != nil {
|
||||
return fmt.Errorf("error validating order in sidecar "+
|
||||
"ticket: %v", err)
|
||||
}
|
||||
|
||||
// Make sure the order signature is valid and the ticket actually exists
|
||||
// in our database. We need to have it stored already since must've done
|
||||
// the register part before.
|
||||
if err := sidecar.VerifyOrder(ctx, t, signer); err != nil {
|
||||
return fmt.Errorf("error validating order in sidecar "+
|
||||
"ticket: %v", err)
|
||||
}
|
||||
if _, err := db.Sidecar(t.ID, t.Offer.SignPubKey); err != nil {
|
||||
return fmt.Errorf("error looking up sidecar order for "+
|
||||
"ticket with ID %x: %v", t.ID[:], err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// AutoAcceptSidecar signals to the acceptor that the recipient of a potential
|
||||
// sidecar channel request automated acceptance of the sidecar channel. We'll
|
||||
// use the cipher box of the provider of the ticket (and a new one we'll create
|
||||
// for the reply side) to finalize negotiation, resulting in a
|
||||
func (a *SidecarAcceptor) AutoAcceptSidecar(ticket *sidecar.Ticket) error {
|
||||
|
||||
log.Infof("Attempting negotiation to receive sidecar ticket: %x",
|
||||
ticket.ID[:])
|
||||
|
||||
// We'll launch a new coroutine that'll handle negotiation in the
|
||||
// background all the way to the final state of the ticket.
|
||||
a.wg.Add(1)
|
||||
go a.autoSidecarReceiver(&SidecarPacket{
|
||||
CurrentState: sidecar.StateRegistered,
|
||||
ProviderTicket: ticket,
|
||||
ReceiverTicket: ticket,
|
||||
})
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// submitSidecarOrder attempts to submit a new bid that's bound to a finalized
|
||||
// sidecar ticket that's in the registered phase. If this method returns
|
||||
// successfully, then the ticket will have transitioned to the
|
||||
// sidecar.StateOrdered state.
|
||||
func (a *SidecarAcceptor) submitSidecarOrder(ctx context.Context,
|
||||
ticket *sidecar.Ticket, bid *order.Bid,
|
||||
acct *account.Account) (*sidecar.Ticket, error) {
|
||||
|
||||
// We'll bind the ticket to the order now as the ticket has all the
|
||||
// necessary information included.
|
||||
bid.SidecarTicket = ticket
|
||||
|
||||
auctionTerms, err := a.client.Terms(ctx)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not query auctioneer terms: %v", err)
|
||||
}
|
||||
|
||||
err = prepareAndSubmitOrder(
|
||||
ctx, bid, auctionTerms, acct, a.client, a.cfg.PrepareOrder,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return bid.SidecarTicket, nil
|
||||
}
|
||||
|
||||
// CoordinateSidecar signals to the sidecar acceptor that it should attempt to
|
||||
// automatically coordinate the negotiation of the ultimate order to be
|
||||
// produced by the side car ticket with the recipient.
|
||||
func (a *SidecarAcceptor) CoordinateSidecar(ticket *sidecar.Ticket,
|
||||
bid *order.Bid, acct *account.Account) error {
|
||||
|
||||
log.Infof("Attempting negotiation to offer sidecar ticket: %x",
|
||||
ticket.ID[:])
|
||||
|
||||
a.wg.Add(1)
|
||||
go a.autoSidecarProvider(&SidecarPacket{
|
||||
CurrentState: sidecar.StateOffered,
|
||||
ProviderTicket: ticket,
|
||||
ReceiverTicket: ticket,
|
||||
}, bid, acct)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// handleServerMessage reacts to a message sent by the server and sends back the
|
||||
// appropriate response message (if needed). The main lock will be held during
|
||||
// the full execution of this method.
|
||||
|
|
@ -277,6 +444,7 @@ func (a *SidecarAcceptor) handleServerMessage(
|
|||
defer a.Unlock()
|
||||
|
||||
switch msg := serverMsg.Msg.(type) {
|
||||
|
||||
case *auctioneerrpc.ServerAuctionMessage_Prepare:
|
||||
batchID := msg.Prepare.BatchId
|
||||
|
||||
|
|
@ -368,7 +536,7 @@ func (a *SidecarAcceptor) matchPrepare(pendingBatch *order.Batch,
|
|||
// peers, and registering funding shim. We don't do a full batch
|
||||
// validation since we don't have any information about the account
|
||||
// that's being used to pay for the sidecar channel.
|
||||
err = a.fundingManager.PrepChannelFunding(batch, a.getSidecarAsOrder)
|
||||
err = a.cfg.FundingManager.PrepChannelFunding(batch, a.getSidecarAsOrder)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error preparing channel funding: %v",
|
||||
err)
|
||||
|
|
@ -398,7 +566,7 @@ func (a *SidecarAcceptor) matchPrepare(pendingBatch *order.Batch,
|
|||
//
|
||||
// NOTE: The lock must be held when calling this method.
|
||||
func (a *SidecarAcceptor) matchSign(batch *order.Batch) error {
|
||||
channelInfos, err := a.fundingManager.SidecarBatchChannelSetup(
|
||||
channelInfos, err := a.cfg.FundingManager.SidecarBatchChannelSetup(
|
||||
a.pendingBatch, a.pendingOpenChanClient, a.getSidecarAsOrder,
|
||||
)
|
||||
if err != nil {
|
||||
|
|
@ -444,7 +612,7 @@ func (a *SidecarAcceptor) matchFinalize(batch *order.Batch) {
|
|||
a.pendingSidecarOrdersMtx.Lock()
|
||||
ticket := a.pendingSidecarOrders[dummyBid.Nonce()]
|
||||
ticket.State = sidecar.StateCompleted
|
||||
if err := a.store.UpdateSidecar(ticket); err != nil {
|
||||
if err := a.cfg.SidecarDB.UpdateSidecar(ticket); err != nil {
|
||||
sdcrLog.Errorf("Error updating sidecar ticket to "+
|
||||
"state complete: %v", err)
|
||||
}
|
||||
|
|
@ -452,7 +620,9 @@ func (a *SidecarAcceptor) matchFinalize(batch *order.Batch) {
|
|||
delete(a.pendingSidecarOrders, ourOrder)
|
||||
a.pendingSidecarOrdersMtx.Unlock()
|
||||
|
||||
a.acceptor.ShimRemoved(dummyBid.(*order.Bid))
|
||||
// TODO(roasbeef): send message to the other goroutine here as well
|
||||
|
||||
a.cfg.Acceptor.ShimRemoved(dummyBid.(*order.Bid))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -500,7 +670,7 @@ func (a *SidecarAcceptor) removeShims(batch *order.Batch) error {
|
|||
// that we may have registered since we may be matched with a distinct
|
||||
// set of channels if this batch is repeated.
|
||||
if err := funding.CancelPendingFundingShims(
|
||||
batch.MatchedOrders, a.baseClient, a.getSidecarAsOrder,
|
||||
batch.MatchedOrders, a.cfg.BaseClient, a.getSidecarAsOrder,
|
||||
); err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
@ -511,7 +681,7 @@ func (a *SidecarAcceptor) removeShims(batch *order.Batch) error {
|
|||
continue
|
||||
}
|
||||
|
||||
a.acceptor.ShimRemoved(dummyBid.(*order.Bid))
|
||||
a.cfg.Acceptor.ShimRemoved(dummyBid.(*order.Bid))
|
||||
}
|
||||
|
||||
return nil
|
||||
|
|
|
|||
|
|
@ -50,14 +50,18 @@ func TestRegisterSidecar(t *testing.T) {
|
|||
mockWallet := test.NewMockWalletKit()
|
||||
mockSigner.Signature = testOfferSig.Serialize()
|
||||
|
||||
acceptor := NewSidecarAcceptor(
|
||||
nil, mockSigner, mockWallet, nil, nil, ourNodePubKey,
|
||||
auctioneer.Config{}, nil,
|
||||
)
|
||||
acceptor := NewSidecarAcceptor(&SidecarAcceptorConfig{
|
||||
SidecarDB: nil,
|
||||
AcctDB: nil,
|
||||
Signer: mockSigner,
|
||||
Wallet: mockWallet,
|
||||
NodePubKey: ourNodePubKey,
|
||||
ClientCfg: auctioneer.Config{},
|
||||
})
|
||||
|
||||
existingTicket, err := sidecar.NewTicket(
|
||||
sidecar.VersionDefault, 1_000_000, 200_000, 2016,
|
||||
providerPubKey,
|
||||
providerPubKey, false,
|
||||
)
|
||||
require.NoError(t, err)
|
||||
|
||||
|
|
@ -114,7 +118,7 @@ func TestRegisterSidecar(t *testing.T) {
|
|||
)
|
||||
|
||||
id := [8]byte{1, 2, 3, 4}
|
||||
newTicket, err := acceptor.store.Sidecar(
|
||||
newTicket, err := acceptor.cfg.SidecarDB.Sidecar(
|
||||
id, providerPubKey,
|
||||
)
|
||||
require.NoError(t, err)
|
||||
|
|
@ -135,13 +139,15 @@ func TestRegisterSidecar(t *testing.T) {
|
|||
err = store.AddSidecar(existingTicket)
|
||||
require.NoError(t, err)
|
||||
|
||||
acceptor.store = store
|
||||
err := acceptor.RegisterSidecar(context.Background(), tc.ticket)
|
||||
acceptor.cfg.SidecarDB = store
|
||||
ticket, err := acceptor.RegisterSidecar(
|
||||
context.Background(), *tc.ticket,
|
||||
)
|
||||
|
||||
if tc.expectedErr == "" {
|
||||
require.NoError(t, err)
|
||||
|
||||
tc.check(t, tc.ticket)
|
||||
tc.check(t, ticket)
|
||||
} else {
|
||||
require.Error(t, err)
|
||||
require.Contains(t, err.Error(), tc.expectedErr)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue