From 7f953bf0b9d36b0397c5a467ca74112aef404cef Mon Sep 17 00:00:00 2001 From: Olaoluwa Osuntokun Date: Thu, 1 Feb 2018 20:16:40 -0800 Subject: [PATCH 1/5] lnrpc: revamp the WalletUnlocker service to have a two-stage init In this commit, we revamp the WalletUnlocker service to now have a two-stage init process. The first (optional) is for the user instantiating a new lnd instance to call the GenSeed method with an optional aezeed passphrase. The response to this will be a freshly generated aezeed mnemonic along with the original enciphered seed. The second step will be the actual wallet initaliztion. By separating this step from seed generation, UI's will be able to ensure that the user has written down the seed, before proceeding and committing the seed to the internal wallet. The new method InitWallet accepts a wallet passphrase, the aezeed mnemonic, and the optional passphrase. --- lnrpc/rpc.pb.go | 1069 +++++++++++++++++++++++----------------- lnrpc/rpc.pb.gw.go | 66 ++- lnrpc/rpc.proto | 102 +++- lnrpc/rpc.swagger.json | 152 ++++-- 4 files changed, 883 insertions(+), 506 deletions(-) diff --git a/lnrpc/rpc.pb.go b/lnrpc/rpc.pb.go index 6b9551a3..644b4fa7 100644 --- a/lnrpc/rpc.pb.go +++ b/lnrpc/rpc.pb.go @@ -8,8 +8,10 @@ It is generated from these files: rpc.proto It has these top-level messages: - CreateWalletRequest - CreateWalletResponse + GenSeedRequest + GenSeedResponse + InitWalletRequest + InitWalletResponse UnlockWalletRequest UnlockWalletResponse Transaction @@ -147,45 +149,139 @@ func (x NewAddressRequest_AddressType) String() string { return proto.EnumName(NewAddressRequest_AddressType_name, int32(x)) } func (NewAddressRequest_AddressType) EnumDescriptor() ([]byte, []int) { - return fileDescriptor0, []int{15, 0} + return fileDescriptor0, []int{17, 0} } -type CreateWalletRequest struct { - Password []byte `protobuf:"bytes,1,opt,name=password,proto3" json:"password,omitempty"` +type GenSeedRequest struct { + // * + // aezeed_passphrase is an optional user provided passphrase that will be used + // to encrypt the generated aezeed cipher seed. + AezeedPassphrase []byte `protobuf:"bytes,1,opt,name=aezeed_passphrase,json=aezeedPassphrase,proto3" json:"aezeed_passphrase,omitempty"` + // * + // seed_entropy is an optional 16-bytes generated via CSPRNG. If not + // specified, then a fresh set of randomness will be used to create the seed. + SeedEntropy []byte `protobuf:"bytes,2,opt,name=seed_entropy,json=seedEntropy,proto3" json:"seed_entropy,omitempty"` } -func (m *CreateWalletRequest) Reset() { *m = CreateWalletRequest{} } -func (m *CreateWalletRequest) String() string { return proto.CompactTextString(m) } -func (*CreateWalletRequest) ProtoMessage() {} -func (*CreateWalletRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{0} } +func (m *GenSeedRequest) Reset() { *m = GenSeedRequest{} } +func (m *GenSeedRequest) String() string { return proto.CompactTextString(m) } +func (*GenSeedRequest) ProtoMessage() {} +func (*GenSeedRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{0} } -func (m *CreateWalletRequest) GetPassword() []byte { +func (m *GenSeedRequest) GetAezeedPassphrase() []byte { if m != nil { - return m.Password + return m.AezeedPassphrase } return nil } -type CreateWalletResponse struct { +func (m *GenSeedRequest) GetSeedEntropy() []byte { + if m != nil { + return m.SeedEntropy + } + return nil } -func (m *CreateWalletResponse) Reset() { *m = CreateWalletResponse{} } -func (m *CreateWalletResponse) String() string { return proto.CompactTextString(m) } -func (*CreateWalletResponse) ProtoMessage() {} -func (*CreateWalletResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{1} } +type GenSeedResponse struct { + // * + // cipher_seed_mnemonic is a 24-word mnemonic that encodes a prior aezeed + // cipher seed obtained by the user. This field is optional, as if not + // provided, then the daemon will generate a new cipher seed for the user. + // Otherwise, then the daemon will attempt to recover the wallet state linked + // to this cipher seed. + CipherSeedMnemonic []string `protobuf:"bytes,1,rep,name=cipher_seed_mnemonic,json=cipherSeedMnemonic" json:"cipher_seed_mnemonic,omitempty"` + // * + // enciphered_seed are the raw aezeed cipher seed bytes. This is the raw + // cipher text before run through our mnemonic encoding scheme. + EncipheredSeed []byte `protobuf:"bytes,2,opt,name=enciphered_seed,json=encipheredSeed,proto3" json:"enciphered_seed,omitempty"` +} + +func (m *GenSeedResponse) Reset() { *m = GenSeedResponse{} } +func (m *GenSeedResponse) String() string { return proto.CompactTextString(m) } +func (*GenSeedResponse) ProtoMessage() {} +func (*GenSeedResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{1} } + +func (m *GenSeedResponse) GetCipherSeedMnemonic() []string { + if m != nil { + return m.CipherSeedMnemonic + } + return nil +} + +func (m *GenSeedResponse) GetEncipheredSeed() []byte { + if m != nil { + return m.EncipheredSeed + } + return nil +} + +type InitWalletRequest struct { + // * + // wallet_password is the passphrase that should be used to encrypt the + // wallet. This MUST be at least 8 chars in length. After creation, this + // password is required to unlock the daemon. + WalletPassword []byte `protobuf:"bytes,1,opt,name=wallet_password,json=walletPassword,proto3" json:"wallet_password,omitempty"` + // * + // cipher_seed_mnemonic is a 24-word mnemonic that encodes a prior aezeed + // cipher seed obtained by the user. This may have been generated by the + // GenSeed method, or be an existing seed. + CipherSeedMnemonic []string `protobuf:"bytes,2,rep,name=cipher_seed_mnemonic,json=cipherSeedMnemonic" json:"cipher_seed_mnemonic,omitempty"` + // * + // aezeed_passphrase is an optional user provided passphrase that will be used + // to encrypt the generated aezeed cipher seed. + AezeedPassphrase []byte `protobuf:"bytes,3,opt,name=aezeed_passphrase,json=aezeedPassphrase,proto3" json:"aezeed_passphrase,omitempty"` +} + +func (m *InitWalletRequest) Reset() { *m = InitWalletRequest{} } +func (m *InitWalletRequest) String() string { return proto.CompactTextString(m) } +func (*InitWalletRequest) ProtoMessage() {} +func (*InitWalletRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{2} } + +func (m *InitWalletRequest) GetWalletPassword() []byte { + if m != nil { + return m.WalletPassword + } + return nil +} + +func (m *InitWalletRequest) GetCipherSeedMnemonic() []string { + if m != nil { + return m.CipherSeedMnemonic + } + return nil +} + +func (m *InitWalletRequest) GetAezeedPassphrase() []byte { + if m != nil { + return m.AezeedPassphrase + } + return nil +} + +type InitWalletResponse struct { +} + +func (m *InitWalletResponse) Reset() { *m = InitWalletResponse{} } +func (m *InitWalletResponse) String() string { return proto.CompactTextString(m) } +func (*InitWalletResponse) ProtoMessage() {} +func (*InitWalletResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{3} } type UnlockWalletRequest struct { - Password []byte `protobuf:"bytes,1,opt,name=password,proto3" json:"password,omitempty"` + // * + // wallet_password should be the current valid passphrase for the daemon. This + // will be required to decrypt on-disk material that the daemon requires to + // function properly. + WalletPassword []byte `protobuf:"bytes,1,opt,name=wallet_password,json=walletPassword,proto3" json:"wallet_password,omitempty"` } func (m *UnlockWalletRequest) Reset() { *m = UnlockWalletRequest{} } func (m *UnlockWalletRequest) String() string { return proto.CompactTextString(m) } func (*UnlockWalletRequest) ProtoMessage() {} -func (*UnlockWalletRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{2} } +func (*UnlockWalletRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{4} } -func (m *UnlockWalletRequest) GetPassword() []byte { +func (m *UnlockWalletRequest) GetWalletPassword() []byte { if m != nil { - return m.Password + return m.WalletPassword } return nil } @@ -196,7 +292,7 @@ type UnlockWalletResponse struct { func (m *UnlockWalletResponse) Reset() { *m = UnlockWalletResponse{} } func (m *UnlockWalletResponse) String() string { return proto.CompactTextString(m) } func (*UnlockWalletResponse) ProtoMessage() {} -func (*UnlockWalletResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{3} } +func (*UnlockWalletResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{5} } type Transaction struct { // / The transaction hash @@ -220,7 +316,7 @@ type Transaction struct { func (m *Transaction) Reset() { *m = Transaction{} } func (m *Transaction) String() string { return proto.CompactTextString(m) } func (*Transaction) ProtoMessage() {} -func (*Transaction) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{4} } +func (*Transaction) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{6} } func (m *Transaction) GetTxHash() string { if m != nil { @@ -284,7 +380,7 @@ type GetTransactionsRequest struct { func (m *GetTransactionsRequest) Reset() { *m = GetTransactionsRequest{} } func (m *GetTransactionsRequest) String() string { return proto.CompactTextString(m) } func (*GetTransactionsRequest) ProtoMessage() {} -func (*GetTransactionsRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{5} } +func (*GetTransactionsRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{7} } type TransactionDetails struct { // / The list of transactions relevant to the wallet. @@ -294,7 +390,7 @@ type TransactionDetails struct { func (m *TransactionDetails) Reset() { *m = TransactionDetails{} } func (m *TransactionDetails) String() string { return proto.CompactTextString(m) } func (*TransactionDetails) ProtoMessage() {} -func (*TransactionDetails) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{6} } +func (*TransactionDetails) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{8} } func (m *TransactionDetails) GetTransactions() []*Transaction { if m != nil { @@ -326,7 +422,7 @@ type SendRequest struct { func (m *SendRequest) Reset() { *m = SendRequest{} } func (m *SendRequest) String() string { return proto.CompactTextString(m) } func (*SendRequest) ProtoMessage() {} -func (*SendRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{7} } +func (*SendRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{9} } func (m *SendRequest) GetDest() []byte { if m != nil { @@ -386,7 +482,7 @@ type SendResponse struct { func (m *SendResponse) Reset() { *m = SendResponse{} } func (m *SendResponse) String() string { return proto.CompactTextString(m) } func (*SendResponse) ProtoMessage() {} -func (*SendResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{8} } +func (*SendResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{10} } func (m *SendResponse) GetPaymentError() string { if m != nil { @@ -421,7 +517,7 @@ type ChannelPoint struct { func (m *ChannelPoint) Reset() { *m = ChannelPoint{} } func (m *ChannelPoint) String() string { return proto.CompactTextString(m) } func (*ChannelPoint) ProtoMessage() {} -func (*ChannelPoint) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{9} } +func (*ChannelPoint) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{11} } type isChannelPoint_FundingTxid interface { isChannelPoint_FundingTxid() @@ -541,7 +637,7 @@ type LightningAddress struct { func (m *LightningAddress) Reset() { *m = LightningAddress{} } func (m *LightningAddress) String() string { return proto.CompactTextString(m) } func (*LightningAddress) ProtoMessage() {} -func (*LightningAddress) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{10} } +func (*LightningAddress) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{12} } func (m *LightningAddress) GetPubkey() string { if m != nil { @@ -569,7 +665,7 @@ type SendManyRequest struct { func (m *SendManyRequest) Reset() { *m = SendManyRequest{} } func (m *SendManyRequest) String() string { return proto.CompactTextString(m) } func (*SendManyRequest) ProtoMessage() {} -func (*SendManyRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{11} } +func (*SendManyRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{13} } func (m *SendManyRequest) GetAddrToAmount() map[string]int64 { if m != nil { @@ -600,7 +696,7 @@ type SendManyResponse struct { func (m *SendManyResponse) Reset() { *m = SendManyResponse{} } func (m *SendManyResponse) String() string { return proto.CompactTextString(m) } func (*SendManyResponse) ProtoMessage() {} -func (*SendManyResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{12} } +func (*SendManyResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{14} } func (m *SendManyResponse) GetTxid() string { if m != nil { @@ -623,7 +719,7 @@ type SendCoinsRequest struct { func (m *SendCoinsRequest) Reset() { *m = SendCoinsRequest{} } func (m *SendCoinsRequest) String() string { return proto.CompactTextString(m) } func (*SendCoinsRequest) ProtoMessage() {} -func (*SendCoinsRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{13} } +func (*SendCoinsRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{15} } func (m *SendCoinsRequest) GetAddr() string { if m != nil { @@ -661,7 +757,7 @@ type SendCoinsResponse struct { func (m *SendCoinsResponse) Reset() { *m = SendCoinsResponse{} } func (m *SendCoinsResponse) String() string { return proto.CompactTextString(m) } func (*SendCoinsResponse) ProtoMessage() {} -func (*SendCoinsResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{14} } +func (*SendCoinsResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{16} } func (m *SendCoinsResponse) GetTxid() string { if m != nil { @@ -684,7 +780,7 @@ type NewAddressRequest struct { func (m *NewAddressRequest) Reset() { *m = NewAddressRequest{} } func (m *NewAddressRequest) String() string { return proto.CompactTextString(m) } func (*NewAddressRequest) ProtoMessage() {} -func (*NewAddressRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{15} } +func (*NewAddressRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{17} } func (m *NewAddressRequest) GetType() NewAddressRequest_AddressType { if m != nil { @@ -699,7 +795,7 @@ type NewWitnessAddressRequest struct { func (m *NewWitnessAddressRequest) Reset() { *m = NewWitnessAddressRequest{} } func (m *NewWitnessAddressRequest) String() string { return proto.CompactTextString(m) } func (*NewWitnessAddressRequest) ProtoMessage() {} -func (*NewWitnessAddressRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{16} } +func (*NewWitnessAddressRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{18} } type NewAddressResponse struct { // / The newly generated wallet address @@ -709,7 +805,7 @@ type NewAddressResponse struct { func (m *NewAddressResponse) Reset() { *m = NewAddressResponse{} } func (m *NewAddressResponse) String() string { return proto.CompactTextString(m) } func (*NewAddressResponse) ProtoMessage() {} -func (*NewAddressResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{17} } +func (*NewAddressResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{19} } func (m *NewAddressResponse) GetAddress() string { if m != nil { @@ -726,7 +822,7 @@ type SignMessageRequest struct { func (m *SignMessageRequest) Reset() { *m = SignMessageRequest{} } func (m *SignMessageRequest) String() string { return proto.CompactTextString(m) } func (*SignMessageRequest) ProtoMessage() {} -func (*SignMessageRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{18} } +func (*SignMessageRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{20} } func (m *SignMessageRequest) GetMsg() []byte { if m != nil { @@ -743,7 +839,7 @@ type SignMessageResponse struct { func (m *SignMessageResponse) Reset() { *m = SignMessageResponse{} } func (m *SignMessageResponse) String() string { return proto.CompactTextString(m) } func (*SignMessageResponse) ProtoMessage() {} -func (*SignMessageResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{19} } +func (*SignMessageResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{21} } func (m *SignMessageResponse) GetSignature() string { if m != nil { @@ -762,7 +858,7 @@ type VerifyMessageRequest struct { func (m *VerifyMessageRequest) Reset() { *m = VerifyMessageRequest{} } func (m *VerifyMessageRequest) String() string { return proto.CompactTextString(m) } func (*VerifyMessageRequest) ProtoMessage() {} -func (*VerifyMessageRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{20} } +func (*VerifyMessageRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{22} } func (m *VerifyMessageRequest) GetMsg() []byte { if m != nil { @@ -788,7 +884,7 @@ type VerifyMessageResponse struct { func (m *VerifyMessageResponse) Reset() { *m = VerifyMessageResponse{} } func (m *VerifyMessageResponse) String() string { return proto.CompactTextString(m) } func (*VerifyMessageResponse) ProtoMessage() {} -func (*VerifyMessageResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{21} } +func (*VerifyMessageResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{23} } func (m *VerifyMessageResponse) GetValid() bool { if m != nil { @@ -815,7 +911,7 @@ type ConnectPeerRequest struct { func (m *ConnectPeerRequest) Reset() { *m = ConnectPeerRequest{} } func (m *ConnectPeerRequest) String() string { return proto.CompactTextString(m) } func (*ConnectPeerRequest) ProtoMessage() {} -func (*ConnectPeerRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{22} } +func (*ConnectPeerRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{24} } func (m *ConnectPeerRequest) GetAddr() *LightningAddress { if m != nil { @@ -837,7 +933,7 @@ type ConnectPeerResponse struct { func (m *ConnectPeerResponse) Reset() { *m = ConnectPeerResponse{} } func (m *ConnectPeerResponse) String() string { return proto.CompactTextString(m) } func (*ConnectPeerResponse) ProtoMessage() {} -func (*ConnectPeerResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{23} } +func (*ConnectPeerResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{25} } type DisconnectPeerRequest struct { // / The pubkey of the node to disconnect from @@ -847,7 +943,7 @@ type DisconnectPeerRequest struct { func (m *DisconnectPeerRequest) Reset() { *m = DisconnectPeerRequest{} } func (m *DisconnectPeerRequest) String() string { return proto.CompactTextString(m) } func (*DisconnectPeerRequest) ProtoMessage() {} -func (*DisconnectPeerRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{24} } +func (*DisconnectPeerRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{26} } func (m *DisconnectPeerRequest) GetPubKey() string { if m != nil { @@ -862,7 +958,7 @@ type DisconnectPeerResponse struct { func (m *DisconnectPeerResponse) Reset() { *m = DisconnectPeerResponse{} } func (m *DisconnectPeerResponse) String() string { return proto.CompactTextString(m) } func (*DisconnectPeerResponse) ProtoMessage() {} -func (*DisconnectPeerResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{25} } +func (*DisconnectPeerResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{27} } type HTLC struct { Incoming bool `protobuf:"varint,1,opt,name=incoming" json:"incoming,omitempty"` @@ -874,7 +970,7 @@ type HTLC struct { func (m *HTLC) Reset() { *m = HTLC{} } func (m *HTLC) String() string { return proto.CompactTextString(m) } func (*HTLC) ProtoMessage() {} -func (*HTLC) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{26} } +func (*HTLC) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{28} } func (m *HTLC) GetIncoming() bool { if m != nil { @@ -962,7 +1058,7 @@ type ActiveChannel struct { func (m *ActiveChannel) Reset() { *m = ActiveChannel{} } func (m *ActiveChannel) String() string { return proto.CompactTextString(m) } func (*ActiveChannel) ProtoMessage() {} -func (*ActiveChannel) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{27} } +func (*ActiveChannel) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{29} } func (m *ActiveChannel) GetActive() bool { if m != nil { @@ -1082,7 +1178,7 @@ type ListChannelsRequest struct { func (m *ListChannelsRequest) Reset() { *m = ListChannelsRequest{} } func (m *ListChannelsRequest) String() string { return proto.CompactTextString(m) } func (*ListChannelsRequest) ProtoMessage() {} -func (*ListChannelsRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{28} } +func (*ListChannelsRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{30} } type ListChannelsResponse struct { // / The list of active channels @@ -1092,7 +1188,7 @@ type ListChannelsResponse struct { func (m *ListChannelsResponse) Reset() { *m = ListChannelsResponse{} } func (m *ListChannelsResponse) String() string { return proto.CompactTextString(m) } func (*ListChannelsResponse) ProtoMessage() {} -func (*ListChannelsResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{29} } +func (*ListChannelsResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{31} } func (m *ListChannelsResponse) GetChannels() []*ActiveChannel { if m != nil { @@ -1123,7 +1219,7 @@ type Peer struct { func (m *Peer) Reset() { *m = Peer{} } func (m *Peer) String() string { return proto.CompactTextString(m) } func (*Peer) ProtoMessage() {} -func (*Peer) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{30} } +func (*Peer) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{32} } func (m *Peer) GetPubKey() string { if m != nil { @@ -1187,7 +1283,7 @@ type ListPeersRequest struct { func (m *ListPeersRequest) Reset() { *m = ListPeersRequest{} } func (m *ListPeersRequest) String() string { return proto.CompactTextString(m) } func (*ListPeersRequest) ProtoMessage() {} -func (*ListPeersRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{31} } +func (*ListPeersRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{33} } type ListPeersResponse struct { // / The list of currently connected peers @@ -1197,7 +1293,7 @@ type ListPeersResponse struct { func (m *ListPeersResponse) Reset() { *m = ListPeersResponse{} } func (m *ListPeersResponse) String() string { return proto.CompactTextString(m) } func (*ListPeersResponse) ProtoMessage() {} -func (*ListPeersResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{32} } +func (*ListPeersResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{34} } func (m *ListPeersResponse) GetPeers() []*Peer { if m != nil { @@ -1212,7 +1308,7 @@ type GetInfoRequest struct { func (m *GetInfoRequest) Reset() { *m = GetInfoRequest{} } func (m *GetInfoRequest) String() string { return proto.CompactTextString(m) } func (*GetInfoRequest) ProtoMessage() {} -func (*GetInfoRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{33} } +func (*GetInfoRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{35} } type GetInfoResponse struct { // / The identity pubkey of the current node. @@ -1244,7 +1340,7 @@ type GetInfoResponse struct { func (m *GetInfoResponse) Reset() { *m = GetInfoResponse{} } func (m *GetInfoResponse) String() string { return proto.CompactTextString(m) } func (*GetInfoResponse) ProtoMessage() {} -func (*GetInfoResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{34} } +func (*GetInfoResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{36} } func (m *GetInfoResponse) GetIdentityPubkey() string { if m != nil { @@ -1339,7 +1435,7 @@ type ConfirmationUpdate struct { func (m *ConfirmationUpdate) Reset() { *m = ConfirmationUpdate{} } func (m *ConfirmationUpdate) String() string { return proto.CompactTextString(m) } func (*ConfirmationUpdate) ProtoMessage() {} -func (*ConfirmationUpdate) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{35} } +func (*ConfirmationUpdate) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{37} } func (m *ConfirmationUpdate) GetBlockSha() []byte { if m != nil { @@ -1369,7 +1465,7 @@ type ChannelOpenUpdate struct { func (m *ChannelOpenUpdate) Reset() { *m = ChannelOpenUpdate{} } func (m *ChannelOpenUpdate) String() string { return proto.CompactTextString(m) } func (*ChannelOpenUpdate) ProtoMessage() {} -func (*ChannelOpenUpdate) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{36} } +func (*ChannelOpenUpdate) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{38} } func (m *ChannelOpenUpdate) GetChannelPoint() *ChannelPoint { if m != nil { @@ -1386,7 +1482,7 @@ type ChannelCloseUpdate struct { func (m *ChannelCloseUpdate) Reset() { *m = ChannelCloseUpdate{} } func (m *ChannelCloseUpdate) String() string { return proto.CompactTextString(m) } func (*ChannelCloseUpdate) ProtoMessage() {} -func (*ChannelCloseUpdate) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{37} } +func (*ChannelCloseUpdate) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{39} } func (m *ChannelCloseUpdate) GetClosingTxid() []byte { if m != nil { @@ -1419,7 +1515,7 @@ type CloseChannelRequest struct { func (m *CloseChannelRequest) Reset() { *m = CloseChannelRequest{} } func (m *CloseChannelRequest) String() string { return proto.CompactTextString(m) } func (*CloseChannelRequest) ProtoMessage() {} -func (*CloseChannelRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{38} } +func (*CloseChannelRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{40} } func (m *CloseChannelRequest) GetChannelPoint() *ChannelPoint { if m != nil { @@ -1460,7 +1556,7 @@ type CloseStatusUpdate struct { func (m *CloseStatusUpdate) Reset() { *m = CloseStatusUpdate{} } func (m *CloseStatusUpdate) String() string { return proto.CompactTextString(m) } func (*CloseStatusUpdate) ProtoMessage() {} -func (*CloseStatusUpdate) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{39} } +func (*CloseStatusUpdate) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{41} } type isCloseStatusUpdate_Update interface { isCloseStatusUpdate_Update() @@ -1609,7 +1705,7 @@ type PendingUpdate struct { func (m *PendingUpdate) Reset() { *m = PendingUpdate{} } func (m *PendingUpdate) String() string { return proto.CompactTextString(m) } func (*PendingUpdate) ProtoMessage() {} -func (*PendingUpdate) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{40} } +func (*PendingUpdate) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{42} } func (m *PendingUpdate) GetTxid() []byte { if m != nil { @@ -1647,7 +1743,7 @@ type OpenChannelRequest struct { func (m *OpenChannelRequest) Reset() { *m = OpenChannelRequest{} } func (m *OpenChannelRequest) String() string { return proto.CompactTextString(m) } func (*OpenChannelRequest) ProtoMessage() {} -func (*OpenChannelRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{41} } +func (*OpenChannelRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{43} } func (m *OpenChannelRequest) GetNodePubkey() []byte { if m != nil { @@ -1716,7 +1812,7 @@ type OpenStatusUpdate struct { func (m *OpenStatusUpdate) Reset() { *m = OpenStatusUpdate{} } func (m *OpenStatusUpdate) String() string { return proto.CompactTextString(m) } func (*OpenStatusUpdate) ProtoMessage() {} -func (*OpenStatusUpdate) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{42} } +func (*OpenStatusUpdate) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{44} } type isOpenStatusUpdate_Update interface { isOpenStatusUpdate_Update() @@ -1878,7 +1974,7 @@ type PendingHTLC struct { func (m *PendingHTLC) Reset() { *m = PendingHTLC{} } func (m *PendingHTLC) String() string { return proto.CompactTextString(m) } func (*PendingHTLC) ProtoMessage() {} -func (*PendingHTLC) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{43} } +func (*PendingHTLC) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{45} } func (m *PendingHTLC) GetIncoming() bool { if m != nil { @@ -1928,7 +2024,7 @@ type PendingChannelsRequest struct { func (m *PendingChannelsRequest) Reset() { *m = PendingChannelsRequest{} } func (m *PendingChannelsRequest) String() string { return proto.CompactTextString(m) } func (*PendingChannelsRequest) ProtoMessage() {} -func (*PendingChannelsRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{44} } +func (*PendingChannelsRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{46} } type PendingChannelsResponse struct { // / The balance in satoshis encumbered in pending channels @@ -1944,7 +2040,7 @@ type PendingChannelsResponse struct { func (m *PendingChannelsResponse) Reset() { *m = PendingChannelsResponse{} } func (m *PendingChannelsResponse) String() string { return proto.CompactTextString(m) } func (*PendingChannelsResponse) ProtoMessage() {} -func (*PendingChannelsResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{45} } +func (*PendingChannelsResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{47} } func (m *PendingChannelsResponse) GetTotalLimboBalance() int64 { if m != nil { @@ -1988,7 +2084,7 @@ func (m *PendingChannelsResponse_PendingChannel) Reset() { func (m *PendingChannelsResponse_PendingChannel) String() string { return proto.CompactTextString(m) } func (*PendingChannelsResponse_PendingChannel) ProtoMessage() {} func (*PendingChannelsResponse_PendingChannel) Descriptor() ([]byte, []int) { - return fileDescriptor0, []int{45, 0} + return fileDescriptor0, []int{47, 0} } func (m *PendingChannelsResponse_PendingChannel) GetRemoteNodePub() string { @@ -2055,7 +2151,7 @@ func (m *PendingChannelsResponse_PendingOpenChannel) String() string { } func (*PendingChannelsResponse_PendingOpenChannel) ProtoMessage() {} func (*PendingChannelsResponse_PendingOpenChannel) Descriptor() ([]byte, []int) { - return fileDescriptor0, []int{45, 1} + return fileDescriptor0, []int{47, 1} } func (m *PendingChannelsResponse_PendingOpenChannel) GetChannel() *PendingChannelsResponse_PendingChannel { @@ -2104,7 +2200,7 @@ func (m *PendingChannelsResponse_ClosedChannel) Reset() { *m = PendingCh func (m *PendingChannelsResponse_ClosedChannel) String() string { return proto.CompactTextString(m) } func (*PendingChannelsResponse_ClosedChannel) ProtoMessage() {} func (*PendingChannelsResponse_ClosedChannel) Descriptor() ([]byte, []int) { - return fileDescriptor0, []int{45, 2} + return fileDescriptor0, []int{47, 2} } func (m *PendingChannelsResponse_ClosedChannel) GetChannel() *PendingChannelsResponse_PendingChannel { @@ -2148,7 +2244,7 @@ func (m *PendingChannelsResponse_ForceClosedChannel) String() string { } func (*PendingChannelsResponse_ForceClosedChannel) ProtoMessage() {} func (*PendingChannelsResponse_ForceClosedChannel) Descriptor() ([]byte, []int) { - return fileDescriptor0, []int{45, 3} + return fileDescriptor0, []int{47, 3} } func (m *PendingChannelsResponse_ForceClosedChannel) GetChannel() *PendingChannelsResponse_PendingChannel { @@ -2208,7 +2304,7 @@ type WalletBalanceRequest struct { func (m *WalletBalanceRequest) Reset() { *m = WalletBalanceRequest{} } func (m *WalletBalanceRequest) String() string { return proto.CompactTextString(m) } func (*WalletBalanceRequest) ProtoMessage() {} -func (*WalletBalanceRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{46} } +func (*WalletBalanceRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{48} } func (m *WalletBalanceRequest) GetWitnessOnly() bool { if m != nil { @@ -2229,7 +2325,7 @@ type WalletBalanceResponse struct { func (m *WalletBalanceResponse) Reset() { *m = WalletBalanceResponse{} } func (m *WalletBalanceResponse) String() string { return proto.CompactTextString(m) } func (*WalletBalanceResponse) ProtoMessage() {} -func (*WalletBalanceResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{47} } +func (*WalletBalanceResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{49} } func (m *WalletBalanceResponse) GetTotalBalance() int64 { if m != nil { @@ -2258,7 +2354,7 @@ type ChannelBalanceRequest struct { func (m *ChannelBalanceRequest) Reset() { *m = ChannelBalanceRequest{} } func (m *ChannelBalanceRequest) String() string { return proto.CompactTextString(m) } func (*ChannelBalanceRequest) ProtoMessage() {} -func (*ChannelBalanceRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{48} } +func (*ChannelBalanceRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{50} } type ChannelBalanceResponse struct { // / Sum of channels balances denominated in satoshis @@ -2268,7 +2364,7 @@ type ChannelBalanceResponse struct { func (m *ChannelBalanceResponse) Reset() { *m = ChannelBalanceResponse{} } func (m *ChannelBalanceResponse) String() string { return proto.CompactTextString(m) } func (*ChannelBalanceResponse) ProtoMessage() {} -func (*ChannelBalanceResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{49} } +func (*ChannelBalanceResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{51} } func (m *ChannelBalanceResponse) GetBalance() int64 { if m != nil { @@ -2289,7 +2385,7 @@ type QueryRoutesRequest struct { func (m *QueryRoutesRequest) Reset() { *m = QueryRoutesRequest{} } func (m *QueryRoutesRequest) String() string { return proto.CompactTextString(m) } func (*QueryRoutesRequest) ProtoMessage() {} -func (*QueryRoutesRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{50} } +func (*QueryRoutesRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{52} } func (m *QueryRoutesRequest) GetPubKey() string { if m != nil { @@ -2319,7 +2415,7 @@ type QueryRoutesResponse struct { func (m *QueryRoutesResponse) Reset() { *m = QueryRoutesResponse{} } func (m *QueryRoutesResponse) String() string { return proto.CompactTextString(m) } func (*QueryRoutesResponse) ProtoMessage() {} -func (*QueryRoutesResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{51} } +func (*QueryRoutesResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{53} } func (m *QueryRoutesResponse) GetRoutes() []*Route { if m != nil { @@ -2343,7 +2439,7 @@ type Hop struct { func (m *Hop) Reset() { *m = Hop{} } func (m *Hop) String() string { return proto.CompactTextString(m) } func (*Hop) ProtoMessage() {} -func (*Hop) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{52} } +func (*Hop) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{54} } func (m *Hop) GetChanId() uint64 { if m != nil { @@ -2413,7 +2509,7 @@ type Route struct { func (m *Route) Reset() { *m = Route{} } func (m *Route) String() string { return proto.CompactTextString(m) } func (*Route) ProtoMessage() {} -func (*Route) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{53} } +func (*Route) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{55} } func (m *Route) GetTotalTimeLock() uint32 { if m != nil { @@ -2451,7 +2547,7 @@ type NodeInfoRequest struct { func (m *NodeInfoRequest) Reset() { *m = NodeInfoRequest{} } func (m *NodeInfoRequest) String() string { return proto.CompactTextString(m) } func (*NodeInfoRequest) ProtoMessage() {} -func (*NodeInfoRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{54} } +func (*NodeInfoRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{56} } func (m *NodeInfoRequest) GetPubKey() string { if m != nil { @@ -2474,7 +2570,7 @@ type NodeInfo struct { func (m *NodeInfo) Reset() { *m = NodeInfo{} } func (m *NodeInfo) String() string { return proto.CompactTextString(m) } func (*NodeInfo) ProtoMessage() {} -func (*NodeInfo) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{55} } +func (*NodeInfo) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{57} } func (m *NodeInfo) GetNode() *LightningNode { if m != nil { @@ -2513,7 +2609,7 @@ type LightningNode struct { func (m *LightningNode) Reset() { *m = LightningNode{} } func (m *LightningNode) String() string { return proto.CompactTextString(m) } func (*LightningNode) ProtoMessage() {} -func (*LightningNode) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{56} } +func (*LightningNode) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{58} } func (m *LightningNode) GetLastUpdate() uint32 { if m != nil { @@ -2558,7 +2654,7 @@ type NodeAddress struct { func (m *NodeAddress) Reset() { *m = NodeAddress{} } func (m *NodeAddress) String() string { return proto.CompactTextString(m) } func (*NodeAddress) ProtoMessage() {} -func (*NodeAddress) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{57} } +func (*NodeAddress) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{59} } func (m *NodeAddress) GetNetwork() string { if m != nil { @@ -2584,7 +2680,7 @@ type RoutingPolicy struct { func (m *RoutingPolicy) Reset() { *m = RoutingPolicy{} } func (m *RoutingPolicy) String() string { return proto.CompactTextString(m) } func (*RoutingPolicy) ProtoMessage() {} -func (*RoutingPolicy) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{58} } +func (*RoutingPolicy) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{60} } func (m *RoutingPolicy) GetTimeLockDelta() uint32 { if m != nil { @@ -2638,7 +2734,7 @@ type ChannelEdge struct { func (m *ChannelEdge) Reset() { *m = ChannelEdge{} } func (m *ChannelEdge) String() string { return proto.CompactTextString(m) } func (*ChannelEdge) ProtoMessage() {} -func (*ChannelEdge) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{59} } +func (*ChannelEdge) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{61} } func (m *ChannelEdge) GetChannelId() uint64 { if m != nil { @@ -2702,7 +2798,7 @@ type ChannelGraphRequest struct { func (m *ChannelGraphRequest) Reset() { *m = ChannelGraphRequest{} } func (m *ChannelGraphRequest) String() string { return proto.CompactTextString(m) } func (*ChannelGraphRequest) ProtoMessage() {} -func (*ChannelGraphRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{60} } +func (*ChannelGraphRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{62} } // / Returns a new instance of the directed channel graph. type ChannelGraph struct { @@ -2715,7 +2811,7 @@ type ChannelGraph struct { func (m *ChannelGraph) Reset() { *m = ChannelGraph{} } func (m *ChannelGraph) String() string { return proto.CompactTextString(m) } func (*ChannelGraph) ProtoMessage() {} -func (*ChannelGraph) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{61} } +func (*ChannelGraph) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{63} } func (m *ChannelGraph) GetNodes() []*LightningNode { if m != nil { @@ -2742,7 +2838,7 @@ type ChanInfoRequest struct { func (m *ChanInfoRequest) Reset() { *m = ChanInfoRequest{} } func (m *ChanInfoRequest) String() string { return proto.CompactTextString(m) } func (*ChanInfoRequest) ProtoMessage() {} -func (*ChanInfoRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{62} } +func (*ChanInfoRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{64} } func (m *ChanInfoRequest) GetChanId() uint64 { if m != nil { @@ -2757,7 +2853,7 @@ type NetworkInfoRequest struct { func (m *NetworkInfoRequest) Reset() { *m = NetworkInfoRequest{} } func (m *NetworkInfoRequest) String() string { return proto.CompactTextString(m) } func (*NetworkInfoRequest) ProtoMessage() {} -func (*NetworkInfoRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{63} } +func (*NetworkInfoRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{65} } type NetworkInfo struct { GraphDiameter uint32 `protobuf:"varint,1,opt,name=graph_diameter" json:"graph_diameter,omitempty"` @@ -2774,7 +2870,7 @@ type NetworkInfo struct { func (m *NetworkInfo) Reset() { *m = NetworkInfo{} } func (m *NetworkInfo) String() string { return proto.CompactTextString(m) } func (*NetworkInfo) ProtoMessage() {} -func (*NetworkInfo) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{64} } +func (*NetworkInfo) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{66} } func (m *NetworkInfo) GetGraphDiameter() uint32 { if m != nil { @@ -2845,7 +2941,7 @@ type StopRequest struct { func (m *StopRequest) Reset() { *m = StopRequest{} } func (m *StopRequest) String() string { return proto.CompactTextString(m) } func (*StopRequest) ProtoMessage() {} -func (*StopRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{65} } +func (*StopRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{67} } type StopResponse struct { } @@ -2853,7 +2949,7 @@ type StopResponse struct { func (m *StopResponse) Reset() { *m = StopResponse{} } func (m *StopResponse) String() string { return proto.CompactTextString(m) } func (*StopResponse) ProtoMessage() {} -func (*StopResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{66} } +func (*StopResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{68} } type GraphTopologySubscription struct { } @@ -2861,7 +2957,7 @@ type GraphTopologySubscription struct { func (m *GraphTopologySubscription) Reset() { *m = GraphTopologySubscription{} } func (m *GraphTopologySubscription) String() string { return proto.CompactTextString(m) } func (*GraphTopologySubscription) ProtoMessage() {} -func (*GraphTopologySubscription) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{67} } +func (*GraphTopologySubscription) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{69} } type GraphTopologyUpdate struct { NodeUpdates []*NodeUpdate `protobuf:"bytes,1,rep,name=node_updates,json=nodeUpdates" json:"node_updates,omitempty"` @@ -2872,7 +2968,7 @@ type GraphTopologyUpdate struct { func (m *GraphTopologyUpdate) Reset() { *m = GraphTopologyUpdate{} } func (m *GraphTopologyUpdate) String() string { return proto.CompactTextString(m) } func (*GraphTopologyUpdate) ProtoMessage() {} -func (*GraphTopologyUpdate) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{68} } +func (*GraphTopologyUpdate) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{70} } func (m *GraphTopologyUpdate) GetNodeUpdates() []*NodeUpdate { if m != nil { @@ -2905,7 +3001,7 @@ type NodeUpdate struct { func (m *NodeUpdate) Reset() { *m = NodeUpdate{} } func (m *NodeUpdate) String() string { return proto.CompactTextString(m) } func (*NodeUpdate) ProtoMessage() {} -func (*NodeUpdate) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{69} } +func (*NodeUpdate) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{71} } func (m *NodeUpdate) GetAddresses() []string { if m != nil { @@ -2951,7 +3047,7 @@ type ChannelEdgeUpdate struct { func (m *ChannelEdgeUpdate) Reset() { *m = ChannelEdgeUpdate{} } func (m *ChannelEdgeUpdate) String() string { return proto.CompactTextString(m) } func (*ChannelEdgeUpdate) ProtoMessage() {} -func (*ChannelEdgeUpdate) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{70} } +func (*ChannelEdgeUpdate) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{72} } func (m *ChannelEdgeUpdate) GetChanId() uint64 { if m != nil { @@ -3009,7 +3105,7 @@ type ClosedChannelUpdate struct { func (m *ClosedChannelUpdate) Reset() { *m = ClosedChannelUpdate{} } func (m *ClosedChannelUpdate) String() string { return proto.CompactTextString(m) } func (*ClosedChannelUpdate) ProtoMessage() {} -func (*ClosedChannelUpdate) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{71} } +func (*ClosedChannelUpdate) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{73} } func (m *ClosedChannelUpdate) GetChanId() uint64 { if m != nil { @@ -3083,7 +3179,7 @@ type Invoice struct { func (m *Invoice) Reset() { *m = Invoice{} } func (m *Invoice) String() string { return proto.CompactTextString(m) } func (*Invoice) ProtoMessage() {} -func (*Invoice) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{72} } +func (*Invoice) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{74} } func (m *Invoice) GetMemo() string { if m != nil { @@ -3188,7 +3284,7 @@ type AddInvoiceResponse struct { func (m *AddInvoiceResponse) Reset() { *m = AddInvoiceResponse{} } func (m *AddInvoiceResponse) String() string { return proto.CompactTextString(m) } func (*AddInvoiceResponse) ProtoMessage() {} -func (*AddInvoiceResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{73} } +func (*AddInvoiceResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{75} } func (m *AddInvoiceResponse) GetRHash() []byte { if m != nil { @@ -3216,7 +3312,7 @@ type PaymentHash struct { func (m *PaymentHash) Reset() { *m = PaymentHash{} } func (m *PaymentHash) String() string { return proto.CompactTextString(m) } func (*PaymentHash) ProtoMessage() {} -func (*PaymentHash) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{74} } +func (*PaymentHash) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{76} } func (m *PaymentHash) GetRHashStr() string { if m != nil { @@ -3240,7 +3336,7 @@ type ListInvoiceRequest struct { func (m *ListInvoiceRequest) Reset() { *m = ListInvoiceRequest{} } func (m *ListInvoiceRequest) String() string { return proto.CompactTextString(m) } func (*ListInvoiceRequest) ProtoMessage() {} -func (*ListInvoiceRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{75} } +func (*ListInvoiceRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{77} } func (m *ListInvoiceRequest) GetPendingOnly() bool { if m != nil { @@ -3256,7 +3352,7 @@ type ListInvoiceResponse struct { func (m *ListInvoiceResponse) Reset() { *m = ListInvoiceResponse{} } func (m *ListInvoiceResponse) String() string { return proto.CompactTextString(m) } func (*ListInvoiceResponse) ProtoMessage() {} -func (*ListInvoiceResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{76} } +func (*ListInvoiceResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{78} } func (m *ListInvoiceResponse) GetInvoices() []*Invoice { if m != nil { @@ -3271,7 +3367,7 @@ type InvoiceSubscription struct { func (m *InvoiceSubscription) Reset() { *m = InvoiceSubscription{} } func (m *InvoiceSubscription) String() string { return proto.CompactTextString(m) } func (*InvoiceSubscription) ProtoMessage() {} -func (*InvoiceSubscription) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{77} } +func (*InvoiceSubscription) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{79} } type Payment struct { // / The payment hash @@ -3291,7 +3387,7 @@ type Payment struct { func (m *Payment) Reset() { *m = Payment{} } func (m *Payment) String() string { return proto.CompactTextString(m) } func (*Payment) ProtoMessage() {} -func (*Payment) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{78} } +func (*Payment) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{80} } func (m *Payment) GetPaymentHash() string { if m != nil { @@ -3341,7 +3437,7 @@ type ListPaymentsRequest struct { func (m *ListPaymentsRequest) Reset() { *m = ListPaymentsRequest{} } func (m *ListPaymentsRequest) String() string { return proto.CompactTextString(m) } func (*ListPaymentsRequest) ProtoMessage() {} -func (*ListPaymentsRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{79} } +func (*ListPaymentsRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{81} } type ListPaymentsResponse struct { // / The list of payments @@ -3351,7 +3447,7 @@ type ListPaymentsResponse struct { func (m *ListPaymentsResponse) Reset() { *m = ListPaymentsResponse{} } func (m *ListPaymentsResponse) String() string { return proto.CompactTextString(m) } func (*ListPaymentsResponse) ProtoMessage() {} -func (*ListPaymentsResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{80} } +func (*ListPaymentsResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{82} } func (m *ListPaymentsResponse) GetPayments() []*Payment { if m != nil { @@ -3366,7 +3462,7 @@ type DeleteAllPaymentsRequest struct { func (m *DeleteAllPaymentsRequest) Reset() { *m = DeleteAllPaymentsRequest{} } func (m *DeleteAllPaymentsRequest) String() string { return proto.CompactTextString(m) } func (*DeleteAllPaymentsRequest) ProtoMessage() {} -func (*DeleteAllPaymentsRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{81} } +func (*DeleteAllPaymentsRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{83} } type DeleteAllPaymentsResponse struct { } @@ -3374,7 +3470,7 @@ type DeleteAllPaymentsResponse struct { func (m *DeleteAllPaymentsResponse) Reset() { *m = DeleteAllPaymentsResponse{} } func (m *DeleteAllPaymentsResponse) String() string { return proto.CompactTextString(m) } func (*DeleteAllPaymentsResponse) ProtoMessage() {} -func (*DeleteAllPaymentsResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{82} } +func (*DeleteAllPaymentsResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{84} } type DebugLevelRequest struct { Show bool `protobuf:"varint,1,opt,name=show" json:"show,omitempty"` @@ -3384,7 +3480,7 @@ type DebugLevelRequest struct { func (m *DebugLevelRequest) Reset() { *m = DebugLevelRequest{} } func (m *DebugLevelRequest) String() string { return proto.CompactTextString(m) } func (*DebugLevelRequest) ProtoMessage() {} -func (*DebugLevelRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{83} } +func (*DebugLevelRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{85} } func (m *DebugLevelRequest) GetShow() bool { if m != nil { @@ -3407,7 +3503,7 @@ type DebugLevelResponse struct { func (m *DebugLevelResponse) Reset() { *m = DebugLevelResponse{} } func (m *DebugLevelResponse) String() string { return proto.CompactTextString(m) } func (*DebugLevelResponse) ProtoMessage() {} -func (*DebugLevelResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{84} } +func (*DebugLevelResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{86} } func (m *DebugLevelResponse) GetSubSystems() string { if m != nil { @@ -3424,7 +3520,7 @@ type PayReqString struct { func (m *PayReqString) Reset() { *m = PayReqString{} } func (m *PayReqString) String() string { return proto.CompactTextString(m) } func (*PayReqString) ProtoMessage() {} -func (*PayReqString) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{85} } +func (*PayReqString) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{87} } func (m *PayReqString) GetPayReq() string { if m != nil { @@ -3448,7 +3544,7 @@ type PayReq struct { func (m *PayReq) Reset() { *m = PayReq{} } func (m *PayReq) String() string { return proto.CompactTextString(m) } func (*PayReq) ProtoMessage() {} -func (*PayReq) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{86} } +func (*PayReq) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{88} } func (m *PayReq) GetDestination() string { if m != nil { @@ -3519,7 +3615,7 @@ type FeeReportRequest struct { func (m *FeeReportRequest) Reset() { *m = FeeReportRequest{} } func (m *FeeReportRequest) String() string { return proto.CompactTextString(m) } func (*FeeReportRequest) ProtoMessage() {} -func (*FeeReportRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{87} } +func (*FeeReportRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{89} } type ChannelFeeReport struct { // / The channel that this fee report belongs to. @@ -3535,7 +3631,7 @@ type ChannelFeeReport struct { func (m *ChannelFeeReport) Reset() { *m = ChannelFeeReport{} } func (m *ChannelFeeReport) String() string { return proto.CompactTextString(m) } func (*ChannelFeeReport) ProtoMessage() {} -func (*ChannelFeeReport) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{88} } +func (*ChannelFeeReport) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{90} } func (m *ChannelFeeReport) GetChanPoint() string { if m != nil { @@ -3573,7 +3669,7 @@ type FeeReportResponse struct { func (m *FeeReportResponse) Reset() { *m = FeeReportResponse{} } func (m *FeeReportResponse) String() string { return proto.CompactTextString(m) } func (*FeeReportResponse) ProtoMessage() {} -func (*FeeReportResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{89} } +func (*FeeReportResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{91} } func (m *FeeReportResponse) GetChannelFees() []*ChannelFeeReport { if m != nil { @@ -3598,7 +3694,7 @@ type PolicyUpdateRequest struct { func (m *PolicyUpdateRequest) Reset() { *m = PolicyUpdateRequest{} } func (m *PolicyUpdateRequest) String() string { return proto.CompactTextString(m) } func (*PolicyUpdateRequest) ProtoMessage() {} -func (*PolicyUpdateRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{90} } +func (*PolicyUpdateRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{92} } type isPolicyUpdateRequest_Scope interface { isPolicyUpdateRequest_Scope() @@ -3735,11 +3831,13 @@ type PolicyUpdateResponse struct { func (m *PolicyUpdateResponse) Reset() { *m = PolicyUpdateResponse{} } func (m *PolicyUpdateResponse) String() string { return proto.CompactTextString(m) } func (*PolicyUpdateResponse) ProtoMessage() {} -func (*PolicyUpdateResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{91} } +func (*PolicyUpdateResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{93} } func init() { - proto.RegisterType((*CreateWalletRequest)(nil), "lnrpc.CreateWalletRequest") - proto.RegisterType((*CreateWalletResponse)(nil), "lnrpc.CreateWalletResponse") + proto.RegisterType((*GenSeedRequest)(nil), "lnrpc.GenSeedRequest") + proto.RegisterType((*GenSeedResponse)(nil), "lnrpc.GenSeedResponse") + proto.RegisterType((*InitWalletRequest)(nil), "lnrpc.InitWalletRequest") + proto.RegisterType((*InitWalletResponse)(nil), "lnrpc.InitWalletResponse") proto.RegisterType((*UnlockWalletRequest)(nil), "lnrpc.UnlockWalletRequest") proto.RegisterType((*UnlockWalletResponse)(nil), "lnrpc.UnlockWalletResponse") proto.RegisterType((*Transaction)(nil), "lnrpc.Transaction") @@ -3848,10 +3946,30 @@ const _ = grpc.SupportPackageIsVersion4 // Client API for WalletUnlocker service type WalletUnlockerClient interface { - // * lncli: `create` - // CreateWallet is used at lnd startup to set the encryption password for - // the wallet database. - CreateWallet(ctx context.Context, in *CreateWalletRequest, opts ...grpc.CallOption) (*CreateWalletResponse, error) + // * + // GenSeed is the first method that should be used to instantiate a new lnd + // instance. This method allows a caller to generate a new aezeed cipher seed + // given an optional passphrase. If provided, the passphrase will be necessary + // to decrypt the cipherseed to expose the internal wallet seed. + // + // Once the cipherseed is obtained and verified by the user, the InitWallet + // method should be used to commit the newly generated seed, and create the + // wallet. + GenSeed(ctx context.Context, in *GenSeedRequest, opts ...grpc.CallOption) (*GenSeedResponse, error) + // * lncli: `init` + // InitWallet is used when lnd is starting up for the first time to fully + // initialize the daemon and its internal wallet. At the very least a wallet + // password must be provided. This will be used to encrypt sensitive material + // on disk. + // + // In the case of a recovery scenario, the user can also specify their aezeed + // mnemonic and passphrase. If set, then the daemon will use this prior state + // to initialize its internal wallet. + // + // Alternatively, this can be used along with the GenSeed RPC to obtain a + // seed, then present it to the user. Once it has been verified by the user, + // the seed can be fed into this RPC in order to commit the new wallet. + InitWallet(ctx context.Context, in *InitWalletRequest, opts ...grpc.CallOption) (*InitWalletResponse, error) // * lncli: `unlock` // UnlockWallet is used at startup of lnd to provide a password to unlock // the wallet database. @@ -3866,9 +3984,18 @@ func NewWalletUnlockerClient(cc *grpc.ClientConn) WalletUnlockerClient { return &walletUnlockerClient{cc} } -func (c *walletUnlockerClient) CreateWallet(ctx context.Context, in *CreateWalletRequest, opts ...grpc.CallOption) (*CreateWalletResponse, error) { - out := new(CreateWalletResponse) - err := grpc.Invoke(ctx, "/lnrpc.WalletUnlocker/CreateWallet", in, out, c.cc, opts...) +func (c *walletUnlockerClient) GenSeed(ctx context.Context, in *GenSeedRequest, opts ...grpc.CallOption) (*GenSeedResponse, error) { + out := new(GenSeedResponse) + err := grpc.Invoke(ctx, "/lnrpc.WalletUnlocker/GenSeed", in, out, c.cc, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *walletUnlockerClient) InitWallet(ctx context.Context, in *InitWalletRequest, opts ...grpc.CallOption) (*InitWalletResponse, error) { + out := new(InitWalletResponse) + err := grpc.Invoke(ctx, "/lnrpc.WalletUnlocker/InitWallet", in, out, c.cc, opts...) if err != nil { return nil, err } @@ -3887,10 +4014,30 @@ func (c *walletUnlockerClient) UnlockWallet(ctx context.Context, in *UnlockWalle // Server API for WalletUnlocker service type WalletUnlockerServer interface { - // * lncli: `create` - // CreateWallet is used at lnd startup to set the encryption password for - // the wallet database. - CreateWallet(context.Context, *CreateWalletRequest) (*CreateWalletResponse, error) + // * + // GenSeed is the first method that should be used to instantiate a new lnd + // instance. This method allows a caller to generate a new aezeed cipher seed + // given an optional passphrase. If provided, the passphrase will be necessary + // to decrypt the cipherseed to expose the internal wallet seed. + // + // Once the cipherseed is obtained and verified by the user, the InitWallet + // method should be used to commit the newly generated seed, and create the + // wallet. + GenSeed(context.Context, *GenSeedRequest) (*GenSeedResponse, error) + // * lncli: `init` + // InitWallet is used when lnd is starting up for the first time to fully + // initialize the daemon and its internal wallet. At the very least a wallet + // password must be provided. This will be used to encrypt sensitive material + // on disk. + // + // In the case of a recovery scenario, the user can also specify their aezeed + // mnemonic and passphrase. If set, then the daemon will use this prior state + // to initialize its internal wallet. + // + // Alternatively, this can be used along with the GenSeed RPC to obtain a + // seed, then present it to the user. Once it has been verified by the user, + // the seed can be fed into this RPC in order to commit the new wallet. + InitWallet(context.Context, *InitWalletRequest) (*InitWalletResponse, error) // * lncli: `unlock` // UnlockWallet is used at startup of lnd to provide a password to unlock // the wallet database. @@ -3901,20 +4048,38 @@ func RegisterWalletUnlockerServer(s *grpc.Server, srv WalletUnlockerServer) { s.RegisterService(&_WalletUnlocker_serviceDesc, srv) } -func _WalletUnlocker_CreateWallet_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(CreateWalletRequest) +func _WalletUnlocker_GenSeed_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(GenSeedRequest) if err := dec(in); err != nil { return nil, err } if interceptor == nil { - return srv.(WalletUnlockerServer).CreateWallet(ctx, in) + return srv.(WalletUnlockerServer).GenSeed(ctx, in) } info := &grpc.UnaryServerInfo{ Server: srv, - FullMethod: "/lnrpc.WalletUnlocker/CreateWallet", + FullMethod: "/lnrpc.WalletUnlocker/GenSeed", } handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(WalletUnlockerServer).CreateWallet(ctx, req.(*CreateWalletRequest)) + return srv.(WalletUnlockerServer).GenSeed(ctx, req.(*GenSeedRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _WalletUnlocker_InitWallet_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(InitWalletRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(WalletUnlockerServer).InitWallet(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/lnrpc.WalletUnlocker/InitWallet", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(WalletUnlockerServer).InitWallet(ctx, req.(*InitWalletRequest)) } return interceptor(ctx, in, info, handler) } @@ -3942,8 +4107,12 @@ var _WalletUnlocker_serviceDesc = grpc.ServiceDesc{ HandlerType: (*WalletUnlockerServer)(nil), Methods: []grpc.MethodDesc{ { - MethodName: "CreateWallet", - Handler: _WalletUnlocker_CreateWallet_Handler, + MethodName: "GenSeed", + Handler: _WalletUnlocker_GenSeed_Handler, + }, + { + MethodName: "InitWallet", + Handler: _WalletUnlocker_InitWallet_Handler, }, { MethodName: "UnlockWallet", @@ -3958,7 +4127,8 @@ var _WalletUnlocker_serviceDesc = grpc.ServiceDesc{ type LightningClient interface { // * lncli: `walletbalance` - // WalletBalance returns total unspent outputs(confirmed and unconfirmed), all confirmed unspent outputs and all unconfirmed unspent outputs under control + // WalletBalance returns total unspent outputs(confirmed and unconfirmed), all + // confirmed unspent outputs and all unconfirmed unspent outputs under control // by the wallet. This method can be modified by having the request specify // only witness outputs should be factored into the final output sum. WalletBalance(ctx context.Context, in *WalletBalanceRequest, opts ...grpc.CallOption) (*WalletBalanceResponse, error) @@ -4644,7 +4814,8 @@ func (c *lightningClient) UpdateChannelPolicy(ctx context.Context, in *PolicyUpd type LightningServer interface { // * lncli: `walletbalance` - // WalletBalance returns total unspent outputs(confirmed and unconfirmed), all confirmed unspent outputs and all unconfirmed unspent outputs under control + // WalletBalance returns total unspent outputs(confirmed and unconfirmed), all + // confirmed unspent outputs and all unconfirmed unspent outputs under control // by the wallet. This method can be modified by having the request specify // only witness outputs should be factored into the final output sum. WalletBalance(context.Context, *WalletBalanceRequest) (*WalletBalanceResponse, error) @@ -5722,314 +5893,322 @@ var _Lightning_serviceDesc = grpc.ServiceDesc{ func init() { proto.RegisterFile("rpc.proto", fileDescriptor0) } var fileDescriptor0 = []byte{ - // 4934 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xbc, 0x7b, 0xcd, 0x6f, 0x24, 0x49, - 0x56, 0x78, 0x67, 0xb9, 0xfc, 0x51, 0xaf, 0x3e, 0x6c, 0x87, 0xdd, 0x76, 0x75, 0x4d, 0x6f, 0x6f, - 0x4f, 0xfe, 0x46, 0x33, 0xfe, 0x35, 0x43, 0xbb, 0xc7, 0xbb, 0x3b, 0xcc, 0x4e, 0x03, 0xab, 0xfe, - 0x9a, 0xf6, 0xec, 0x7a, 0x7a, 0xbc, 0xe9, 0xee, 0x1d, 0xd8, 0x11, 0x14, 0xe9, 0xaa, 0x70, 0x39, - 0xb7, 0xb3, 0x32, 0x73, 0x32, 0xb3, 0xec, 0xae, 0x1d, 0x5a, 0xe2, 0x43, 0xe2, 0x04, 0xe2, 0x00, - 0x12, 0x5a, 0xa4, 0xe5, 0x00, 0x17, 0x38, 0xf0, 0x07, 0xa0, 0x95, 0xe0, 0xbe, 0x12, 0xe2, 0xb0, - 0x27, 0x04, 0x37, 0x38, 0x2d, 0x47, 0xc4, 0x85, 0x13, 0x7a, 0x2f, 0x3e, 0x32, 0x22, 0x33, 0xdd, - 0xdd, 0xc3, 0x02, 0xb7, 0x8a, 0x17, 0x2f, 0x5f, 0x44, 0xbc, 0x78, 0xf1, 0xbe, 0x0b, 0x5a, 0x69, - 0x32, 0xba, 0x99, 0xa4, 0x71, 0x1e, 0xb3, 0xc5, 0x30, 0x4a, 0x93, 0xd1, 0xe0, 0xea, 0x24, 0x8e, - 0x27, 0x21, 0xdf, 0xf5, 0x93, 0x60, 0xd7, 0x8f, 0xa2, 0x38, 0xf7, 0xf3, 0x20, 0x8e, 0x32, 0x81, - 0xe4, 0xbe, 0x03, 0x1b, 0xf7, 0x52, 0xee, 0xe7, 0xfc, 0x13, 0x3f, 0x0c, 0x79, 0xee, 0xf1, 0xcf, - 0x66, 0x3c, 0xcb, 0xd9, 0x00, 0x56, 0x12, 0x3f, 0xcb, 0xce, 0xe3, 0x74, 0xdc, 0x77, 0xae, 0x3b, - 0x3b, 0x1d, 0x4f, 0x8f, 0xdd, 0x2d, 0xd8, 0xb4, 0x3f, 0xc9, 0x92, 0x38, 0xca, 0x38, 0x92, 0x7a, - 0x12, 0x85, 0xf1, 0xe8, 0xe9, 0x17, 0x22, 0x65, 0x7f, 0x22, 0x49, 0xfd, 0xa0, 0x01, 0xed, 0xc7, - 0xa9, 0x1f, 0x65, 0xfe, 0x08, 0x37, 0xcb, 0xfa, 0xb0, 0x9c, 0x3f, 0x1b, 0x9e, 0xfa, 0xd9, 0x29, - 0x91, 0x68, 0x79, 0x6a, 0xc8, 0xb6, 0x60, 0xc9, 0x9f, 0xc6, 0xb3, 0x28, 0xef, 0x37, 0xae, 0x3b, - 0x3b, 0x0b, 0x9e, 0x1c, 0xb1, 0xb7, 0x61, 0x3d, 0x9a, 0x4d, 0x87, 0xa3, 0x38, 0x3a, 0x09, 0xd2, - 0xa9, 0x38, 0x72, 0x7f, 0xe1, 0xba, 0xb3, 0xb3, 0xe8, 0x55, 0x27, 0xd8, 0x35, 0x80, 0x63, 0xdc, - 0x86, 0x58, 0xa2, 0x49, 0x4b, 0x18, 0x10, 0xe6, 0x42, 0x47, 0x8e, 0x78, 0x30, 0x39, 0xcd, 0xfb, - 0x8b, 0x44, 0xc8, 0x82, 0x21, 0x8d, 0x3c, 0x98, 0xf2, 0x61, 0x96, 0xfb, 0xd3, 0xa4, 0xbf, 0x44, - 0xbb, 0x31, 0x20, 0x34, 0x1f, 0xe7, 0x7e, 0x38, 0x3c, 0xe1, 0x3c, 0xeb, 0x2f, 0xcb, 0x79, 0x0d, - 0x61, 0x6f, 0x42, 0x6f, 0xcc, 0xb3, 0x7c, 0xe8, 0x8f, 0xc7, 0x29, 0xcf, 0x32, 0x9e, 0xf5, 0x57, - 0xae, 0x2f, 0xec, 0xb4, 0xbc, 0x12, 0xd4, 0xed, 0xc3, 0xd6, 0x43, 0x9e, 0x1b, 0xdc, 0xc9, 0x24, - 0xa7, 0xdd, 0x03, 0x60, 0x06, 0xf8, 0x3e, 0xcf, 0xfd, 0x20, 0xcc, 0xd8, 0xbb, 0xd0, 0xc9, 0x0d, - 0xe4, 0xbe, 0x73, 0x7d, 0x61, 0xa7, 0xbd, 0xc7, 0x6e, 0x92, 0x74, 0xdc, 0x34, 0x3e, 0xf0, 0x2c, - 0x3c, 0xf7, 0x3f, 0x1d, 0x68, 0x1f, 0xf1, 0x68, 0xac, 0xee, 0x91, 0x41, 0x13, 0x77, 0x22, 0xef, - 0x90, 0x7e, 0xb3, 0x2f, 0x43, 0x9b, 0x76, 0x97, 0xe5, 0x69, 0x10, 0x4d, 0xe8, 0x0a, 0x5a, 0x1e, - 0x20, 0xe8, 0x88, 0x20, 0x6c, 0x0d, 0x16, 0xfc, 0x69, 0x4e, 0x8c, 0x5f, 0xf0, 0xf0, 0x27, 0x7b, - 0x1d, 0x3a, 0x89, 0x3f, 0x9f, 0xf2, 0x28, 0x2f, 0x98, 0xdd, 0xf1, 0xda, 0x12, 0xb6, 0x8f, 0xdc, - 0xbe, 0x09, 0x1b, 0x26, 0x8a, 0xa2, 0xbe, 0x48, 0xd4, 0xd7, 0x0d, 0x4c, 0xb9, 0xc8, 0x5b, 0xb0, - 0xaa, 0xf0, 0x53, 0xb1, 0x59, 0x62, 0x7f, 0xcb, 0xeb, 0x49, 0xb0, 0x3a, 0xc2, 0x0e, 0xac, 0x9d, - 0x04, 0x91, 0x1f, 0x0e, 0x47, 0x61, 0x7e, 0x36, 0x1c, 0xf3, 0x30, 0xf7, 0xe9, 0x22, 0x16, 0xbd, - 0x1e, 0xc1, 0xef, 0x85, 0xf9, 0xd9, 0x7d, 0x84, 0xba, 0x7f, 0xec, 0x40, 0x47, 0x1c, 0x5e, 0x48, - 0x24, 0x7b, 0x03, 0xba, 0x6a, 0x0d, 0x9e, 0xa6, 0x71, 0x2a, 0xe5, 0xd0, 0x06, 0xb2, 0x1b, 0xb0, - 0xa6, 0x00, 0x49, 0xca, 0x83, 0xa9, 0x3f, 0xe1, 0xc4, 0x94, 0x8e, 0x57, 0x81, 0xb3, 0xbd, 0x82, - 0x62, 0x1a, 0xcf, 0x72, 0x4e, 0x4c, 0x6a, 0xef, 0x75, 0xe4, 0xc5, 0x78, 0x08, 0xf3, 0x6c, 0x14, - 0xf7, 0xcf, 0x1d, 0xe8, 0xdc, 0x3b, 0xf5, 0xa3, 0x88, 0x87, 0x87, 0x71, 0x10, 0xe5, 0xec, 0x16, - 0xb0, 0x93, 0x59, 0x34, 0x0e, 0xa2, 0xc9, 0x30, 0x7f, 0x16, 0x8c, 0x87, 0xc7, 0xf3, 0x9c, 0x67, - 0xe2, 0x8a, 0xf6, 0x2f, 0x79, 0x35, 0x73, 0xec, 0x6d, 0x58, 0xb3, 0xa0, 0x59, 0x9e, 0x8a, 0x7b, - 0xdb, 0xbf, 0xe4, 0x55, 0x66, 0x50, 0xf0, 0xe3, 0x59, 0x9e, 0xcc, 0xf2, 0x61, 0x10, 0x8d, 0xf9, - 0x33, 0xda, 0x63, 0xd7, 0xb3, 0x60, 0x77, 0x7b, 0xd0, 0x31, 0xbf, 0x73, 0x7f, 0x19, 0xd6, 0x0e, - 0xf0, 0x45, 0x44, 0x41, 0x34, 0xb9, 0x23, 0xc4, 0x16, 0x9f, 0x69, 0x32, 0x3b, 0x7e, 0xca, 0xe7, - 0x92, 0x6f, 0x72, 0x84, 0x42, 0x75, 0x1a, 0x67, 0xb9, 0x94, 0x1c, 0xfa, 0xed, 0xfe, 0x8b, 0x03, - 0xab, 0xc8, 0xfb, 0x8f, 0xfc, 0x68, 0xae, 0x6e, 0xee, 0x00, 0x3a, 0x48, 0xea, 0x71, 0x7c, 0x47, - 0x3c, 0x76, 0x21, 0xc4, 0x3b, 0x92, 0x57, 0x25, 0xec, 0x9b, 0x26, 0xea, 0x83, 0x28, 0x4f, 0xe7, - 0x9e, 0xf5, 0x35, 0x8a, 0x6d, 0xee, 0xa7, 0x13, 0x9e, 0x93, 0x1a, 0x90, 0x6a, 0x01, 0x04, 0xe8, - 0x5e, 0x1c, 0x9d, 0xb0, 0xeb, 0xd0, 0xc9, 0xfc, 0x7c, 0x98, 0xf0, 0x94, 0xb8, 0x46, 0xa2, 0xb7, - 0xe0, 0x41, 0xe6, 0xe7, 0x87, 0x3c, 0xbd, 0x3b, 0xcf, 0xf9, 0xe0, 0x1b, 0xb0, 0x5e, 0x59, 0x05, - 0xa5, 0xbd, 0x38, 0x22, 0xfe, 0x64, 0x9b, 0xb0, 0x78, 0xe6, 0x87, 0x33, 0x2e, 0xb5, 0x93, 0x18, - 0xbc, 0xdf, 0x78, 0xcf, 0x71, 0xdf, 0x84, 0xb5, 0x62, 0xdb, 0x52, 0xc8, 0x18, 0x34, 0x91, 0x83, - 0x92, 0x00, 0xfd, 0x76, 0x7f, 0xdb, 0x11, 0x88, 0xf7, 0xe2, 0x40, 0xbf, 0x74, 0x44, 0x44, 0x85, - 0xa0, 0x10, 0xf1, 0xf7, 0x85, 0x9a, 0xf0, 0x67, 0x3f, 0xac, 0xfb, 0x16, 0xac, 0x1b, 0x5b, 0x78, - 0xc1, 0x66, 0xff, 0xcc, 0x81, 0xf5, 0x47, 0xfc, 0x5c, 0xde, 0xba, 0xda, 0xed, 0x7b, 0xd0, 0xcc, - 0xe7, 0x09, 0x27, 0xcc, 0xde, 0xde, 0x1b, 0xf2, 0xd2, 0x2a, 0x78, 0x37, 0xe5, 0xf0, 0xf1, 0x3c, - 0xe1, 0x1e, 0x7d, 0xe1, 0x7e, 0x0c, 0x6d, 0x03, 0xc8, 0xb6, 0x61, 0xe3, 0x93, 0x0f, 0x1f, 0x3f, - 0x7a, 0x70, 0x74, 0x34, 0x3c, 0x7c, 0x72, 0xf7, 0x5b, 0x0f, 0x7e, 0x75, 0xb8, 0x7f, 0xe7, 0x68, - 0x7f, 0xed, 0x12, 0xdb, 0x02, 0xf6, 0xe8, 0xc1, 0xd1, 0xe3, 0x07, 0xf7, 0x2d, 0xb8, 0xc3, 0x56, - 0xa1, 0x6d, 0x02, 0x1a, 0xee, 0x00, 0xfa, 0x8f, 0xf8, 0xf9, 0x27, 0x41, 0x1e, 0xf1, 0x2c, 0xb3, - 0x97, 0x77, 0x6f, 0x02, 0x33, 0xf7, 0x24, 0x8f, 0xd9, 0x87, 0x65, 0xa9, 0x7b, 0x95, 0xe9, 0x91, - 0x43, 0xf7, 0x4d, 0x60, 0x47, 0xc1, 0x24, 0xfa, 0x88, 0x67, 0x99, 0x3f, 0xe1, 0xea, 0xb0, 0x6b, - 0xb0, 0x30, 0xcd, 0x26, 0x52, 0x4b, 0xe2, 0x4f, 0xf7, 0x2b, 0xb0, 0x61, 0xe1, 0x49, 0xc2, 0x57, - 0xa1, 0x95, 0x05, 0x93, 0xc8, 0xcf, 0x67, 0x29, 0x97, 0xa4, 0x0b, 0x80, 0xfb, 0x01, 0x6c, 0x7e, - 0x87, 0xa7, 0xc1, 0xc9, 0xfc, 0x65, 0xe4, 0x6d, 0x3a, 0x8d, 0x32, 0x9d, 0x07, 0x70, 0xb9, 0x44, - 0x47, 0x2e, 0x2f, 0x24, 0x53, 0xde, 0xdf, 0x8a, 0x27, 0x06, 0xc6, 0x3b, 0x6d, 0x98, 0xef, 0xd4, - 0x7d, 0x02, 0xec, 0x5e, 0x1c, 0x45, 0x7c, 0x94, 0x1f, 0x72, 0x9e, 0xaa, 0xcd, 0xfc, 0x9c, 0x21, - 0x86, 0xed, 0xbd, 0x6d, 0x79, 0xb1, 0xe5, 0xc7, 0x2f, 0xe5, 0x93, 0x41, 0x33, 0xe1, 0xe9, 0x94, - 0x08, 0xaf, 0x78, 0xf4, 0xdb, 0xbd, 0x0c, 0x1b, 0x16, 0x59, 0xed, 0x49, 0x5c, 0xbe, 0x1f, 0x64, - 0xa3, 0xea, 0x82, 0x7d, 0x58, 0x4e, 0x66, 0xc7, 0xc3, 0xe2, 0x91, 0xa9, 0x21, 0x5a, 0xc5, 0xf2, - 0x27, 0x92, 0xd8, 0xef, 0x39, 0xd0, 0xdc, 0x7f, 0x7c, 0x70, 0x0f, 0x1d, 0x91, 0x20, 0x1a, 0xc5, - 0x53, 0xb4, 0x25, 0xe2, 0xd0, 0x7a, 0x7c, 0xe1, 0xe3, 0xb9, 0x0a, 0x2d, 0x32, 0x41, 0x68, 0xe8, - 0xe9, 0xe9, 0x74, 0xbc, 0x02, 0x80, 0x4e, 0x06, 0x7f, 0x96, 0x04, 0x29, 0x79, 0x11, 0xca, 0x37, - 0x68, 0x92, 0x8a, 0xac, 0x4e, 0xb8, 0x3f, 0x6d, 0x42, 0xf7, 0xce, 0x28, 0x0f, 0xce, 0xb8, 0x54, - 0xe1, 0xb4, 0x2a, 0x01, 0xe4, 0x7e, 0xe4, 0x08, 0x8d, 0x4d, 0xca, 0xa7, 0x71, 0xce, 0x87, 0xd6, - 0x65, 0xd8, 0x40, 0xc4, 0x1a, 0x09, 0x42, 0xc3, 0x04, 0x8d, 0x01, 0xed, 0xaf, 0xe5, 0xd9, 0x40, - 0x64, 0x19, 0x02, 0x86, 0xc1, 0x98, 0x76, 0xd6, 0xf4, 0xd4, 0x10, 0xf9, 0x31, 0xf2, 0x13, 0x7f, - 0x14, 0xe4, 0x73, 0xf9, 0xe6, 0xf5, 0x18, 0x69, 0x87, 0xf1, 0xc8, 0x0f, 0x87, 0xc7, 0x7e, 0xe8, - 0x47, 0x23, 0x2e, 0xfd, 0x19, 0x1b, 0x88, 0x2e, 0x8b, 0xdc, 0x92, 0x42, 0x13, 0x6e, 0x4d, 0x09, - 0x8a, 0xae, 0xcf, 0x28, 0x9e, 0x4e, 0x83, 0x1c, 0x3d, 0x9d, 0xfe, 0x8a, 0xd0, 0x2f, 0x05, 0x84, - 0x4e, 0x22, 0x46, 0xe7, 0x82, 0x87, 0x2d, 0xb1, 0x9a, 0x05, 0x44, 0x2a, 0x27, 0x9c, 0x93, 0x9e, - 0x7a, 0x7a, 0xde, 0x07, 0x41, 0xa5, 0x80, 0xe0, 0x6d, 0xcc, 0xa2, 0x8c, 0xe7, 0x79, 0xc8, 0xc7, - 0x7a, 0x43, 0x6d, 0x42, 0xab, 0x4e, 0xb0, 0x5b, 0xb0, 0x21, 0x9c, 0xaf, 0xcc, 0xcf, 0xe3, 0xec, - 0x34, 0xc8, 0x86, 0x19, 0x8f, 0xf2, 0x7e, 0x87, 0xf0, 0xeb, 0xa6, 0xd8, 0x7b, 0xb0, 0x5d, 0x02, - 0xa7, 0x7c, 0xc4, 0x83, 0x33, 0x3e, 0xee, 0x77, 0xe9, 0xab, 0x8b, 0xa6, 0xd9, 0x75, 0x68, 0xa3, - 0xcf, 0x39, 0x4b, 0xc6, 0x3e, 0x9a, 0xe7, 0x1e, 0xdd, 0x83, 0x09, 0x62, 0xef, 0x40, 0x37, 0xe1, - 0xc2, 0x86, 0x9e, 0xe6, 0xe1, 0x28, 0xeb, 0xaf, 0x92, 0x81, 0x6b, 0xcb, 0x27, 0x85, 0xf2, 0xeb, - 0xd9, 0x18, 0x28, 0x9a, 0xa3, 0x8c, 0xbc, 0x18, 0x7f, 0xde, 0x5f, 0x23, 0xa1, 0x2b, 0x00, 0xf8, - 0xb2, 0x0e, 0x82, 0x2c, 0x97, 0x92, 0xa6, 0x75, 0xdc, 0x3e, 0x6c, 0xda, 0x60, 0xa9, 0x0d, 0x6e, - 0xc1, 0x8a, 0x14, 0x9b, 0xac, 0xdf, 0xa6, 0xa5, 0x37, 0xe5, 0xd2, 0x96, 0xc4, 0x7a, 0x1a, 0xcb, - 0xfd, 0xa9, 0x03, 0x4d, 0x7c, 0x67, 0x17, 0xbf, 0x49, 0x53, 0x75, 0x2e, 0x58, 0xaa, 0x93, 0xfc, - 0x6d, 0xf4, 0x46, 0x04, 0xcf, 0x85, 0x5c, 0x1a, 0x90, 0x62, 0x3e, 0xe5, 0xa3, 0x33, 0x12, 0x4e, - 0x3d, 0x8f, 0x10, 0x14, 0x5d, 0x34, 0x59, 0xf4, 0xb5, 0x90, 0x4c, 0x3d, 0x56, 0x73, 0xf4, 0xe5, - 0x72, 0x31, 0x47, 0xdf, 0xf5, 0x61, 0x39, 0x88, 0x8e, 0xe3, 0x59, 0x34, 0x26, 0x29, 0x5c, 0xf1, - 0xd4, 0x10, 0xb9, 0x99, 0x90, 0x07, 0x13, 0x4c, 0xb9, 0x14, 0xbf, 0x02, 0xe0, 0x32, 0x74, 0x69, - 0x32, 0xd2, 0x2b, 0x9a, 0x95, 0xef, 0xc2, 0xba, 0x01, 0x93, 0x7c, 0x7c, 0x1d, 0x16, 0x13, 0x04, - 0x48, 0x07, 0x45, 0xdd, 0x1f, 0x29, 0x24, 0x31, 0xe3, 0xae, 0x41, 0xef, 0x21, 0xcf, 0x3f, 0x8c, - 0x4e, 0x62, 0x45, 0xe9, 0xef, 0x16, 0x60, 0x55, 0x83, 0x24, 0xa1, 0x1d, 0x58, 0x0d, 0xc6, 0x3c, - 0xca, 0x83, 0x7c, 0x3e, 0xb4, 0x3c, 0xa7, 0x32, 0x18, 0x15, 0xb9, 0x1f, 0x06, 0x7e, 0x26, 0x95, - 0x84, 0x18, 0xb0, 0x3d, 0xd8, 0x44, 0xf9, 0x52, 0x22, 0xa3, 0x2f, 0x57, 0x38, 0x70, 0xb5, 0x73, - 0xf8, 0x24, 0x10, 0x2e, 0x94, 0x50, 0xf1, 0x89, 0x50, 0x68, 0x75, 0x53, 0xc8, 0x35, 0x41, 0x09, - 0x8f, 0xbc, 0x28, 0x64, 0x50, 0x03, 0x2a, 0x51, 0xd3, 0x92, 0x70, 0x1e, 0xcb, 0x51, 0x93, 0x11, - 0x79, 0xad, 0x54, 0x22, 0xaf, 0x1d, 0x58, 0xcd, 0xe6, 0xd1, 0x88, 0x8f, 0x87, 0x79, 0x8c, 0xeb, - 0x06, 0x11, 0xdd, 0xce, 0x8a, 0x57, 0x06, 0x53, 0x8c, 0xc8, 0xb3, 0x3c, 0xe2, 0x39, 0xe9, 0x86, - 0x15, 0x4f, 0x0d, 0x51, 0xcd, 0x12, 0x8a, 0x10, 0xed, 0x96, 0x27, 0x47, 0x68, 0x91, 0x66, 0x69, - 0x90, 0xf5, 0x3b, 0x04, 0xa5, 0xdf, 0xec, 0xab, 0x70, 0xf9, 0x18, 0x23, 0x9a, 0x53, 0xee, 0x8f, - 0x79, 0x4a, 0xb7, 0x2f, 0x02, 0x3a, 0xf1, 0xc4, 0xeb, 0x27, 0xdd, 0xef, 0x93, 0x79, 0xd4, 0x01, - 0xe5, 0x13, 0x7a, 0xd5, 0xec, 0x35, 0x68, 0x89, 0x93, 0x64, 0xa7, 0xbe, 0x0a, 0x7d, 0x09, 0x70, - 0x74, 0xea, 0x63, 0x1c, 0x64, 0x31, 0xa7, 0x41, 0x7e, 0x59, 0x9b, 0x60, 0xfb, 0x82, 0x37, 0x6f, - 0x40, 0x4f, 0x85, 0xaa, 0xd9, 0x30, 0xe4, 0x27, 0xb9, 0x72, 0xbf, 0xa3, 0xd9, 0x14, 0x97, 0xcb, - 0x0e, 0xf8, 0x49, 0xee, 0x3e, 0x82, 0x75, 0xf9, 0x3a, 0x3f, 0x4e, 0xb8, 0x5a, 0xfa, 0xeb, 0x65, - 0xdb, 0x20, 0x4c, 0xf4, 0x86, 0x94, 0x47, 0x33, 0x86, 0x28, 0x19, 0x0c, 0xd7, 0x03, 0x26, 0xa7, - 0xef, 0x85, 0x71, 0xc6, 0x25, 0x41, 0x17, 0x3a, 0xa3, 0x30, 0xce, 0x94, 0x93, 0x2f, 0x8f, 0x63, - 0xc1, 0xf0, 0x06, 0xb2, 0xd9, 0x68, 0x84, 0xef, 0x5d, 0x18, 0x79, 0x35, 0x74, 0xff, 0xd2, 0x81, - 0x0d, 0xa2, 0xa6, 0xf4, 0x88, 0xf6, 0x0c, 0x5f, 0x7d, 0x9b, 0x9d, 0x91, 0x19, 0xf8, 0x6c, 0xc2, - 0xe2, 0x49, 0x9c, 0x8e, 0xb8, 0x5c, 0x49, 0x0c, 0xbe, 0xb8, 0xaf, 0xdb, 0xac, 0xf8, 0xba, 0xff, - 0xe8, 0xc0, 0x3a, 0x6d, 0xf5, 0x28, 0xf7, 0xf3, 0x59, 0x26, 0x8f, 0xff, 0x8b, 0xd0, 0xc5, 0xa3, - 0x72, 0xf5, 0x68, 0xe4, 0x46, 0x37, 0xf5, 0xfb, 0x26, 0xa8, 0x40, 0xde, 0xbf, 0xe4, 0xd9, 0xc8, - 0xec, 0x1b, 0xd0, 0x31, 0xf3, 0x0d, 0xb4, 0xe7, 0xf6, 0xde, 0x15, 0x75, 0xca, 0x8a, 0xe4, 0xec, - 0x5f, 0xf2, 0xac, 0x0f, 0xd8, 0x6d, 0x00, 0xb2, 0xda, 0x44, 0x56, 0x06, 0x8a, 0x57, 0x6c, 0x26, - 0x19, 0x97, 0xb5, 0x7f, 0xc9, 0x33, 0xd0, 0xef, 0xae, 0xc0, 0x92, 0x30, 0x33, 0xee, 0x43, 0xe8, - 0x5a, 0x3b, 0xb5, 0x7c, 0xf8, 0x8e, 0xf0, 0xe1, 0x2b, 0x21, 0x5f, 0xa3, 0x1a, 0xf2, 0xb9, 0x7f, - 0xd3, 0x00, 0x86, 0xd2, 0x56, 0xba, 0x4e, 0xb4, 0x73, 0xf1, 0xd8, 0xf2, 0x5a, 0x3a, 0x9e, 0x09, - 0x62, 0x37, 0x81, 0x19, 0x43, 0x15, 0xd9, 0x0b, 0xeb, 0x50, 0x33, 0x83, 0x6a, 0x4c, 0xb8, 0x1c, - 0x2a, 0xc2, 0x94, 0x5e, 0x9a, 0xb8, 0xb7, 0xda, 0x39, 0x4a, 0x38, 0xcd, 0xb2, 0x53, 0xb4, 0xc3, - 0xca, 0xaf, 0x51, 0xe3, 0xb2, 0x80, 0x2c, 0xbd, 0x54, 0x40, 0x96, 0xcb, 0x02, 0x42, 0xf6, 0x2e, - 0x0d, 0xce, 0xfc, 0x9c, 0x2b, 0x1b, 0x22, 0x87, 0xe8, 0xc6, 0x4c, 0x83, 0x88, 0xcc, 0xf3, 0x70, - 0x8a, 0xab, 0x4b, 0x37, 0xc6, 0x02, 0xba, 0x3f, 0x71, 0x60, 0x0d, 0x79, 0x67, 0xc9, 0xd7, 0xfb, - 0x40, 0xe2, 0xfd, 0x8a, 0xe2, 0x65, 0xe1, 0xfe, 0xec, 0xd2, 0xf5, 0x1e, 0xb4, 0x88, 0x60, 0x9c, - 0xf0, 0x48, 0x0a, 0x57, 0xdf, 0x16, 0xae, 0x42, 0xb3, 0xec, 0x5f, 0xf2, 0x0a, 0x64, 0x43, 0xb4, - 0xfe, 0xc1, 0x81, 0xb6, 0xdc, 0xe6, 0x7f, 0xdb, 0xd9, 0x1e, 0xc0, 0x0a, 0x4a, 0x99, 0xe1, 0xcb, - 0xea, 0x31, 0xda, 0x81, 0x29, 0x46, 0x34, 0x68, 0xf8, 0x2c, 0x47, 0xbb, 0x0c, 0x46, 0x2b, 0x46, - 0x4a, 0x34, 0x1b, 0xe6, 0x41, 0x38, 0x54, 0xb3, 0x32, 0x65, 0x57, 0x37, 0x85, 0xba, 0x24, 0xcb, - 0xfd, 0x09, 0x97, 0x06, 0x4a, 0x0c, 0x30, 0xa2, 0x90, 0x07, 0x2a, 0x3b, 0x51, 0x3f, 0x06, 0xd8, - 0xae, 0x4c, 0x69, 0x47, 0x4a, 0xfa, 0x8e, 0x61, 0x30, 0x3d, 0x8e, 0xb5, 0x1b, 0xea, 0x98, 0x6e, - 0xa5, 0x35, 0xc5, 0x26, 0x70, 0x59, 0x59, 0x62, 0xe4, 0x69, 0x61, 0x77, 0x1b, 0xe4, 0x42, 0xbc, - 0x63, 0xcb, 0x40, 0x79, 0x41, 0x05, 0x37, 0x5f, 0x63, 0x3d, 0x3d, 0x76, 0x0a, 0x7d, 0x6d, 0xf2, - 0xa5, 0xda, 0x36, 0xdc, 0x02, 0x5c, 0xeb, 0xed, 0x97, 0xac, 0x45, 0x3a, 0x66, 0xac, 0x96, 0xb9, - 0x90, 0x1a, 0x9b, 0xc3, 0x35, 0x35, 0x47, 0x7a, 0xb9, 0xba, 0x5e, 0xf3, 0x95, 0xce, 0xf6, 0x01, - 0x7e, 0x6c, 0x2f, 0xfa, 0x12, 0xc2, 0x83, 0x1f, 0x3b, 0xd0, 0xb3, 0xc9, 0xa1, 0xe8, 0xc8, 0x78, - 0x44, 0x29, 0x18, 0xe5, 0x4a, 0x95, 0xc0, 0xd5, 0x88, 0xaa, 0x51, 0x17, 0x51, 0x99, 0x71, 0xd3, - 0xc2, 0xcb, 0xe2, 0xa6, 0xe6, 0xab, 0xc5, 0x4d, 0x8b, 0x75, 0x71, 0xd3, 0xe0, 0x3f, 0x1c, 0x60, - 0xd5, 0xfb, 0x65, 0x0f, 0x45, 0x48, 0x17, 0xf1, 0x50, 0xea, 0x89, 0x9f, 0x7f, 0x35, 0x19, 0x51, - 0x3c, 0x54, 0x5f, 0xa3, 0xb0, 0x9a, 0x8a, 0xc0, 0x74, 0x45, 0xba, 0x5e, 0xdd, 0x54, 0x29, 0x92, - 0x6b, 0xbe, 0x3c, 0x92, 0x5b, 0x7c, 0x79, 0x24, 0xb7, 0x54, 0x8e, 0xe4, 0x06, 0xbf, 0x09, 0x5d, - 0xeb, 0xd6, 0xff, 0xe7, 0x4e, 0x5c, 0x76, 0x63, 0xc4, 0x05, 0x5b, 0xb0, 0xc1, 0xbf, 0x35, 0x80, - 0x55, 0x25, 0xef, 0xff, 0x74, 0x0f, 0x24, 0x47, 0x96, 0x02, 0x59, 0x90, 0x72, 0x64, 0xa9, 0x8e, - 0xff, 0x4d, 0xa5, 0xf8, 0x36, 0xac, 0xa7, 0x7c, 0x14, 0x9f, 0xf1, 0xd4, 0x88, 0xa6, 0xc5, 0x55, - 0x55, 0x27, 0xd0, 0x91, 0xb3, 0xe3, 0xd7, 0x15, 0xab, 0xca, 0x60, 0x58, 0x86, 0x52, 0x18, 0xeb, - 0x7e, 0x1d, 0x36, 0x45, 0xf1, 0xe7, 0xae, 0x20, 0xa5, 0x7c, 0x89, 0xd7, 0xa1, 0x73, 0x2e, 0xd2, - 0x74, 0xc3, 0x38, 0x0a, 0xe7, 0xd2, 0x88, 0xb4, 0x25, 0xec, 0xe3, 0x28, 0x9c, 0xbb, 0x3f, 0x74, - 0xe0, 0x72, 0xe9, 0xdb, 0x22, 0x5b, 0x2f, 0x54, 0xad, 0xad, 0x7f, 0x6d, 0x20, 0x1e, 0x51, 0xca, - 0xb8, 0x71, 0x44, 0x61, 0x92, 0xaa, 0x13, 0xc8, 0xc2, 0x59, 0x54, 0xc5, 0x17, 0x17, 0x53, 0x37, - 0xe5, 0x6e, 0xc3, 0x65, 0x79, 0xf9, 0xf6, 0xd9, 0xdc, 0x3d, 0xd8, 0x2a, 0x4f, 0x14, 0xd9, 0x46, - 0x7b, 0xcb, 0x6a, 0xe8, 0xfe, 0x3a, 0xb0, 0x6f, 0xcf, 0x78, 0x3a, 0xa7, 0xba, 0x80, 0x4e, 0xad, - 0x6e, 0x97, 0x83, 0xef, 0xa5, 0x64, 0x76, 0xfc, 0x2d, 0x3e, 0x57, 0x85, 0x97, 0x46, 0x51, 0x78, - 0xf9, 0x12, 0x00, 0x46, 0x13, 0x54, 0x48, 0x50, 0xa5, 0x30, 0x0c, 0xd6, 0x04, 0x41, 0xf7, 0x36, - 0x6c, 0x58, 0xf4, 0x35, 0x27, 0x97, 0xe4, 0x17, 0x22, 0xa2, 0xb5, 0xcb, 0x13, 0x72, 0xce, 0xfd, - 0x13, 0x07, 0x16, 0xf6, 0xe3, 0xc4, 0x4c, 0x36, 0x39, 0x76, 0xb2, 0x49, 0xaa, 0xd6, 0xa1, 0xd6, - 0x9c, 0x0d, 0xa9, 0x18, 0x4c, 0x20, 0x2a, 0x46, 0x7f, 0x9a, 0x63, 0x4c, 0x77, 0x12, 0xa7, 0xe7, - 0x7e, 0x3a, 0x96, 0xec, 0x2d, 0x41, 0xf1, 0x74, 0x85, 0xfe, 0xc1, 0x9f, 0xe8, 0x53, 0x50, 0xc6, - 0x6d, 0x2e, 0xc3, 0x50, 0x39, 0x72, 0xff, 0xd0, 0x81, 0x45, 0xda, 0x2b, 0x3e, 0x16, 0x71, 0xfd, - 0x54, 0x93, 0xa3, 0x84, 0x9e, 0x23, 0x1e, 0x4b, 0x09, 0x5c, 0xaa, 0xd4, 0x35, 0x2a, 0x95, 0xba, - 0xab, 0xd0, 0x12, 0xa3, 0xa2, 0xb4, 0x55, 0x00, 0xd8, 0x35, 0x68, 0x9e, 0xc6, 0x89, 0x32, 0x71, - 0xa0, 0x32, 0x38, 0x71, 0xe2, 0x11, 0xdc, 0xbd, 0x01, 0xab, 0x8f, 0xe2, 0x31, 0x37, 0x12, 0x00, - 0x17, 0xde, 0xa2, 0xfb, 0x5b, 0x0e, 0xac, 0x28, 0x64, 0xb6, 0x03, 0x4d, 0xb4, 0x54, 0x25, 0xdf, - 0x50, 0x67, 0x5b, 0x11, 0xcf, 0x23, 0x0c, 0xd4, 0x30, 0x14, 0x38, 0x16, 0x9e, 0x84, 0x0a, 0x1b, - 0x0b, 0x1b, 0xfd, 0x26, 0xf4, 0xc4, 0x9e, 0x4b, 0xb6, 0xac, 0x04, 0x75, 0xff, 0xca, 0x81, 0xae, - 0xb5, 0x06, 0x7a, 0xf9, 0xa1, 0x9f, 0xe5, 0x32, 0x77, 0x25, 0x99, 0x68, 0x82, 0xcc, 0x94, 0x50, - 0xc3, 0x4e, 0x09, 0xe9, 0x64, 0xc5, 0x82, 0x99, 0xac, 0xb8, 0x05, 0xad, 0xa2, 0xea, 0xd9, 0xb4, - 0x34, 0x07, 0xae, 0xa8, 0xf2, 0xc8, 0x05, 0x12, 0xd2, 0x19, 0xc5, 0x61, 0x9c, 0xca, 0xa2, 0xa0, - 0x18, 0xb8, 0xb7, 0xa1, 0x6d, 0xe0, 0xe3, 0x36, 0x22, 0x9e, 0x9f, 0xc7, 0xe9, 0x53, 0x95, 0x99, - 0x92, 0x43, 0x5d, 0x3f, 0x69, 0x14, 0xf5, 0x13, 0xf7, 0xaf, 0x1d, 0xe8, 0xa2, 0xa4, 0x04, 0xd1, - 0xe4, 0x30, 0x0e, 0x83, 0xd1, 0x9c, 0x24, 0x46, 0x09, 0x85, 0xac, 0x16, 0x2a, 0x89, 0xb1, 0xc1, - 0xe8, 0x12, 0x28, 0x27, 0x5f, 0xca, 0x8b, 0x1e, 0xa3, 0xe4, 0xa3, 0x69, 0x3b, 0xf6, 0x33, 0x2e, - 0xa2, 0x02, 0xa9, 0xca, 0x2d, 0x20, 0x6a, 0x17, 0x04, 0xa4, 0x7e, 0xce, 0x87, 0xd3, 0x20, 0x0c, - 0x03, 0x81, 0x2b, 0x24, 0xbc, 0x6e, 0xca, 0xfd, 0x51, 0x03, 0xda, 0x52, 0x8b, 0x3c, 0x18, 0x4f, - 0x44, 0x92, 0x55, 0xfa, 0x29, 0xfa, 0xf9, 0x19, 0x10, 0x35, 0x6f, 0x79, 0x36, 0x06, 0xa4, 0x7c, - 0xad, 0x0b, 0xd5, 0x6b, 0xbd, 0x0a, 0x2d, 0x14, 0xaf, 0x77, 0xc8, 0x85, 0x12, 0x45, 0xf2, 0x02, - 0xa0, 0x66, 0xf7, 0x68, 0x76, 0xb1, 0x98, 0x25, 0x80, 0xe5, 0x34, 0x2d, 0x95, 0x9c, 0xa6, 0xf7, - 0xa0, 0x23, 0xc9, 0x10, 0xdf, 0x29, 0xe6, 0x2a, 0x04, 0xdc, 0xba, 0x13, 0xcf, 0xc2, 0x54, 0x5f, - 0xee, 0xa9, 0x2f, 0x57, 0x5e, 0xf6, 0xa5, 0xc2, 0xa4, 0xca, 0x83, 0xe0, 0xcd, 0xc3, 0xd4, 0x4f, - 0x4e, 0x95, 0x66, 0x1e, 0xeb, 0xfa, 0x2a, 0x81, 0xd9, 0x0d, 0x58, 0xc4, 0xcf, 0x94, 0xf6, 0xab, - 0x7f, 0x74, 0x02, 0x85, 0xed, 0xc0, 0x22, 0x1f, 0x4f, 0xb8, 0x72, 0xdc, 0x99, 0x1d, 0x42, 0xe1, - 0x1d, 0x79, 0x02, 0x01, 0x55, 0x00, 0x42, 0x4b, 0x2a, 0xc0, 0xd6, 0x9c, 0x4b, 0x38, 0xfc, 0x70, - 0xec, 0x6e, 0x02, 0x7b, 0x24, 0xa4, 0xd6, 0x4c, 0x19, 0xfe, 0xee, 0x02, 0xb4, 0x0d, 0x30, 0xbe, - 0xe6, 0x09, 0x6e, 0x78, 0x38, 0x0e, 0xfc, 0x29, 0xcf, 0x79, 0x2a, 0x25, 0xb5, 0x04, 0x25, 0x05, - 0x7b, 0x36, 0x19, 0xc6, 0xb3, 0x7c, 0x38, 0xe6, 0x93, 0x94, 0x0b, 0x7b, 0xe7, 0x78, 0x25, 0x28, - 0xe2, 0x4d, 0xfd, 0x67, 0x26, 0x9e, 0x90, 0x87, 0x12, 0x54, 0x25, 0x00, 0x05, 0x8f, 0x9a, 0x45, - 0x02, 0x50, 0x70, 0xa4, 0xac, 0x87, 0x16, 0x6b, 0xf4, 0xd0, 0xbb, 0xb0, 0x25, 0x34, 0x8e, 0x7c, - 0x9b, 0xc3, 0x92, 0x98, 0x5c, 0x30, 0xcb, 0x6e, 0xc0, 0x1a, 0xee, 0x59, 0x09, 0x78, 0x16, 0x7c, - 0x5f, 0x04, 0xeb, 0x8e, 0x57, 0x81, 0x23, 0x2e, 0x3e, 0x47, 0x0b, 0x57, 0x54, 0x21, 0x2a, 0x70, - 0xc2, 0xf5, 0x9f, 0xd9, 0xb8, 0x2d, 0x89, 0x5b, 0x82, 0xbb, 0x5d, 0x68, 0x1f, 0xe5, 0x71, 0xa2, - 0x2e, 0xa5, 0x07, 0x1d, 0x31, 0x94, 0x95, 0xa7, 0xd7, 0xe0, 0x0a, 0x49, 0xd1, 0xe3, 0x38, 0x89, - 0xc3, 0x78, 0x32, 0x3f, 0x9a, 0x1d, 0x67, 0xa3, 0x34, 0x48, 0xd0, 0xa1, 0x76, 0xff, 0xde, 0x81, - 0x0d, 0x6b, 0x56, 0x66, 0x02, 0xbe, 0x2a, 0x44, 0x5a, 0x17, 0x0b, 0x84, 0xe0, 0xad, 0x1b, 0xea, - 0x50, 0x20, 0x8a, 0xbc, 0xca, 0x13, 0x59, 0x3f, 0xb8, 0x03, 0xab, 0x6a, 0x67, 0xea, 0x43, 0x21, - 0x85, 0xfd, 0xaa, 0x14, 0xca, 0xef, 0x7b, 0xf2, 0x03, 0x45, 0xe2, 0x97, 0x84, 0x5b, 0xca, 0xc7, - 0x74, 0x46, 0x15, 0x12, 0x0e, 0xd4, 0xf7, 0xa6, 0x2f, 0xac, 0x76, 0x30, 0xd2, 0xc0, 0xcc, 0xfd, - 0x7d, 0x07, 0xa0, 0xd8, 0x1d, 0x0a, 0x46, 0xa1, 0xd2, 0x1d, 0x4a, 0xb0, 0x1a, 0xea, 0xfb, 0x75, - 0xe8, 0xe8, 0x34, 0x76, 0x61, 0x25, 0xda, 0x0a, 0x86, 0x0e, 0xcc, 0x5b, 0xb0, 0x3a, 0x09, 0xe3, - 0x63, 0xb2, 0xb9, 0x54, 0xca, 0xcc, 0x64, 0xfd, 0xad, 0x27, 0xc0, 0x1f, 0x48, 0x68, 0x61, 0x52, - 0x9a, 0x86, 0x49, 0x71, 0xff, 0xa0, 0xa1, 0xd3, 0xa2, 0xc5, 0x99, 0x2f, 0x7c, 0x65, 0x6c, 0xaf, - 0xa2, 0x1c, 0x2f, 0xc8, 0x42, 0x52, 0xf2, 0xe3, 0xf0, 0xa5, 0x71, 0xe0, 0x6d, 0xe8, 0xa5, 0x42, - 0xfb, 0x28, 0xd5, 0xd4, 0x7c, 0x81, 0x6a, 0xea, 0xa6, 0x96, 0xdd, 0xf9, 0xff, 0xb0, 0xe6, 0x8f, - 0xcf, 0x78, 0x9a, 0x07, 0x14, 0x10, 0x90, 0xd1, 0x17, 0x0a, 0x75, 0xd5, 0x80, 0x93, 0x2d, 0x7e, - 0x0b, 0x56, 0x65, 0xcd, 0x53, 0x63, 0xca, 0xd6, 0x97, 0x02, 0x8c, 0x88, 0xee, 0x5f, 0xa8, 0x0c, - 0xac, 0x7d, 0x87, 0x17, 0x73, 0xc4, 0x3c, 0x5d, 0xa3, 0x74, 0xba, 0xff, 0x27, 0xb3, 0xa1, 0x63, - 0x15, 0x75, 0xc8, 0xbc, 0xb4, 0x00, 0xca, 0xec, 0xb5, 0xcd, 0xd2, 0xe6, 0xab, 0xb0, 0xd4, 0xfd, - 0xe1, 0x02, 0x2c, 0x7f, 0x18, 0x9d, 0xc5, 0xc1, 0x88, 0x72, 0x93, 0x53, 0x3e, 0x8d, 0x55, 0x7f, - 0x01, 0xfe, 0x46, 0x8b, 0x4e, 0x45, 0xb5, 0x24, 0x97, 0xc9, 0x45, 0x35, 0x44, 0xeb, 0x96, 0x16, - 0x3d, 0x37, 0x42, 0x52, 0x0c, 0x08, 0xfa, 0x87, 0xa9, 0xd9, 0x70, 0x24, 0x47, 0x45, 0x83, 0xc6, - 0xa2, 0xd1, 0xa0, 0x41, 0x99, 0x6c, 0x51, 0x2f, 0x24, 0x76, 0xae, 0x78, 0x6a, 0x48, 0x7e, 0x6c, - 0xca, 0x45, 0x4c, 0x4c, 0x76, 0x72, 0x59, 0xfa, 0xb1, 0x26, 0x10, 0x6d, 0xa9, 0xf8, 0x40, 0xe0, - 0x08, 0x5d, 0x63, 0x82, 0xd0, 0xb7, 0x28, 0xf7, 0x2c, 0xb5, 0xc4, 0x15, 0x97, 0xc0, 0xa8, 0x90, - 0xc6, 0x5c, 0xeb, 0x0d, 0x71, 0x06, 0x10, 0x3d, 0x45, 0x65, 0xb8, 0xe1, 0x05, 0x8b, 0xba, 0xa7, - 0x1c, 0x91, 0x0f, 0xe2, 0x87, 0xe1, 0xb1, 0x3f, 0x7a, 0x4a, 0x9d, 0x64, 0x54, 0xe6, 0x6c, 0x79, - 0x36, 0x10, 0x77, 0x4d, 0x8d, 0x51, 0x92, 0x44, 0x57, 0x94, 0x29, 0x0d, 0x90, 0xfb, 0x1d, 0x60, - 0x77, 0xc6, 0x63, 0x79, 0x43, 0x3a, 0x46, 0x28, 0x78, 0xeb, 0x58, 0xbc, 0xad, 0x39, 0x63, 0xa3, - 0xf6, 0x8c, 0xee, 0x03, 0x68, 0x1f, 0x1a, 0x0d, 0x60, 0x74, 0x99, 0xaa, 0xf5, 0x4b, 0x0a, 0x80, - 0x01, 0x31, 0x16, 0x6c, 0x98, 0x0b, 0xba, 0xbf, 0x00, 0xec, 0x20, 0xc8, 0x72, 0xbd, 0x3f, 0x1d, - 0x49, 0xea, 0x84, 0x98, 0x11, 0x49, 0x4a, 0x18, 0x45, 0x92, 0x77, 0x44, 0xb5, 0xb4, 0x7c, 0xb0, - 0x1b, 0xb0, 0x12, 0x08, 0x90, 0xd2, 0xc3, 0x3d, 0x29, 0xc0, 0x0a, 0x53, 0xcf, 0xa3, 0x43, 0x21, - 0x81, 0x96, 0x9a, 0xff, 0x91, 0x03, 0xcb, 0xf2, 0x68, 0x68, 0x0e, 0xad, 0xd6, 0x37, 0x71, 0x30, - 0x0b, 0x56, 0xdf, 0x30, 0x54, 0x95, 0xba, 0x85, 0x3a, 0xa9, 0x63, 0xd0, 0x4c, 0xfc, 0xfc, 0x94, - 0x3c, 0xe8, 0x96, 0x47, 0xbf, 0x55, 0xa4, 0xb4, 0x58, 0x44, 0x4a, 0x75, 0x3d, 0x6a, 0x42, 0x67, - 0x54, 0xe0, 0xaa, 0x8a, 0x2c, 0x0f, 0xa0, 0x13, 0xa0, 0x77, 0x45, 0x15, 0xb9, 0x00, 0x17, 0xfc, - 0x92, 0x24, 0xca, 0xfc, 0x92, 0xa8, 0x9e, 0x9e, 0x77, 0x07, 0xd0, 0xbf, 0xcf, 0x43, 0x9e, 0xf3, - 0x3b, 0x61, 0x58, 0xa6, 0xff, 0x1a, 0x5c, 0xa9, 0x99, 0x93, 0x56, 0xf5, 0x03, 0x58, 0xbf, 0xcf, - 0x8f, 0x67, 0x93, 0x03, 0x7e, 0x56, 0x54, 0x1e, 0x18, 0x34, 0xb3, 0xd3, 0xf8, 0x5c, 0xde, 0x2d, - 0xfd, 0xc6, 0x80, 0x37, 0x44, 0x9c, 0x61, 0x96, 0xf0, 0x91, 0xea, 0x8c, 0x21, 0xc8, 0x51, 0xc2, - 0x47, 0xee, 0xbb, 0xc0, 0x4c, 0x3a, 0xf2, 0x08, 0xf8, 0x72, 0x67, 0xc7, 0xc3, 0x6c, 0x9e, 0xe5, - 0x7c, 0xaa, 0x5a, 0x7e, 0x4c, 0x90, 0xfb, 0x16, 0x74, 0x0e, 0xfd, 0xb9, 0xc7, 0x3f, 0x93, 0xdd, - 0x87, 0x18, 0xbc, 0xf9, 0x73, 0x14, 0x65, 0x1d, 0xbc, 0xd1, 0xb4, 0xfb, 0xb7, 0x0d, 0x58, 0x12, - 0x98, 0x48, 0x75, 0xcc, 0xb3, 0x3c, 0x88, 0x44, 0x86, 0x5e, 0x52, 0x35, 0x40, 0x15, 0xd9, 0x68, - 0xd4, 0xc8, 0x86, 0x74, 0xa7, 0x54, 0x7f, 0x81, 0x14, 0x02, 0x0b, 0x46, 0xb1, 0xa9, 0xae, 0x59, - 0x36, 0x65, 0x6c, 0xaa, 0x00, 0xa5, 0x28, 0xb9, 0xd0, 0x0f, 0x62, 0x7f, 0x4a, 0x68, 0xa5, 0x38, - 0x98, 0xa0, 0x5a, 0x2d, 0xb4, 0x2c, 0xa4, 0xa6, 0xa2, 0x85, 0x2a, 0xda, 0x66, 0xe5, 0x15, 0xb4, - 0x8d, 0xf0, 0xb1, 0x2c, 0x6d, 0xc3, 0x60, 0xed, 0x03, 0xce, 0x3d, 0x9e, 0xc4, 0xa9, 0x6a, 0xe1, - 0x74, 0x7f, 0xe0, 0xc0, 0x9a, 0xb4, 0x1e, 0x7a, 0x8e, 0xbd, 0x6e, 0x99, 0x1a, 0xa7, 0x2e, 0x69, - 0xfb, 0x06, 0x74, 0x29, 0xd8, 0xc2, 0x48, 0x8a, 0x22, 0x2b, 0x99, 0x7f, 0xb0, 0x80, 0xb8, 0x27, - 0x95, 0x86, 0x9c, 0x06, 0xa1, 0x64, 0xb0, 0x09, 0x42, 0xb3, 0xa8, 0x82, 0x31, 0x62, 0xaf, 0xe3, - 0xe9, 0xb1, 0x7b, 0x08, 0xeb, 0xc6, 0x7e, 0xa5, 0x40, 0xdd, 0x06, 0x55, 0xb8, 0x14, 0xe9, 0x04, - 0xf1, 0x2e, 0xb6, 0x6d, 0x43, 0x58, 0x7c, 0x66, 0x21, 0xbb, 0xff, 0xe4, 0xc0, 0x86, 0x70, 0x0a, - 0xa4, 0xcb, 0xa5, 0xfb, 0xa0, 0x96, 0x84, 0x17, 0x24, 0x04, 0x7e, 0xff, 0x92, 0x27, 0xc7, 0xec, - 0x6b, 0xaf, 0xe8, 0xc8, 0xe8, 0x1a, 0xe1, 0x05, 0xec, 0x59, 0xa8, 0x63, 0xcf, 0x0b, 0x0e, 0x5f, - 0x17, 0x2c, 0x2f, 0xd6, 0x06, 0xcb, 0x77, 0x97, 0x61, 0x31, 0x1b, 0xc5, 0x09, 0x77, 0xb7, 0x60, - 0xd3, 0x3e, 0x9c, 0x60, 0xd9, 0xde, 0x3f, 0x3b, 0xd0, 0x13, 0x79, 0x3d, 0xd1, 0x1c, 0xce, 0x53, - 0x86, 0x71, 0x99, 0xd1, 0x73, 0xce, 0xb4, 0x5b, 0x5a, 0xed, 0x5d, 0x1f, 0xbc, 0x56, 0x3b, 0xa7, - 0x7c, 0xf2, 0xdf, 0xf9, 0xc9, 0xbf, 0xfe, 0x51, 0xe3, 0xb2, 0xbb, 0xb6, 0x7b, 0xf6, 0xce, 0x2e, - 0xa9, 0x4f, 0x7e, 0x4e, 0x18, 0xef, 0x3b, 0x37, 0x70, 0x15, 0xb3, 0x1d, 0x5d, 0xaf, 0x52, 0xd3, - 0xd6, 0xae, 0x57, 0xa9, 0xed, 0x5f, 0xb7, 0x56, 0x99, 0x11, 0x86, 0x5e, 0x65, 0xef, 0xdf, 0x07, - 0xd0, 0xd2, 0x01, 0x24, 0xfb, 0x1e, 0x74, 0xad, 0x1c, 0x26, 0x53, 0x84, 0xeb, 0xb2, 0xa2, 0x83, - 0xab, 0xf5, 0x93, 0x72, 0xd9, 0x6b, 0xb4, 0x6c, 0x9f, 0x6d, 0xe1, 0xb2, 0x32, 0x71, 0xb8, 0x4b, - 0xc9, 0x5d, 0xd1, 0x22, 0xf1, 0x14, 0x7a, 0x76, 0xde, 0x91, 0x5d, 0xb5, 0x45, 0xa3, 0xb4, 0xda, - 0x97, 0x2e, 0x98, 0x95, 0xcb, 0x5d, 0xa5, 0xe5, 0xb6, 0xd8, 0xa6, 0xb9, 0x9c, 0x0e, 0xec, 0x38, - 0x35, 0xb5, 0x98, 0x7d, 0xea, 0x4c, 0xd1, 0xab, 0xef, 0x5f, 0x1f, 0x5c, 0xa9, 0xf6, 0xa4, 0xcb, - 0x26, 0x76, 0xb7, 0x4f, 0x4b, 0x31, 0x46, 0x0c, 0x35, 0xdb, 0xd4, 0xd9, 0xa7, 0xd0, 0xd2, 0xbd, - 0xa9, 0x6c, 0xdb, 0x68, 0x08, 0x36, 0x1b, 0x66, 0x07, 0xfd, 0xea, 0x44, 0xdd, 0x55, 0x99, 0x94, - 0x51, 0x20, 0x0e, 0xe0, 0xb2, 0xb4, 0xe6, 0xc7, 0xfc, 0x8b, 0x9c, 0xa4, 0xa6, 0xbb, 0xfe, 0x96, - 0xc3, 0x6e, 0xc3, 0x8a, 0x6a, 0xf9, 0x65, 0x5b, 0xf5, 0xad, 0xcb, 0x83, 0xed, 0x0a, 0x5c, 0xea, - 0x91, 0x3b, 0x00, 0x45, 0x77, 0x2a, 0xeb, 0x5f, 0xd4, 0x44, 0xab, 0x99, 0x58, 0xd3, 0xca, 0x3a, - 0xa1, 0xe6, 0x5c, 0xbb, 0xf9, 0x95, 0x7d, 0xb9, 0xc0, 0xaf, 0x6d, 0x8b, 0x7d, 0x01, 0x41, 0x77, - 0x8b, 0x78, 0xb7, 0xc6, 0x7a, 0xc8, 0xbb, 0x88, 0x9f, 0xab, 0xf6, 0xae, 0xfb, 0xd0, 0x36, 0x3a, - 0x5e, 0x99, 0xa2, 0x50, 0xed, 0x96, 0x1d, 0x0c, 0xea, 0xa6, 0xe4, 0x76, 0xbf, 0x09, 0x5d, 0xab, - 0x75, 0x55, 0xbf, 0x8c, 0xba, 0xc6, 0x58, 0xfd, 0x32, 0xea, 0xbb, 0x5d, 0xbf, 0x0b, 0x6d, 0xa3, - 0xd1, 0x94, 0x19, 0xc5, 0xf1, 0x52, 0x8b, 0xa9, 0xde, 0x51, 0x5d, 0x5f, 0xea, 0x26, 0x9d, 0xb7, - 0xe7, 0xb6, 0xf0, 0xbc, 0xd4, 0xe3, 0x84, 0x42, 0xf2, 0x3d, 0xe8, 0xd9, 0xad, 0xa7, 0xfa, 0x55, - 0xd5, 0x36, 0xb1, 0xea, 0x57, 0x75, 0x41, 0xbf, 0xaa, 0x14, 0xc8, 0x1b, 0x1b, 0x7a, 0x91, 0xdd, - 0xcf, 0x65, 0xfa, 0xf4, 0x39, 0xfb, 0x36, 0xaa, 0x0e, 0xd9, 0x74, 0xc6, 0x8a, 0x86, 0x5b, 0xbb, - 0x35, 0x4d, 0x4b, 0x7b, 0xa5, 0x3f, 0xcd, 0x5d, 0x27, 0xe2, 0x6d, 0x56, 0x9c, 0x80, 0x7d, 0x04, - 0xcb, 0xb2, 0xf9, 0x8c, 0x5d, 0x2e, 0xa4, 0xda, 0x48, 0x36, 0x0d, 0xb6, 0xca, 0x60, 0x49, 0x6c, - 0x83, 0x88, 0x75, 0x59, 0x1b, 0x89, 0x4d, 0x78, 0x1e, 0x20, 0x8d, 0x08, 0x56, 0x4b, 0x05, 0x31, - 0xfd, 0x58, 0xea, 0xcb, 0xe9, 0x83, 0x6b, 0x2f, 0xae, 0xa3, 0xd9, 0x6a, 0x46, 0xa9, 0x97, 0x5d, - 0xd5, 0xfd, 0xf0, 0x6b, 0xd0, 0x31, 0x3b, 0x1a, 0xb5, 0xce, 0xae, 0xe9, 0x7e, 0xd4, 0x3a, 0xbb, - 0xae, 0x05, 0x52, 0x5d, 0x2e, 0xeb, 0x98, 0xcb, 0xb0, 0xef, 0xc2, 0xaa, 0x51, 0x7a, 0x3d, 0x9a, - 0x47, 0x23, 0x2d, 0x3c, 0xd5, 0x06, 0x98, 0x41, 0x9d, 0xa5, 0x75, 0xb7, 0x89, 0xf0, 0xba, 0x6b, - 0x11, 0x46, 0xc1, 0xb9, 0x07, 0x6d, 0xb3, 0xac, 0xfb, 0x02, 0xba, 0xdb, 0xc6, 0x94, 0xd9, 0x37, - 0x72, 0xcb, 0x61, 0x7f, 0xea, 0x40, 0xc7, 0x6c, 0xad, 0x62, 0x56, 0xc6, 0xa6, 0x44, 0xa7, 0x6f, - 0xce, 0x99, 0x84, 0x5c, 0x8f, 0x36, 0x79, 0x70, 0xe3, 0x9b, 0x16, 0x93, 0x3f, 0xb7, 0x9c, 0xa8, - 0x9b, 0xe5, 0xbf, 0x87, 0x3c, 0x2f, 0x23, 0x98, 0x4d, 0x42, 0xcf, 0x6f, 0x39, 0xec, 0x7d, 0xf1, - 0x17, 0x22, 0x15, 0x00, 0x31, 0x43, 0xb9, 0x95, 0x59, 0x66, 0xfe, 0xdb, 0x66, 0xc7, 0xb9, 0xe5, - 0xb0, 0xdf, 0x10, 0xff, 0x02, 0x91, 0xdf, 0x12, 0xe7, 0x5f, 0xf5, 0x7b, 0xf7, 0x0d, 0x3a, 0xcd, - 0x35, 0xf7, 0x8a, 0x75, 0x9a, 0xb2, 0x76, 0x3f, 0x04, 0x28, 0xa2, 0x59, 0x56, 0x0a, 0xed, 0xb4, - 0xde, 0xab, 0x06, 0xbc, 0xf6, 0x8d, 0xaa, 0x08, 0x10, 0x29, 0x7e, 0x2a, 0x84, 0x51, 0xe2, 0x67, - 0xfa, 0x4a, 0xab, 0x51, 0xe9, 0x60, 0x50, 0x37, 0x55, 0x27, 0x8a, 0x8a, 0x3e, 0x7b, 0x02, 0xdd, - 0x83, 0x38, 0x7e, 0x3a, 0x4b, 0x74, 0x86, 0xc4, 0x0e, 0xae, 0x30, 0x74, 0x1e, 0x94, 0x4e, 0xe1, - 0x5e, 0x27, 0x52, 0x03, 0xd6, 0x37, 0x48, 0xed, 0x7e, 0x5e, 0xc4, 0xd2, 0xcf, 0x99, 0x0f, 0xeb, - 0xda, 0xc6, 0xe9, 0x8d, 0x0f, 0x6c, 0x32, 0x66, 0x48, 0x5b, 0x59, 0xc2, 0xf2, 0x3a, 0xd4, 0x6e, - 0x77, 0x33, 0x45, 0xf3, 0x96, 0xc3, 0x0e, 0xa1, 0x73, 0x9f, 0x8f, 0xe2, 0x31, 0x97, 0xe1, 0xd0, - 0x46, 0xb1, 0x71, 0x1d, 0x47, 0x0d, 0xba, 0x16, 0xd0, 0x7e, 0xf5, 0x89, 0x3f, 0x4f, 0xf9, 0x67, - 0xbb, 0x9f, 0xcb, 0x40, 0xeb, 0xb9, 0x7a, 0xf5, 0x2a, 0x38, 0xb4, 0x5e, 0x7d, 0x29, 0x9a, 0xb4, - 0x5e, 0x7d, 0x25, 0x9a, 0xb4, 0x58, 0xad, 0x82, 0x53, 0x16, 0x62, 0x8c, 0x59, 0x0a, 0x40, 0xb5, - 0xa5, 0xbc, 0x28, 0x6c, 0x1d, 0x5c, 0xbf, 0x18, 0xc1, 0x5e, 0xed, 0x86, 0xbd, 0xda, 0x11, 0x74, - 0xef, 0x73, 0xc1, 0x2c, 0x51, 0x75, 0x18, 0xd8, 0x6a, 0xc4, 0xac, 0x50, 0x94, 0x55, 0x0c, 0xcd, - 0xd9, 0x6a, 0x9d, 0x52, 0xfe, 0xec, 0x53, 0x68, 0x3f, 0xe4, 0xb9, 0x2a, 0x33, 0x68, 0x7f, 0xa3, - 0x54, 0x77, 0x18, 0xd4, 0x54, 0x29, 0x6c, 0x99, 0x21, 0x6a, 0xbb, 0x7c, 0x3c, 0xe1, 0xe2, 0xb1, - 0x0f, 0x83, 0xf1, 0x73, 0xf6, 0x2b, 0x44, 0x5c, 0x57, 0x26, 0xb7, 0x8c, 0xec, 0xb4, 0x49, 0x7c, - 0xb5, 0x04, 0xaf, 0xa3, 0x1c, 0xc5, 0x63, 0x6e, 0x18, 0xb8, 0x08, 0xda, 0x46, 0x19, 0x5a, 0x3f, - 0xa0, 0x6a, 0xe9, 0x5b, 0x3f, 0xa0, 0x9a, 0xaa, 0xb5, 0xbb, 0x43, 0xeb, 0xb8, 0xec, 0x7a, 0xb1, - 0x8e, 0xa8, 0x54, 0x17, 0x2b, 0xed, 0x7e, 0xee, 0x4f, 0xf3, 0xe7, 0xec, 0x13, 0xea, 0xc6, 0x36, - 0x4b, 0x29, 0x85, 0xbf, 0x53, 0xae, 0xba, 0x68, 0x66, 0x19, 0x53, 0xb6, 0x0f, 0x24, 0x96, 0x22, - 0x3b, 0xf8, 0x35, 0x80, 0xa3, 0x3c, 0x4e, 0xee, 0xfb, 0x7c, 0x1a, 0x47, 0x85, 0xe6, 0x2a, 0xca, - 0x05, 0x85, 0xe6, 0x32, 0x6a, 0x06, 0xec, 0x13, 0xc3, 0xe3, 0xb4, 0x2a, 0x51, 0x4a, 0xb8, 0x2e, - 0xac, 0x28, 0x68, 0x86, 0xd4, 0x54, 0x15, 0x6e, 0x39, 0xe8, 0x3f, 0x16, 0xe9, 0x0e, 0xed, 0x3f, - 0x56, 0x32, 0x29, 0x5a, 0xed, 0xd5, 0xe4, 0x46, 0x0e, 0xa1, 0x55, 0xc4, 0xdc, 0xca, 0x24, 0x95, - 0x23, 0x74, 0x6d, 0x63, 0x2a, 0xa1, 0xb0, 0xbb, 0x46, 0xac, 0x02, 0xb6, 0x82, 0xac, 0xa2, 0x4a, - 0x7a, 0x00, 0x1b, 0x62, 0x83, 0xda, 0x60, 0x52, 0x02, 0x5c, 0x9d, 0xa4, 0x26, 0xf4, 0xd5, 0xaf, - 0xb9, 0x2e, 0x72, 0x74, 0xaf, 0xd0, 0x0a, 0x1b, 0x6e, 0x4f, 0xe9, 0x7d, 0x91, 0x7c, 0x7f, 0xdf, - 0xb9, 0x71, 0xbc, 0x44, 0xff, 0x77, 0xfe, 0xca, 0x7f, 0x05, 0x00, 0x00, 0xff, 0xff, 0x0e, 0x23, - 0xfe, 0x34, 0x21, 0x3d, 0x00, 0x00, + // 5069 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xbc, 0x5b, 0xcd, 0x93, 0x1c, 0xc9, + 0x55, 0x57, 0xf5, 0xf4, 0x7c, 0xf4, 0xeb, 0x8f, 0x99, 0xc9, 0x19, 0xcd, 0xb4, 0x7a, 0x65, 0x59, + 0x5b, 0x6c, 0xac, 0x84, 0x58, 0x34, 0xda, 0xb1, 0xbd, 0xac, 0x57, 0x60, 0x87, 0xbe, 0x56, 0xb3, + 0xf6, 0xac, 0x3c, 0xae, 0x91, 0xbc, 0xe0, 0x05, 0xda, 0x35, 0xdd, 0x39, 0x3d, 0x65, 0x55, 0x57, + 0xd5, 0x56, 0x55, 0xcf, 0xa8, 0x77, 0x51, 0x04, 0x1f, 0x11, 0x9c, 0x20, 0x38, 0x40, 0x04, 0x61, + 0x08, 0x73, 0xc0, 0x17, 0x38, 0xf0, 0x07, 0x10, 0x8e, 0x80, 0xbb, 0x23, 0x08, 0x0e, 0x3e, 0x11, + 0x1c, 0xe1, 0x64, 0x8e, 0x04, 0x17, 0x4e, 0xc4, 0x7b, 0xf9, 0x51, 0x99, 0x55, 0x35, 0x92, 0x6c, + 0x03, 0xb7, 0xce, 0x5f, 0x66, 0xbd, 0xcc, 0x7c, 0xf9, 0xf2, 0x7d, 0xe4, 0x7b, 0x0d, 0xad, 0x34, + 0x19, 0xdd, 0x4c, 0xd2, 0x38, 0x8f, 0xd9, 0x62, 0x18, 0xa5, 0xc9, 0x68, 0x70, 0x79, 0x12, 0xc7, + 0x93, 0x90, 0xef, 0xf8, 0x49, 0xb0, 0xe3, 0x47, 0x51, 0x9c, 0xfb, 0x79, 0x10, 0x47, 0x99, 0x18, + 0xe4, 0x7e, 0x07, 0x7a, 0x0f, 0x79, 0x74, 0xc8, 0xf9, 0xd8, 0xe3, 0x9f, 0xcc, 0x78, 0x96, 0xb3, + 0x5f, 0x82, 0x75, 0x9f, 0x7f, 0xca, 0xf9, 0x78, 0x98, 0xf8, 0x59, 0x96, 0x9c, 0xa4, 0x7e, 0xc6, + 0xfb, 0xce, 0x55, 0xe7, 0x7a, 0xc7, 0x5b, 0x13, 0x1d, 0x07, 0x1a, 0x67, 0xaf, 0x43, 0x27, 0xc3, + 0xa1, 0x3c, 0xca, 0xd3, 0x38, 0x99, 0xf7, 0x1b, 0x34, 0xae, 0x8d, 0xd8, 0x03, 0x01, 0xb9, 0x21, + 0xac, 0xea, 0x19, 0xb2, 0x24, 0x8e, 0x32, 0xce, 0x6e, 0xc1, 0xe6, 0x28, 0x48, 0x4e, 0x78, 0x3a, + 0xa4, 0x8f, 0xa7, 0x11, 0x9f, 0xc6, 0x51, 0x30, 0xea, 0x3b, 0x57, 0x17, 0xae, 0xb7, 0x3c, 0x26, + 0xfa, 0xf0, 0x8b, 0x0f, 0x65, 0x0f, 0xbb, 0x06, 0xab, 0x3c, 0x12, 0x38, 0x1f, 0xd3, 0x57, 0x72, + 0xaa, 0x5e, 0x01, 0xe3, 0x07, 0xee, 0x5f, 0x3a, 0xb0, 0xfe, 0x41, 0x14, 0xe4, 0x1f, 0xf9, 0x61, + 0xc8, 0x73, 0xb5, 0xa7, 0x6b, 0xb0, 0x7a, 0x46, 0x00, 0xed, 0xe9, 0x2c, 0x4e, 0xc7, 0x72, 0x47, + 0x3d, 0x01, 0x1f, 0x48, 0xf4, 0xdc, 0x95, 0x35, 0xce, 0x5d, 0x59, 0x2d, 0xbb, 0x16, 0xea, 0xd9, + 0xe5, 0x6e, 0x02, 0x33, 0x17, 0x27, 0xd8, 0xe1, 0x7e, 0x05, 0x36, 0x9e, 0x44, 0x61, 0x3c, 0x7a, + 0xfa, 0xb3, 0x2d, 0xda, 0xdd, 0x82, 0x4d, 0xfb, 0x7b, 0x49, 0xf7, 0x7b, 0x0d, 0x68, 0x3f, 0x4e, + 0xfd, 0x28, 0xf3, 0x47, 0x78, 0xe4, 0xac, 0x0f, 0xcb, 0xf9, 0xb3, 0xe1, 0x89, 0x9f, 0x9d, 0x10, + 0xa1, 0x96, 0xa7, 0x9a, 0x6c, 0x0b, 0x96, 0xfc, 0x69, 0x3c, 0x8b, 0x72, 0xe2, 0xea, 0x82, 0x27, + 0x5b, 0xec, 0x2d, 0x58, 0x8f, 0x66, 0xd3, 0xe1, 0x28, 0x8e, 0x8e, 0x83, 0x74, 0x2a, 0x04, 0x87, + 0x36, 0xb7, 0xe8, 0x55, 0x3b, 0xd8, 0x15, 0x80, 0x23, 0x5c, 0x86, 0x98, 0xa2, 0x49, 0x53, 0x18, + 0x08, 0x73, 0xa1, 0x23, 0x5b, 0x3c, 0x98, 0x9c, 0xe4, 0xfd, 0x45, 0x22, 0x64, 0x61, 0x48, 0x23, + 0x0f, 0xa6, 0x7c, 0x98, 0xe5, 0xfe, 0x34, 0xe9, 0x2f, 0xd1, 0x6a, 0x0c, 0x84, 0xfa, 0xe3, 0xdc, + 0x0f, 0x87, 0xc7, 0x9c, 0x67, 0xfd, 0x65, 0xd9, 0xaf, 0x11, 0xf6, 0x26, 0xf4, 0xc6, 0x3c, 0xcb, + 0x87, 0xfe, 0x78, 0x9c, 0xf2, 0x2c, 0xe3, 0x59, 0x7f, 0x85, 0x8e, 0xae, 0x84, 0xba, 0x7d, 0xd8, + 0x7a, 0xc8, 0x73, 0x83, 0x3b, 0x99, 0x64, 0xbb, 0xbb, 0x0f, 0xcc, 0x80, 0xef, 0xf3, 0xdc, 0x0f, + 0xc2, 0x8c, 0xbd, 0x03, 0x9d, 0xdc, 0x18, 0x4c, 0xa2, 0xda, 0xde, 0x65, 0x37, 0xe9, 0x8e, 0xdd, + 0x34, 0x3e, 0xf0, 0xac, 0x71, 0xee, 0x7f, 0x3b, 0xd0, 0x3e, 0xe4, 0x91, 0xbe, 0x5d, 0x0c, 0x9a, + 0xb8, 0x12, 0x79, 0x92, 0xf4, 0x9b, 0x7d, 0x1e, 0xda, 0xb4, 0xba, 0x2c, 0x4f, 0x83, 0x68, 0x42, + 0x47, 0xd0, 0xf2, 0x00, 0xa1, 0x43, 0x42, 0xd8, 0x1a, 0x2c, 0xf8, 0xd3, 0x9c, 0x18, 0xbf, 0xe0, + 0xe1, 0x4f, 0xbc, 0x77, 0x89, 0x3f, 0x9f, 0xf2, 0x28, 0x2f, 0x98, 0xdd, 0xf1, 0xda, 0x12, 0xdb, + 0x43, 0x6e, 0xdf, 0x84, 0x0d, 0x73, 0x88, 0xa2, 0xbe, 0x48, 0xd4, 0xd7, 0x8d, 0x91, 0x72, 0x92, + 0x6b, 0xb0, 0xaa, 0xc6, 0xa7, 0x62, 0xb1, 0xc4, 0xfe, 0x96, 0xd7, 0x93, 0xb0, 0xda, 0xc2, 0x75, + 0x58, 0x3b, 0x0e, 0x22, 0x3f, 0x1c, 0x8e, 0xc2, 0xfc, 0x74, 0x38, 0xe6, 0x61, 0xee, 0xd3, 0x41, + 0x2c, 0x7a, 0x3d, 0xc2, 0xef, 0x85, 0xf9, 0xe9, 0x7d, 0x44, 0xdd, 0x3f, 0x73, 0xa0, 0x23, 0x36, + 0x2f, 0x2f, 0xfe, 0x1b, 0xd0, 0x55, 0x73, 0xf0, 0x34, 0x8d, 0x53, 0x29, 0x87, 0x36, 0xc8, 0x6e, + 0xc0, 0x9a, 0x02, 0x92, 0x94, 0x07, 0x53, 0x7f, 0xc2, 0xe5, 0x6d, 0xaf, 0xe0, 0x6c, 0xb7, 0xa0, + 0x98, 0xc6, 0xb3, 0x5c, 0x5c, 0xbd, 0xf6, 0x6e, 0x47, 0x1e, 0x8c, 0x87, 0x98, 0x67, 0x0f, 0x71, + 0xff, 0xda, 0x81, 0xce, 0xbd, 0x13, 0x3f, 0x8a, 0x78, 0x78, 0x10, 0x07, 0x51, 0xce, 0x6e, 0x01, + 0x3b, 0x9e, 0x45, 0xe3, 0x20, 0x9a, 0x0c, 0xf3, 0x67, 0xc1, 0x78, 0x78, 0x34, 0xcf, 0x79, 0x26, + 0x8e, 0x68, 0xef, 0x82, 0x57, 0xd3, 0xc7, 0xde, 0x82, 0x35, 0x0b, 0xcd, 0xf2, 0x54, 0x9c, 0xdb, + 0xde, 0x05, 0xaf, 0xd2, 0x83, 0x82, 0x1f, 0xcf, 0xf2, 0x64, 0x96, 0x0f, 0x83, 0x68, 0xcc, 0x9f, + 0xd1, 0x1a, 0xbb, 0x9e, 0x85, 0xdd, 0xed, 0x41, 0xc7, 0xfc, 0xce, 0xfd, 0x0a, 0xac, 0xed, 0xe3, + 0x8d, 0x88, 0x82, 0x68, 0x72, 0x47, 0x88, 0x2d, 0x5e, 0xd3, 0x64, 0x76, 0xf4, 0x94, 0xcf, 0x25, + 0xdf, 0x64, 0x0b, 0x85, 0xea, 0x24, 0xce, 0x72, 0x29, 0x39, 0xf4, 0xdb, 0xfd, 0x37, 0x07, 0x56, + 0x91, 0xf7, 0x1f, 0xfa, 0xd1, 0x5c, 0x9d, 0xdc, 0x3e, 0x74, 0x90, 0xd4, 0xe3, 0xf8, 0x8e, 0xb8, + 0xec, 0x42, 0x88, 0xaf, 0x4b, 0x5e, 0x95, 0x46, 0xdf, 0x34, 0x87, 0xa2, 0x32, 0x9f, 0x7b, 0xd6, + 0xd7, 0x28, 0xb6, 0xb9, 0x9f, 0x4e, 0x78, 0x4e, 0x6a, 0x40, 0xaa, 0x05, 0x10, 0xd0, 0xbd, 0x38, + 0x3a, 0x66, 0x57, 0xa1, 0x93, 0xf9, 0xf9, 0x30, 0xe1, 0x29, 0x71, 0x8d, 0x44, 0x6f, 0xc1, 0x83, + 0xcc, 0xcf, 0x0f, 0x78, 0x7a, 0x77, 0x9e, 0xf3, 0xc1, 0x57, 0x61, 0xbd, 0x32, 0x0b, 0x4a, 0x7b, + 0xb1, 0x45, 0xfc, 0xc9, 0x36, 0x61, 0xf1, 0xd4, 0x0f, 0x67, 0x5c, 0x6a, 0x27, 0xd1, 0x78, 0xaf, + 0xf1, 0xae, 0xe3, 0xbe, 0x09, 0x6b, 0xc5, 0xb2, 0xa5, 0x90, 0x31, 0x68, 0x22, 0x07, 0x25, 0x01, + 0xfa, 0xed, 0xfe, 0x9e, 0x23, 0x06, 0xde, 0x8b, 0x03, 0x7d, 0xd3, 0x71, 0x20, 0x2a, 0x04, 0x35, + 0x10, 0x7f, 0x9f, 0xab, 0x09, 0x7f, 0xfe, 0xcd, 0xba, 0xd7, 0x60, 0xdd, 0x58, 0xc2, 0x0b, 0x16, + 0xfb, 0x57, 0x0e, 0xac, 0x3f, 0xe2, 0x67, 0xf2, 0xd4, 0xd5, 0x6a, 0xdf, 0x85, 0x66, 0x3e, 0x4f, + 0x84, 0x29, 0xee, 0xed, 0xbe, 0x21, 0x0f, 0xad, 0x32, 0xee, 0xa6, 0x6c, 0x3e, 0x9e, 0x27, 0xdc, + 0xa3, 0x2f, 0xdc, 0x6f, 0x40, 0xdb, 0x00, 0xd9, 0x36, 0x6c, 0x7c, 0xf4, 0xc1, 0xe3, 0x47, 0x0f, + 0x0e, 0x0f, 0x87, 0x07, 0x4f, 0xee, 0x7e, 0xfd, 0xc1, 0x6f, 0x0c, 0xf7, 0xee, 0x1c, 0xee, 0xad, + 0x5d, 0x60, 0x5b, 0xc0, 0x1e, 0x3d, 0x38, 0x7c, 0xfc, 0xe0, 0xbe, 0x85, 0x3b, 0x6c, 0x15, 0xda, + 0x26, 0xd0, 0x70, 0x07, 0xd0, 0x7f, 0xc4, 0xcf, 0x3e, 0x0a, 0xf2, 0x88, 0x67, 0x99, 0x3d, 0xbd, + 0x7b, 0x13, 0x98, 0xb9, 0x26, 0xb9, 0xcd, 0x3e, 0x2c, 0x4b, 0xdd, 0xab, 0x4c, 0x8f, 0x6c, 0xba, + 0x6f, 0x02, 0x3b, 0x0c, 0x26, 0xd1, 0x87, 0x3c, 0xcb, 0xfc, 0x09, 0x57, 0x9b, 0x5d, 0x83, 0x85, + 0x69, 0x36, 0x91, 0x5a, 0x12, 0x7f, 0xba, 0x5f, 0x80, 0x0d, 0x6b, 0x9c, 0x24, 0x7c, 0x19, 0x5a, + 0x59, 0x30, 0x89, 0xfc, 0x7c, 0x96, 0x72, 0x49, 0xba, 0x00, 0xdc, 0xf7, 0x61, 0xf3, 0x5b, 0x3c, + 0x0d, 0x8e, 0xe7, 0x2f, 0x23, 0x6f, 0xd3, 0x69, 0x94, 0xe9, 0x3c, 0x80, 0x8b, 0x25, 0x3a, 0x72, + 0x7a, 0x21, 0x99, 0xf2, 0xfc, 0x56, 0x3c, 0xd1, 0x30, 0xee, 0x69, 0xc3, 0xbc, 0xa7, 0xee, 0x13, + 0x60, 0xf7, 0xe2, 0x28, 0xe2, 0xa3, 0xfc, 0x80, 0xf3, 0xb4, 0x70, 0xb8, 0x0a, 0x31, 0x6c, 0xef, + 0x6e, 0xcb, 0x83, 0x2d, 0x5f, 0x7e, 0x29, 0x9f, 0x0c, 0x9a, 0x09, 0x4f, 0xa7, 0x44, 0x78, 0xc5, + 0xa3, 0xdf, 0xee, 0x45, 0xd8, 0xb0, 0xc8, 0x4a, 0xf3, 0xff, 0x36, 0x5c, 0xbc, 0x1f, 0x64, 0xa3, + 0xea, 0x84, 0x7d, 0x58, 0x4e, 0x66, 0x47, 0xc3, 0xe2, 0x92, 0xa9, 0x26, 0x5a, 0xc5, 0xf2, 0x27, + 0x92, 0xd8, 0x1f, 0x3a, 0xd0, 0xdc, 0x7b, 0xbc, 0x7f, 0x8f, 0x0d, 0x60, 0x25, 0x88, 0x46, 0xf1, + 0x14, 0x6d, 0x89, 0xd8, 0xb4, 0x6e, 0x9f, 0x7b, 0x79, 0x2e, 0x43, 0x8b, 0x4c, 0x10, 0x1a, 0x7a, + 0xe9, 0x1b, 0x15, 0x00, 0x3a, 0x19, 0xfc, 0x59, 0x12, 0xa4, 0xe4, 0x45, 0x28, 0xdf, 0xa0, 0x49, + 0x2a, 0xb2, 0xda, 0xe1, 0xfe, 0xa4, 0x09, 0xdd, 0x3b, 0xa3, 0x3c, 0x38, 0xe5, 0x52, 0x85, 0xd3, + 0xac, 0x04, 0xc8, 0xf5, 0xc8, 0x16, 0x1a, 0x9b, 0x94, 0x4f, 0xe3, 0x9c, 0x0f, 0xad, 0xc3, 0xb0, + 0x41, 0x1c, 0x35, 0x12, 0x84, 0x86, 0x09, 0x1a, 0x03, 0x5a, 0x5f, 0xcb, 0xb3, 0x41, 0x64, 0x19, + 0x02, 0xc3, 0x60, 0x4c, 0x2b, 0x6b, 0x7a, 0xaa, 0x89, 0xfc, 0x18, 0xf9, 0x89, 0x3f, 0x0a, 0xf2, + 0xb9, 0xbc, 0xf3, 0xba, 0x8d, 0xb4, 0xc3, 0x78, 0xe4, 0x87, 0xc3, 0x23, 0x3f, 0xf4, 0xa3, 0x11, + 0x97, 0xfe, 0x8c, 0x0d, 0xa2, 0xcb, 0x22, 0x97, 0xa4, 0x86, 0x09, 0xb7, 0xa6, 0x84, 0xa2, 0xeb, + 0x33, 0x8a, 0xa7, 0xd3, 0x20, 0x47, 0x4f, 0xa7, 0xbf, 0x22, 0xf4, 0x4b, 0x81, 0xd0, 0x4e, 0x44, + 0xeb, 0x4c, 0xf0, 0xb0, 0x25, 0x66, 0xb3, 0x40, 0xa4, 0x72, 0xcc, 0x39, 0xe9, 0xa9, 0xa7, 0x67, + 0x7d, 0x10, 0x54, 0x0a, 0x04, 0x4f, 0x63, 0x16, 0x65, 0x3c, 0xcf, 0x43, 0x3e, 0xd6, 0x0b, 0x6a, + 0xd3, 0xb0, 0x6a, 0x07, 0xbb, 0x05, 0x1b, 0xc2, 0xf9, 0xca, 0xfc, 0x3c, 0xce, 0x4e, 0x82, 0x6c, + 0x98, 0xf1, 0x28, 0xef, 0x77, 0x68, 0x7c, 0x5d, 0x17, 0x7b, 0x17, 0xb6, 0x4b, 0x70, 0xca, 0x47, + 0x3c, 0x38, 0xe5, 0xe3, 0x7e, 0x97, 0xbe, 0x3a, 0xaf, 0x9b, 0x5d, 0x85, 0x36, 0xfa, 0x9c, 0xb3, + 0x64, 0xec, 0xa3, 0x79, 0xee, 0xd1, 0x39, 0x98, 0x10, 0x7b, 0x1b, 0xba, 0x09, 0x17, 0x36, 0xf4, + 0x24, 0x0f, 0x47, 0x59, 0x7f, 0x95, 0x0c, 0x5c, 0x5b, 0x5e, 0x29, 0x94, 0x5f, 0xcf, 0x1e, 0x81, + 0xa2, 0x39, 0xca, 0xc8, 0x8b, 0xf1, 0xe7, 0xfd, 0x35, 0x12, 0xba, 0x02, 0xc0, 0x9b, 0xb5, 0x1f, + 0x64, 0xb9, 0x94, 0x34, 0xad, 0xe3, 0xf6, 0x60, 0xd3, 0x86, 0x75, 0x5c, 0xb3, 0x22, 0xc5, 0x26, + 0xeb, 0xb7, 0x69, 0xea, 0x4d, 0x39, 0xb5, 0x25, 0xb1, 0x9e, 0x1e, 0xe5, 0xfe, 0xc4, 0x81, 0x26, + 0xde, 0xb3, 0xf3, 0xef, 0xa4, 0xa9, 0x3a, 0x17, 0x2c, 0xd5, 0x49, 0xfe, 0x36, 0x7a, 0x23, 0x82, + 0xe7, 0x42, 0x2e, 0x0d, 0xa4, 0xe8, 0x4f, 0xf9, 0xe8, 0x94, 0x84, 0x53, 0xf7, 0x23, 0x82, 0xa2, + 0x8b, 0x26, 0x8b, 0xbe, 0x16, 0x92, 0xa9, 0xdb, 0xaa, 0x8f, 0xbe, 0x5c, 0x2e, 0xfa, 0xe8, 0xbb, + 0x3e, 0x2c, 0x07, 0xd1, 0x51, 0x3c, 0x8b, 0xc6, 0x24, 0x85, 0x2b, 0x9e, 0x6a, 0x22, 0x37, 0x13, + 0xf2, 0x60, 0x82, 0x29, 0x97, 0xe2, 0x57, 0x00, 0x2e, 0x43, 0x97, 0x26, 0x23, 0xbd, 0xa2, 0x59, + 0xf9, 0x0e, 0xac, 0x1b, 0x98, 0xe4, 0xe3, 0xeb, 0xb0, 0x98, 0x20, 0x20, 0x1d, 0x14, 0x75, 0x7e, + 0xa4, 0x90, 0x44, 0x8f, 0xbb, 0x86, 0x71, 0x6b, 0xfe, 0x41, 0x74, 0x1c, 0x2b, 0x4a, 0xff, 0xb8, + 0x80, 0x81, 0xa6, 0x84, 0x24, 0xa1, 0xeb, 0xb0, 0x1a, 0x8c, 0x79, 0x94, 0x07, 0xf9, 0x7c, 0x68, + 0x79, 0x4e, 0x65, 0x18, 0x15, 0xb9, 0x1f, 0x06, 0x7e, 0x26, 0x95, 0x84, 0x68, 0xb0, 0x5d, 0xd8, + 0x44, 0xf9, 0x52, 0x22, 0xa3, 0x0f, 0x57, 0x38, 0x70, 0xb5, 0x7d, 0x78, 0x25, 0x10, 0x17, 0x4a, + 0xa8, 0xf8, 0x44, 0x28, 0xb4, 0xba, 0x2e, 0xe4, 0x9a, 0xa0, 0x84, 0x5b, 0x5e, 0x14, 0x32, 0xa8, + 0x81, 0x4a, 0xd4, 0xb4, 0x24, 0x9c, 0xc7, 0x72, 0xd4, 0x64, 0x44, 0x5e, 0x2b, 0x95, 0xc8, 0xeb, + 0x3a, 0xac, 0x66, 0xf3, 0x68, 0xc4, 0xc7, 0xc3, 0x3c, 0xc6, 0x79, 0x83, 0x88, 0x4e, 0x67, 0xc5, + 0x2b, 0xc3, 0x14, 0x23, 0xf2, 0x2c, 0x8f, 0x78, 0x4e, 0xba, 0x61, 0xc5, 0x53, 0x4d, 0x54, 0xb3, + 0x34, 0x44, 0x88, 0x76, 0xcb, 0x93, 0x2d, 0xb4, 0x48, 0xb3, 0x34, 0xc8, 0xfa, 0x1d, 0x42, 0xe9, + 0x37, 0xfb, 0x22, 0x5c, 0x3c, 0xc2, 0x88, 0xe6, 0x84, 0xfb, 0x63, 0x9e, 0xd2, 0xe9, 0x8b, 0x80, + 0x4e, 0x5c, 0xf1, 0xfa, 0x4e, 0xf7, 0x53, 0x32, 0x8f, 0x3a, 0xa0, 0x7c, 0x42, 0xb7, 0x9a, 0xbd, + 0x06, 0x2d, 0xb1, 0x93, 0xec, 0xc4, 0x97, 0x16, 0x7b, 0x85, 0x80, 0xc3, 0x13, 0x1f, 0xe3, 0x20, + 0x8b, 0x39, 0x0d, 0xf2, 0xcb, 0xda, 0x84, 0xed, 0x09, 0xde, 0xbc, 0x01, 0x3d, 0x15, 0xaa, 0x66, + 0xc3, 0x90, 0x1f, 0xe7, 0xca, 0xfd, 0x8e, 0x66, 0x53, 0x9c, 0x2e, 0xdb, 0xe7, 0xc7, 0xb9, 0xfb, + 0x08, 0xd6, 0xe5, 0xed, 0xfc, 0x46, 0xc2, 0xd5, 0xd4, 0x5f, 0x2e, 0xdb, 0x06, 0x61, 0xa2, 0x37, + 0xa4, 0x3c, 0x9a, 0x31, 0x44, 0xc9, 0x60, 0xb8, 0x1e, 0x30, 0xd9, 0x7d, 0x2f, 0x8c, 0x33, 0x2e, + 0x09, 0xba, 0xd0, 0x19, 0x85, 0x71, 0xa6, 0x9c, 0x7c, 0xb9, 0x1d, 0x0b, 0xc3, 0x13, 0xc8, 0x66, + 0xa3, 0x11, 0xde, 0x77, 0x61, 0xe4, 0x55, 0xd3, 0xfd, 0x1b, 0x07, 0x36, 0x88, 0x9a, 0xd2, 0x23, + 0xda, 0x33, 0x7c, 0xf5, 0x65, 0x76, 0x46, 0x66, 0xe0, 0xb3, 0x09, 0x8b, 0xc7, 0x71, 0x3a, 0xe2, + 0x72, 0x26, 0xd1, 0xf8, 0xe9, 0x7d, 0xdd, 0x66, 0xc5, 0xd7, 0xfd, 0x17, 0x07, 0xd6, 0x69, 0xa9, + 0x87, 0xb9, 0x9f, 0xcf, 0x32, 0xb9, 0xfd, 0x5f, 0x85, 0x2e, 0x6e, 0x95, 0xab, 0x4b, 0x23, 0x17, + 0xba, 0xa9, 0xef, 0x37, 0xa1, 0x62, 0xf0, 0xde, 0x05, 0xcf, 0x1e, 0xcc, 0xbe, 0x0a, 0x1d, 0xf3, + 0xbd, 0x81, 0xd6, 0xdc, 0xde, 0xbd, 0xa4, 0x76, 0x59, 0x91, 0x9c, 0xbd, 0x0b, 0x9e, 0xf5, 0x01, + 0xbb, 0x0d, 0x40, 0x56, 0x9b, 0xc8, 0xca, 0x40, 0xf1, 0x92, 0xcd, 0x24, 0xe3, 0xb0, 0xf6, 0x2e, + 0x78, 0xc6, 0xf0, 0xbb, 0x2b, 0xb0, 0x24, 0xcc, 0x8c, 0xfb, 0x10, 0xba, 0xd6, 0x4a, 0x2d, 0x1f, + 0xbe, 0x23, 0x7c, 0xf8, 0x4a, 0xc8, 0xd7, 0xa8, 0x86, 0x7c, 0xee, 0xdf, 0x37, 0x80, 0xa1, 0xb4, + 0x95, 0x8e, 0x13, 0xed, 0x5c, 0x3c, 0xb6, 0xbc, 0x96, 0x8e, 0x67, 0x42, 0xec, 0x26, 0x30, 0xa3, + 0xa9, 0x22, 0x7b, 0x61, 0x1d, 0x6a, 0x7a, 0x50, 0x8d, 0x09, 0x97, 0x43, 0x45, 0x98, 0xd2, 0x4b, + 0x13, 0xe7, 0x56, 0xdb, 0x87, 0x06, 0x20, 0x99, 0x65, 0x27, 0x68, 0x87, 0x95, 0x5f, 0xa3, 0xda, + 0x65, 0x01, 0x59, 0x7a, 0xa9, 0x80, 0x2c, 0x97, 0x05, 0x84, 0xec, 0x5d, 0x1a, 0x9c, 0xfa, 0x39, + 0x57, 0x36, 0x44, 0x36, 0xd1, 0x8d, 0x99, 0x06, 0x11, 0x99, 0xe7, 0xe1, 0x14, 0x67, 0x97, 0x6e, + 0x8c, 0x05, 0xba, 0x3f, 0x76, 0x60, 0x0d, 0x79, 0x67, 0xc9, 0xd7, 0x7b, 0x40, 0xe2, 0xfd, 0x8a, + 0xe2, 0x65, 0x8d, 0xfd, 0xf9, 0xa5, 0xeb, 0x5d, 0x68, 0x11, 0xc1, 0x38, 0xe1, 0x91, 0x14, 0xae, + 0xbe, 0x2d, 0x5c, 0x85, 0x66, 0xd9, 0xbb, 0xe0, 0x15, 0x83, 0x0d, 0xd1, 0xfa, 0x67, 0x07, 0xda, + 0x72, 0x99, 0x3f, 0xb3, 0xb3, 0x3d, 0x80, 0x15, 0x94, 0x32, 0xc3, 0x97, 0xd5, 0x6d, 0xb4, 0x03, + 0x53, 0x8c, 0x68, 0xd0, 0xf0, 0x59, 0x8e, 0x76, 0x19, 0x46, 0x2b, 0x46, 0x4a, 0x34, 0x1b, 0xe6, + 0x41, 0x38, 0x54, 0xbd, 0xf2, 0xc9, 0xae, 0xae, 0x0b, 0x75, 0x49, 0x96, 0xfb, 0x13, 0x2e, 0x0d, + 0x94, 0x68, 0x60, 0x44, 0x21, 0x37, 0x54, 0x76, 0xa2, 0x7e, 0x04, 0xb0, 0x5d, 0xe9, 0xd2, 0x8e, + 0x94, 0xf4, 0x1d, 0xc3, 0x60, 0x7a, 0x14, 0x6b, 0x37, 0xd4, 0x31, 0xdd, 0x4a, 0xab, 0x8b, 0x4d, + 0xe0, 0xa2, 0xb2, 0xc4, 0xc8, 0xd3, 0xc2, 0xee, 0x36, 0xc8, 0x85, 0x78, 0xdb, 0x96, 0x81, 0xf2, + 0x84, 0x0a, 0x37, 0x6f, 0x63, 0x3d, 0x3d, 0x76, 0x02, 0x7d, 0x6d, 0xf2, 0xa5, 0xda, 0x36, 0xdc, + 0x02, 0x9c, 0xeb, 0xad, 0x97, 0xcc, 0x45, 0x3a, 0x66, 0xac, 0xa6, 0x39, 0x97, 0x1a, 0x9b, 0xc3, + 0x15, 0xd5, 0x47, 0x7a, 0xb9, 0x3a, 0x5f, 0xf3, 0x95, 0xf6, 0xf6, 0x3e, 0x7e, 0x6c, 0x4f, 0xfa, + 0x12, 0xc2, 0x83, 0x1f, 0x39, 0xd0, 0xb3, 0xc9, 0xa1, 0xe8, 0xc8, 0x78, 0x44, 0x29, 0x18, 0xe5, + 0x4a, 0x95, 0xe0, 0x6a, 0x44, 0xd5, 0xa8, 0x8b, 0xa8, 0xcc, 0xb8, 0x69, 0xe1, 0x65, 0x71, 0x53, + 0xf3, 0xd5, 0xe2, 0xa6, 0xc5, 0xba, 0xb8, 0x69, 0xf0, 0x5f, 0x0e, 0xb0, 0xea, 0xf9, 0xb2, 0x87, + 0x22, 0xa4, 0x8b, 0x78, 0x28, 0xf5, 0xc4, 0x2f, 0xbf, 0x9a, 0x8c, 0x28, 0x1e, 0xaa, 0xaf, 0x51, + 0x58, 0x4d, 0x45, 0x60, 0xba, 0x22, 0x5d, 0xaf, 0xae, 0xab, 0x14, 0xc9, 0x35, 0x5f, 0x1e, 0xc9, + 0x2d, 0xbe, 0x3c, 0x92, 0x5b, 0x2a, 0x47, 0x72, 0x83, 0xdf, 0x81, 0xae, 0x75, 0xea, 0xff, 0x7b, + 0x3b, 0x2e, 0xbb, 0x31, 0xe2, 0x80, 0x2d, 0x6c, 0xf0, 0x1f, 0x0d, 0x60, 0x55, 0xc9, 0xfb, 0x7f, + 0x5d, 0x03, 0xc9, 0x91, 0xa5, 0x40, 0x16, 0xa4, 0x1c, 0x59, 0xaa, 0xe3, 0xff, 0x52, 0x29, 0xbe, + 0x05, 0xeb, 0x29, 0x1f, 0xc5, 0xa7, 0x94, 0xb6, 0xb2, 0x5f, 0x01, 0xaa, 0x1d, 0xe8, 0xc8, 0xd9, + 0xf1, 0xeb, 0x8a, 0x95, 0x65, 0x30, 0x2c, 0x43, 0x29, 0x8c, 0x75, 0xbf, 0x0c, 0x9b, 0x22, 0xf9, + 0x73, 0x57, 0x90, 0x52, 0xbe, 0xc4, 0xeb, 0xd0, 0x39, 0x13, 0xcf, 0x74, 0xc3, 0x38, 0x0a, 0xe7, + 0xd2, 0x88, 0xb4, 0x25, 0xf6, 0x8d, 0x28, 0x9c, 0xbb, 0xdf, 0x77, 0xe0, 0x62, 0xe9, 0xdb, 0xe2, + 0xb5, 0x5e, 0xa8, 0x5a, 0x5b, 0xff, 0xda, 0x20, 0x6e, 0x51, 0xca, 0xb8, 0xb1, 0x45, 0x61, 0x92, + 0xaa, 0x1d, 0xc8, 0xc2, 0x59, 0x54, 0x1d, 0x2f, 0x0e, 0xa6, 0xae, 0xcb, 0xdd, 0x86, 0x8b, 0xf2, + 0xf0, 0xed, 0xbd, 0xb9, 0xbb, 0xb0, 0x55, 0xee, 0x28, 0x5e, 0x1b, 0xed, 0x25, 0xab, 0xa6, 0xfb, + 0xdb, 0xc0, 0xbe, 0x39, 0xe3, 0xe9, 0x9c, 0xf2, 0x02, 0xfa, 0x69, 0x75, 0xbb, 0x1c, 0x7c, 0x2f, + 0x25, 0xb3, 0xa3, 0xaf, 0xf3, 0xb9, 0x4a, 0xbc, 0x34, 0x8a, 0xc4, 0xcb, 0xe7, 0x00, 0x30, 0x9a, + 0xa0, 0x44, 0x82, 0x4a, 0x85, 0x61, 0xb0, 0x26, 0x08, 0xba, 0xb7, 0x61, 0xc3, 0xa2, 0xaf, 0x39, + 0xb9, 0x24, 0xbf, 0x10, 0x11, 0xad, 0x9d, 0x9e, 0x90, 0x7d, 0xee, 0x9f, 0x3b, 0xb0, 0xb0, 0x17, + 0x27, 0xe6, 0x63, 0x93, 0x63, 0x3f, 0x36, 0x49, 0xd5, 0x3a, 0xd4, 0x9a, 0xb3, 0x21, 0x15, 0x83, + 0x09, 0xa2, 0x62, 0xf4, 0xa7, 0x39, 0xc6, 0x74, 0xc7, 0x71, 0x7a, 0xe6, 0xa7, 0x63, 0xc9, 0xde, + 0x12, 0x8a, 0xbb, 0x2b, 0xf4, 0x0f, 0xfe, 0x44, 0x9f, 0x82, 0x5e, 0xdc, 0xe6, 0x32, 0x0c, 0x95, + 0x2d, 0xf7, 0x4f, 0x1c, 0x58, 0xa4, 0xb5, 0xe2, 0x65, 0x11, 0xc7, 0x4f, 0x39, 0x39, 0x7a, 0xd0, + 0x73, 0xc4, 0x65, 0x29, 0xc1, 0xa5, 0x4c, 0x5d, 0xa3, 0x92, 0xa9, 0xbb, 0x0c, 0x2d, 0xd1, 0x2a, + 0x52, 0x5b, 0x05, 0xc0, 0xae, 0x40, 0xf3, 0x24, 0x4e, 0x94, 0x89, 0x03, 0xf5, 0x82, 0x13, 0x27, + 0x1e, 0xe1, 0xee, 0x0d, 0x58, 0x7d, 0x14, 0x8f, 0xb9, 0xf1, 0x00, 0x70, 0xee, 0x29, 0xba, 0xbf, + 0xeb, 0xc0, 0x8a, 0x1a, 0xcc, 0xae, 0x43, 0x13, 0x2d, 0x55, 0xc9, 0x37, 0xd4, 0xaf, 0xad, 0x38, + 0xce, 0xa3, 0x11, 0xa8, 0x61, 0x28, 0x70, 0x2c, 0x3c, 0x09, 0x15, 0x36, 0x16, 0x36, 0xfa, 0x4d, + 0xe8, 0x89, 0x35, 0x97, 0x6c, 0x59, 0x09, 0x75, 0xff, 0xd6, 0x81, 0xae, 0x35, 0x07, 0x7a, 0xf9, + 0xa1, 0x9f, 0xe5, 0xf2, 0xed, 0x4a, 0x32, 0xd1, 0x84, 0xcc, 0x27, 0xa1, 0x86, 0xfd, 0x24, 0xa4, + 0x1f, 0x2b, 0x16, 0xcc, 0xc7, 0x8a, 0x5b, 0xd0, 0x2a, 0xb2, 0x9e, 0x4d, 0x4b, 0x73, 0xe0, 0x8c, + 0xea, 0x1d, 0xb9, 0x18, 0x84, 0x74, 0x46, 0x71, 0x18, 0xa7, 0x32, 0x29, 0x28, 0x1a, 0xee, 0x6d, + 0x68, 0x1b, 0xe3, 0x71, 0x19, 0x11, 0xcf, 0xcf, 0xe2, 0xf4, 0xa9, 0x7a, 0x99, 0x92, 0x4d, 0x9d, + 0x3f, 0x69, 0x14, 0xf9, 0x13, 0xf7, 0xef, 0x1c, 0xe8, 0xa2, 0xa4, 0x04, 0xd1, 0xe4, 0x20, 0x0e, + 0x83, 0xd1, 0x9c, 0x24, 0x46, 0x09, 0x85, 0xcc, 0x16, 0x2a, 0x89, 0xb1, 0x61, 0x74, 0x09, 0x94, + 0x93, 0x2f, 0xe5, 0x45, 0xb7, 0x51, 0xf2, 0xd1, 0xb4, 0x1d, 0xf9, 0x19, 0x17, 0x51, 0x81, 0x54, + 0xe5, 0x16, 0x88, 0xda, 0x05, 0x81, 0xd4, 0xcf, 0xf9, 0x70, 0x1a, 0x84, 0x61, 0x20, 0xc6, 0x0a, + 0x09, 0xaf, 0xeb, 0x72, 0x7f, 0xd8, 0x80, 0xb6, 0xd4, 0x22, 0x0f, 0xc6, 0x13, 0xf1, 0xc8, 0x2a, + 0xfd, 0x14, 0x7d, 0xfd, 0x0c, 0x44, 0xf5, 0x5b, 0x9e, 0x8d, 0x81, 0x94, 0x8f, 0x75, 0xa1, 0x7a, + 0xac, 0x97, 0xa1, 0x85, 0xe2, 0xf5, 0x36, 0xb9, 0x50, 0x22, 0x49, 0x5e, 0x00, 0xaa, 0x77, 0x97, + 0x7a, 0x17, 0x8b, 0x5e, 0x02, 0x2c, 0xa7, 0x69, 0xa9, 0xe4, 0x34, 0xbd, 0x0b, 0x1d, 0x49, 0x86, + 0xf8, 0x4e, 0x31, 0x57, 0x21, 0xe0, 0xd6, 0x99, 0x78, 0xd6, 0x48, 0xf5, 0xe5, 0xae, 0xfa, 0x72, + 0xe5, 0x65, 0x5f, 0xaa, 0x91, 0x94, 0x79, 0x10, 0xbc, 0x79, 0x98, 0xfa, 0xc9, 0x89, 0xd2, 0xcc, + 0x63, 0x9d, 0x5f, 0x25, 0x98, 0xdd, 0x80, 0x45, 0xfc, 0x4c, 0x69, 0xbf, 0xfa, 0x4b, 0x27, 0x86, + 0xb0, 0xeb, 0xb0, 0xc8, 0xc7, 0x13, 0xae, 0x1c, 0x77, 0x66, 0x87, 0x50, 0x78, 0x46, 0x9e, 0x18, + 0x80, 0x2a, 0x00, 0xd1, 0x92, 0x0a, 0xb0, 0x35, 0xe7, 0x12, 0x36, 0x3f, 0x18, 0xbb, 0x9b, 0xc0, + 0x1e, 0x09, 0xa9, 0x35, 0x9f, 0x0c, 0xff, 0x60, 0x01, 0xda, 0x06, 0x8c, 0xb7, 0x79, 0x82, 0x0b, + 0x1e, 0x8e, 0x03, 0x7f, 0xca, 0x73, 0x9e, 0x4a, 0x49, 0x2d, 0xa1, 0xa4, 0x60, 0x4f, 0x27, 0xc3, + 0x78, 0x96, 0x0f, 0xc7, 0x7c, 0x92, 0x72, 0x61, 0xef, 0x1c, 0xaf, 0x84, 0xe2, 0xb8, 0xa9, 0xff, + 0xcc, 0x1c, 0x27, 0xe4, 0xa1, 0x84, 0xaa, 0x07, 0x40, 0xc1, 0xa3, 0x66, 0xf1, 0x00, 0x28, 0x38, + 0x52, 0xd6, 0x43, 0x8b, 0x35, 0x7a, 0xe8, 0x1d, 0xd8, 0x12, 0x1a, 0x47, 0xde, 0xcd, 0x61, 0x49, + 0x4c, 0xce, 0xe9, 0x65, 0x37, 0x60, 0x0d, 0xd7, 0xac, 0x04, 0x3c, 0x0b, 0x3e, 0x15, 0xc1, 0xba, + 0xe3, 0x55, 0x70, 0x1c, 0x8b, 0xd7, 0xd1, 0x1a, 0x2b, 0xb2, 0x10, 0x15, 0x9c, 0xc6, 0xfa, 0xcf, + 0xec, 0xb1, 0x2d, 0x39, 0xb6, 0x84, 0xbb, 0x5d, 0x68, 0x1f, 0xe6, 0x71, 0xa2, 0x0e, 0xa5, 0x07, + 0x1d, 0xd1, 0x94, 0x99, 0xa7, 0xd7, 0xe0, 0x12, 0x49, 0xd1, 0xe3, 0x38, 0x89, 0xc3, 0x78, 0x32, + 0x3f, 0x9c, 0x1d, 0x65, 0xa3, 0x34, 0x48, 0xd0, 0xa1, 0x76, 0xff, 0xc9, 0x81, 0x0d, 0xab, 0x57, + 0xbe, 0x04, 0x7c, 0x51, 0x88, 0xb4, 0x4e, 0x16, 0x08, 0xc1, 0x5b, 0x37, 0xd4, 0xa1, 0x18, 0x28, + 0xde, 0x55, 0x9e, 0xc8, 0xfc, 0xc1, 0x1d, 0x58, 0x55, 0x2b, 0x53, 0x1f, 0x0a, 0x29, 0xec, 0x57, + 0xa5, 0x50, 0x7e, 0xdf, 0x93, 0x1f, 0x28, 0x12, 0xbf, 0x26, 0xdc, 0x52, 0x3e, 0xa6, 0x3d, 0xaa, + 0x90, 0x70, 0xa0, 0xbe, 0x37, 0x7d, 0x61, 0xb5, 0x82, 0x91, 0x06, 0x33, 0xf7, 0x8f, 0x1c, 0x80, + 0x62, 0x75, 0x28, 0x18, 0x85, 0x4a, 0x17, 0xd5, 0x51, 0x86, 0xfa, 0x7e, 0x1d, 0x3a, 0xfa, 0x19, + 0xbb, 0xb0, 0x12, 0x6d, 0x85, 0xa1, 0x03, 0x73, 0x0d, 0x56, 0x27, 0x61, 0x7c, 0x44, 0x36, 0x97, + 0x52, 0x99, 0x99, 0xcc, 0xbf, 0xf5, 0x04, 0xfc, 0xbe, 0x44, 0x0b, 0x93, 0xd2, 0x34, 0x4c, 0x8a, + 0xfb, 0xc7, 0x0d, 0xfd, 0x2c, 0x5a, 0xec, 0xf9, 0xdc, 0x5b, 0xc6, 0x76, 0x2b, 0xca, 0xf1, 0x9c, + 0x57, 0x48, 0x7a, 0xfc, 0x38, 0x78, 0x69, 0x1c, 0x78, 0x1b, 0x7a, 0xa9, 0xd0, 0x3e, 0x4a, 0x35, + 0x35, 0x5f, 0xa0, 0x9a, 0xba, 0xa9, 0x65, 0x77, 0x7e, 0x11, 0xd6, 0xfc, 0xf1, 0x29, 0x4f, 0xf3, + 0x80, 0x02, 0x02, 0x32, 0xfa, 0x42, 0xa1, 0xae, 0x1a, 0x38, 0xd9, 0xe2, 0x6b, 0xb0, 0x2a, 0x73, + 0x9e, 0x7a, 0xa4, 0x2c, 0x7d, 0x29, 0x60, 0x1c, 0xe8, 0xfe, 0x40, 0xbd, 0xc0, 0xda, 0x67, 0x78, + 0x3e, 0x47, 0xcc, 0xdd, 0x35, 0x4a, 0xbb, 0xfb, 0x05, 0xf9, 0x1a, 0x3a, 0x56, 0x51, 0x87, 0x7c, + 0x97, 0x16, 0xa0, 0x7c, 0xbd, 0xb6, 0x59, 0xda, 0x7c, 0x15, 0x96, 0xba, 0xdf, 0x5f, 0x80, 0xe5, + 0x0f, 0xa2, 0xd3, 0x38, 0x18, 0xd1, 0xdb, 0xe4, 0x94, 0x4f, 0x63, 0x55, 0x5f, 0x80, 0xbf, 0xd1, + 0xa2, 0x53, 0x52, 0x2d, 0xc9, 0xe5, 0xe3, 0xa2, 0x6a, 0xa2, 0x75, 0x4b, 0x8b, 0x9a, 0x1b, 0x21, + 0x29, 0x06, 0x82, 0xfe, 0x61, 0x6a, 0x16, 0x1c, 0xc9, 0x56, 0x51, 0xa0, 0xb1, 0x68, 0x14, 0x68, + 0xd0, 0x4b, 0xb6, 0xc8, 0x17, 0x12, 0x3b, 0x57, 0x3c, 0xd5, 0x24, 0x3f, 0x36, 0xe5, 0x22, 0x26, + 0x26, 0x3b, 0xb9, 0x2c, 0xfd, 0x58, 0x13, 0x44, 0x5b, 0x2a, 0x3e, 0x10, 0x63, 0x84, 0xae, 0x31, + 0x21, 0xf4, 0x2d, 0xca, 0x35, 0x4b, 0x2d, 0x71, 0xc4, 0x25, 0x18, 0x15, 0xd2, 0x98, 0x6b, 0xbd, + 0x21, 0xf6, 0x00, 0xa2, 0xa6, 0xa8, 0x8c, 0x1b, 0x5e, 0xb0, 0xc8, 0x7b, 0xca, 0x16, 0xf9, 0x20, + 0x7e, 0x18, 0x1e, 0xf9, 0xa3, 0xa7, 0x54, 0x49, 0x46, 0x69, 0xce, 0x96, 0x67, 0x83, 0xb8, 0x6a, + 0x2a, 0x8c, 0x92, 0x24, 0xba, 0x22, 0x4d, 0x69, 0x40, 0xee, 0xb7, 0x80, 0xdd, 0x19, 0x8f, 0xe5, + 0x09, 0xe9, 0x18, 0xa1, 0xe0, 0xad, 0x63, 0xf1, 0xb6, 0x66, 0x8f, 0x8d, 0xda, 0x3d, 0xba, 0x0f, + 0xa0, 0x7d, 0x60, 0x14, 0x80, 0xd1, 0x61, 0xaa, 0xd2, 0x2f, 0x29, 0x00, 0x06, 0x62, 0x4c, 0xd8, + 0x30, 0x27, 0x74, 0x7f, 0x05, 0xd8, 0x7e, 0x90, 0xe5, 0x7a, 0x7d, 0x3a, 0x92, 0xd4, 0x0f, 0x62, + 0x46, 0x24, 0x29, 0x31, 0x8a, 0x24, 0xef, 0x88, 0x6c, 0x69, 0x79, 0x63, 0x37, 0x60, 0x25, 0x10, + 0x90, 0xd2, 0xc3, 0x3d, 0x29, 0xc0, 0x6a, 0xa4, 0xee, 0x47, 0x87, 0x42, 0x82, 0x96, 0x9a, 0xff, + 0xa1, 0x03, 0xcb, 0x72, 0x6b, 0x68, 0x0e, 0xad, 0xd2, 0x37, 0xb1, 0x31, 0x0b, 0xab, 0x2f, 0x18, + 0xaa, 0x4a, 0xdd, 0x42, 0x9d, 0xd4, 0x31, 0x68, 0x26, 0x7e, 0x7e, 0x42, 0x1e, 0x74, 0xcb, 0xa3, + 0xdf, 0x2a, 0x52, 0x5a, 0x2c, 0x22, 0xa5, 0xba, 0x1a, 0x35, 0xa1, 0x33, 0x2a, 0xb8, 0xca, 0x22, + 0xcb, 0x0d, 0xe8, 0x07, 0xd0, 0xbb, 0x22, 0x8b, 0x5c, 0xc0, 0x05, 0xbf, 0x24, 0x89, 0x32, 0xbf, + 0xe4, 0x50, 0x4f, 0xf7, 0xbb, 0x03, 0xe8, 0xdf, 0xe7, 0x21, 0xcf, 0xf9, 0x9d, 0x30, 0x2c, 0xd3, + 0x7f, 0x0d, 0x2e, 0xd5, 0xf4, 0x49, 0xab, 0xfa, 0x3e, 0xac, 0xdf, 0xe7, 0x47, 0xb3, 0xc9, 0x3e, + 0x3f, 0x2d, 0x32, 0x0f, 0x0c, 0x9a, 0xd9, 0x49, 0x7c, 0x26, 0xcf, 0x96, 0x7e, 0x63, 0xc0, 0x1b, + 0xe2, 0x98, 0x61, 0x96, 0xf0, 0x91, 0xaa, 0x8c, 0x21, 0xe4, 0x30, 0xe1, 0x23, 0xf7, 0x1d, 0x60, + 0x26, 0x1d, 0xb9, 0x05, 0xbc, 0xb9, 0xb3, 0xa3, 0x61, 0x36, 0xcf, 0x72, 0x3e, 0x55, 0x25, 0x3f, + 0x26, 0xe4, 0x5e, 0x83, 0xce, 0x81, 0x3f, 0xf7, 0xf8, 0x27, 0xb2, 0xfa, 0x10, 0x83, 0x37, 0x7f, + 0x8e, 0xa2, 0xac, 0x83, 0x37, 0xea, 0x76, 0xff, 0xa1, 0x01, 0x4b, 0x62, 0x24, 0x52, 0x1d, 0xf3, + 0x2c, 0x0f, 0x22, 0xf1, 0x42, 0x2f, 0xa9, 0x1a, 0x50, 0x45, 0x36, 0x1a, 0x35, 0xb2, 0x21, 0xdd, + 0x29, 0x55, 0x5f, 0x20, 0x85, 0xc0, 0xc2, 0x28, 0x36, 0xd5, 0x39, 0xcb, 0xa6, 0x8c, 0x4d, 0x15, + 0x50, 0x8a, 0x92, 0x0b, 0xfd, 0x20, 0xd6, 0xa7, 0x84, 0x56, 0x8a, 0x83, 0x09, 0xd5, 0x6a, 0xa1, + 0x65, 0x21, 0x35, 0x15, 0x2d, 0x54, 0xd1, 0x36, 0x2b, 0xaf, 0xa0, 0x6d, 0x84, 0x8f, 0x65, 0x69, + 0x1b, 0x06, 0x6b, 0xef, 0x73, 0xee, 0xf1, 0x24, 0x4e, 0x55, 0x09, 0xa7, 0xfb, 0x3d, 0x07, 0xd6, + 0xa4, 0xf5, 0xd0, 0x7d, 0xec, 0x75, 0xcb, 0xd4, 0x38, 0x75, 0x8f, 0xb6, 0x6f, 0x40, 0x97, 0x82, + 0x2d, 0x8c, 0xa4, 0x28, 0xb2, 0x92, 0xef, 0x0f, 0x16, 0x88, 0x6b, 0x52, 0xcf, 0x90, 0xd3, 0x20, + 0x94, 0x0c, 0x36, 0x21, 0x34, 0x8b, 0x2a, 0x18, 0x23, 0xf6, 0x3a, 0x9e, 0x6e, 0xbb, 0x07, 0xb0, + 0x6e, 0xac, 0x57, 0x0a, 0xd4, 0x6d, 0x50, 0x89, 0x4b, 0xf1, 0x9c, 0x20, 0xee, 0xc5, 0xb6, 0x6d, + 0x08, 0x8b, 0xcf, 0xac, 0xc1, 0xee, 0xbf, 0x3a, 0xb0, 0x21, 0x9c, 0x02, 0xe9, 0x72, 0xe9, 0x3a, + 0xa8, 0x25, 0xe1, 0x05, 0x09, 0x81, 0xdf, 0xbb, 0xe0, 0xc9, 0x36, 0xfb, 0xd2, 0x2b, 0x3a, 0x32, + 0x3a, 0x47, 0x78, 0x0e, 0x7b, 0x16, 0xea, 0xd8, 0xf3, 0x82, 0xcd, 0xd7, 0x05, 0xcb, 0x8b, 0xb5, + 0xc1, 0xf2, 0xdd, 0x65, 0x58, 0xcc, 0x46, 0x71, 0xc2, 0xdd, 0x2d, 0xd8, 0xb4, 0x37, 0x27, 0x58, + 0xb6, 0xfb, 0x83, 0x06, 0xf4, 0xc4, 0xbb, 0x9e, 0x28, 0x0e, 0xe7, 0x29, 0xfb, 0x10, 0x96, 0x65, + 0x29, 0x3e, 0xbb, 0x28, 0x77, 0x63, 0x17, 0xff, 0x0f, 0xb6, 0xca, 0xb0, 0x54, 0x17, 0x1b, 0xbf, + 0xff, 0xe3, 0x7f, 0xff, 0xd3, 0x46, 0x97, 0xb5, 0x77, 0x4e, 0xdf, 0xde, 0x99, 0xf0, 0x28, 0x43, + 0x1a, 0xbf, 0x09, 0x50, 0x54, 0xb3, 0xb3, 0xbe, 0x56, 0xea, 0xa5, 0xea, 0xfb, 0xc1, 0xa5, 0x9a, + 0x1e, 0x49, 0xf7, 0x12, 0xd1, 0xdd, 0x70, 0x7b, 0x48, 0x37, 0x88, 0x82, 0x5c, 0x94, 0xb6, 0xbf, + 0xe7, 0xdc, 0x60, 0x63, 0xe8, 0x98, 0x55, 0xed, 0x4c, 0xf9, 0xd0, 0x35, 0xa5, 0xf2, 0x83, 0xd7, + 0x6a, 0xfb, 0x54, 0x00, 0x41, 0x73, 0x5c, 0x74, 0xd7, 0x70, 0x8e, 0x19, 0x8d, 0xd0, 0xb3, 0xec, + 0xfe, 0xe7, 0x00, 0x5a, 0x3a, 0x0e, 0x65, 0xdf, 0x85, 0xae, 0xf5, 0x14, 0xca, 0x14, 0xe1, 0xba, + 0xc7, 0xd5, 0xc1, 0xe5, 0xfa, 0x4e, 0x39, 0xed, 0x15, 0x9a, 0xb6, 0xcf, 0xb6, 0x70, 0x5a, 0xf9, + 0xfe, 0xb8, 0x43, 0x6f, 0xc4, 0xa2, 0xd2, 0xe2, 0x29, 0xf4, 0xec, 0xe7, 0x4b, 0x76, 0xd9, 0x96, + 0xb0, 0xd2, 0x6c, 0x9f, 0x3b, 0xa7, 0x57, 0x4e, 0x77, 0x99, 0xa6, 0xdb, 0x62, 0x9b, 0xe6, 0x74, + 0x3a, 0x3e, 0xe4, 0x54, 0x1b, 0x63, 0x96, 0xbb, 0xb3, 0xcf, 0xe9, 0xa3, 0xae, 0x2b, 0x83, 0xd7, + 0x87, 0x56, 0xad, 0x85, 0x77, 0xfb, 0x34, 0x15, 0x63, 0xc4, 0x50, 0xb3, 0xda, 0x9d, 0x7d, 0x0c, + 0x2d, 0x5d, 0xe2, 0xca, 0xb6, 0x8d, 0xba, 0x62, 0xb3, 0xee, 0x76, 0xd0, 0xaf, 0x76, 0xd4, 0x1d, + 0x95, 0x49, 0x19, 0x05, 0x62, 0x1f, 0x2e, 0x4a, 0xa7, 0xe0, 0x88, 0xff, 0x34, 0x3b, 0xa9, 0x29, + 0xd2, 0xbf, 0xe5, 0xb0, 0xdb, 0xb0, 0xa2, 0x2a, 0x87, 0xd9, 0x56, 0x7d, 0x05, 0xf4, 0x60, 0xbb, + 0x82, 0x4b, 0x75, 0x74, 0x07, 0xa0, 0x28, 0x72, 0xd5, 0x92, 0x5f, 0xa9, 0xc5, 0xd5, 0x4c, 0xac, + 0xa9, 0x88, 0x9d, 0x50, 0x8d, 0xaf, 0x5d, 0x43, 0xcb, 0x3e, 0x5f, 0x8c, 0xaf, 0xad, 0xae, 0x7d, + 0x01, 0x41, 0x77, 0x8b, 0x78, 0xb7, 0xc6, 0xe8, 0x2a, 0x45, 0xfc, 0x4c, 0x55, 0x89, 0xdd, 0x87, + 0xb6, 0x51, 0x38, 0xcb, 0x14, 0x85, 0x6a, 0xd1, 0xed, 0x60, 0x50, 0xd7, 0x25, 0x97, 0xfb, 0x35, + 0xe8, 0x5a, 0x15, 0xb0, 0xfa, 0x66, 0xd4, 0xd5, 0xd7, 0xea, 0x9b, 0x51, 0x5f, 0x34, 0xfb, 0x6d, + 0x68, 0x1b, 0xf5, 0xaa, 0xcc, 0xc8, 0xb1, 0x97, 0x2a, 0x55, 0xf5, 0x8a, 0xea, 0xca, 0x5b, 0x37, + 0x69, 0xbf, 0x3d, 0xb7, 0x85, 0xfb, 0xa5, 0x52, 0x29, 0x14, 0x92, 0xef, 0x42, 0xcf, 0xae, 0x60, + 0xd5, 0xb7, 0xaa, 0xb6, 0x16, 0x56, 0xdf, 0xaa, 0x73, 0xca, 0x5e, 0xa5, 0x40, 0xde, 0xd8, 0xd0, + 0x93, 0xec, 0x7c, 0x26, 0x5f, 0x61, 0x9f, 0xb3, 0x6f, 0xa2, 0xea, 0x90, 0xb5, 0x6b, 0xac, 0xa8, + 0xdb, 0xb5, 0x2b, 0xdc, 0xb4, 0xb4, 0x57, 0xca, 0xdc, 0xdc, 0x75, 0x22, 0xde, 0x66, 0xc5, 0x0e, + 0x84, 0x86, 0xa6, 0x1a, 0x36, 0x43, 0x43, 0x9b, 0x65, 0x6e, 0x86, 0x86, 0xb6, 0x4a, 0xdd, 0xca, + 0x1a, 0x3a, 0x0f, 0x90, 0x46, 0x04, 0xab, 0xa5, 0xbc, 0x9a, 0xbe, 0x2c, 0xf5, 0x59, 0xf9, 0xc1, + 0x95, 0x17, 0xa7, 0xe3, 0x6c, 0x35, 0xa3, 0xd4, 0xcb, 0x8e, 0x2a, 0xa2, 0xf8, 0x2d, 0xe8, 0x98, + 0x85, 0x91, 0x5a, 0x67, 0xd7, 0x14, 0x51, 0x6a, 0x9d, 0x5d, 0x57, 0x49, 0xa9, 0x0e, 0x97, 0x75, + 0xcc, 0x69, 0xd8, 0xb7, 0x61, 0xd5, 0xc8, 0xe0, 0x1e, 0xce, 0xa3, 0x91, 0x16, 0x9e, 0x6a, 0x1d, + 0xcd, 0xa0, 0xce, 0x60, 0xbb, 0xdb, 0x44, 0x78, 0xdd, 0xb5, 0x08, 0xa3, 0xe0, 0xdc, 0x83, 0xb6, + 0x99, 0x1d, 0x7e, 0x01, 0xdd, 0x6d, 0xa3, 0xcb, 0x2c, 0x3f, 0xb9, 0xe5, 0xb0, 0xbf, 0x70, 0xa0, + 0x63, 0x56, 0x68, 0x31, 0xeb, 0xe1, 0xa7, 0x44, 0xa7, 0x6f, 0xf6, 0x99, 0x84, 0x5c, 0x8f, 0x16, + 0xb9, 0x7f, 0xe3, 0x6b, 0x16, 0x93, 0x3f, 0xb3, 0x7c, 0xb1, 0x9b, 0xe5, 0x7f, 0x99, 0x3c, 0x2f, + 0x0f, 0x30, 0x6b, 0x8d, 0x9e, 0xdf, 0x72, 0xd8, 0x7b, 0xe2, 0x9f, 0x48, 0x2a, 0x8e, 0x62, 0x86, + 0x72, 0x2b, 0xb3, 0xcc, 0xfc, 0xd3, 0xce, 0x75, 0xe7, 0x96, 0xc3, 0xbe, 0x23, 0xfe, 0x4c, 0x22, + 0xbf, 0x25, 0xce, 0xbf, 0xea, 0xf7, 0xee, 0x1b, 0xb4, 0x9b, 0x2b, 0xee, 0x25, 0x6b, 0x37, 0x65, + 0xed, 0x7e, 0x00, 0x50, 0x04, 0xc5, 0xac, 0x14, 0x21, 0x6a, 0xbd, 0x57, 0x8d, 0x9b, 0xed, 0x13, + 0x55, 0x81, 0x24, 0x52, 0xfc, 0x58, 0x08, 0xa3, 0x1c, 0x9f, 0xe9, 0x23, 0xad, 0x06, 0xb7, 0x83, + 0x41, 0x5d, 0x57, 0x9d, 0x28, 0x2a, 0xfa, 0xec, 0x09, 0x74, 0xf7, 0xe3, 0xf8, 0xe9, 0x2c, 0xd1, + 0x0f, 0x2d, 0x76, 0x8c, 0x86, 0x11, 0xf8, 0xa0, 0xb4, 0x0b, 0xf7, 0x2a, 0x91, 0x1a, 0xb0, 0xbe, + 0x41, 0x6a, 0xe7, 0xb3, 0x22, 0x24, 0x7f, 0xce, 0x7c, 0x58, 0xd7, 0x36, 0x4e, 0x2f, 0x7c, 0x60, + 0x93, 0x31, 0x23, 0xe3, 0xca, 0x14, 0x96, 0xd7, 0xa1, 0x56, 0xbb, 0x93, 0x29, 0x9a, 0xb7, 0x1c, + 0x76, 0x00, 0x9d, 0xfb, 0x7c, 0x14, 0x8f, 0xb9, 0x8c, 0xaa, 0x36, 0x8a, 0x85, 0xeb, 0x70, 0x6c, + 0xd0, 0xb5, 0x40, 0xfb, 0xd6, 0x27, 0xfe, 0x3c, 0xe5, 0x9f, 0xec, 0x7c, 0x26, 0xe3, 0xb5, 0xe7, + 0xea, 0xd6, 0xab, 0x18, 0xd3, 0xba, 0xf5, 0xa5, 0xa0, 0xd4, 0xba, 0xf5, 0x95, 0xa0, 0xd4, 0x62, + 0xb5, 0x8a, 0x71, 0x59, 0x88, 0xa1, 0x6a, 0x29, 0x8e, 0xd5, 0x96, 0xf2, 0xbc, 0xe8, 0x77, 0x70, + 0xf5, 0xfc, 0x01, 0xf6, 0x6c, 0x37, 0xec, 0xd9, 0x0e, 0xa1, 0x7b, 0x9f, 0x0b, 0x66, 0x89, 0xe4, + 0xc5, 0xc0, 0x56, 0x23, 0x66, 0xa2, 0xa3, 0xac, 0x62, 0xa8, 0xcf, 0x56, 0xeb, 0x94, 0x39, 0x60, + 0x1f, 0x43, 0xfb, 0x21, 0xcf, 0x55, 0xb6, 0x42, 0xfb, 0x1b, 0xa5, 0xf4, 0xc5, 0xa0, 0x26, 0xd9, + 0x61, 0xcb, 0x0c, 0x51, 0xdb, 0xe1, 0xe3, 0x09, 0x17, 0x97, 0x7d, 0x18, 0x8c, 0x9f, 0xb3, 0x5f, + 0x27, 0xe2, 0x3a, 0xc1, 0xb9, 0x65, 0x3c, 0x72, 0x9b, 0xc4, 0x57, 0x4b, 0x78, 0x1d, 0xe5, 0x28, + 0x1e, 0x73, 0xc3, 0xc0, 0x45, 0xd0, 0x36, 0xb2, 0xd9, 0xfa, 0x02, 0x55, 0x33, 0xe8, 0xfa, 0x02, + 0xd5, 0x24, 0xbf, 0xdd, 0xeb, 0x34, 0x8f, 0xcb, 0xae, 0x16, 0xf3, 0x88, 0x84, 0x77, 0x31, 0xd3, + 0xce, 0x67, 0xfe, 0x34, 0x7f, 0xce, 0x3e, 0xa2, 0xa2, 0x6e, 0x33, 0x23, 0x53, 0xf8, 0x3b, 0xe5, + 0xe4, 0x8d, 0x66, 0x96, 0xd1, 0x65, 0xfb, 0x40, 0x62, 0x2a, 0xb2, 0x83, 0x5f, 0x02, 0x38, 0xcc, + 0xe3, 0xe4, 0xbe, 0xcf, 0xa7, 0x71, 0x54, 0x68, 0xae, 0x22, 0xeb, 0x50, 0x68, 0x2e, 0x23, 0xf5, + 0xc0, 0x3e, 0x32, 0x3c, 0x4e, 0x2b, 0xa1, 0xa5, 0x84, 0xeb, 0xdc, 0xc4, 0x84, 0x66, 0x48, 0x4d, + 0x72, 0xe2, 0x96, 0x83, 0xfe, 0x63, 0xf1, 0x6a, 0xa2, 0xfd, 0xc7, 0xca, 0x83, 0x8c, 0x56, 0x7b, + 0x35, 0x4f, 0x2c, 0x07, 0xd0, 0x2a, 0x42, 0x77, 0x65, 0x92, 0xca, 0x81, 0xbe, 0xb6, 0x31, 0x95, + 0x88, 0xda, 0x5d, 0x23, 0x56, 0x01, 0x5b, 0x41, 0x56, 0x51, 0x42, 0x3e, 0x80, 0x0d, 0xb1, 0x40, + 0x6d, 0x30, 0xe9, 0x1d, 0x5d, 0xed, 0xa4, 0x26, 0x82, 0xd6, 0xb7, 0xb9, 0x2e, 0x00, 0xb5, 0x63, + 0x3b, 0x94, 0x56, 0xf1, 0x86, 0xff, 0x9e, 0x73, 0xe3, 0x68, 0x89, 0xfe, 0x7c, 0xfe, 0x85, 0xff, + 0x09, 0x00, 0x00, 0xff, 0xff, 0xce, 0xee, 0xff, 0xf6, 0xae, 0x3e, 0x00, 0x00, } diff --git a/lnrpc/rpc.pb.gw.go b/lnrpc/rpc.pb.gw.go index 6633df86..b3fa5e8c 100644 --- a/lnrpc/rpc.pb.gw.go +++ b/lnrpc/rpc.pb.gw.go @@ -28,15 +28,32 @@ var _ status.Status var _ = runtime.String var _ = utilities.NewDoubleArray -func request_WalletUnlocker_CreateWallet_0(ctx context.Context, marshaler runtime.Marshaler, client WalletUnlockerClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { - var protoReq CreateWalletRequest +var ( + filter_WalletUnlocker_GenSeed_0 = &utilities.DoubleArray{Encoding: map[string]int{}, Base: []int(nil), Check: []int(nil)} +) + +func request_WalletUnlocker_GenSeed_0(ctx context.Context, marshaler runtime.Marshaler, client WalletUnlockerClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq GenSeedRequest + var metadata runtime.ServerMetadata + + if err := runtime.PopulateQueryParameters(&protoReq, req.URL.Query(), filter_WalletUnlocker_GenSeed_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := client.GenSeed(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func request_WalletUnlocker_InitWallet_0(ctx context.Context, marshaler runtime.Marshaler, client WalletUnlockerClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq InitWalletRequest var metadata runtime.ServerMetadata if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) } - msg, err := client.CreateWallet(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + msg, err := client.InitWallet(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) return msg, metadata, err } @@ -564,7 +581,7 @@ func RegisterWalletUnlockerHandlerFromEndpoint(ctx context.Context, mux *runtime func RegisterWalletUnlockerHandler(ctx context.Context, mux *runtime.ServeMux, conn *grpc.ClientConn) error { client := NewWalletUnlockerClient(conn) - mux.Handle("POST", pattern_WalletUnlocker_CreateWallet_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + mux.Handle("GET", pattern_WalletUnlocker_GenSeed_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(ctx) defer cancel() if cn, ok := w.(http.CloseNotifier); ok { @@ -582,14 +599,43 @@ func RegisterWalletUnlockerHandler(ctx context.Context, mux *runtime.ServeMux, c runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } - resp, md, err := request_WalletUnlocker_CreateWallet_0(rctx, inboundMarshaler, client, req, pathParams) + resp, md, err := request_WalletUnlocker_GenSeed_0(rctx, inboundMarshaler, client, req, pathParams) ctx = runtime.NewServerMetadataContext(ctx, md) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } - forward_WalletUnlocker_CreateWallet_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + forward_WalletUnlocker_GenSeed_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("POST", pattern_WalletUnlocker_InitWallet_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(ctx) + defer cancel() + if cn, ok := w.(http.CloseNotifier); ok { + go func(done <-chan struct{}, closed <-chan bool) { + select { + case <-done: + case <-closed: + cancel() + } + }(ctx.Done(), cn.CloseNotify()) + } + 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_WalletUnlocker_InitWallet_0(rctx, inboundMarshaler, client, req, pathParams) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_WalletUnlocker_InitWallet_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) }) @@ -626,13 +672,17 @@ func RegisterWalletUnlockerHandler(ctx context.Context, mux *runtime.ServeMux, c } var ( - pattern_WalletUnlocker_CreateWallet_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{"v1", "createwallet"}, "")) + pattern_WalletUnlocker_GenSeed_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{"v1", "genseed"}, "")) + + pattern_WalletUnlocker_InitWallet_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{"v1", "initwallet"}, "")) pattern_WalletUnlocker_UnlockWallet_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{"v1", "unlockwallet"}, "")) ) var ( - forward_WalletUnlocker_CreateWallet_0 = runtime.ForwardResponseMessage + forward_WalletUnlocker_GenSeed_0 = runtime.ForwardResponseMessage + + forward_WalletUnlocker_InitWallet_0 = runtime.ForwardResponseMessage forward_WalletUnlocker_UnlockWallet_0 = runtime.ForwardResponseMessage ) diff --git a/lnrpc/rpc.proto b/lnrpc/rpc.proto index 9e057c60..d7c19967 100644 --- a/lnrpc/rpc.proto +++ b/lnrpc/rpc.proto @@ -28,13 +28,39 @@ package lnrpc; // The WalletUnlocker service is used to set up a wallet password for // lnd at first startup, and unlock a previously set up wallet. service WalletUnlocker { - /** lncli: `create` - CreateWallet is used at lnd startup to set the encryption password for - the wallet database. + /** + GenSeed is the first method that should be used to instantiate a new lnd + instance. This method allows a caller to generate a new aezeed cipher seed + given an optional passphrase. If provided, the passphrase will be necessary + to decrypt the cipherseed to expose the internal wallet seed. + + Once the cipherseed is obtained and verified by the user, the InitWallet + method should be used to commit the newly generated seed, and create the + wallet. */ - rpc CreateWallet(CreateWalletRequest) returns (CreateWalletResponse) { + rpc GenSeed(GenSeedRequest) returns (GenSeedResponse) { option (google.api.http) = { - post: "/v1/createwallet" + get: "/v1/genseed" + }; + } + + /** lncli: `init` + InitWallet is used when lnd is starting up for the first time to fully + initialize the daemon and its internal wallet. At the very least a wallet + password must be provided. This will be used to encrypt sensitive material + on disk. + + In the case of a recovery scenario, the user can also specify their aezeed + mnemonic and passphrase. If set, then the daemon will use this prior state + to initialize its internal wallet. + + Alternatively, this can be used along with the GenSeed RPC to obtain a + seed, then present it to the user. Once it has been verified by the user, + the seed can be fed into this RPC in order to commit the new wallet. + */ + rpc InitWallet(InitWalletRequest) returns (InitWalletResponse) { + option (google.api.http) = { + post: "/v1/initwallet" body: "*" }; } @@ -51,20 +77,74 @@ service WalletUnlocker { } } -message CreateWalletRequest { - bytes password = 1; -} -message CreateWalletResponse {} +message GenSeedRequest { + /** + aezeed_passphrase is an optional user provided passphrase that will be used + to encrypt the generated aezeed cipher seed. + */ + bytes aezeed_passphrase = 1; + /** + seed_entropy is an optional 16-bytes generated via CSPRNG. If not + specified, then a fresh set of randomness will be used to create the seed. + */ + bytes seed_entropy = 2; +} +message GenSeedResponse { + /** + cipher_seed_mnemonic is a 24-word mnemonic that encodes a prior aezeed + cipher seed obtained by the user. This field is optional, as if not + provided, then the daemon will generate a new cipher seed for the user. + Otherwise, then the daemon will attempt to recover the wallet state linked + to this cipher seed. + */ + repeated string cipher_seed_mnemonic = 1; + + /** + enciphered_seed are the raw aezeed cipher seed bytes. This is the raw + cipher text before run through our mnemonic encoding scheme. + */ + bytes enciphered_seed = 2; +} + +message InitWalletRequest { + /** + wallet_password is the passphrase that should be used to encrypt the + wallet. This MUST be at least 8 chars in length. After creation, this + password is required to unlock the daemon. + */ + bytes wallet_password = 1; + + /** + cipher_seed_mnemonic is a 24-word mnemonic that encodes a prior aezeed + cipher seed obtained by the user. This may have been generated by the + GenSeed method, or be an existing seed. + */ + repeated string cipher_seed_mnemonic = 2; + + /** + aezeed_passphrase is an optional user provided passphrase that will be used + to encrypt the generated aezeed cipher seed. + */ + bytes aezeed_passphrase = 3; +} +message InitWalletResponse { +} message UnlockWalletRequest { - bytes password = 1; + /** + wallet_password should be the current valid passphrase for the daemon. This + will be required to decrypt on-disk material that the daemon requires to + function properly. + */ + bytes wallet_password = 1; } message UnlockWalletResponse {} service Lightning { /** lncli: `walletbalance` - WalletBalance returns total unspent outputs(confirmed and unconfirmed), all confirmed unspent outputs and all unconfirmed unspent outputs under control + WalletBalance returns total unspent outputs(confirmed and unconfirmed), all + confirmed unspent outputs and all unconfirmed unspent outputs under control by the wallet. This method can be modified by having the request specify only witness outputs should be factored into the final output sum. */ diff --git a/lnrpc/rpc.swagger.json b/lnrpc/rpc.swagger.json index 93f83995..31f20465 100644 --- a/lnrpc/rpc.swagger.json +++ b/lnrpc/rpc.swagger.json @@ -17,7 +17,7 @@ "paths": { "/v1/balance/blockchain": { "get": { - "summary": "* lncli: `walletbalance`\nWalletBalance returns total unspent outputs(confirmed and unconfirmed), all confirmed unspent outputs and all unconfirmed unspent outputs under control\nby the wallet. This method can be modified by having the request specify\nonly witness outputs should be factored into the final output sum.", + "summary": "* lncli: `walletbalance`\nWalletBalance returns total unspent outputs(confirmed and unconfirmed), all\nconfirmed unspent outputs and all unconfirmed unspent outputs under control\nby the wallet. This method can be modified by having the request specify\nonly witness outputs should be factored into the final output sum.", "operationId": "WalletBalance", "responses": { "200": { @@ -204,33 +204,6 @@ ] } }, - "/v1/createwallet": { - "post": { - "summary": "* lncli: `create`\nCreateWallet is used at lnd startup to set the encryption password for\nthe wallet database.", - "operationId": "CreateWallet", - "responses": { - "200": { - "description": "", - "schema": { - "$ref": "#/definitions/lnrpcCreateWalletResponse" - } - } - }, - "parameters": [ - { - "name": "body", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/lnrpcCreateWalletRequest" - } - } - ], - "tags": [ - "WalletUnlocker" - ] - } - }, "/v1/fees": { "get": { "summary": "* lncli: `feereport`\nFeeReport allows the caller to obtain a report detailing the current fee\nschedule enforced by the node globally for each channel.", @@ -248,6 +221,42 @@ ] } }, + "/v1/genseed": { + "get": { + "summary": "*\nGenSeed is the first method that should be used to instantiate a new lnd\ninstance. This method allows a caller to generate a new aezeed cipher seed\ngiven an optional passphrase. If provided, the passphrase will be necessary\nto decrypt the cipherseed to expose the internal wallet seed.", + "description": "Once the cipherseed is obtained and verified by the user, the InitWallet\nmethod should be used to commit the newly generated seed, and create the\nwallet.", + "operationId": "GenSeed", + "responses": { + "200": { + "description": "", + "schema": { + "$ref": "#/definitions/lnrpcGenSeedResponse" + } + } + }, + "parameters": [ + { + "name": "aezeed_passphrase", + "description": "*\naezeed_passphrase is an optional user provided passphrase that will be used\nto encrypt the generated aezeed cipher seed.", + "in": "query", + "required": false, + "type": "string", + "format": "byte" + }, + { + "name": "seed_entropy", + "description": "*\nseed_entropy is an optional 16-bytes generated via CSPRNG. If not\nspecified, then a fresh set of randomness will be used to create the seed.", + "in": "query", + "required": false, + "type": "string", + "format": "byte" + } + ], + "tags": [ + "WalletUnlocker" + ] + } + }, "/v1/getinfo": { "get": { "summary": "* lncli: `getinfo`\nGetInfo returns general information concerning the lightning node including\nit's identity pubkey, alias, the chains it is connected to, and information\nconcerning the number of open+pending channels.", @@ -390,6 +399,34 @@ ] } }, + "/v1/initwallet": { + "post": { + "summary": "* lncli: `init`\nInitWallet is used when lnd is starting up for the first time to fully\ninitialize the daemon and its internal wallet. At the very least a wallet\npassword must be provided. This will be used to encrypt sensitive material\non disk.", + "description": "In the case of a recovery scenario, the user can also specify their aezeed\nmnemonic and passphrase. If set, then the daemon will use this prior state\nto initialize its internal wallet.\n\nAlternatively, this can be used along with the GenSeed RPC to obtain a\nseed, then present it to the user. Once it has been verified by the user,\nthe seed can be fed into this RPC in order to commit the new wallet.", + "operationId": "InitWallet", + "responses": { + "200": { + "description": "", + "schema": { + "$ref": "#/definitions/lnrpcInitWalletResponse" + } + } + }, + "parameters": [ + { + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/lnrpcInitWalletRequest" + } + } + ], + "tags": [ + "WalletUnlocker" + ] + } + }, "/v1/invoice/{r_hash_str}": { "get": { "summary": "* lncli: `lookupinvoice`\nLookupInvoice attempts to look up an invoice according to its payment hash.\nThe passed payment hash *must* be exactly 32 bytes, if not, an error is\nreturned.", @@ -1129,18 +1166,6 @@ "lnrpcConnectPeerResponse": { "type": "object" }, - "lnrpcCreateWalletRequest": { - "type": "object", - "properties": { - "password": { - "type": "string", - "format": "byte" - } - } - }, - "lnrpcCreateWalletResponse": { - "type": "object" - }, "lnrpcDebugLevelResponse": { "type": "object", "properties": { @@ -1167,6 +1192,23 @@ } } }, + "lnrpcGenSeedResponse": { + "type": "object", + "properties": { + "cipher_seed_mnemonic": { + "type": "array", + "items": { + "type": "string" + }, + "description": "*\ncipher_seed_mnemonic is a 24-word mnemonic that encodes a prior aezeed\ncipher seed obtained by the user. This field is optional, as if not\nprovided, then the daemon will generate a new cipher seed for the user.\nOtherwise, then the daemon will attempt to recover the wallet state linked\nto this cipher seed." + }, + "enciphered_seed": { + "type": "string", + "format": "byte", + "description": "*\nenciphered_seed are the raw aezeed cipher seed bytes. This is the raw\ncipher text before run through our mnemonic encoding scheme." + } + } + }, "lnrpcGetInfoResponse": { "type": "object", "properties": { @@ -1303,6 +1345,31 @@ } } }, + "lnrpcInitWalletRequest": { + "type": "object", + "properties": { + "wallet_password": { + "type": "string", + "format": "byte", + "description": "*\nwallet_password is the passphrase that should be used to encrypt the\nwallet. This MUST be at least 8 chars in length. After creation, this\npassword is required to unlock the daemon." + }, + "cipher_seed_mnemonic": { + "type": "array", + "items": { + "type": "string" + }, + "description": "*\ncipher_seed_mnemonic is a 24-word mnemonic that encodes a prior aezeed\ncipher seed obtained by the user. This may have been generated by the\nGenSeed method, or be an existing seed." + }, + "aezeed_passphrase": { + "type": "string", + "format": "byte", + "description": "*\naezeed_passphrase is an optional user provided passphrase that will be used\nto encrypt the generated aezeed cipher seed." + } + } + }, + "lnrpcInitWalletResponse": { + "type": "object" + }, "lnrpcInvoice": { "type": "object", "properties": { @@ -2062,9 +2129,10 @@ "lnrpcUnlockWalletRequest": { "type": "object", "properties": { - "password": { + "wallet_password": { "type": "string", - "format": "byte" + "format": "byte", + "description": "*\nwallet_password should be the current valid passphrase for the daemon. This\nwill be required to decrypt on-disk material that the daemon requires to\nfunction properly." } } }, From 428693cb6e477127cfb22d75ca505bc836eaaa93 Mon Sep 17 00:00:00 2001 From: Olaoluwa Osuntokun Date: Thu, 1 Feb 2018 20:44:40 -0800 Subject: [PATCH 2/5] walletunlocker: modify service to implement new 2-step wallet creation In this commit, we extend the UnlockerService to account for the new changes in the lnrpc definition. Setting up the daemon for the first time is now two step process: first the user will generate a new seed via the GenSeed method, then the user will present this new seed (and optional pass) to the InitWallet method which then will finalize the wallet creation. This two step process ensures that we don't commit the wallet changes in the case that the user doesn't actually "ACK" the new seed. In the case that the user already has an existing seed, they can re-enter it and skip straight to the InitWallet step. We also update the tests to account for the new API changes. --- walletunlocker/service.go | 177 ++++++++++++++++++++---- walletunlocker/service_test.go | 246 ++++++++++++++++++++++++++++++--- 2 files changed, 374 insertions(+), 49 deletions(-) diff --git a/walletunlocker/service.go b/walletunlocker/service.go index 5b62afd7..b80daca1 100644 --- a/walletunlocker/service.go +++ b/walletunlocker/service.go @@ -1,8 +1,11 @@ package walletunlocker import ( + "crypto/rand" "fmt" + "time" + "github.com/lightningnetwork/lnd/aezeed" "github.com/lightningnetwork/lnd/lnrpc" "github.com/lightningnetwork/lnd/lnwallet/btcwallet" "github.com/lightningnetwork/lnd/macaroons" @@ -11,13 +14,28 @@ import ( "golang.org/x/net/context" ) +// WalletInitMsg is a message sent to the UnlockerService when a user wishes to +// set up the internal wallet for the first time. The user MUST provide a +// passphrase, but is also able to provide their own source of entropy. If +// provided, then this source of entropy will be used to generate the wallet's +// HD seed. Otherwise, the wallet will generate one itself. +type WalletInitMsg struct { + // Passphrase is the passphrase that will be used to encrypt the wallet + // itself. This MUST be at least 8 characters. + Passphrase []byte + + // WalletSeed is the deciphered cipher seed that the wallet should use + // to initialize itself. + WalletSeed *aezeed.CipherSeed +} + // UnlockerService implements the WalletUnlocker service used to provide lnd -// with a password for wallet encryption at startup. +// with a password for wallet encryption at startup. Additionally, during +// initial setup, users can provide their own source of entropy which will be +// used to generate the seed that's ultimately used within the wallet. type UnlockerService struct { - // CreatePasswords is a channel where passwords provided by the rpc - // client to be used to initially create and encrypt a wallet will be - // sent. - CreatePasswords chan []byte + // InitMsgs is a channel that carries all wallet init messages. + InitMsgs chan *WalletInitMsg // UnlockPasswords is a channel where passwords provided by the rpc // client to be used to unlock and decrypt an existing wallet will be @@ -32,42 +50,141 @@ type UnlockerService struct { // New creates and returns a new UnlockerService. func New(authSvc *macaroons.Service, chainDir string, params *chaincfg.Params) *UnlockerService { + return &UnlockerService{ - CreatePasswords: make(chan []byte, 1), + InitMsgs: make(chan *WalletInitMsg, 1), UnlockPasswords: make(chan []byte, 1), chainDir: chainDir, netParams: params, } } -// CreateWallet will read the password provided in the CreateWalletRequest and -// send it over the CreatePasswords channel in case no wallet already exist in -// the chain's wallet database directory. -func (u *UnlockerService) CreateWallet(ctx context.Context, - in *lnrpc.CreateWalletRequest) (*lnrpc.CreateWalletResponse, error) { +// GenSeed is the first method that should be used to instantiate a new lnd +// instance. This method allows a caller to generate a new aezeed cipher seed +// given an optional passphrase. If provided, the passphrase will be necessary +// to decrypt the cipherseed to expose the internal wallet seed. +// +// Once the cipherseed is obtained and verified by the user, the InitWallet +// method should be used to commit the newly generated seed, and create the +// wallet. +func (u *UnlockerService) GenSeed(ctx context.Context, + in *lnrpc.GenSeedRequest) (*lnrpc.GenSeedResponse, error) { - // Require the provided password to have a length of at - // least 8 characters. - password := in.Password + // Before we start, we'll ensure that the wallet hasn't already created + // so we don't show a *new* seed to the user if one already exists. + netDir := btcwallet.NetworkDir(u.chainDir, u.netParams) + loader := wallet.NewLoader(u.netParams, netDir) + walletExists, err := loader.WalletExists() + if err != nil { + return nil, err + } + if walletExists { + return nil, fmt.Errorf("wallet already exists") + } + + var entropy [aezeed.EntropySize]byte + + switch { + // If the user provided any entropy, then we'll make sure it's sized + // properly. + case len(in.SeedEntropy) != 0 && len(in.SeedEntropy) != aezeed.EntropySize: + return nil, fmt.Errorf("incorrect entropy length: expected "+ + "16 bytes, instead got %v bytes", len(in.SeedEntropy)) + + // If the user provided the correct number of bytes, then we'll copy it + // over into our buffer for usage. + case len(in.SeedEntropy) == aezeed.EntropySize: + copy(entropy[:], in.SeedEntropy[:]) + + // Otherwise, we'll generate a fresh new set of bytes to use as entropy + // to generate the seed. + default: + if _, err := rand.Read(entropy[:]); err != nil { + return nil, err + } + } + + // Now that we have our set of entropy, we'll create a new cipher seed + // instance. + // + // TODO(roasbeef): should use current keychain version here + cipherSeed, err := aezeed.New(0, &entropy, time.Now()) + if err != nil { + return nil, err + } + + // With our raw cipher seed obtained, we'll convert it into an encoded + // mnemonic using the user specified pass phrase. + mnemonic, err := cipherSeed.ToMnemonic(in.AezeedPassphrase) + if err != nil { + return nil, err + } + + // Additionally, we'll also obtain the raw enciphered cipher seed as + // well to return to the user. + encipheredSeed, err := cipherSeed.Encipher(in.AezeedPassphrase) + if err != nil { + return nil, err + } + + return &lnrpc.GenSeedResponse{ + CipherSeedMnemonic: []string(mnemonic[:]), + EncipheredSeed: encipheredSeed[:], + }, nil +} + +// InitWallet is used when lnd is starting up for the first time to fully +// initialize the daemon and its internal wallet. At the very least a wallet +// password must be provided. This will be used to encrypt sensitive material +// on disk. +// +// In the case of a recovery scenario, the user can also specify their aezeed +// mnemonic and passphrase. If set, then the daemon will use this prior state +// to initialize its internal wallet. +// +// Alternatively, this can be used along with the GenSeed RPC to obtain a +// seed, then present it to the user. Once it has been verified by the user, +// the seed can be fed into this RPC in order to commit the new wallet. +func (u *UnlockerService) InitWallet(ctx context.Context, + in *lnrpc.InitWalletRequest) (*lnrpc.InitWalletResponse, error) { + + // Require the provided password to have a length of at least 8 + // characters. + password := in.WalletPassword if len(password) < 8 { return nil, fmt.Errorf("password must have " + "at least 8 characters") } + // We'll then open up the directory that will be used to store the + // wallet's files so we can check if the wallet already exists. netDir := btcwallet.NetworkDir(u.chainDir, u.netParams) loader := wallet.NewLoader(u.netParams, netDir) - // Check if wallet already exists. walletExists, err := loader.WalletExists() if err != nil { return nil, err } + // If the wallet already exists, then we'll exit early as we can't + // create the wallet if it already exists! if walletExists { - // Cannot create wallet if it already exists! return nil, fmt.Errorf("wallet already exists") } + // At this point, we know that the wallet doesn't already exist. So + // we'll map the user provided aezeed and passphrase into a decoded + // cipher seed instance. + var mnemonic aezeed.Mnemonic + copy(mnemonic[:], in.CipherSeedMnemonic[:]) + + // If we're unable to map it back into the ciphertext, then either the + // mnemonic is wrong, or the passphrase is wrong. + cipherSeed, err := mnemonic.ToCipherSeed(in.AezeedPassphrase) + if err != nil { + return nil, err + } + // Attempt to create a password for the macaroon service. if u.authSvc != nil { err = u.authSvc.CreateUnlock(&password) @@ -77,11 +194,17 @@ func (u *UnlockerService) CreateWallet(ctx context.Context, } } - // We send the password over the CreatePasswords channel, such that it - // can be used by lnd to open or create the wallet. - u.CreatePasswords <- password + // With the cipher seed deciphered, and the auth service created, we'll + // now send over the wallet password and the seed. This will allow the + // daemon to initialize itself and startup. + initMsg := &WalletInitMsg{ + Passphrase: password, + WalletSeed: cipherSeed, + } - return &lnrpc.CreateWalletResponse{}, nil + u.InitMsgs <- initMsg + + return &lnrpc.InitWalletResponse{}, nil } // UnlockWallet sends the password provided by the incoming UnlockWalletRequest @@ -105,15 +228,15 @@ func (u *UnlockerService) UnlockWallet(ctx context.Context, } // Try opening the existing wallet with the provided password. - _, err = loader.OpenExistingWallet(in.Password, false) + _, err = loader.OpenExistingWallet(in.WalletPassword, false) if err != nil { - // Could not open wallet, most likely this means that - // provided password was incorrect. + // Could not open wallet, most likely this means that provided + // password was incorrect. return nil, err } - // We successfully opened the wallet, but we'll need to unload - // it to make sure lnd can open it later. + // We successfully opened the wallet, but we'll need to unload it to + // make sure lnd can open it later. if err := loader.UnloadWallet(); err != nil { // TODO: not return error here? return nil, err @@ -121,7 +244,7 @@ func (u *UnlockerService) UnlockWallet(ctx context.Context, // Attempt to create a password for the macaroon service. if u.authSvc != nil { - err = u.authSvc.CreateUnlock(&in.Password) + err = u.authSvc.CreateUnlock(&in.WalletPassword) if err != nil { return nil, fmt.Errorf("unable to create/unlock "+ "macaroon store: %v", err) @@ -131,7 +254,7 @@ func (u *UnlockerService) UnlockWallet(ctx context.Context, // At this point we was able to open the existing wallet with the // provided password. We send the password over the UnlockPasswords // channel, such that it can be used by lnd to open the wallet. - u.UnlockPasswords <- in.Password + u.UnlockPasswords <- in.WalletPassword return &lnrpc.UnlockWalletResponse{}, nil } diff --git a/walletunlocker/service_test.go b/walletunlocker/service_test.go index d6e7eb8d..4e2514a2 100644 --- a/walletunlocker/service_test.go +++ b/walletunlocker/service_test.go @@ -4,9 +4,11 @@ import ( "bytes" "io/ioutil" "os" + "strings" "testing" "time" + "github.com/lightningnetwork/lnd/aezeed" "github.com/lightningnetwork/lnd/lnrpc" "github.com/lightningnetwork/lnd/lnwallet/btcwallet" "github.com/lightningnetwork/lnd/walletunlocker" @@ -23,6 +25,13 @@ var ( testPassword = []byte("test-password") testSeed = []byte("test-seed-123456789") + testEntropy = [aezeed.EntropySize]byte{ + 0x81, 0xb6, 0x37, 0xd8, + 0x63, 0x59, 0xe6, 0x96, + 0x0d, 0xe7, 0x95, 0xe4, + 0x1e, 0x0b, 0x4c, 0xfd, + } + testNetParams = &chaincfg.MainNetParams ) @@ -39,10 +48,127 @@ func createTestWallet(t *testing.T, dir string, netParams *chaincfg.Params) { } } -// TestCreateWallet checks that CreateWallet correctly returns a password that -// can be used for creating a wallet if no wallet exists from before, and -// returns an error when it already exists. -func TestCreateWallet(t *testing.T) { +// TestGenSeedUserEntropy tests that the gen seed method generates a valid +// cipher seed mnemonic phrase and user provided source of entropy. +func TestGenSeed(t *testing.T) { + t.Parallel() + + // First, we'll create a new test directory and unlocker service for + // that directory. + testDir, err := ioutil.TempDir("", "testcreate") + if err != nil { + t.Fatalf("unable to create temp directory: %v", err) + } + defer func() { + os.RemoveAll(testDir) + }() + service := walletunlocker.New(nil, testDir, testNetParams) + + // Now that the service has been created, we'll ask it to generate a + // new seed for us given a test passphrase. + aezeedPass := []byte("kek") + genSeedReq := &lnrpc.GenSeedRequest{ + AezeedPassphrase: aezeedPass, + SeedEntropy: testEntropy[:], + } + + ctx := context.Background() + seedResp, err := service.GenSeed(ctx, genSeedReq) + if err != nil { + t.Fatalf("unable to generate seed: %v", err) + } + + // We should then be able to take the generated mnemonic, and properly + // decipher both it. + var mnemonic aezeed.Mnemonic + copy(mnemonic[:], seedResp.CipherSeedMnemonic[:]) + _, err = mnemonic.ToCipherSeed(aezeedPass) + if err != nil { + t.Fatalf("unable to decipher cipher seed: %v", err) + } +} + +// TestGenSeedInvalidEntropy tests that the gen seed method generates a valid +// cipher seed mnemonic pass phrase even when the user doesn't provide its own +// source of entropy. +func TestGenSeedGenerateEntropy(t *testing.T) { + t.Parallel() + + // First, we'll create a new test directory and unlocker service for + // that directory. + testDir, err := ioutil.TempDir("", "testcreate") + if err != nil { + t.Fatalf("unable to create temp directory: %v", err) + } + defer func() { + os.RemoveAll(testDir) + }() + service := walletunlocker.New(nil, testDir, testNetParams) + + // Now that the service has been created, we'll ask it to generate a + // new seed for us given a test passphrase. Note that we don't actually + aezeedPass := []byte("kek") + genSeedReq := &lnrpc.GenSeedRequest{ + AezeedPassphrase: aezeedPass, + } + + ctx := context.Background() + seedResp, err := service.GenSeed(ctx, genSeedReq) + if err != nil { + t.Fatalf("unable to generate seed: %v", err) + } + + // We should then be able to take the generated mnemonic, and properly + // decipher both it. + var mnemonic aezeed.Mnemonic + copy(mnemonic[:], seedResp.CipherSeedMnemonic[:]) + _, err = mnemonic.ToCipherSeed(aezeedPass) + if err != nil { + t.Fatalf("unable to decipher cipher seed: %v", err) + } +} + +// TestGenSeedInvalidEntropy tests that if a user attempt to create a seed with +// the wrong number of bytes for the initial entropy, then the proper error is +// returned. +func TestGenSeedInvalidEntropy(t *testing.T) { + t.Parallel() + + // First, we'll create a new test directory and unlocker service for + // that directory. + testDir, err := ioutil.TempDir("", "testcreate") + if err != nil { + t.Fatalf("unable to create temp directory: %v", err) + } + defer func() { + os.RemoveAll(testDir) + }() + service := walletunlocker.New(nil, testDir, testNetParams) + + // Now that the service has been created, we'll ask it to generate a + // new seed for us given a test passphrase. However, we'll be using an + // invalid set of entropy that's 55 bytes, instead of 15 bytes. + aezeedPass := []byte("kek") + genSeedReq := &lnrpc.GenSeedRequest{ + AezeedPassphrase: aezeedPass, + SeedEntropy: bytes.Repeat([]byte("a"), 55), + } + + // We should get an error now since the entropy source was invalid. + ctx := context.Background() + _, err = service.GenSeed(ctx, genSeedReq) + if err == nil { + t.Fatalf("seed creation should've failed") + } + + if !strings.Contains(err.Error(), "incorrect entropy length") { + t.Fatalf("wrong error, expected incorrect entropy length") + } +} + +// TestInitWallet tests that the user is able to properly initialize the wallet +// given an existing cipher seed passphrase. +func TestInitWallet(t *testing.T) { t.Parallel() // testDir is empty, meaning wallet was not created from before. @@ -57,22 +183,60 @@ func TestCreateWallet(t *testing.T) { // Create new UnlockerService. service := walletunlocker.New(nil, testDir, testNetParams) - ctx := context.Background() - req := &lnrpc.CreateWalletRequest{ - Password: testPassword, - } - _, err = service.CreateWallet(ctx, req) + // Once we have the unlocker service created, we'll now instantiate a + // new cipher seed instance. + cipherSeed, err := aezeed.New(0, &testEntropy, time.Now()) if err != nil { - t.Fatalf("CreateWallet call failed: %v", err) + t.Fatalf("unable to create seed: %v", err) } - // Password should be sent over the channel. + // With the new seed created, we'll convert it into a mnemonic phrase + // that we'll send over to initialize the wallet. + pass := []byte("test") + mnemonic, err := cipherSeed.ToMnemonic(pass) + if err != nil { + t.Fatalf("unable to create mnemonic: %v", err) + } + + // Now that we have all the necessary items, we'll now issue the Init + // command to the wallet. This should check the validity of the cipher + // seed, then send over the initialization information over the init + // channel. + ctx := context.Background() + req := &lnrpc.InitWalletRequest{ + WalletPassword: testPassword, + CipherSeedMnemonic: []string(mnemonic[:]), + AezeedPassphrase: pass, + } + _, err = service.InitWallet(ctx, req) + if err != nil { + t.Fatalf("InitWallet call failed: %v", err) + } + + // The same user passphrase, and also the plaintext cipher seed + // should be sent over and match exactly. select { - case pw := <-service.CreatePasswords: - if !bytes.Equal(pw, testPassword) { - t.Fatalf("expected to receive password %x, got %x", - testPassword, pw) + case msg := <-service.InitMsgs: + if !bytes.Equal(msg.Passphrase, testPassword) { + t.Fatalf("expected to receive password %x, "+ + "got %x", testPassword, msg.Passphrase) } + if msg.WalletSeed.InternalVersion != cipherSeed.InternalVersion { + t.Fatalf("mismatched versions: expected %v, "+ + "got %v", cipherSeed.InternalVersion, + msg.WalletSeed.InternalVersion) + } + if msg.WalletSeed.Birthday != cipherSeed.Birthday { + t.Fatalf("mismatched birthday: expected %v, "+ + "got %v", cipherSeed.Birthday, + msg.WalletSeed.Birthday) + } + if msg.WalletSeed.Entropy != cipherSeed.Entropy { + t.Fatalf("mismatched versions: expected %x, "+ + "got %x", cipherSeed.Entropy[:], + msg.WalletSeed.Entropy[:]) + } + case <-time.After(3 * time.Second): t.Fatalf("password not received") } @@ -80,11 +244,50 @@ func TestCreateWallet(t *testing.T) { // Create a wallet in testDir. createTestWallet(t, testDir, testNetParams) - // Now calling CreateWallet should fail, since a wallet already exists - // in the directory. - _, err = service.CreateWallet(ctx, req) + // Now calling InitWallet should fail, since a wallet already exists in + // the directory. + _, err = service.InitWallet(ctx, req) if err == nil { - t.Fatalf("CreateWallet did not fail as expected") + t.Fatalf("InitWallet did not fail as expected") + } + + // Similarly, if we try to do GenSeed again, we should get an error as + // the wallet already exists. + _, err = service.GenSeed(ctx, &lnrpc.GenSeedRequest{}) + if err == nil { + t.Fatalf("seed generation should have failed") + } +} + +// TestInitWalletInvalidCipherSeed tests that if we attempt to create a wallet +// with an invalid cipher seed, then we'll receive an error. +func TestCreateWalletInvalidEntropy(t *testing.T) { + t.Parallel() + + // testDir is empty, meaning wallet was not created from before. + testDir, err := ioutil.TempDir("", "testcreate") + if err != nil { + t.Fatalf("unable to create temp directory: %v", err) + } + defer func() { + os.RemoveAll(testDir) + }() + + // Create new UnlockerService. + service := walletunlocker.New(nil, testDir, testNetParams) + + // We'll attempt to init the wallet with an invalid cipher seed and + // passphrase. + req := &lnrpc.InitWalletRequest{ + WalletPassword: testPassword, + CipherSeedMnemonic: []string{"invalid", "seed"}, + AezeedPassphrase: []byte("fake pass"), + } + + ctx := context.Background() + _, err = service.InitWallet(ctx, req) + if err == nil { + t.Fatalf("wallet creation should have failed") } } @@ -108,7 +311,7 @@ func TestUnlockWallet(t *testing.T) { ctx := context.Background() req := &lnrpc.UnlockWalletRequest{ - Password: testPassword, + WalletPassword: testPassword, } // Should fail to unlock non-existing wallet. @@ -122,7 +325,7 @@ func TestUnlockWallet(t *testing.T) { // Try unlocking this wallet with the wrong passphrase. wrongReq := &lnrpc.UnlockWalletRequest{ - Password: []byte("wrong-ofc"), + WalletPassword: []byte("wrong-ofc"), } _, err = service.UnlockWallet(ctx, wrongReq) if err == nil { @@ -145,5 +348,4 @@ func TestUnlockWallet(t *testing.T) { case <-time.After(3 * time.Second): t.Fatalf("password not received") } - } From 6289a2fb844dece81ae953d5f85ba5195af18d95 Mon Sep 17 00:00:00 2001 From: Olaoluwa Osuntokun Date: Thu, 1 Feb 2018 20:49:34 -0800 Subject: [PATCH 3/5] lnd: move seed generation outside wallet to allow for user supplied entropy MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In this commit, we extend the initial wallet creation set up case with the goal of giving the user the ability to restore a prior wallet from seed, or obtain the mnemonic for a newly generated wallet. As the WalletUnlocker has been extended to allow passing a user source of entropy, if this is detected, then we’ll use BIP39 to covert it into an HD wallet seed. Otherwise, we’ll generate our own entropy, then convert that into the wallet seed. In order to make this change, we’ll now manually create the default wallet ourselves. In the case that the user didn’t provide their own seed, we’ll send the seed we generated back to the user. This will allow frontends to display the newly generated seed to the end user. --- lnd.go | 45 ++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 40 insertions(+), 5 deletions(-) diff --git a/lnd.go b/lnd.go index 295a277d..d88affda 100644 --- a/lnd.go +++ b/lnd.go @@ -39,12 +39,14 @@ import ( "github.com/lightningnetwork/lnd/channeldb" "github.com/lightningnetwork/lnd/lnrpc" "github.com/lightningnetwork/lnd/lnwallet" + "github.com/lightningnetwork/lnd/lnwallet/btcwallet" "github.com/lightningnetwork/lnd/lnwire" "github.com/lightningnetwork/lnd/macaroons" "github.com/lightningnetwork/lnd/walletunlocker" "github.com/roasbeef/btcd/btcec" "github.com/roasbeef/btcd/wire" "github.com/roasbeef/btcutil" + "github.com/roasbeef/btcwallet/wallet" ) const ( @@ -820,14 +822,47 @@ func waitForWalletPassword(grpcEndpoints, restEndpoints []string, "Use `lncli create` to create wallet, or " + "`lncli unlock` to unlock already created wallet.") - // We currently don't distinguish between getting a password to - // be used for creation or unlocking, as a new wallet db will be - // created if none exists when creating the chain control. + // We currently don't distinguish between getting a password to be used + // for creation or unlocking, as a new wallet db will be created if + // none exists when creating the chain control. select { - case walletPw := <-pwService.CreatePasswords: - return walletPw, walletPw, nil + + // The wallet is being created for the first time, we'll check to see + // if the user provided any entropy for seed creation. If so, then + // we'll create the wallet early to load the seed. + case initMsg := <-pwService.InitMsgs: + password := initMsg.Passphrase + cipherSeed := initMsg.WalletSeed + + netDir := btcwallet.NetworkDir( + chainConfig.ChainDir, activeNetParams.Params, + ) + loader := wallet.NewLoader(activeNetParams.Params, netDir) + + // With the seed, we can now use the wallet loader to create + // the wallet, then unload it so it can be opened shortly + // after. + // + // TODO(roasbeef): extend loader to also accept birthday + // * also check with keychain version + _, err = loader.CreateNewWallet( + password, password, cipherSeed.Entropy[:], + ) + if err != nil { + return nil, nil, err + } + + if err := loader.UnloadWallet(); err != nil { + return nil, nil, err + } + + return password, password, nil + + // The wallet has already been created in the past, and is simply being + // unlocked. So we'll just return these passphrases. case walletPw := <-pwService.UnlockPasswords: return walletPw, walletPw, nil + case <-shutdownChannel: return nil, nil, fmt.Errorf("shutting down") } From d8ce90306d77ac031cae7c1e1cd08bed5faae63d Mon Sep 17 00:00:00 2001 From: Olaoluwa Osuntokun Date: Thu, 1 Feb 2018 20:50:58 -0800 Subject: [PATCH 4/5] lnwallet/btcwallet: during initial creation catch the case of an existing wallet In this commit, due to the recent changes within lnd itself, it may be possible that a wallet already exists when the wallet has been signaled to be created. As a result, *always* open the wallet ourselves, but allow an existing wallet to already be in place. --- lnwallet/btcwallet/btcwallet.go | 30 +++++++++++++++++++++--------- 1 file changed, 21 insertions(+), 9 deletions(-) diff --git a/lnwallet/btcwallet/btcwallet.go b/lnwallet/btcwallet/btcwallet.go index d5b1af00..2855d319 100644 --- a/lnwallet/btcwallet/btcwallet.go +++ b/lnwallet/btcwallet/btcwallet.go @@ -80,21 +80,33 @@ func New(cfg Config) (*BtcWallet, error) { var wallet *base.Wallet if !walletExists { // Wallet has never been created, perform initial set up. - wallet, err = loader.CreateNewWallet(pubPass, cfg.PrivatePass, - cfg.HdSeed) - if err != nil { + wallet, err = loader.CreateNewWallet( + pubPass, cfg.PrivatePass, cfg.HdSeed, + ) + + switch { + // If the wallet already exists, then we'll ignore this error + // and proceed directly to opening the wallet. + case err == base.ErrExists: + + // Otherwise, there's a greater error here, and we'll return + // early. + case err != nil: return nil, err } - } else { - // Wallet has been created and been initialized at this point, - // open it along with all the required DB namespaces, and the - // DB itself. - wallet, err = loader.OpenExistingWallet(pubPass, false) - if err != nil { + + if err := loader.UnloadWallet(); err != nil { return nil, err } } + // Wallet has been created and been initialized at this point, open it + // along with all the required DB namepsaces, and the DB itself. + wallet, err = loader.OpenExistingWallet(pubPass, false) + if err != nil { + return nil, err + } + // Create a bucket within the wallet's database dedicated to storing // our LN specific data. db := wallet.Database() From 3356a370c70c4f4a995427bc88d84679593b7ecc Mon Sep 17 00:00:00 2001 From: Olaoluwa Osuntokun Date: Thu, 1 Feb 2018 20:52:40 -0800 Subject: [PATCH 5/5] cmd/lncli: extend initial wallet creation with aezeed seed support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In this commit, we extend the `lncli create` command to allow users to specify their own side (if they want). In the case that the user *doesn’t* specify their own seed, we’ll return the entropy generated by the wallet in a 24-word mnemonic format for easy backup. With this change, it’s now possible for users to restore an existing lnd wallet seed. --- cmd/lncli/commands.go | 220 ++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 211 insertions(+), 9 deletions(-) diff --git a/cmd/lncli/commands.go b/cmd/lncli/commands.go index d1d860ce..4092a2f3 100644 --- a/cmd/lncli/commands.go +++ b/cmd/lncli/commands.go @@ -1,6 +1,7 @@ package main import ( + "bufio" "bytes" "encoding/hex" "encoding/json" @@ -776,16 +777,63 @@ func listPeers(ctx *cli.Context) error { } var createCommand = cli.Command{ - Name: "create", - Usage: "Used to set the wallet password at lnd startup", + Name: "create", + Description: ` + The create command is used to initialize an lnd wallet from scratch for + the very first time. This is interactive command with one required + argument (the password), and one optional argument (the mnemonic + passphrase). + + The first argument (the password) is required and MUST be greater than + 8 characters. This will be used to encrypt the wallet within lnd. This + MUST be remembered as it will be required to fully start up the daemon. + + The second argument is an optional 24-word mnemonic derived from BIP + 39. If provided, then the internal wallet will use the seed derived + from this mnemonic to generate all keys. + + This command returns a 24-word seed in the scenario that NO mnemonic + was provided by the user. This should be written down as it can be used + to potentially recover all on-chain funds, and most off-chain funds as + well. + `, Action: actionDecorator(create), } +// monowidthColumns takes a set of words, and the number of desired columns, +// and returns a new set of words that have had white space appended to the +// word in order to create a mono-width column. +func monowidthColumns(words []string, ncols int) []string { + // Determine max size of words in each column. + colWidths := make([]int, ncols) + for i, word := range words { + col := i % ncols + curWidth := colWidths[col] + if len(word) > curWidth { + colWidths[col] = len(word) + } + } + + // Append whitespace to each word to make columns mono-width. + finalWords := make([]string, len(words)) + for i, word := range words { + col := i % ncols + width := colWidths[col] + + diff := width - len(word) + finalWords[i] = word + strings.Repeat(" ", diff) + } + + return finalWords +} + func create(ctx *cli.Context) error { ctxb := context.Background() client, cleanUp := getWalletUnlockerClient(ctx) defer cleanUp() + // First, we'll prompt the user for their passphrase twice to ensure + // both attempts match up properly. fmt.Printf("Input wallet password: ") pw1, err := terminal.ReadPassword(int(syscall.Stdin)) if err != nil { @@ -800,24 +848,176 @@ func create(ctx *cli.Context) error { } fmt.Println() + // If the passwords don't match, then we'll return an error. if !bytes.Equal(pw1, pw2) { return fmt.Errorf("passwords don't match") } - req := &lnrpc.CreateWalletRequest{ - Password: pw1, + // Next, we'll see if the user has 24-word mnemonic they want to use to + // derive a seed within the wallet. + var ( + hasMnemonic bool + ) + +mnemonicCheck: + for { + fmt.Println() + fmt.Printf("Do you have an existing cipher seed " + + "mnemonic you want to use? (Enter y/n): ") + + reader := bufio.NewReader(os.Stdin) + answer, err := reader.ReadString('\n') + if err != nil { + return err + } + + fmt.Println() + + answer = strings.TrimSpace(answer) + answer = strings.ToLower(answer) + + switch answer { + case "y": + hasMnemonic = true + break mnemonicCheck + case "n": + hasMnemonic = false + break mnemonicCheck + } } - _, err = client.CreateWallet(ctxb, req) - if err != nil { + + // If the user *does* have an existing seed they want to use, then + // we'll read that in directly from the terminal. + var ( + cipherSeedMnemonic []string + aezeedPass []byte + ) + if hasMnemonic { + // We'll now prompt the user to enter in their 24-word + // mnemonic. + fmt.Printf("Input your 24-word mnemonic separated by spaces: ") + reader := bufio.NewReader(os.Stdin) + mnemonic, err := reader.ReadString('\n') + if err != nil { + return err + } + + // We'll trim off extra spaces, and ensure the mnemonic is all + // lower case, then populate our request. + mnemonic = strings.TrimSpace(mnemonic) + mnemonic = strings.ToLower(mnemonic) + + cipherSeedMnemonic = strings.Split(mnemonic, " ") + + fmt.Println() + + // Additionally, the user may have a passphrase, that will also + // need to be provided so the daemon can properly decipher the + // cipher seed. + fmt.Printf("Input your cipher seed passphrase (press enter if " + + "your seed doesn't have a passphrase): ") + passphrase, err := terminal.ReadPassword(int(syscall.Stdin)) + if err != nil { + return err + } + + aezeedPass = []byte(passphrase) + + fmt.Println() + } else { + // Otherwise, if the user doesn't have a mnemonic that they + // want to use, we'll generate a fresh one with the GenSeed + // command. + fmt.Println("Your cipher seed can optionally be encrypted.") + fmt.Printf("Input your passphrase you wish to encrypt it " + + "(or press enter to proceed without a cipher seed " + + "passphrase): ") + aezeedPass1, err := terminal.ReadPassword(int(syscall.Stdin)) + if err != nil { + return err + } + fmt.Println() + + if len(aezeedPass1) != 0 { + fmt.Printf("Confirm cipher seed passphrase: ") + aezeedPass2, err := terminal.ReadPassword( + int(syscall.Stdin), + ) + if err != nil { + return err + } + fmt.Println() + + // If the passwords don't match, then we'll return an + // error. + if !bytes.Equal(aezeedPass1, aezeedPass2) { + return fmt.Errorf("cipher seed pass phrases " + + "don't match") + } + } + + fmt.Println() + fmt.Println("Generating fresh cipher seed...") + fmt.Println() + + genSeedReq := &lnrpc.GenSeedRequest{ + AezeedPassphrase: aezeedPass1, + } + seedResp, err := client.GenSeed(ctxb, genSeedReq) + if err != nil { + return fmt.Errorf("unable to generate seed: %v", err) + } + + cipherSeedMnemonic = seedResp.CipherSeedMnemonic + aezeedPass = aezeedPass1 + } + + // Before we initialize the wallet, we'll display the cipher seed to + // the user so they can write it down. + mnemonicWords := cipherSeedMnemonic + + fmt.Println("!!!YOU MUST WRITE DOWN THIS SEED TO BE ABLE TO " + + "RESTORE THE WALLET!!!\n") + + fmt.Println("---------------BEGIN LND CIPHER SEED---------------") + + numCols := 4 + colWords := monowidthColumns(mnemonicWords, numCols) + for i := 0; i < len(colWords); i += numCols { + fmt.Printf("%2d. %3s %2d. %3s %2d. %3s %2d. %3s\n", + i+1, colWords[i], i+2, colWords[i+1], i+3, + colWords[i+2], i+4, colWords[i+3]) + } + + fmt.Println("---------------END LND CIPHER SEED-----------------") + + fmt.Println("\n!!!YOU MUST WRITE DOWN THIS SEED TO BE ABLE TO " + + "RESTORE THE WALLET!!!") + + // With either the user's prior cipher seed, or a newly generated one, + // we'll go ahead and initialize the wallet. + req := &lnrpc.InitWalletRequest{ + WalletPassword: pw1, + CipherSeedMnemonic: cipherSeedMnemonic, + AezeedPassphrase: aezeedPass, + } + if _, err := client.InitWallet(ctxb, req); err != nil { return err } + fmt.Println("\nlnd successfully initialized!") + return nil } var unlockCommand = cli.Command{ - Name: "unlock", - Usage: "Unlock encrypted wallet at lnd startup", + Name: "unlock", + Description: ` + The unlock command is used to decrypt lnd's wallet state in order to + start up. This command MUST be run after booting up lnd before it's + able to carry out its duties. An exception is if a user is running with + --noencryptwallet, then a default passphrase will be used. + `, Action: actionDecorator(unlock), } @@ -834,13 +1034,15 @@ func unlock(ctx *cli.Context) error { fmt.Println() req := &lnrpc.UnlockWalletRequest{ - Password: pw, + WalletPassword: pw, } _, err = client.UnlockWallet(ctxb, req) if err != nil { return err } + fmt.Println("\nlnd successfully unlocked!") + return nil }