Add support for Out Of Round txs (#359)

* [common] rework address encoding

* new address encoding

* replace offchain address by vtxo output key in DB

* merge migrations files into init one

* fix txbuilder fixtures

* fix transaction events

* OOR scheme

* fix conflicts

* [sdk] OOR

* update WASM wrappers

* revert renaming

* revert API changes

* update parser.go

* fix vtxosToTxsCovenantless

* add settled and spent in Utxo and Transaction

* Fixes (#5)

* Revert unneeded changes and rename claim to settle

* Revert changes to wasm and rename claim to settle

---------

Co-authored-by: Pietralberto Mazza <18440657+altafan@users.noreply.github.com>
This commit is contained in:
Louis Singer
2024-10-24 17:43:27 +02:00
committed by GitHub
parent b536a9e652
commit bcb2b2075f
41 changed files with 1103 additions and 1390 deletions

View File

@@ -1070,7 +1070,7 @@
"swept": {
"type": "boolean"
},
"pending": {
"isOor": {
"type": "boolean"
},
"redeemTx": {

View File

@@ -318,7 +318,7 @@ message Vtxo {
string spent_by = 4;
int64 expire_at = 5;
bool swept = 6;
bool pending = 7;
bool is_oor = 7;
string redeem_tx = 8;
uint64 amount = 9;
string pubkey = 10;

View File

@@ -2436,7 +2436,7 @@ type Vtxo struct {
SpentBy string `protobuf:"bytes,4,opt,name=spent_by,json=spentBy,proto3" json:"spent_by,omitempty"`
ExpireAt int64 `protobuf:"varint,5,opt,name=expire_at,json=expireAt,proto3" json:"expire_at,omitempty"`
Swept bool `protobuf:"varint,6,opt,name=swept,proto3" json:"swept,omitempty"`
Pending bool `protobuf:"varint,7,opt,name=pending,proto3" json:"pending,omitempty"`
IsOor bool `protobuf:"varint,7,opt,name=is_oor,json=isOor,proto3" json:"is_oor,omitempty"`
RedeemTx string `protobuf:"bytes,8,opt,name=redeem_tx,json=redeemTx,proto3" json:"redeem_tx,omitempty"`
Amount uint64 `protobuf:"varint,9,opt,name=amount,proto3" json:"amount,omitempty"`
Pubkey string `protobuf:"bytes,10,opt,name=pubkey,proto3" json:"pubkey,omitempty"`
@@ -2516,9 +2516,9 @@ func (x *Vtxo) GetSwept() bool {
return false
}
func (x *Vtxo) GetPending() bool {
func (x *Vtxo) GetIsOor() bool {
if x != nil {
return x.Pending
return x.IsOor
}
return false
}
@@ -3067,7 +3067,7 @@ var file_ark_v1_service_proto_rawDesc = []byte{
0x52, 0x04, 0x74, 0x78, 0x69, 0x64, 0x12, 0x0e, 0x0a, 0x02, 0x74, 0x78, 0x18, 0x02, 0x20, 0x01,
0x28, 0x09, 0x52, 0x02, 0x74, 0x78, 0x12, 0x1f, 0x0a, 0x0b, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74,
0x5f, 0x74, 0x78, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x70, 0x61, 0x72,
0x65, 0x6e, 0x74, 0x54, 0x78, 0x69, 0x64, 0x22, 0x9e, 0x02, 0x0a, 0x04, 0x56, 0x74, 0x78, 0x6f,
0x65, 0x6e, 0x74, 0x54, 0x78, 0x69, 0x64, 0x22, 0x9b, 0x02, 0x0a, 0x04, 0x56, 0x74, 0x78, 0x6f,
0x12, 0x2c, 0x0a, 0x08, 0x6f, 0x75, 0x74, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01,
0x28, 0x0b, 0x32, 0x10, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x4f, 0x75, 0x74, 0x70,
0x6f, 0x69, 0x6e, 0x74, 0x52, 0x08, 0x6f, 0x75, 0x74, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x12, 0x14,
@@ -3079,177 +3079,177 @@ var file_ark_v1_service_proto_rawDesc = []byte{
0x0a, 0x09, 0x65, 0x78, 0x70, 0x69, 0x72, 0x65, 0x5f, 0x61, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28,
0x03, 0x52, 0x08, 0x65, 0x78, 0x70, 0x69, 0x72, 0x65, 0x41, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x73,
0x77, 0x65, 0x70, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x08, 0x52, 0x05, 0x73, 0x77, 0x65, 0x70,
0x74, 0x12, 0x18, 0x0a, 0x07, 0x70, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x18, 0x07, 0x20, 0x01,
0x28, 0x08, 0x52, 0x07, 0x70, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x12, 0x1b, 0x0a, 0x09, 0x72,
0x65, 0x64, 0x65, 0x65, 0x6d, 0x5f, 0x74, 0x78, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08,
0x72, 0x65, 0x64, 0x65, 0x65, 0x6d, 0x54, 0x78, 0x12, 0x16, 0x0a, 0x06, 0x61, 0x6d, 0x6f, 0x75,
0x6e, 0x74, 0x18, 0x09, 0x20, 0x01, 0x28, 0x04, 0x52, 0x06, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74,
0x12, 0x16, 0x0a, 0x06, 0x70, 0x75, 0x62, 0x6b, 0x65, 0x79, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09,
0x52, 0x06, 0x70, 0x75, 0x62, 0x6b, 0x65, 0x79, 0x22, 0x1e, 0x0a, 0x1c, 0x47, 0x65, 0x74, 0x54,
0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x53, 0x74, 0x72, 0x65, 0x61,
0x6d, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x8c, 0x01, 0x0a, 0x1d, 0x47, 0x65, 0x74,
0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x53, 0x74, 0x72, 0x65,
0x61, 0x6d, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x30, 0x0a, 0x05, 0x72, 0x6f,
0x75, 0x6e, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x61, 0x72, 0x6b, 0x2e,
0x76, 0x31, 0x2e, 0x52, 0x6f, 0x75, 0x6e, 0x64, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74,
0x69, 0x6f, 0x6e, 0x48, 0x00, 0x52, 0x05, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x12, 0x33, 0x0a, 0x06,
0x72, 0x65, 0x64, 0x65, 0x65, 0x6d, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x61,
0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x64, 0x65, 0x65, 0x6d, 0x54, 0x72, 0x61, 0x6e,
0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x48, 0x00, 0x52, 0x06, 0x72, 0x65, 0x64, 0x65, 0x65,
0x6d, 0x42, 0x04, 0x0a, 0x02, 0x74, 0x78, 0x22, 0xd8, 0x01, 0x0a, 0x10, 0x52, 0x6f, 0x75, 0x6e,
0x64, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x12, 0x0a, 0x04,
0x74, 0x78, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x78, 0x69, 0x64,
0x12, 0x31, 0x0a, 0x0b, 0x73, 0x70, 0x65, 0x6e, 0x74, 0x5f, 0x76, 0x74, 0x78, 0x6f, 0x73, 0x18,
0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x4f,
0x75, 0x74, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x52, 0x0a, 0x73, 0x70, 0x65, 0x6e, 0x74, 0x56, 0x74,
0x78, 0x6f, 0x73, 0x12, 0x35, 0x0a, 0x0f, 0x73, 0x70, 0x65, 0x6e, 0x64, 0x61, 0x62, 0x6c, 0x65,
0x5f, 0x76, 0x74, 0x78, 0x6f, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x61,
0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x56, 0x74, 0x78, 0x6f, 0x52, 0x0e, 0x73, 0x70, 0x65, 0x6e,
0x64, 0x61, 0x62, 0x6c, 0x65, 0x56, 0x74, 0x78, 0x6f, 0x73, 0x12, 0x46, 0x0a, 0x16, 0x63, 0x6c,
0x61, 0x69, 0x6d, 0x65, 0x64, 0x5f, 0x62, 0x6f, 0x61, 0x72, 0x64, 0x69, 0x6e, 0x67, 0x5f, 0x75,
0x74, 0x78, 0x6f, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x61, 0x72, 0x6b,
0x2e, 0x76, 0x31, 0x2e, 0x4f, 0x75, 0x74, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x52, 0x14, 0x63, 0x6c,
0x61, 0x69, 0x6d, 0x65, 0x64, 0x42, 0x6f, 0x61, 0x72, 0x64, 0x69, 0x6e, 0x67, 0x55, 0x74, 0x78,
0x6f, 0x73, 0x22, 0x91, 0x01, 0x0a, 0x11, 0x52, 0x65, 0x64, 0x65, 0x65, 0x6d, 0x54, 0x72, 0x61,
0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x78, 0x69, 0x64,
0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x78, 0x69, 0x64, 0x12, 0x31, 0x0a, 0x0b,
0x73, 0x70, 0x65, 0x6e, 0x74, 0x5f, 0x76, 0x74, 0x78, 0x6f, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28,
0x0b, 0x32, 0x10, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x4f, 0x75, 0x74, 0x70, 0x6f,
0x69, 0x6e, 0x74, 0x52, 0x0a, 0x73, 0x70, 0x65, 0x6e, 0x74, 0x56, 0x74, 0x78, 0x6f, 0x73, 0x12,
0x35, 0x0a, 0x0f, 0x73, 0x70, 0x65, 0x6e, 0x64, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x76, 0x74, 0x78,
0x6f, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76,
0x31, 0x2e, 0x56, 0x74, 0x78, 0x6f, 0x52, 0x0e, 0x73, 0x70, 0x65, 0x6e, 0x64, 0x61, 0x62, 0x6c,
0x65, 0x56, 0x74, 0x78, 0x6f, 0x73, 0x2a, 0x98, 0x01, 0x0a, 0x0a, 0x52, 0x6f, 0x75, 0x6e, 0x64,
0x53, 0x74, 0x61, 0x67, 0x65, 0x12, 0x1b, 0x0a, 0x17, 0x52, 0x4f, 0x55, 0x4e, 0x44, 0x5f, 0x53,
0x54, 0x41, 0x47, 0x45, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44,
0x10, 0x00, 0x12, 0x1c, 0x0a, 0x18, 0x52, 0x4f, 0x55, 0x4e, 0x44, 0x5f, 0x53, 0x54, 0x41, 0x47,
0x45, 0x5f, 0x52, 0x45, 0x47, 0x49, 0x53, 0x54, 0x52, 0x41, 0x54, 0x49, 0x4f, 0x4e, 0x10, 0x01,
0x12, 0x1c, 0x0a, 0x18, 0x52, 0x4f, 0x55, 0x4e, 0x44, 0x5f, 0x53, 0x54, 0x41, 0x47, 0x45, 0x5f,
0x46, 0x49, 0x4e, 0x41, 0x4c, 0x49, 0x5a, 0x41, 0x54, 0x49, 0x4f, 0x4e, 0x10, 0x02, 0x12, 0x19,
0x0a, 0x15, 0x52, 0x4f, 0x55, 0x4e, 0x44, 0x5f, 0x53, 0x54, 0x41, 0x47, 0x45, 0x5f, 0x46, 0x49,
0x4e, 0x41, 0x4c, 0x49, 0x5a, 0x45, 0x44, 0x10, 0x03, 0x12, 0x16, 0x0a, 0x12, 0x52, 0x4f, 0x55,
0x4e, 0x44, 0x5f, 0x53, 0x54, 0x41, 0x47, 0x45, 0x5f, 0x46, 0x41, 0x49, 0x4c, 0x45, 0x44, 0x10,
0x04, 0x32, 0xeb, 0x0d, 0x0a, 0x0a, 0x41, 0x72, 0x6b, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65,
0x12, 0x4c, 0x0a, 0x07, 0x47, 0x65, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x16, 0x2e, 0x61, 0x72,
0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, 0x75,
0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74,
0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x10, 0x82, 0xd3,
0xe4, 0x93, 0x02, 0x0a, 0x12, 0x08, 0x2f, 0x76, 0x31, 0x2f, 0x69, 0x6e, 0x66, 0x6f, 0x12, 0x74,
0x0a, 0x12, 0x47, 0x65, 0x74, 0x42, 0x6f, 0x61, 0x72, 0x64, 0x69, 0x6e, 0x67, 0x41, 0x64, 0x64,
0x72, 0x65, 0x73, 0x73, 0x12, 0x21, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65,
0x74, 0x12, 0x15, 0x0a, 0x06, 0x69, 0x73, 0x5f, 0x6f, 0x6f, 0x72, 0x18, 0x07, 0x20, 0x01, 0x28,
0x08, 0x52, 0x05, 0x69, 0x73, 0x4f, 0x6f, 0x72, 0x12, 0x1b, 0x0a, 0x09, 0x72, 0x65, 0x64, 0x65,
0x65, 0x6d, 0x5f, 0x74, 0x78, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x72, 0x65, 0x64,
0x65, 0x65, 0x6d, 0x54, 0x78, 0x12, 0x16, 0x0a, 0x06, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x18,
0x09, 0x20, 0x01, 0x28, 0x04, 0x52, 0x06, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x16, 0x0a,
0x06, 0x70, 0x75, 0x62, 0x6b, 0x65, 0x79, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x70,
0x75, 0x62, 0x6b, 0x65, 0x79, 0x22, 0x1e, 0x0a, 0x1c, 0x47, 0x65, 0x74, 0x54, 0x72, 0x61, 0x6e,
0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x52, 0x65,
0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x8c, 0x01, 0x0a, 0x1d, 0x47, 0x65, 0x74, 0x54, 0x72, 0x61,
0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x52,
0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x30, 0x0a, 0x05, 0x72, 0x6f, 0x75, 0x6e, 0x64,
0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e,
0x52, 0x6f, 0x75, 0x6e, 0x64, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e,
0x48, 0x00, 0x52, 0x05, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x12, 0x33, 0x0a, 0x06, 0x72, 0x65, 0x64,
0x65, 0x65, 0x6d, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x61, 0x72, 0x6b, 0x2e,
0x76, 0x31, 0x2e, 0x52, 0x65, 0x64, 0x65, 0x65, 0x6d, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63,
0x74, 0x69, 0x6f, 0x6e, 0x48, 0x00, 0x52, 0x06, 0x72, 0x65, 0x64, 0x65, 0x65, 0x6d, 0x42, 0x04,
0x0a, 0x02, 0x74, 0x78, 0x22, 0xd8, 0x01, 0x0a, 0x10, 0x52, 0x6f, 0x75, 0x6e, 0x64, 0x54, 0x72,
0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x78, 0x69,
0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x78, 0x69, 0x64, 0x12, 0x31, 0x0a,
0x0b, 0x73, 0x70, 0x65, 0x6e, 0x74, 0x5f, 0x76, 0x74, 0x78, 0x6f, 0x73, 0x18, 0x02, 0x20, 0x03,
0x28, 0x0b, 0x32, 0x10, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x4f, 0x75, 0x74, 0x70,
0x6f, 0x69, 0x6e, 0x74, 0x52, 0x0a, 0x73, 0x70, 0x65, 0x6e, 0x74, 0x56, 0x74, 0x78, 0x6f, 0x73,
0x12, 0x35, 0x0a, 0x0f, 0x73, 0x70, 0x65, 0x6e, 0x64, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x76, 0x74,
0x78, 0x6f, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x61, 0x72, 0x6b, 0x2e,
0x76, 0x31, 0x2e, 0x56, 0x74, 0x78, 0x6f, 0x52, 0x0e, 0x73, 0x70, 0x65, 0x6e, 0x64, 0x61, 0x62,
0x6c, 0x65, 0x56, 0x74, 0x78, 0x6f, 0x73, 0x12, 0x46, 0x0a, 0x16, 0x63, 0x6c, 0x61, 0x69, 0x6d,
0x65, 0x64, 0x5f, 0x62, 0x6f, 0x61, 0x72, 0x64, 0x69, 0x6e, 0x67, 0x5f, 0x75, 0x74, 0x78, 0x6f,
0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31,
0x2e, 0x4f, 0x75, 0x74, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x52, 0x14, 0x63, 0x6c, 0x61, 0x69, 0x6d,
0x65, 0x64, 0x42, 0x6f, 0x61, 0x72, 0x64, 0x69, 0x6e, 0x67, 0x55, 0x74, 0x78, 0x6f, 0x73, 0x22,
0x91, 0x01, 0x0a, 0x11, 0x52, 0x65, 0x64, 0x65, 0x65, 0x6d, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61,
0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x78, 0x69, 0x64, 0x18, 0x01, 0x20,
0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x78, 0x69, 0x64, 0x12, 0x31, 0x0a, 0x0b, 0x73, 0x70, 0x65,
0x6e, 0x74, 0x5f, 0x76, 0x74, 0x78, 0x6f, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x10,
0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x4f, 0x75, 0x74, 0x70, 0x6f, 0x69, 0x6e, 0x74,
0x52, 0x0a, 0x73, 0x70, 0x65, 0x6e, 0x74, 0x56, 0x74, 0x78, 0x6f, 0x73, 0x12, 0x35, 0x0a, 0x0f,
0x73, 0x70, 0x65, 0x6e, 0x64, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x76, 0x74, 0x78, 0x6f, 0x73, 0x18,
0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x56,
0x74, 0x78, 0x6f, 0x52, 0x0e, 0x73, 0x70, 0x65, 0x6e, 0x64, 0x61, 0x62, 0x6c, 0x65, 0x56, 0x74,
0x78, 0x6f, 0x73, 0x2a, 0x98, 0x01, 0x0a, 0x0a, 0x52, 0x6f, 0x75, 0x6e, 0x64, 0x53, 0x74, 0x61,
0x67, 0x65, 0x12, 0x1b, 0x0a, 0x17, 0x52, 0x4f, 0x55, 0x4e, 0x44, 0x5f, 0x53, 0x54, 0x41, 0x47,
0x45, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12,
0x1c, 0x0a, 0x18, 0x52, 0x4f, 0x55, 0x4e, 0x44, 0x5f, 0x53, 0x54, 0x41, 0x47, 0x45, 0x5f, 0x52,
0x45, 0x47, 0x49, 0x53, 0x54, 0x52, 0x41, 0x54, 0x49, 0x4f, 0x4e, 0x10, 0x01, 0x12, 0x1c, 0x0a,
0x18, 0x52, 0x4f, 0x55, 0x4e, 0x44, 0x5f, 0x53, 0x54, 0x41, 0x47, 0x45, 0x5f, 0x46, 0x49, 0x4e,
0x41, 0x4c, 0x49, 0x5a, 0x41, 0x54, 0x49, 0x4f, 0x4e, 0x10, 0x02, 0x12, 0x19, 0x0a, 0x15, 0x52,
0x4f, 0x55, 0x4e, 0x44, 0x5f, 0x53, 0x54, 0x41, 0x47, 0x45, 0x5f, 0x46, 0x49, 0x4e, 0x41, 0x4c,
0x49, 0x5a, 0x45, 0x44, 0x10, 0x03, 0x12, 0x16, 0x0a, 0x12, 0x52, 0x4f, 0x55, 0x4e, 0x44, 0x5f,
0x53, 0x54, 0x41, 0x47, 0x45, 0x5f, 0x46, 0x41, 0x49, 0x4c, 0x45, 0x44, 0x10, 0x04, 0x32, 0xeb,
0x0d, 0x0a, 0x0a, 0x41, 0x72, 0x6b, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x4c, 0x0a,
0x07, 0x47, 0x65, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x16, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76,
0x31, 0x2e, 0x47, 0x65, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74,
0x1a, 0x17, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x49, 0x6e, 0x66,
0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x10, 0x82, 0xd3, 0xe4, 0x93, 0x02,
0x0a, 0x12, 0x08, 0x2f, 0x76, 0x31, 0x2f, 0x69, 0x6e, 0x66, 0x6f, 0x12, 0x74, 0x0a, 0x12, 0x47,
0x65, 0x74, 0x42, 0x6f, 0x61, 0x72, 0x64, 0x69, 0x6e, 0x67, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73,
0x73, 0x12, 0x21, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x42, 0x6f,
0x61, 0x72, 0x64, 0x69, 0x6e, 0x67, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x52, 0x65, 0x71,
0x75, 0x65, 0x73, 0x74, 0x1a, 0x22, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65,
0x74, 0x42, 0x6f, 0x61, 0x72, 0x64, 0x69, 0x6e, 0x67, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73,
0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x22, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31,
0x2e, 0x47, 0x65, 0x74, 0x42, 0x6f, 0x61, 0x72, 0x64, 0x69, 0x6e, 0x67, 0x41, 0x64, 0x64, 0x72,
0x65, 0x73, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x17, 0x82, 0xd3, 0xe4,
0x93, 0x02, 0x11, 0x3a, 0x01, 0x2a, 0x22, 0x0c, 0x2f, 0x76, 0x31, 0x2f, 0x62, 0x6f, 0x61, 0x72,
0x64, 0x69, 0x6e, 0x67, 0x12, 0x98, 0x01, 0x0a, 0x1a, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65,
0x72, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x73, 0x46, 0x6f, 0x72, 0x4e, 0x65, 0x78, 0x74, 0x52, 0x6f,
0x75, 0x6e, 0x64, 0x12, 0x29, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x67,
0x69, 0x73, 0x74, 0x65, 0x72, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x73, 0x46, 0x6f, 0x72, 0x4e, 0x65,
0x78, 0x74, 0x52, 0x6f, 0x75, 0x6e, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2a,
0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72,
0x49, 0x6e, 0x70, 0x75, 0x74, 0x73, 0x46, 0x6f, 0x72, 0x4e, 0x65, 0x78, 0x74, 0x52, 0x6f, 0x75,
0x6e, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x23, 0x82, 0xd3, 0xe4, 0x93,
0x02, 0x1d, 0x3a, 0x01, 0x2a, 0x22, 0x18, 0x2f, 0x76, 0x31, 0x2f, 0x72, 0x6f, 0x75, 0x6e, 0x64,
0x2f, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x73, 0x12,
0x9c, 0x01, 0x0a, 0x1b, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x4f, 0x75, 0x74, 0x70,
0x75, 0x74, 0x73, 0x46, 0x6f, 0x72, 0x4e, 0x65, 0x78, 0x74, 0x52, 0x6f, 0x75, 0x6e, 0x64, 0x12,
0x2a, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65,
0x72, 0x4f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x73, 0x46, 0x6f, 0x72, 0x4e, 0x65, 0x78, 0x74, 0x52,
0x6f, 0x75, 0x6e, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2b, 0x2e, 0x61, 0x72,
0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x4f, 0x75, 0x74,
0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x17, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x11,
0x3a, 0x01, 0x2a, 0x22, 0x0c, 0x2f, 0x76, 0x31, 0x2f, 0x62, 0x6f, 0x61, 0x72, 0x64, 0x69, 0x6e,
0x67, 0x12, 0x98, 0x01, 0x0a, 0x1a, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x49, 0x6e,
0x70, 0x75, 0x74, 0x73, 0x46, 0x6f, 0x72, 0x4e, 0x65, 0x78, 0x74, 0x52, 0x6f, 0x75, 0x6e, 0x64,
0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x24, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1e,
0x3a, 0x01, 0x2a, 0x22, 0x19, 0x2f, 0x76, 0x31, 0x2f, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x2f, 0x72,
0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x4f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x73, 0x12, 0x7d,
0x0a, 0x10, 0x53, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x54, 0x72, 0x65, 0x65, 0x4e, 0x6f, 0x6e, 0x63,
0x65, 0x73, 0x12, 0x1f, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x75, 0x62, 0x6d,
0x69, 0x74, 0x54, 0x72, 0x65, 0x65, 0x4e, 0x6f, 0x6e, 0x63, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75,
0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x75, 0x62,
0x6d, 0x69, 0x74, 0x54, 0x72, 0x65, 0x65, 0x4e, 0x6f, 0x6e, 0x63, 0x65, 0x73, 0x52, 0x65, 0x73,
0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x26, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x20, 0x3a, 0x01, 0x2a,
0x22, 0x1b, 0x2f, 0x76, 0x31, 0x2f, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x2f, 0x74, 0x72, 0x65, 0x65,
0x2f, 0x73, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x4e, 0x6f, 0x6e, 0x63, 0x65, 0x73, 0x12, 0x8d, 0x01,
0x0a, 0x14, 0x53, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x54, 0x72, 0x65, 0x65, 0x53, 0x69, 0x67, 0x6e,
0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x12, 0x23, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e,
0x53, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x54, 0x72, 0x65, 0x65, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x74,
0x75, 0x72, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x24, 0x2e, 0x61, 0x72,
0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x54, 0x72, 0x65, 0x65, 0x53,
0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73,
0x65, 0x22, 0x2a, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x24, 0x3a, 0x01, 0x2a, 0x22, 0x1f, 0x2f, 0x76,
0x31, 0x2f, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x2f, 0x74, 0x72, 0x65, 0x65, 0x2f, 0x73, 0x75, 0x62,
0x6d, 0x69, 0x74, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x12, 0x8e, 0x01,
0x0a, 0x16, 0x53, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x53, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x46, 0x6f,
0x72, 0x66, 0x65, 0x69, 0x74, 0x54, 0x78, 0x73, 0x12, 0x25, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76,
0x31, 0x2e, 0x53, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x53, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x46, 0x6f,
0x72, 0x66, 0x65, 0x69, 0x74, 0x54, 0x78, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a,
0x26, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x53,
0x69, 0x67, 0x6e, 0x65, 0x64, 0x46, 0x6f, 0x72, 0x66, 0x65, 0x69, 0x74, 0x54, 0x78, 0x73, 0x52,
0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x25, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1f, 0x3a,
0x01, 0x2a, 0x22, 0x1a, 0x2f, 0x76, 0x31, 0x2f, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x2f, 0x73, 0x75,
0x62, 0x6d, 0x69, 0x74, 0x46, 0x6f, 0x72, 0x66, 0x65, 0x69, 0x74, 0x54, 0x78, 0x73, 0x12, 0x6b,
0x0a, 0x0e, 0x47, 0x65, 0x74, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d,
0x12, 0x1d, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x45, 0x76, 0x65,
0x6e, 0x74, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a,
0x1e, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x45, 0x76, 0x65, 0x6e,
0x74, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22,
0x18, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x12, 0x12, 0x10, 0x2f, 0x76, 0x31, 0x2f, 0x72, 0x6f, 0x75,
0x6e, 0x64, 0x2f, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x30, 0x01, 0x12, 0x56, 0x0a, 0x04, 0x50,
0x69, 0x6e, 0x67, 0x12, 0x13, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x69, 0x6e,
0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x14, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76,
0x31, 0x2e, 0x50, 0x69, 0x6e, 0x67, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x23,
0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1d, 0x12, 0x1b, 0x2f, 0x76, 0x31, 0x2f, 0x72, 0x6f, 0x75, 0x6e,
0x64, 0x2f, 0x70, 0x69, 0x6e, 0x67, 0x2f, 0x7b, 0x70, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x5f,
0x69, 0x64, 0x7d, 0x12, 0x64, 0x0a, 0x0d, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x50, 0x61, 0x79,
0x6d, 0x65, 0x6e, 0x74, 0x12, 0x1c, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x72,
0x65, 0x61, 0x74, 0x65, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65,
0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x72, 0x65, 0x61,
0x74, 0x65, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73,
0x65, 0x22, 0x16, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x10, 0x3a, 0x01, 0x2a, 0x22, 0x0b, 0x2f, 0x76,
0x31, 0x2f, 0x70, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x12, 0x73, 0x0a, 0x0f, 0x43, 0x6f, 0x6d,
0x70, 0x6c, 0x65, 0x74, 0x65, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x12, 0x1e, 0x2e, 0x61,
0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x50, 0x61,
0x79, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x61,
0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x50, 0x61,
0x79, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1f, 0x82,
0xd3, 0xe4, 0x93, 0x02, 0x19, 0x3a, 0x01, 0x2a, 0x22, 0x14, 0x2f, 0x76, 0x31, 0x2f, 0x70, 0x61,
0x79, 0x6d, 0x65, 0x6e, 0x74, 0x2f, 0x63, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x12, 0x57,
0x0a, 0x08, 0x47, 0x65, 0x74, 0x52, 0x6f, 0x75, 0x6e, 0x64, 0x12, 0x17, 0x2e, 0x61, 0x72, 0x6b,
0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x52, 0x6f, 0x75, 0x6e, 0x64, 0x52, 0x65, 0x71, 0x75,
0x65, 0x73, 0x74, 0x1a, 0x18, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74,
0x52, 0x6f, 0x75, 0x6e, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x18, 0x82,
0xd3, 0xe4, 0x93, 0x02, 0x12, 0x12, 0x10, 0x2f, 0x76, 0x31, 0x2f, 0x72, 0x6f, 0x75, 0x6e, 0x64,
0x2f, 0x7b, 0x74, 0x78, 0x69, 0x64, 0x7d, 0x12, 0x64, 0x0a, 0x0c, 0x47, 0x65, 0x74, 0x52, 0x6f,
0x75, 0x6e, 0x64, 0x42, 0x79, 0x49, 0x64, 0x12, 0x1b, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31,
0x2e, 0x47, 0x65, 0x74, 0x52, 0x6f, 0x75, 0x6e, 0x64, 0x42, 0x79, 0x49, 0x64, 0x52, 0x65, 0x71,
0x75, 0x65, 0x73, 0x74, 0x1a, 0x1c, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65,
0x74, 0x52, 0x6f, 0x75, 0x6e, 0x64, 0x42, 0x79, 0x49, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e,
0x73, 0x65, 0x22, 0x19, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x13, 0x12, 0x11, 0x2f, 0x76, 0x31, 0x2f,
0x72, 0x6f, 0x75, 0x6e, 0x64, 0x2f, 0x69, 0x64, 0x2f, 0x7b, 0x69, 0x64, 0x7d, 0x12, 0x5d, 0x0a,
0x09, 0x4c, 0x69, 0x73, 0x74, 0x56, 0x74, 0x78, 0x6f, 0x73, 0x12, 0x18, 0x2e, 0x61, 0x72, 0x6b,
0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x56, 0x74, 0x78, 0x6f, 0x73, 0x52, 0x65, 0x71,
0x75, 0x65, 0x73, 0x74, 0x1a, 0x19, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69,
0x73, 0x74, 0x56, 0x74, 0x78, 0x6f, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22,
0x1b, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x15, 0x12, 0x13, 0x2f, 0x76, 0x31, 0x2f, 0x76, 0x74, 0x78,
0x6f, 0x73, 0x2f, 0x7b, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x7d, 0x12, 0x80, 0x01, 0x0a,
0x15, 0x47, 0x65, 0x74, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73,
0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x12, 0x24, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e,
0x47, 0x65, 0x74, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x53,
0x74, 0x72, 0x65, 0x61, 0x6d, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x25, 0x2e, 0x61,
0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63,
0x74, 0x69, 0x6f, 0x6e, 0x73, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x52, 0x65, 0x73, 0x70, 0x6f,
0x6e, 0x73, 0x65, 0x22, 0x18, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x12, 0x12, 0x10, 0x2f, 0x76, 0x31,
0x2f, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x30, 0x01, 0x42,
0x92, 0x01, 0x0a, 0x0a, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x42, 0x0c,
0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x3d,
0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x61, 0x72, 0x6b, 0x2d, 0x6e,
0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x2f, 0x61, 0x72, 0x6b, 0x2f, 0x61, 0x70, 0x69, 0x2d, 0x73,
0x70, 0x65, 0x63, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x67, 0x65, 0x6e,
0x2f, 0x61, 0x72, 0x6b, 0x2f, 0x76, 0x31, 0x3b, 0x61, 0x72, 0x6b, 0x76, 0x31, 0xa2, 0x02, 0x03,
0x41, 0x58, 0x58, 0xaa, 0x02, 0x06, 0x41, 0x72, 0x6b, 0x2e, 0x56, 0x31, 0xca, 0x02, 0x06, 0x41,
0x72, 0x6b, 0x5c, 0x56, 0x31, 0xe2, 0x02, 0x12, 0x41, 0x72, 0x6b, 0x5c, 0x56, 0x31, 0x5c, 0x47,
0x50, 0x42, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0xea, 0x02, 0x07, 0x41, 0x72, 0x6b,
0x3a, 0x3a, 0x56, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
0x12, 0x29, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74,
0x65, 0x72, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x73, 0x46, 0x6f, 0x72, 0x4e, 0x65, 0x78, 0x74, 0x52,
0x6f, 0x75, 0x6e, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2a, 0x2e, 0x61, 0x72,
0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x49, 0x6e, 0x70,
0x75, 0x74, 0x73, 0x46, 0x6f, 0x72, 0x4e, 0x65, 0x78, 0x74, 0x52, 0x6f, 0x75, 0x6e, 0x64, 0x52,
0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x23, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1d, 0x3a,
0x01, 0x2a, 0x22, 0x18, 0x2f, 0x76, 0x31, 0x2f, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x2f, 0x72, 0x65,
0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x73, 0x12, 0x9c, 0x01, 0x0a,
0x1b, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x4f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x73,
0x46, 0x6f, 0x72, 0x4e, 0x65, 0x78, 0x74, 0x52, 0x6f, 0x75, 0x6e, 0x64, 0x12, 0x2a, 0x2e, 0x61,
0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x4f, 0x75,
0x74, 0x70, 0x75, 0x74, 0x73, 0x46, 0x6f, 0x72, 0x4e, 0x65, 0x78, 0x74, 0x52, 0x6f, 0x75, 0x6e,
0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2b, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76,
0x31, 0x2e, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x4f, 0x75, 0x74, 0x70, 0x75, 0x74,
0x73, 0x46, 0x6f, 0x72, 0x4e, 0x65, 0x78, 0x74, 0x52, 0x6f, 0x75, 0x6e, 0x64, 0x52, 0x65, 0x73,
0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x24, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1e, 0x3a, 0x01, 0x2a,
0x22, 0x19, 0x2f, 0x76, 0x31, 0x2f, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x2f, 0x72, 0x65, 0x67, 0x69,
0x73, 0x74, 0x65, 0x72, 0x4f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x73, 0x12, 0x7d, 0x0a, 0x10, 0x53,
0x75, 0x62, 0x6d, 0x69, 0x74, 0x54, 0x72, 0x65, 0x65, 0x4e, 0x6f, 0x6e, 0x63, 0x65, 0x73, 0x12,
0x1f, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x54,
0x72, 0x65, 0x65, 0x4e, 0x6f, 0x6e, 0x63, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74,
0x1a, 0x20, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x75, 0x62, 0x6d, 0x69, 0x74,
0x54, 0x72, 0x65, 0x65, 0x4e, 0x6f, 0x6e, 0x63, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e,
0x73, 0x65, 0x22, 0x26, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x20, 0x3a, 0x01, 0x2a, 0x22, 0x1b, 0x2f,
0x76, 0x31, 0x2f, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x2f, 0x74, 0x72, 0x65, 0x65, 0x2f, 0x73, 0x75,
0x62, 0x6d, 0x69, 0x74, 0x4e, 0x6f, 0x6e, 0x63, 0x65, 0x73, 0x12, 0x8d, 0x01, 0x0a, 0x14, 0x53,
0x75, 0x62, 0x6d, 0x69, 0x74, 0x54, 0x72, 0x65, 0x65, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75,
0x72, 0x65, 0x73, 0x12, 0x23, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x75, 0x62,
0x6d, 0x69, 0x74, 0x54, 0x72, 0x65, 0x65, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65,
0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x24, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76,
0x31, 0x2e, 0x53, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x54, 0x72, 0x65, 0x65, 0x53, 0x69, 0x67, 0x6e,
0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x2a,
0x82, 0xd3, 0xe4, 0x93, 0x02, 0x24, 0x3a, 0x01, 0x2a, 0x22, 0x1f, 0x2f, 0x76, 0x31, 0x2f, 0x72,
0x6f, 0x75, 0x6e, 0x64, 0x2f, 0x74, 0x72, 0x65, 0x65, 0x2f, 0x73, 0x75, 0x62, 0x6d, 0x69, 0x74,
0x53, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x12, 0x8e, 0x01, 0x0a, 0x16, 0x53,
0x75, 0x62, 0x6d, 0x69, 0x74, 0x53, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x46, 0x6f, 0x72, 0x66, 0x65,
0x69, 0x74, 0x54, 0x78, 0x73, 0x12, 0x25, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x53,
0x75, 0x62, 0x6d, 0x69, 0x74, 0x53, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x46, 0x6f, 0x72, 0x66, 0x65,
0x69, 0x74, 0x54, 0x78, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x26, 0x2e, 0x61,
0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x53, 0x69, 0x67, 0x6e,
0x65, 0x64, 0x46, 0x6f, 0x72, 0x66, 0x65, 0x69, 0x74, 0x54, 0x78, 0x73, 0x52, 0x65, 0x73, 0x70,
0x6f, 0x6e, 0x73, 0x65, 0x22, 0x25, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1f, 0x3a, 0x01, 0x2a, 0x22,
0x1a, 0x2f, 0x76, 0x31, 0x2f, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x2f, 0x73, 0x75, 0x62, 0x6d, 0x69,
0x74, 0x46, 0x6f, 0x72, 0x66, 0x65, 0x69, 0x74, 0x54, 0x78, 0x73, 0x12, 0x6b, 0x0a, 0x0e, 0x47,
0x65, 0x74, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x12, 0x1d, 0x2e,
0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x53,
0x74, 0x72, 0x65, 0x61, 0x6d, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x61,
0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x53, 0x74,
0x72, 0x65, 0x61, 0x6d, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x18, 0x82, 0xd3,
0xe4, 0x93, 0x02, 0x12, 0x12, 0x10, 0x2f, 0x76, 0x31, 0x2f, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x2f,
0x65, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x30, 0x01, 0x12, 0x56, 0x0a, 0x04, 0x50, 0x69, 0x6e, 0x67,
0x12, 0x13, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x69, 0x6e, 0x67, 0x52, 0x65,
0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x14, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x50,
0x69, 0x6e, 0x67, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x23, 0x82, 0xd3, 0xe4,
0x93, 0x02, 0x1d, 0x12, 0x1b, 0x2f, 0x76, 0x31, 0x2f, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x2f, 0x70,
0x69, 0x6e, 0x67, 0x2f, 0x7b, 0x70, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x5f, 0x69, 0x64, 0x7d,
0x12, 0x64, 0x0a, 0x0d, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e,
0x74, 0x12, 0x1c, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74,
0x65, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a,
0x1d, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x50,
0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x16,
0x82, 0xd3, 0xe4, 0x93, 0x02, 0x10, 0x3a, 0x01, 0x2a, 0x22, 0x0b, 0x2f, 0x76, 0x31, 0x2f, 0x70,
0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x12, 0x73, 0x0a, 0x0f, 0x43, 0x6f, 0x6d, 0x70, 0x6c, 0x65,
0x74, 0x65, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x12, 0x1e, 0x2e, 0x61, 0x72, 0x6b, 0x2e,
0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x50, 0x61, 0x79, 0x6d, 0x65,
0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x61, 0x72, 0x6b, 0x2e,
0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x50, 0x61, 0x79, 0x6d, 0x65,
0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1f, 0x82, 0xd3, 0xe4, 0x93,
0x02, 0x19, 0x3a, 0x01, 0x2a, 0x22, 0x14, 0x2f, 0x76, 0x31, 0x2f, 0x70, 0x61, 0x79, 0x6d, 0x65,
0x6e, 0x74, 0x2f, 0x63, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x12, 0x57, 0x0a, 0x08, 0x47,
0x65, 0x74, 0x52, 0x6f, 0x75, 0x6e, 0x64, 0x12, 0x17, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31,
0x2e, 0x47, 0x65, 0x74, 0x52, 0x6f, 0x75, 0x6e, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74,
0x1a, 0x18, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x52, 0x6f, 0x75,
0x6e, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x18, 0x82, 0xd3, 0xe4, 0x93,
0x02, 0x12, 0x12, 0x10, 0x2f, 0x76, 0x31, 0x2f, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x2f, 0x7b, 0x74,
0x78, 0x69, 0x64, 0x7d, 0x12, 0x64, 0x0a, 0x0c, 0x47, 0x65, 0x74, 0x52, 0x6f, 0x75, 0x6e, 0x64,
0x42, 0x79, 0x49, 0x64, 0x12, 0x1b, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65,
0x74, 0x52, 0x6f, 0x75, 0x6e, 0x64, 0x42, 0x79, 0x49, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73,
0x74, 0x1a, 0x1c, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x52, 0x6f,
0x75, 0x6e, 0x64, 0x42, 0x79, 0x49, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22,
0x19, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x13, 0x12, 0x11, 0x2f, 0x76, 0x31, 0x2f, 0x72, 0x6f, 0x75,
0x6e, 0x64, 0x2f, 0x69, 0x64, 0x2f, 0x7b, 0x69, 0x64, 0x7d, 0x12, 0x5d, 0x0a, 0x09, 0x4c, 0x69,
0x73, 0x74, 0x56, 0x74, 0x78, 0x6f, 0x73, 0x12, 0x18, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31,
0x2e, 0x4c, 0x69, 0x73, 0x74, 0x56, 0x74, 0x78, 0x6f, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73,
0x74, 0x1a, 0x19, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x56,
0x74, 0x78, 0x6f, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1b, 0x82, 0xd3,
0xe4, 0x93, 0x02, 0x15, 0x12, 0x13, 0x2f, 0x76, 0x31, 0x2f, 0x76, 0x74, 0x78, 0x6f, 0x73, 0x2f,
0x7b, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x7d, 0x12, 0x80, 0x01, 0x0a, 0x15, 0x47, 0x65,
0x74, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x53, 0x74, 0x72,
0x65, 0x61, 0x6d, 0x12, 0x24, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74,
0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x53, 0x74, 0x72, 0x65,
0x61, 0x6d, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x25, 0x2e, 0x61, 0x72, 0x6b, 0x2e,
0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f,
0x6e, 0x73, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65,
0x22, 0x18, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x12, 0x12, 0x10, 0x2f, 0x76, 0x31, 0x2f, 0x74, 0x72,
0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x30, 0x01, 0x42, 0x92, 0x01, 0x0a,
0x0a, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x42, 0x0c, 0x53, 0x65, 0x72,
0x76, 0x69, 0x63, 0x65, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x3d, 0x67, 0x69, 0x74,
0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x61, 0x72, 0x6b, 0x2d, 0x6e, 0x65, 0x74, 0x77,
0x6f, 0x72, 0x6b, 0x2f, 0x61, 0x72, 0x6b, 0x2f, 0x61, 0x70, 0x69, 0x2d, 0x73, 0x70, 0x65, 0x63,
0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x67, 0x65, 0x6e, 0x2f, 0x61, 0x72,
0x6b, 0x2f, 0x76, 0x31, 0x3b, 0x61, 0x72, 0x6b, 0x76, 0x31, 0xa2, 0x02, 0x03, 0x41, 0x58, 0x58,
0xaa, 0x02, 0x06, 0x41, 0x72, 0x6b, 0x2e, 0x56, 0x31, 0xca, 0x02, 0x06, 0x41, 0x72, 0x6b, 0x5c,
0x56, 0x31, 0xe2, 0x02, 0x12, 0x41, 0x72, 0x6b, 0x5c, 0x56, 0x31, 0x5c, 0x47, 0x50, 0x42, 0x4d,
0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0xea, 0x02, 0x07, 0x41, 0x72, 0x6b, 0x3a, 0x3a, 0x56,
0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
}
var (

View File

@@ -39,7 +39,7 @@ func main() {
&configCommand,
&dumpCommand,
&receiveCommand,
&claimCmd,
&settleCmd,
&sendCommand,
&balanceCommand,
&redeemCommand,
@@ -160,11 +160,11 @@ var (
return receive(ctx)
},
}
claimCmd = cli.Command{
Name: "claim",
Usage: "Claim onboarding funds or pending payments",
settleCmd = cli.Command{
Name: "settle",
Usage: "Settle onboarding funds or oor payments",
Action: func(ctx *cli.Context) error {
return claim(ctx)
return settle(ctx)
},
Flags: []cli.Flag{passwordFlag},
}
@@ -266,7 +266,7 @@ func receive(ctx *cli.Context) error {
})
}
func claim(ctx *cli.Context) error {
func settle(ctx *cli.Context) error {
password, err := readPassword(ctx)
if err != nil {
return err
@@ -275,7 +275,7 @@ func claim(ctx *cli.Context) error {
return err
}
txID, err := arkSdkClient.Claim(ctx.Context)
txID, err := arkSdkClient.Settle(ctx.Context)
if err != nil {
return err
}

View File

@@ -15,20 +15,17 @@ import (
type VtxoScript common.VtxoScript[bitcoinTapTree]
func ParseVtxoScript(desc string) (VtxoScript, error) {
v := &DefaultVtxoScript{}
// TODO add other type
err := v.FromDescriptor(desc)
if err != nil {
v := &ReversibleVtxoScript{}
err = v.FromDescriptor(desc)
if err != nil {
return nil, fmt.Errorf("invalid vtxo descriptor: %s", desc)
}
return v, nil
types := []VtxoScript{
&DefaultVtxoScript{},
}
return v, nil
for _, v := range types {
if err := v.FromDescriptor(desc); err == nil {
return v, nil
}
}
return nil, fmt.Errorf("invalid vtxo descriptor: %s", desc)
}
/*
@@ -101,94 +98,6 @@ func (v *DefaultVtxoScript) TapTree() (*secp256k1.PublicKey, bitcoinTapTree, err
return taprootKey, bitcoinTapTree{tapTree}, nil
}
/*
* ReversibleVtxoScript allows sender of the VTXO to revert the transaction
* unilateral exit is in favor of the sender
* - Owner and ASP (forfeit owner)
* - Sender and ASP (forfeit sender)
* - Sender after t (unilateral exit)
*/
type ReversibleVtxoScript struct {
Asp *secp256k1.PublicKey
Sender *secp256k1.PublicKey
Owner *secp256k1.PublicKey
ExitDelay uint
}
func (v *ReversibleVtxoScript) ToDescriptor() string {
owner := hex.EncodeToString(schnorr.SerializePubKey(v.Owner))
sender := hex.EncodeToString(schnorr.SerializePubKey(v.Sender))
asp := hex.EncodeToString(schnorr.SerializePubKey(v.Asp))
return fmt.Sprintf(
descriptor.ReversibleVtxoScriptTemplate,
hex.EncodeToString(UnspendableKey().SerializeCompressed()),
sender,
asp,
v.ExitDelay,
sender,
owner,
asp,
)
}
func (v *ReversibleVtxoScript) FromDescriptor(desc string) error {
owner, sender, asp, exitDelay, err := descriptor.ParseReversibleVtxoDescriptor(desc)
if err != nil {
return err
}
v.Owner = owner
v.Sender = sender
v.Asp = asp
v.ExitDelay = exitDelay
return nil
}
func (v *ReversibleVtxoScript) TapTree() (*secp256k1.PublicKey, bitcoinTapTree, error) {
redeemClosure := &CSVSigClosure{
Pubkey: v.Sender,
Seconds: v.ExitDelay,
}
redeemLeaf, err := redeemClosure.Leaf()
if err != nil {
return nil, bitcoinTapTree{}, err
}
forfeitClosure := &MultisigClosure{
Pubkey: v.Owner,
AspPubkey: v.Asp,
}
forfeitLeaf, err := forfeitClosure.Leaf()
if err != nil {
return nil, bitcoinTapTree{}, err
}
reverseForfeitClosure := &MultisigClosure{
Pubkey: v.Sender,
AspPubkey: v.Asp,
}
reverseForfeitLeaf, err := reverseForfeitClosure.Leaf()
if err != nil {
return nil, bitcoinTapTree{}, err
}
tapTree := txscript.AssembleTaprootScriptTree(
*redeemLeaf, *forfeitLeaf, *reverseForfeitLeaf,
)
root := tapTree.RootNode.TapHash()
taprootKey := txscript.ComputeTaprootOutputKey(
UnspendableKey(),
root[:],
)
return taprootKey, bitcoinTapTree{tapTree}, nil
}
// bitcoinTapTree is a wrapper around txscript.IndexedTapScriptTree to implement the common.TaprootTree interface
type bitcoinTapTree struct {
*txscript.IndexedTapScriptTree

View File

@@ -19,12 +19,8 @@ func TestParseDescriptor(t *testing.T) {
aliceKey, err := secp256k1.GeneratePrivateKey()
require.NoError(t, err)
bobKey, err := secp256k1.GeneratePrivateKey()
require.NoError(t, err)
aspPubKey := hex.EncodeToString(schnorr.SerializePubKey(aspKey.PubKey()))
alicePubKey := hex.EncodeToString(schnorr.SerializePubKey(aliceKey.PubKey()))
bobPubKey := hex.EncodeToString(schnorr.SerializePubKey(bobKey.PubKey()))
unspendableKey := hex.EncodeToString(bitcointree.UnspendableKey().SerializeCompressed())
@@ -44,24 +40,4 @@ func TestParseDescriptor(t *testing.T) {
require.Equal(t, defaultScriptDescriptor, vtxo.ToDescriptor())
require.Equal(t, alicePubKey, hex.EncodeToString(schnorr.SerializePubKey(vtxo.(*bitcointree.DefaultVtxoScript).Owner)))
require.Equal(t, aspPubKey, hex.EncodeToString(schnorr.SerializePubKey(vtxo.(*bitcointree.DefaultVtxoScript).Asp)))
reversibleScriptDescriptor := fmt.Sprintf(
descriptor.ReversibleVtxoScriptTemplate,
unspendableKey,
alicePubKey,
aspPubKey,
512,
alicePubKey,
bobPubKey,
aspPubKey,
)
vtxo, err = bitcointree.ParseVtxoScript(reversibleScriptDescriptor)
require.NoError(t, err)
require.IsType(t, &bitcointree.ReversibleVtxoScript{}, vtxo)
require.Equal(t, reversibleScriptDescriptor, vtxo.ToDescriptor())
require.Equal(t, alicePubKey, hex.EncodeToString(schnorr.SerializePubKey(vtxo.(*bitcointree.ReversibleVtxoScript).Sender)))
require.Equal(t, bobPubKey, hex.EncodeToString(schnorr.SerializePubKey(vtxo.(*bitcointree.ReversibleVtxoScript).Owner)))
require.Equal(t, aspPubKey, hex.EncodeToString(schnorr.SerializePubKey(vtxo.(*bitcointree.ReversibleVtxoScript).Asp)))
}

View File

@@ -11,91 +11,6 @@ import (
// tr(unspendable, { and(pk(user), pk(asp)), and(older(timeout), pk(user)) })
const DefaultVtxoDescriptorTemplate = "tr(%s,{ and(pk(%s), pk(%s)), and(older(%d), pk(%s)) })"
// tr(unspendable, { { and(pk(sender), pk(asp)), and(older(timeout), pk(sender)) }, and(pk(receiver), pk(asp)) })
const ReversibleVtxoScriptTemplate = "tr(%s,{ { and(pk(%s), pk(%s)), and(older(%d), pk(%s)) }, and(pk(%s), pk(%s)) })"
func ParseReversibleVtxoDescriptor(
descriptor string,
) (user, sender, asp *secp256k1.PublicKey, timeout uint, err error) {
desc, err := ParseTaprootDescriptor(descriptor)
if err != nil {
return nil, nil, nil, 0, err
}
if len(desc.ScriptTree) != 3 {
return nil, nil, nil, 0, errors.New("not a reversible vtxo script descriptor")
}
for _, leaf := range desc.ScriptTree {
if andLeaf, ok := leaf.(*And); ok {
if first, ok := andLeaf.First.(*PK); ok {
if second, ok := andLeaf.Second.(*PK); ok {
keyBytes, err := hex.DecodeString(first.Key.Hex)
if err != nil {
return nil, nil, nil, 0, err
}
if sender == nil {
sender, err = schnorr.ParsePubKey(keyBytes)
if err != nil {
return nil, nil, nil, 0, err
}
} else {
user, err = schnorr.ParsePubKey(keyBytes)
if err != nil {
return nil, nil, nil, 0, err
}
}
if asp == nil {
keyBytes, err = hex.DecodeString(second.Key.Hex)
if err != nil {
return nil, nil, nil, 0, err
}
asp, err = schnorr.ParsePubKey(keyBytes)
if err != nil {
return nil, nil, nil, 0, err
}
}
}
}
if first, ok := andLeaf.First.(*Older); ok {
if second, ok := andLeaf.Second.(*PK); ok {
timeout = first.Timeout
keyBytes, err := hex.DecodeString(second.Key.Hex)
if err != nil {
return nil, nil, nil, 0, err
}
sender, err = schnorr.ParsePubKey(keyBytes)
if err != nil {
return nil, nil, nil, 0, err
}
}
}
}
}
if user == nil {
return nil, nil, nil, 0, errors.New("descriptor is invalid")
}
if asp == nil {
return nil, nil, nil, 0, errors.New("descriptor is invalid")
}
if timeout == 0 {
return nil, nil, nil, 0, errors.New("descriptor is invalid")
}
if sender == nil {
return nil, nil, nil, 0, errors.New("descriptor is invalid")
}
return
}
func ParseDefaultVtxoDescriptor(
descriptor string,
) (user, asp *secp256k1.PublicKey, timeout uint, err error) {

View File

@@ -25,7 +25,7 @@ type ArkClient interface {
ctx context.Context, addr string, amount uint64, withExpiryCoinselect bool,
) (string, error)
SendAsync(ctx context.Context, withExpiryCoinselect bool, receivers []Receiver) (string, error)
Claim(ctx context.Context) (string, error)
Settle(ctx context.Context) (string, error)
ListVtxos(ctx context.Context) (spendable, spent []client.Vtxo, err error)
Dump(ctx context.Context) (seed string, err error)
GetTransactionHistory(ctx context.Context) ([]types.Transaction, error)

View File

@@ -270,6 +270,27 @@ func (a *arkClient) ping(
return ticker.Stop
}
func (a *arkClient) ListVtxos(
ctx context.Context,
) (spendableVtxos, spentVtxos []client.Vtxo, err error) {
offchainAddrs, _, _, err := a.wallet.GetAddresses(ctx)
if err != nil {
return
}
for _, addr := range offchainAddrs {
spendable, spent, err := a.client.ListVtxos(ctx, addr.Address)
if err != nil {
return nil, nil, err
}
spendableVtxos = append(spendableVtxos, spendable...)
spentVtxos = append(spentVtxos, spent...)
}
return
}
func getClient(
supportedClients utils.SupportedType[utils.ClientFactory], clientType, aspUrl string,
) (client.ASPClient, error) {
@@ -329,3 +350,15 @@ func getWalletStore(storeType, datadir string) (walletstore.WalletStore, error)
func getCreatedAtFromExpiry(roundLifetime int64, expiry time.Time) time.Time {
return expiry.Add(-time.Duration(roundLifetime) * time.Second)
}
func filterByOutpoints(vtxos []client.Vtxo, outpoints []client.Outpoint) []client.Vtxo {
filtered := make([]client.Vtxo, 0, len(vtxos))
for _, vtxo := range vtxos {
for _, outpoint := range outpoints {
if vtxo.Outpoint.Equals(outpoint) {
filtered = append(filtered, vtxo)
}
}
}
return filtered
}

View File

@@ -78,6 +78,10 @@ type Outpoint struct {
VOut uint32
}
func (o Outpoint) Equals(other Outpoint) bool {
return o.Txid == other.Txid && o.VOut == other.VOut
}
type Input struct {
Outpoint
Descriptor string
@@ -95,7 +99,7 @@ type Vtxo struct {
RoundTxid string
ExpiresAt *time.Time
RedeemTx string
Pending bool
IsOOR bool
SpentBy string
}

View File

@@ -359,7 +359,7 @@ func (c *grpcClient) GetTransactionsStream(
Round: &client.RoundTransaction{
Txid: tx.Round.Txid,
SpentVtxos: outpointsFromProto(tx.Round.SpentVtxos),
SpendableVtxos: vtxosFromProto(tx.Round.SpendableVtxos),
SpendableVtxos: vtxos(tx.Round.SpendableVtxos).toVtxos(),
ClaimedBoardingUtxos: outpointsFromProto(tx.Round.ClaimedBoardingUtxos),
},
}
@@ -368,7 +368,7 @@ func (c *grpcClient) GetTransactionsStream(
Redeem: &client.RedeemTransaction{
Txid: tx.Redeem.Txid,
SpentVtxos: outpointsFromProto(tx.Redeem.SpentVtxos),
SpendableVtxos: vtxosFromProto(tx.Redeem.SpendableVtxos),
SpendableVtxos: vtxos(tx.Redeem.SpendableVtxos).toVtxos(),
},
}
}
@@ -394,24 +394,3 @@ func outpointsFromProto(protoOutpoints []*arkv1.Outpoint) []client.Outpoint {
}
return outpoints
}
func vtxosFromProto(protoVtxos []*arkv1.Vtxo) []client.Vtxo {
vtxos := make([]client.Vtxo, len(protoVtxos))
for i, v := range protoVtxos {
expiresAt := time.Unix(v.ExpireAt, 0)
vtxos[i] = client.Vtxo{
Outpoint: client.Outpoint{
Txid: v.Outpoint.Txid,
VOut: v.Outpoint.Vout,
},
Pubkey: v.Pubkey,
Amount: v.Amount,
RoundTxid: v.RoundTxid,
ExpiresAt: &expiresAt,
RedeemTx: v.RedeemTx,
Pending: v.Pending,
SpentBy: v.SpentBy,
}
}
return vtxos
}

View File

@@ -125,7 +125,7 @@ func (v vtxo) toVtxo() client.Vtxo {
Amount: v.GetAmount(),
RoundTxid: v.GetRoundTxid(),
ExpiresAt: expiresAt,
Pending: v.GetPending(),
IsOOR: v.GetIsOor(),
RedeemTx: v.GetRedeemTx(),
SpentBy: v.GetSpentBy(),
Pubkey: v.GetPubkey(),

View File

@@ -499,7 +499,7 @@ func (a *restClient) ListVtxos(
Amount: uint64(amount),
RoundTxid: v.RoundTxid,
ExpiresAt: expiresAt,
Pending: v.Pending,
IsOOR: v.IsOor,
RedeemTx: v.RedeemTx,
SpentBy: v.SpentBy,
Pubkey: v.Pubkey,
@@ -701,7 +701,7 @@ func vtxosFromRest(restVtxos []*models.V1Vtxo) []client.Vtxo {
RoundTxid: v.RoundTxid,
ExpiresAt: expiresAt,
RedeemTx: v.RedeemTx,
Pending: v.Pending,
IsOOR: v.IsOor,
SpentBy: v.SpentBy,
}
}

View File

@@ -24,12 +24,12 @@ type V1Vtxo struct {
// expire at
ExpireAt string `json:"expireAt,omitempty"`
// is oor
IsOor bool `json:"isOor,omitempty"`
// outpoint
Outpoint *V1Outpoint `json:"outpoint,omitempty"`
// pending
Pending bool `json:"pending,omitempty"`
// pubkey
Pubkey string `json:"pubkey,omitempty"`

View File

@@ -33,7 +33,6 @@ func TestVtxosToTxs(t *testing.T) {
},
Amount: 1000,
Type: sdktypes.TxSent,
IsPending: false,
CreatedAt: time.Unix(1726054898, 0),
},
},
@@ -48,7 +47,6 @@ func TestVtxosToTxs(t *testing.T) {
},
Amount: 1000,
Type: sdktypes.TxReceived,
IsPending: true,
CreatedAt: time.Unix(1726054898, 0),
},
{
@@ -57,7 +55,6 @@ func TestVtxosToTxs(t *testing.T) {
},
Amount: 2000,
Type: sdktypes.TxReceived,
IsPending: true,
CreatedAt: time.Unix(1726486359, 0),
},
},
@@ -72,7 +69,6 @@ func TestVtxosToTxs(t *testing.T) {
},
Amount: 1000,
Type: sdktypes.TxReceived,
IsPending: false,
CreatedAt: time.Unix(1726054898, 0),
},
{
@@ -81,7 +77,6 @@ func TestVtxosToTxs(t *testing.T) {
},
Amount: 2000,
Type: sdktypes.TxReceived,
IsPending: false,
CreatedAt: time.Unix(1726486359, 0),
},
},
@@ -96,7 +91,6 @@ func TestVtxosToTxs(t *testing.T) {
},
Amount: 1000,
Type: sdktypes.TxReceived,
IsPending: false,
CreatedAt: time.Unix(1726054898, 0),
},
{
@@ -105,7 +99,6 @@ func TestVtxosToTxs(t *testing.T) {
},
Amount: 2000,
Type: sdktypes.TxReceived,
IsPending: false,
CreatedAt: time.Unix(1726486359, 0),
},
{
@@ -114,7 +107,6 @@ func TestVtxosToTxs(t *testing.T) {
},
Amount: 2100,
Type: sdktypes.TxSent,
IsPending: false,
CreatedAt: time.Unix(1726503865, 0),
},
},
@@ -138,7 +130,6 @@ func TestVtxosToTxs(t *testing.T) {
require.Equal(t, wantTx.RedeemTxid, gotTx.RedeemTxid)
require.Equal(t, int(wantTx.Amount), int(gotTx.Amount))
require.Equal(t, wantTx.Type, gotTx.Type)
require.Equal(t, wantTx.IsPending, gotTx.IsPending)
}
})
}
@@ -161,16 +152,12 @@ func loadFixtures(jsonStr string) (vtxos, map[string]struct{}, error) {
Address string `json:"address"`
Amount string `json:"amount"`
} `json:"receiver"`
Spent bool `json:"spent"`
PoolTxid string `json:"poolTxid"`
SpentBy string `json:"spentBy"`
ExpireAt string `json:"expireAt"`
Swept bool `json:"swept"`
Pending bool `json:"pending"`
PendingData struct {
RedeemTx string `json:"redeemTx"`
UnconditionalForfeitTxs []string `json:"unconditionalForfeitTxs"`
} `json:"pendingData"`
Spent bool `json:"spent"`
PoolTxid string `json:"poolTxid"`
SpentBy string `json:"spentBy"`
ExpireAt string `json:"expireAt"`
Swept bool `json:"swept"`
RedeemTx string `json:"redeemTx"`
} `json:"spendableVtxos"`
SpentVtxos []struct {
Outpoint struct {
@@ -181,16 +168,12 @@ func loadFixtures(jsonStr string) (vtxos, map[string]struct{}, error) {
Address string `json:"address"`
Amount string `json:"amount"`
} `json:"receiver"`
Spent bool `json:"spent"`
PoolTxid string `json:"poolTxid"`
SpentBy string `json:"spentBy"`
ExpireAt string `json:"expireAt"`
Swept bool `json:"swept"`
Pending bool `json:"pending"`
PendingData struct {
RedeemTx string `json:"redeemTx"`
UnconditionalForfeitTxs []string `json:"unconditionalForfeitTxs"`
} `json:"pendingData"`
Spent bool `json:"spent"`
PoolTxid string `json:"poolTxid"`
SpentBy string `json:"spentBy"`
ExpireAt string `json:"expireAt"`
Swept bool `json:"swept"`
RedeemTx string `json:"redeemTx"`
} `json:"spentVtxos"`
}
@@ -216,8 +199,7 @@ func loadFixtures(jsonStr string) (vtxos, map[string]struct{}, error) {
Amount: amount,
RoundTxid: vtxo.PoolTxid,
ExpiresAt: &expireAt,
RedeemTx: vtxo.PendingData.RedeemTx,
Pending: vtxo.Pending,
RedeemTx: vtxo.RedeemTx,
SpentBy: vtxo.SpentBy,
}
}
@@ -240,8 +222,7 @@ func loadFixtures(jsonStr string) (vtxos, map[string]struct{}, error) {
Amount: amount,
RoundTxid: vtxo.PoolTxid,
ExpiresAt: &expireAt,
RedeemTx: vtxo.PendingData.RedeemTx,
Pending: vtxo.Pending,
RedeemTx: vtxo.RedeemTx,
SpentBy: vtxo.SpentBy,
}
}
@@ -301,9 +282,7 @@ var (
"poolTxid": "377fa2fbd27c82bdbc095478384c88b6c75432c0ef464189e49c965194446cdf",
"spentBy": "",
"expireAt": "1726054928",
"swept": false,
"pending": false,
"pendingData": null
"swept": false
}
],
"spentVtxos": []
@@ -329,13 +308,7 @@ var (
"spentBy": "",
"expireAt": "1726054928",
"swept": false,
"pending": false,
"pendingData": {
"redeemTx": "cHNidP8BAIkCAAAAAWwxXUjG5tidFA0LmUljX//jwW6xWaS6HKyRCw5StsxpAAAAAAD/////AugDAAAAAAAAIlEgt2eR8LtqTP7yUcQtSydeGrRiHnVmHHnZwYjdC23G7MZwSQAAAAAAACJRIKfUzf/o9h+r0v9y4nmyOt9qO8EkDumQPQZGTbEv8fSFAAAAAAABASsgTgAAAAAAACJRIKfUzf/o9h+r0v9y4nmyOt9qO8EkDumQPQZGTbEv8fSFIgYDp9sz9Cto7FhF+C929Y/jJw1zkRWxq6NURd9kX05mShcYAAAAAFYAAIAAAACAAQAAgAAAAAAAAAAAQRSvBIBey3T0IV353FkuGLMmMJFpqHTrliIsJwJsfJkzq7J0B8bQ0j9842h5lUfOWcbj2TeoFx6OCpgoHIqWIBhHQAFqkBLiRmP3AZ8MS77s1QIWZswMV3L72D9gN0f0MbD6XHkmzZeC1clF3uzxr+13wsF0vcFe29Zl3e2gAhMNGYVCFcFQkpt0waBJVLeLS2A16XpeB4paDyjsltVHv+6azoA6wKRtST8P7teUpSF4DAEbfJj5OIXITx5QGbZns/AtxqGyRSCn2zP0K2jsWEX4L3b1j+MnDXORFbGro1RF32RfTmZKF60grwSAXst09CFd+dxZLhizJjCRaah065YiLCcCbHyZM6uswCEWp9sz9Cto7FhF+C929Y/jJw1zkRWxq6NURd9kX05mShc5AbJ0B8bQ0j9842h5lUfOWcbj2TeoFx6OCpgoHIqWIBhHAAAAAFYAAIAAAACAAQAAgAAAAAAAAAAAARcgUJKbdMGgSVS3i0tgNel6XgeKWg8o7JbVR7/ums6AOsAAAAA=",
"unconditionalForfeitTxs": [
"cHNidP8BAFICAAAAAWwxXUjG5tidFA0LmUljX//jwW6xWaS6HKyRCw5StsxpAAAAAAD/////AVhNAAAAAAAAFgAUSU38/3Mzx5BdILG4oUO+JoHcoT8AAAAAAAEBKyBOAAAAAAAAIlEgp9TN/+j2H6vS/3LiebI632o7wSQO6ZA9BkZNsS/x9IVBFK8EgF7LdPQhXfncWS4YsyYwkWmodOuWIiwnAmx8mTOrsnQHxtDSP3zjaHmVR85ZxuPZN6gXHo4KmCgcipYgGEdAjH8Mg1Z3GdjGzp78Mg2xq1fop9KDfeji+xoyMgYS7q0Nl0AGOAaNzkDRW4cNcefll5jZC2i3nfygKdXsUsR+LEIVwVCSm3TBoElUt4tLYDXpel4HiloPKOyW1Ue/7prOgDrApG1JPw/u15SlIXgMARt8mPk4hchPHlAZtmez8C3GobJFIKfbM/QraOxYRfgvdvWP4ycNc5EVsaujVEXfZF9OZkoXrSCvBIBey3T0IV353FkuGLMmMJFpqHTrliIsJwJsfJkzq6zAARcgUJKbdMGgSVS3i0tgNel6XgeKWg8o7JbVR7/ums6AOsAAAA=="
]
}
"redeemTx": "cHNidP8BAIkCAAAAAWwxXUjG5tidFA0LmUljX//jwW6xWaS6HKyRCw5StsxpAAAAAAD/////AugDAAAAAAAAIlEgt2eR8LtqTP7yUcQtSydeGrRiHnVmHHnZwYjdC23G7MZwSQAAAAAAACJRIKfUzf/o9h+r0v9y4nmyOt9qO8EkDumQPQZGTbEv8fSFAAAAAAABASsgTgAAAAAAACJRIKfUzf/o9h+r0v9y4nmyOt9qO8EkDumQPQZGTbEv8fSFIgYDp9sz9Cto7FhF+C929Y/jJw1zkRWxq6NURd9kX05mShcYAAAAAFYAAIAAAACAAQAAgAAAAAAAAAAAQRSvBIBey3T0IV353FkuGLMmMJFpqHTrliIsJwJsfJkzq7J0B8bQ0j9842h5lUfOWcbj2TeoFx6OCpgoHIqWIBhHQAFqkBLiRmP3AZ8MS77s1QIWZswMV3L72D9gN0f0MbD6XHkmzZeC1clF3uzxr+13wsF0vcFe29Zl3e2gAhMNGYVCFcFQkpt0waBJVLeLS2A16XpeB4paDyjsltVHv+6azoA6wKRtST8P7teUpSF4DAEbfJj5OIXITx5QGbZns/AtxqGyRSCn2zP0K2jsWEX4L3b1j+MnDXORFbGro1RF32RfTmZKF60grwSAXst09CFd+dxZLhizJjCRaah065YiLCcCbHyZM6uswCEWp9sz9Cto7FhF+C929Y/jJw1zkRWxq6NURd9kX05mShc5AbJ0B8bQ0j9842h5lUfOWcbj2TeoFx6OCpgoHIqWIBhHAAAAAFYAAIAAAACAAQAAgAAAAAAAAAAAARcgUJKbdMGgSVS3i0tgNel6XgeKWg8o7JbVR7/ums6AOsAAAAA="
}
],
"spentVtxos": [
@@ -353,8 +326,7 @@ var (
"spentBy": "94fa598302f17f00c8881e742ec0ce2f8c8d16f3d54fe6ba0fb7d13a493d84ad",
"expireAt": "1726054928",
"swept": false,
"pending": false,
"pendingData": null
"redeemTx": ""
}
]
}`
@@ -376,13 +348,7 @@ var (
"spentBy": "",
"expireAt": "1726054928",
"swept": false,
"pending": true,
"pendingData": {
"redeemTx": "cHNidP8BAIkCAAAAAWwxXUjG5tidFA0LmUljX//jwW6xWaS6HKyRCw5StsxpAAAAAAD/////AugDAAAAAAAAIlEgt2eR8LtqTP7yUcQtSydeGrRiHnVmHHnZwYjdC23G7MZwSQAAAAAAACJRIKfUzf/o9h+r0v9y4nmyOt9qO8EkDumQPQZGTbEv8fSFAAAAAAABASsgTgAAAAAAACJRIKfUzf/o9h+r0v9y4nmyOt9qO8EkDumQPQZGTbEv8fSFIgYDp9sz9Cto7FhF+C929Y/jJw1zkRWxq6NURd9kX05mShcYAAAAAFYAAIAAAACAAQAAgAAAAAAAAAAAQRSvBIBey3T0IV353FkuGLMmMJFpqHTrliIsJwJsfJkzq7J0B8bQ0j9842h5lUfOWcbj2TeoFx6OCpgoHIqWIBhHQAFqkBLiRmP3AZ8MS77s1QIWZswMV3L72D9gN0f0MbD6XHkmzZeC1clF3uzxr+13wsF0vcFe29Zl3e2gAhMNGYVCFcFQkpt0waBJVLeLS2A16XpeB4paDyjsltVHv+6azoA6wKRtST8P7teUpSF4DAEbfJj5OIXITx5QGbZns/AtxqGyRSCn2zP0K2jsWEX4L3b1j+MnDXORFbGro1RF32RfTmZKF60grwSAXst09CFd+dxZLhizJjCRaah065YiLCcCbHyZM6uswCEWp9sz9Cto7FhF+C929Y/jJw1zkRWxq6NURd9kX05mShc5AbJ0B8bQ0j9842h5lUfOWcbj2TeoFx6OCpgoHIqWIBhHAAAAAFYAAIAAAACAAQAAgAAAAAAAAAAAARcgUJKbdMGgSVS3i0tgNel6XgeKWg8o7JbVR7/ums6AOsAAAAA=",
"unconditionalForfeitTxs": [
"cHNidP8BAFICAAAAAWwxXUjG5tidFA0LmUljX//jwW6xWaS6HKyRCw5StsxpAAAAAAD/////AVhNAAAAAAAAFgAUSU38/3Mzx5BdILG4oUO+JoHcoT8AAAAAAAEBKyBOAAAAAAAAIlEgp9TN/+j2H6vS/3LiebI632o7wSQO6ZA9BkZNsS/x9IVBFK8EgF7LdPQhXfncWS4YsyYwkWmodOuWIiwnAmx8mTOrsnQHxtDSP3zjaHmVR85ZxuPZN6gXHo4KmCgcipYgGEdAjH8Mg1Z3GdjGzp78Mg2xq1fop9KDfeji+xoyMgYS7q0Nl0AGOAaNzkDRW4cNcefll5jZC2i3nfygKdXsUsR+LEIVwVCSm3TBoElUt4tLYDXpel4HiloPKOyW1Ue/7prOgDrApG1JPw/u15SlIXgMARt8mPk4hchPHlAZtmez8C3GobJFIKfbM/QraOxYRfgvdvWP4ycNc5EVsaujVEXfZF9OZkoXrSCvBIBey3T0IV353FkuGLMmMJFpqHTrliIsJwJsfJkzq6zAARcgUJKbdMGgSVS3i0tgNel6XgeKWg8o7JbVR7/ums6AOsAAAA=="
]
}
"redeemTx": "cHNidP8BAIkCAAAAAWwxXUjG5tidFA0LmUljX//jwW6xWaS6HKyRCw5StsxpAAAAAAD/////AugDAAAAAAAAIlEgt2eR8LtqTP7yUcQtSydeGrRiHnVmHHnZwYjdC23G7MZwSQAAAAAAACJRIKfUzf/o9h+r0v9y4nmyOt9qO8EkDumQPQZGTbEv8fSFAAAAAAABASsgTgAAAAAAACJRIKfUzf/o9h+r0v9y4nmyOt9qO8EkDumQPQZGTbEv8fSFIgYDp9sz9Cto7FhF+C929Y/jJw1zkRWxq6NURd9kX05mShcYAAAAAFYAAIAAAACAAQAAgAAAAAAAAAAAQRSvBIBey3T0IV353FkuGLMmMJFpqHTrliIsJwJsfJkzq7J0B8bQ0j9842h5lUfOWcbj2TeoFx6OCpgoHIqWIBhHQAFqkBLiRmP3AZ8MS77s1QIWZswMV3L72D9gN0f0MbD6XHkmzZeC1clF3uzxr+13wsF0vcFe29Zl3e2gAhMNGYVCFcFQkpt0waBJVLeLS2A16XpeB4paDyjsltVHv+6azoA6wKRtST8P7teUpSF4DAEbfJj5OIXITx5QGbZns/AtxqGyRSCn2zP0K2jsWEX4L3b1j+MnDXORFbGro1RF32RfTmZKF60grwSAXst09CFd+dxZLhizJjCRaah065YiLCcCbHyZM6uswCEWp9sz9Cto7FhF+C929Y/jJw1zkRWxq6NURd9kX05mShc5AbJ0B8bQ0j9842h5lUfOWcbj2TeoFx6OCpgoHIqWIBhHAAAAAFYAAIAAAACAAQAAgAAAAAAAAAAAARcgUJKbdMGgSVS3i0tgNel6XgeKWg8o7JbVR7/ums6AOsAAAAA="
},
{
"outpoint": {
@@ -398,13 +364,7 @@ var (
"spentBy": "",
"expireAt": "1726486389",
"swept": false,
"pending": true,
"pendingData": {
"redeemTx": "cHNidP8BAIkCAAAAARH6LJRGP/pFIkD/o5bBp8fXAhjl8yjfN7MhJsxdt5lrAQAAAAD/////AtAHAAAAAAAAIlEguuBh3KQUVZp+NHV2sixQ/mrsngCuLCGXzsgJPC1FzY7ANQ8AAAAAACJRIP7uXXtl4jLUcVVQU+sX7WFXmx2H6iCMzn7gye0v1Y8JAAAAAAABAStYPg8AAAAAACJRIP7uXXtl4jLUcVVQU+sX7WFXmx2H6iCMzn7gye0v1Y8JIgYCHVQFdXiyvwIlLdnji8FvbUgb+ECuTRNAB67owJnjcaMYAAAAAFYAAIAAAACAAQAAgAAAAAAAAAAAQRSc1yjQ/vHRHev23fKGANLvbOhkNYmGtmRWt8fSszlOJUzRFbnfxd1fq9gIEpaI0vrZww8tlZ94iEL75QoaIbVqQJsPdLYf7fAoXO82VoqwYHu1WevE4g6LxUGBPzfd96q5EEZkoW5qqg+v5dWJUEY467Q6qZLFHwziUaB3KEY8yEpCFcBQkpt0waBJVLeLS2A16XpeB4paDyjsltVHv+6azoA6wO5D2Mh3x0XNGxFCS67GNughkENFodpFeVpZjn76chI8RSAdVAV1eLK/AiUt2eOLwW9tSBv4QK5NE0AHrujAmeNxo60gnNco0P7x0R3r9t3yhgDS72zoZDWJhrZkVrfH0rM5TiWswCEWHVQFdXiyvwIlLdnji8FvbUgb+ECuTRNAB67owJnjcaM5AUzRFbnfxd1fq9gIEpaI0vrZww8tlZ94iEL75QoaIbVqAAAAAFYAAIAAAACAAQAAgAAAAAAAAAAAARcgUJKbdMGgSVS3i0tgNel6XgeKWg8o7JbVR7/ums6AOsAAAAA=",
"unconditionalForfeitTxs": [
"cHNidP8BAFICAAAAARH6LJRGP/pFIkD/o5bBp8fXAhjl8yjfN7MhJsxdt5lrAQAAAAD/////AZA9DwAAAAAAFgAU+9NJhjFhe8jX1hrXh3NvDyHZ1cYAAAAAAAEBK1g+DwAAAAAAIlEg/u5de2XiMtRxVVBT6xftYVebHYfqIIzOfuDJ7S/VjwlBFJzXKND+8dEd6/bd8oYA0u9s6GQ1iYa2ZFa3x9KzOU4lTNEVud/F3V+r2AgSlojS+tnDDy2Vn3iIQvvlChohtWpARJzBjlEkN/kTpyFEtpvP2Ui7ypevuxb9J/NUAwhYf8Pmnnj1l3WuKCSi4Fcp1O+lQjIiZlNpwY6J73q/V8Fe2kIVwFCSm3TBoElUt4tLYDXpel4HiloPKOyW1Ue/7prOgDrA7kPYyHfHRc0bEUJLrsY26CGQQ0Wh2kV5WlmOfvpyEjxFIB1UBXV4sr8CJS3Z44vBb21IG/hArk0TQAeu6MCZ43GjrSCc1yjQ/vHRHev23fKGANLvbOhkNYmGtmRWt8fSszlOJazAARcgUJKbdMGgSVS3i0tgNel6XgeKWg8o7JbVR7/ums6AOsAAAA=="
]
}
"redeemTx": "cHNidP8BAIkCAAAAARH6LJRGP/pFIkD/o5bBp8fXAhjl8yjfN7MhJsxdt5lrAQAAAAD/////AtAHAAAAAAAAIlEguuBh3KQUVZp+NHV2sixQ/mrsngCuLCGXzsgJPC1FzY7ANQ8AAAAAACJRIP7uXXtl4jLUcVVQU+sX7WFXmx2H6iCMzn7gye0v1Y8JAAAAAAABAStYPg8AAAAAACJRIP7uXXtl4jLUcVVQU+sX7WFXmx2H6iCMzn7gye0v1Y8JIgYCHVQFdXiyvwIlLdnji8FvbUgb+ECuTRNAB67owJnjcaMYAAAAAFYAAIAAAACAAQAAgAAAAAAAAAAAQRSc1yjQ/vHRHev23fKGANLvbOhkNYmGtmRWt8fSszlOJUzRFbnfxd1fq9gIEpaI0vrZww8tlZ94iEL75QoaIbVqQJsPdLYf7fAoXO82VoqwYHu1WevE4g6LxUGBPzfd96q5EEZkoW5qqg+v5dWJUEY467Q6qZLFHwziUaB3KEY8yEpCFcBQkpt0waBJVLeLS2A16XpeB4paDyjsltVHv+6azoA6wO5D2Mh3x0XNGxFCS67GNughkENFodpFeVpZjn76chI8RSAdVAV1eLK/AiUt2eOLwW9tSBv4QK5NE0AHrujAmeNxo60gnNco0P7x0R3r9t3yhgDS72zoZDWJhrZkVrfH0rM5TiWswCEWHVQFdXiyvwIlLdnji8FvbUgb+ECuTRNAB67owJnjcaM5AUzRFbnfxd1fq9gIEpaI0vrZww8tlZ94iEL75QoaIbVqAAAAAFYAAIAAAACAAQAAgAAAAAAAAAAAARcgUJKbdMGgSVS3i0tgNel6XgeKWg8o7JbVR7/ums6AOsAAAAA="
}
],
"spentVtxos": []
@@ -426,8 +386,7 @@ var (
"spentBy": "",
"expireAt": "1726503895",
"swept": false,
"pending": false,
"pendingData": null
"redeemTx": ""
}
],
"spentVtxos": [
@@ -445,13 +404,7 @@ var (
"spentBy": "d6684a5b9e6939dccdf07d1f0eaf7fdd7b31de4d123e63e400d23de739800d4e",
"expireAt": "1726054928",
"swept": false,
"pending": true,
"pendingData": {
"redeemTx": "cHNidP8BAIkCAAAAAWwxXUjG5tidFA0LmUljX//jwW6xWaS6HKyRCw5StsxpAAAAAAD/////AugDAAAAAAAAIlEgt2eR8LtqTP7yUcQtSydeGrRiHnVmHHnZwYjdC23G7MZwSQAAAAAAACJRIKfUzf/o9h+r0v9y4nmyOt9qO8EkDumQPQZGTbEv8fSFAAAAAAABASsgTgAAAAAAACJRIKfUzf/o9h+r0v9y4nmyOt9qO8EkDumQPQZGTbEv8fSFIgYDp9sz9Cto7FhF+C929Y/jJw1zkRWxq6NURd9kX05mShcYAAAAAFYAAIAAAACAAQAAgAAAAAAAAAAAQRSvBIBey3T0IV353FkuGLMmMJFpqHTrliIsJwJsfJkzq7J0B8bQ0j9842h5lUfOWcbj2TeoFx6OCpgoHIqWIBhHQAFqkBLiRmP3AZ8MS77s1QIWZswMV3L72D9gN0f0MbD6XHkmzZeC1clF3uzxr+13wsF0vcFe29Zl3e2gAhMNGYVCFcFQkpt0waBJVLeLS2A16XpeB4paDyjsltVHv+6azoA6wKRtST8P7teUpSF4DAEbfJj5OIXITx5QGbZns/AtxqGyRSCn2zP0K2jsWEX4L3b1j+MnDXORFbGro1RF32RfTmZKF60grwSAXst09CFd+dxZLhizJjCRaah065YiLCcCbHyZM6uswCEWp9sz9Cto7FhF+C929Y/jJw1zkRWxq6NURd9kX05mShc5AbJ0B8bQ0j9842h5lUfOWcbj2TeoFx6OCpgoHIqWIBhHAAAAAFYAAIAAAACAAQAAgAAAAAAAAAAAARcgUJKbdMGgSVS3i0tgNel6XgeKWg8o7JbVR7/ums6AOsAAAAA=",
"unconditionalForfeitTxs": [
"cHNidP8BAFICAAAAAWwxXUjG5tidFA0LmUljX//jwW6xWaS6HKyRCw5StsxpAAAAAAD/////AVhNAAAAAAAAFgAUSU38/3Mzx5BdILG4oUO+JoHcoT8AAAAAAAEBKyBOAAAAAAAAIlEgp9TN/+j2H6vS/3LiebI632o7wSQO6ZA9BkZNsS/x9IVBFK8EgF7LdPQhXfncWS4YsyYwkWmodOuWIiwnAmx8mTOrsnQHxtDSP3zjaHmVR85ZxuPZN6gXHo4KmCgcipYgGEdAjH8Mg1Z3GdjGzp78Mg2xq1fop9KDfeji+xoyMgYS7q0Nl0AGOAaNzkDRW4cNcefll5jZC2i3nfygKdXsUsR+LEIVwVCSm3TBoElUt4tLYDXpel4HiloPKOyW1Ue/7prOgDrApG1JPw/u15SlIXgMARt8mPk4hchPHlAZtmez8C3GobJFIKfbM/QraOxYRfgvdvWP4ycNc5EVsaujVEXfZF9OZkoXrSCvBIBey3T0IV353FkuGLMmMJFpqHTrliIsJwJsfJkzq6zAARcgUJKbdMGgSVS3i0tgNel6XgeKWg8o7JbVR7/ums6AOsAAAA=="
]
}
"redeemTx": "cHNidP8BAIkCAAAAAWwxXUjG5tidFA0LmUljX//jwW6xWaS6HKyRCw5StsxpAAAAAAD/////AugDAAAAAAAAIlEgt2eR8LtqTP7yUcQtSydeGrRiHnVmHHnZwYjdC23G7MZwSQAAAAAAACJRIKfUzf/o9h+r0v9y4nmyOt9qO8EkDumQPQZGTbEv8fSFAAAAAAABASsgTgAAAAAAACJRIKfUzf/o9h+r0v9y4nmyOt9qO8EkDumQPQZGTbEv8fSFIgYDp9sz9Cto7FhF+C929Y/jJw1zkRWxq6NURd9kX05mShcYAAAAAFYAAIAAAACAAQAAgAAAAAAAAAAAQRSvBIBey3T0IV353FkuGLMmMJFpqHTrliIsJwJsfJkzq7J0B8bQ0j9842h5lUfOWcbj2TeoFx6OCpgoHIqWIBhHQAFqkBLiRmP3AZ8MS77s1QIWZswMV3L72D9gN0f0MbD6XHkmzZeC1clF3uzxr+13wsF0vcFe29Zl3e2gAhMNGYVCFcFQkpt0waBJVLeLS2A16XpeB4paDyjsltVHv+6azoA6wKRtST8P7teUpSF4DAEbfJj5OIXITx5QGbZns/AtxqGyRSCn2zP0K2jsWEX4L3b1j+MnDXORFbGro1RF32RfTmZKF60grwSAXst09CFd+dxZLhizJjCRaah065YiLCcCbHyZM6uswCEWp9sz9Cto7FhF+C929Y/jJw1zkRWxq6NURd9kX05mShc5AbJ0B8bQ0j9842h5lUfOWcbj2TeoFx6OCpgoHIqWIBhHAAAAAFYAAIAAAACAAQAAgAAAAAAAAAAAARcgUJKbdMGgSVS3i0tgNel6XgeKWg8o7JbVR7/ums6AOsAAAAA="
},
{
"outpoint": {
@@ -467,13 +420,7 @@ var (
"spentBy": "d6684a5b9e6939dccdf07d1f0eaf7fdd7b31de4d123e63e400d23de739800d4e",
"expireAt": "1726486389",
"swept": false,
"pending": true,
"pendingData": {
"redeemTx": "cHNidP8BAIkCAAAAARH6LJRGP/pFIkD/o5bBp8fXAhjl8yjfN7MhJsxdt5lrAQAAAAD/////AtAHAAAAAAAAIlEguuBh3KQUVZp+NHV2sixQ/mrsngCuLCGXzsgJPC1FzY7ANQ8AAAAAACJRIP7uXXtl4jLUcVVQU+sX7WFXmx2H6iCMzn7gye0v1Y8JAAAAAAABAStYPg8AAAAAACJRIP7uXXtl4jLUcVVQU+sX7WFXmx2H6iCMzn7gye0v1Y8JIgYCHVQFdXiyvwIlLdnji8FvbUgb+ECuTRNAB67owJnjcaMYAAAAAFYAAIAAAACAAQAAgAAAAAAAAAAAQRSc1yjQ/vHRHev23fKGANLvbOhkNYmGtmRWt8fSszlOJUzRFbnfxd1fq9gIEpaI0vrZww8tlZ94iEL75QoaIbVqQJsPdLYf7fAoXO82VoqwYHu1WevE4g6LxUGBPzfd96q5EEZkoW5qqg+v5dWJUEY467Q6qZLFHwziUaB3KEY8yEpCFcBQkpt0waBJVLeLS2A16XpeB4paDyjsltVHv+6azoA6wO5D2Mh3x0XNGxFCS67GNughkENFodpFeVpZjn76chI8RSAdVAV1eLK/AiUt2eOLwW9tSBv4QK5NE0AHrujAmeNxo60gnNco0P7x0R3r9t3yhgDS72zoZDWJhrZkVrfH0rM5TiWswCEWHVQFdXiyvwIlLdnji8FvbUgb+ECuTRNAB67owJnjcaM5AUzRFbnfxd1fq9gIEpaI0vrZww8tlZ94iEL75QoaIbVqAAAAAFYAAIAAAACAAQAAgAAAAAAAAAAAARcgUJKbdMGgSVS3i0tgNel6XgeKWg8o7JbVR7/ums6AOsAAAAA=",
"unconditionalForfeitTxs": [
"cHNidP8BAFICAAAAARH6LJRGP/pFIkD/o5bBp8fXAhjl8yjfN7MhJsxdt5lrAQAAAAD/////AZA9DwAAAAAAFgAU+9NJhjFhe8jX1hrXh3NvDyHZ1cYAAAAAAAEBK1g+DwAAAAAAIlEg/u5de2XiMtRxVVBT6xftYVebHYfqIIzOfuDJ7S/VjwlBFJzXKND+8dEd6/bd8oYA0u9s6GQ1iYa2ZFa3x9KzOU4lTNEVud/F3V+r2AgSlojS+tnDDy2Vn3iIQvvlChohtWpARJzBjlEkN/kTpyFEtpvP2Ui7ypevuxb9J/NUAwhYf8Pmnnj1l3WuKCSi4Fcp1O+lQjIiZlNpwY6J73q/V8Fe2kIVwFCSm3TBoElUt4tLYDXpel4HiloPKOyW1Ue/7prOgDrA7kPYyHfHRc0bEUJLrsY26CGQQ0Wh2kV5WlmOfvpyEjxFIB1UBXV4sr8CJS3Z44vBb21IG/hArk0TQAeu6MCZ43GjrSCc1yjQ/vHRHev23fKGANLvbOhkNYmGtmRWt8fSszlOJazAARcgUJKbdMGgSVS3i0tgNel6XgeKWg8o7JbVR7/ums6AOsAAAA=="
]
}
"redeemTx": "cHNidP8BAIkCAAAAARH6LJRGP/pFIkD/o5bBp8fXAhjl8yjfN7MhJsxdt5lrAQAAAAD/////AtAHAAAAAAAAIlEguuBh3KQUVZp+NHV2sixQ/mrsngCuLCGXzsgJPC1FzY7ANQ8AAAAAACJRIP7uXXtl4jLUcVVQU+sX7WFXmx2H6iCMzn7gye0v1Y8JAAAAAAABAStYPg8AAAAAACJRIP7uXXtl4jLUcVVQU+sX7WFXmx2H6iCMzn7gye0v1Y8JIgYCHVQFdXiyvwIlLdnji8FvbUgb+ECuTRNAB67owJnjcaMYAAAAAFYAAIAAAACAAQAAgAAAAAAAAAAAQRSc1yjQ/vHRHev23fKGANLvbOhkNYmGtmRWt8fSszlOJUzRFbnfxd1fq9gIEpaI0vrZww8tlZ94iEL75QoaIbVqQJsPdLYf7fAoXO82VoqwYHu1WevE4g6LxUGBPzfd96q5EEZkoW5qqg+v5dWJUEY467Q6qZLFHwziUaB3KEY8yEpCFcBQkpt0waBJVLeLS2A16XpeB4paDyjsltVHv+6azoA6wO5D2Mh3x0XNGxFCS67GNughkENFodpFeVpZjn76chI8RSAdVAV1eLK/AiUt2eOLwW9tSBv4QK5NE0AHrujAmeNxo60gnNco0P7x0R3r9t3yhgDS72zoZDWJhrZkVrfH0rM5TiWswCEWHVQFdXiyvwIlLdnji8FvbUgb+ECuTRNAB67owJnjcaM5AUzRFbnfxd1fq9gIEpaI0vrZww8tlZ94iEL75QoaIbVqAAAAAFYAAIAAAACAAQAAgAAAAAAAAAAAARcgUJKbdMGgSVS3i0tgNel6XgeKWg8o7JbVR7/ums6AOsAAAAA="
}
]
}`
@@ -494,13 +441,7 @@ var (
"spentBy": "",
"expireAt": "1726503895",
"swept": false,
"pending": false,
"pendingData": {
"redeemTx": "cHNidP8BAIkCAAAAAdOK9YzYw1ceJznqJxtRXGe0KeHj6CLcLtqLVwcbMCivAAAAAAD/////ArgLAAAAAAAAIlEgC39Vxhw3dIa4heHgFS6X4XwDl1mBggsKLVTBwF1h3qEgegEAAAAAACJRIMkktfIFxFNTtAmy3K0p+7JqVn2kcA0P6y2vJ1QX2zysAAAAAAABASughgEAAAAAACJRIMkktfIFxFNTtAmy3K0p+7JqVn2kcA0P6y2vJ1QX2zysIgYDjGeMfnNwCrU45iB3iRqiFdWTADaiJ968+w3ruFuq1F0YAAAAAFYAAIAAAACAAQAAgAAAAAAAAAAAQRTYEOuHJ0hyLBGzY8nSHpD2F1nby5/XQ5Sh2Je+cQ5Wsx0ZucLmB/LLspxMRN9JcJn3Q2KJRMhhg7415cCg1d0gQNSvgaBk/1WLYqQxCKxCfv8ViVJ7vjBxvNO5tc2FEDy27V9cIrfL1jPJoVrhgPZT0GwY7dkVZS7saIKI03CbipBCFcBQkpt0waBJVLeLS2A16XpeB4paDyjsltVHv+6azoA6wPKiQ0JM6aw2kcUByijEbOydM3gTIVCGN/69q+dmyxcqRSCMZ4x+c3AKtTjmIHeJGqIV1ZMANqIn3rz7Deu4W6rUXa0g2BDrhydIciwRs2PJ0h6Q9hdZ28uf10OUodiXvnEOVrOswCEWjGeMfnNwCrU45iB3iRqiFdWTADaiJ968+w3ruFuq1F05AR0ZucLmB/LLspxMRN9JcJn3Q2KJRMhhg7415cCg1d0gAAAAAFYAAIAAAACAAQAAgAAAAAAAAAAAARcgUJKbdMGgSVS3i0tgNel6XgeKWg8o7JbVR7/ums6AOsAAAAA=",
"unconditionalForfeitTxs": [
"cHNidP8BAFICAAAAAdOK9YzYw1ceJznqJxtRXGe0KeHj6CLcLtqLVwcbMCivAAAAAAD/////AdiFAQAAAAAAFgAUlsBYsQa9BEiB8ZumuN4J50lbQIoAAAAAAAEBK6CGAQAAAAAAIlEgySS18gXEU1O0CbLcrSn7smpWfaRwDQ/rLa8nVBfbPKxBFNgQ64cnSHIsEbNjydIekPYXWdvLn9dDlKHYl75xDlazHRm5wuYH8suynExE30lwmfdDYolEyGGDvjXlwKDV3SBAZadgbU8gCDvq3XN0EeLIwGKGSAYHZRkGbAnr9ZjCHGKAQlfFNYS0af1Lz4j7Th2osVY8JJv7O736sC5NNQome0IVwFCSm3TBoElUt4tLYDXpel4HiloPKOyW1Ue/7prOgDrA8qJDQkzprDaRxQHKKMRs7J0zeBMhUIY3/r2r52bLFypFIIxnjH5zcAq1OOYgd4kaohXVkwA2oifevPsN67hbqtRdrSDYEOuHJ0hyLBGzY8nSHpD2F1nby5/XQ5Sh2Je+cQ5Ws6zAARcgUJKbdMGgSVS3i0tgNel6XgeKWg8o7JbVR7/ums6AOsAAAA=="
]
}
"redeemTx": "cHNidP8BAIkCAAAAAdOK9YzYw1ceJznqJxtRXGe0KeHj6CLcLtqLVwcbMCivAAAAAAD/////ArgLAAAAAAAAIlEgC39Vxhw3dIa4heHgFS6X4XwDl1mBggsKLVTBwF1h3qEgegEAAAAAACJRIMkktfIFxFNTtAmy3K0p+7JqVn2kcA0P6y2vJ1QX2zysAAAAAAABASughgEAAAAAACJRIMkktfIFxFNTtAmy3K0p+7JqVn2kcA0P6y2vJ1QX2zysIgYDjGeMfnNwCrU45iB3iRqiFdWTADaiJ968+w3ruFuq1F0YAAAAAFYAAIAAAACAAQAAgAAAAAAAAAAAQRTYEOuHJ0hyLBGzY8nSHpD2F1nby5/XQ5Sh2Je+cQ5Wsx0ZucLmB/LLspxMRN9JcJn3Q2KJRMhhg7415cCg1d0gQNSvgaBk/1WLYqQxCKxCfv8ViVJ7vjBxvNO5tc2FEDy27V9cIrfL1jPJoVrhgPZT0GwY7dkVZS7saIKI03CbipBCFcBQkpt0waBJVLeLS2A16XpeB4paDyjsltVHv+6azoA6wPKiQ0JM6aw2kcUByijEbOydM3gTIVCGN/69q+dmyxcqRSCMZ4x+c3AKtTjmIHeJGqIV1ZMANqIn3rz7Deu4W6rUXa0g2BDrhydIciwRs2PJ0h6Q9hdZ28uf10OUodiXvnEOVrOswCEWjGeMfnNwCrU45iB3iRqiFdWTADaiJ968+w3ruFuq1F05AR0ZucLmB/LLspxMRN9JcJn3Q2KJRMhhg7415cCg1d0gAAAAAFYAAIAAAACAAQAAgAAAAAAAAAAAARcgUJKbdMGgSVS3i0tgNel6XgeKWg8o7JbVR7/ums6AOsAAAAA="
}
],
"spentVtxos": [
@@ -518,13 +459,7 @@ var (
"spentBy": "d6684a5b9e6939dccdf07d1f0eaf7fdd7b31de4d123e63e400d23de739800d4e",
"expireAt": "1726054928",
"swept": false,
"pending": true,
"pendingData": {
"redeemTx": "cHNidP8BAIkCAAAAAWwxXUjG5tidFA0LmUljX//jwW6xWaS6HKyRCw5StsxpAAAAAAD/////AugDAAAAAAAAIlEgt2eR8LtqTP7yUcQtSydeGrRiHnVmHHnZwYjdC23G7MZwSQAAAAAAACJRIKfUzf/o9h+r0v9y4nmyOt9qO8EkDumQPQZGTbEv8fSFAAAAAAABASsgTgAAAAAAACJRIKfUzf/o9h+r0v9y4nmyOt9qO8EkDumQPQZGTbEv8fSFIgYDp9sz9Cto7FhF+C929Y/jJw1zkRWxq6NURd9kX05mShcYAAAAAFYAAIAAAACAAQAAgAAAAAAAAAAAQRSvBIBey3T0IV353FkuGLMmMJFpqHTrliIsJwJsfJkzq7J0B8bQ0j9842h5lUfOWcbj2TeoFx6OCpgoHIqWIBhHQAFqkBLiRmP3AZ8MS77s1QIWZswMV3L72D9gN0f0MbD6XHkmzZeC1clF3uzxr+13wsF0vcFe29Zl3e2gAhMNGYVCFcFQkpt0waBJVLeLS2A16XpeB4paDyjsltVHv+6azoA6wKRtST8P7teUpSF4DAEbfJj5OIXITx5QGbZns/AtxqGyRSCn2zP0K2jsWEX4L3b1j+MnDXORFbGro1RF32RfTmZKF60grwSAXst09CFd+dxZLhizJjCRaah065YiLCcCbHyZM6uswCEWp9sz9Cto7FhF+C929Y/jJw1zkRWxq6NURd9kX05mShc5AbJ0B8bQ0j9842h5lUfOWcbj2TeoFx6OCpgoHIqWIBhHAAAAAFYAAIAAAACAAQAAgAAAAAAAAAAAARcgUJKbdMGgSVS3i0tgNel6XgeKWg8o7JbVR7/ums6AOsAAAAA=",
"unconditionalForfeitTxs": [
"cHNidP8BAFICAAAAAWwxXUjG5tidFA0LmUljX//jwW6xWaS6HKyRCw5StsxpAAAAAAD/////AVhNAAAAAAAAFgAUSU38/3Mzx5BdILG4oUO+JoHcoT8AAAAAAAEBKyBOAAAAAAAAIlEgp9TN/+j2H6vS/3LiebI632o7wSQO6ZA9BkZNsS/x9IVBFK8EgF7LdPQhXfncWS4YsyYwkWmodOuWIiwnAmx8mTOrsnQHxtDSP3zjaHmVR85ZxuPZN6gXHo4KmCgcipYgGEdAjH8Mg1Z3GdjGzp78Mg2xq1fop9KDfeji+xoyMgYS7q0Nl0AGOAaNzkDRW4cNcefll5jZC2i3nfygKdXsUsR+LEIVwVCSm3TBoElUt4tLYDXpel4HiloPKOyW1Ue/7prOgDrApG1JPw/u15SlIXgMARt8mPk4hchPHlAZtmez8C3GobJFIKfbM/QraOxYRfgvdvWP4ycNc5EVsaujVEXfZF9OZkoXrSCvBIBey3T0IV353FkuGLMmMJFpqHTrliIsJwJsfJkzq6zAARcgUJKbdMGgSVS3i0tgNel6XgeKWg8o7JbVR7/ums6AOsAAAA=="
]
}
"redeemTx": "cHNidP8BAIkCAAAAAWwxXUjG5tidFA0LmUljX//jwW6xWaS6HKyRCw5StsxpAAAAAAD/////AugDAAAAAAAAIlEgt2eR8LtqTP7yUcQtSydeGrRiHnVmHHnZwYjdC23G7MZwSQAAAAAAACJRIKfUzf/o9h+r0v9y4nmyOt9qO8EkDumQPQZGTbEv8fSFAAAAAAABASsgTgAAAAAAACJRIKfUzf/o9h+r0v9y4nmyOt9qO8EkDumQPQZGTbEv8fSFIgYDp9sz9Cto7FhF+C929Y/jJw1zkRWxq6NURd9kX05mShcYAAAAAFYAAIAAAACAAQAAgAAAAAAAAAAAQRSvBIBey3T0IV353FkuGLMmMJFpqHTrliIsJwJsfJkzq7J0B8bQ0j9842h5lUfOWcbj2TeoFx6OCpgoHIqWIBhHQAFqkBLiRmP3AZ8MS77s1QIWZswMV3L72D9gN0f0MbD6XHkmzZeC1clF3uzxr+13wsF0vcFe29Zl3e2gAhMNGYVCFcFQkpt0waBJVLeLS2A16XpeB4paDyjsltVHv+6azoA6wKRtST8P7teUpSF4DAEbfJj5OIXITx5QGbZns/AtxqGyRSCn2zP0K2jsWEX4L3b1j+MnDXORFbGro1RF32RfTmZKF60grwSAXst09CFd+dxZLhizJjCRaah065YiLCcCbHyZM6uswCEWp9sz9Cto7FhF+C929Y/jJw1zkRWxq6NURd9kX05mShc5AbJ0B8bQ0j9842h5lUfOWcbj2TeoFx6OCpgoHIqWIBhHAAAAAFYAAIAAAACAAQAAgAAAAAAAAAAAARcgUJKbdMGgSVS3i0tgNel6XgeKWg8o7JbVR7/ums6AOsAAAAA="
},
{
"outpoint": {
@@ -541,12 +476,7 @@ var (
"expireAt": "1726486389",
"swept": false,
"pending": true,
"pendingData": {
"redeemTx": "cHNidP8BAIkCAAAAARH6LJRGP/pFIkD/o5bBp8fXAhjl8yjfN7MhJsxdt5lrAQAAAAD/////AtAHAAAAAAAAIlEguuBh3KQUVZp+NHV2sixQ/mrsngCuLCGXzsgJPC1FzY7ANQ8AAAAAACJRIP7uXXtl4jLUcVVQU+sX7WFXmx2H6iCMzn7gye0v1Y8JAAAAAAABAStYPg8AAAAAACJRIP7uXXtl4jLUcVVQU+sX7WFXmx2H6iCMzn7gye0v1Y8JIgYCHVQFdXiyvwIlLdnji8FvbUgb+ECuTRNAB67owJnjcaMYAAAAAFYAAIAAAACAAQAAgAAAAAAAAAAAQRSc1yjQ/vHRHev23fKGANLvbOhkNYmGtmRWt8fSszlOJUzRFbnfxd1fq9gIEpaI0vrZww8tlZ94iEL75QoaIbVqQJsPdLYf7fAoXO82VoqwYHu1WevE4g6LxUGBPzfd96q5EEZkoW5qqg+v5dWJUEY467Q6qZLFHwziUaB3KEY8yEpCFcBQkpt0waBJVLeLS2A16XpeB4paDyjsltVHv+6azoA6wO5D2Mh3x0XNGxFCS67GNughkENFodpFeVpZjn76chI8RSAdVAV1eLK/AiUt2eOLwW9tSBv4QK5NE0AHrujAmeNxo60gnNco0P7x0R3r9t3yhgDS72zoZDWJhrZkVrfH0rM5TiWswCEWHVQFdXiyvwIlLdnji8FvbUgb+ECuTRNAB67owJnjcaM5AUzRFbnfxd1fq9gIEpaI0vrZww8tlZ94iEL75QoaIbVqAAAAAFYAAIAAAACAAQAAgAAAAAAAAAAAARcgUJKbdMGgSVS3i0tgNel6XgeKWg8o7JbVR7/ums6AOsAAAAA=",
"unconditionalForfeitTxs": [
"cHNidP8BAFICAAAAARH6LJRGP/pFIkD/o5bBp8fXAhjl8yjfN7MhJsxdt5lrAQAAAAD/////AZA9DwAAAAAAFgAU+9NJhjFhe8jX1hrXh3NvDyHZ1cYAAAAAAAEBK1g+DwAAAAAAIlEg/u5de2XiMtRxVVBT6xftYVebHYfqIIzOfuDJ7S/VjwlBFJzXKND+8dEd6/bd8oYA0u9s6GQ1iYa2ZFa3x9KzOU4lTNEVud/F3V+r2AgSlojS+tnDDy2Vn3iIQvvlChohtWpARJzBjlEkN/kTpyFEtpvP2Ui7ypevuxb9J/NUAwhYf8Pmnnj1l3WuKCSi4Fcp1O+lQjIiZlNpwY6J73q/V8Fe2kIVwFCSm3TBoElUt4tLYDXpel4HiloPKOyW1Ue/7prOgDrA7kPYyHfHRc0bEUJLrsY26CGQQ0Wh2kV5WlmOfvpyEjxFIB1UBXV4sr8CJS3Z44vBb21IG/hArk0TQAeu6MCZ43GjrSCc1yjQ/vHRHev23fKGANLvbOhkNYmGtmRWt8fSszlOJazAARcgUJKbdMGgSVS3i0tgNel6XgeKWg8o7JbVR7/ums6AOsAAAA=="
]
}
"redeemTx": "cHNidP8BAIkCAAAAARH6LJRGP/pFIkD/o5bBp8fXAhjl8yjfN7MhJsxdt5lrAQAAAAD/////AtAHAAAAAAAAIlEguuBh3KQUVZp+NHV2sixQ/mrsngCuLCGXzsgJPC1FzY7ANQ8AAAAAACJRIP7uXXtl4jLUcVVQU+sX7WFXmx2H6iCMzn7gye0v1Y8JAAAAAAABAStYPg8AAAAAACJRIP7uXXtl4jLUcVVQU+sX7WFXmx2H6iCMzn7gye0v1Y8JIgYCHVQFdXiyvwIlLdnji8FvbUgb+ECuTRNAB67owJnjcaMYAAAAAFYAAIAAAACAAQAAgAAAAAAAAAAAQRSc1yjQ/vHRHev23fKGANLvbOhkNYmGtmRWt8fSszlOJUzRFbnfxd1fq9gIEpaI0vrZww8tlZ94iEL75QoaIbVqQJsPdLYf7fAoXO82VoqwYHu1WevE4g6LxUGBPzfd96q5EEZkoW5qqg+v5dWJUEY467Q6qZLFHwziUaB3KEY8yEpCFcBQkpt0waBJVLeLS2A16XpeB4paDyjsltVHv+6azoA6wO5D2Mh3x0XNGxFCS67GNughkENFodpFeVpZjn76chI8RSAdVAV1eLK/AiUt2eOLwW9tSBv4QK5NE0AHrujAmeNxo60gnNco0P7x0R3r9t3yhgDS72zoZDWJhrZkVrfH0rM5TiWswCEWHVQFdXiyvwIlLdnji8FvbUgb+ECuTRNAB67owJnjcaM5AUzRFbnfxd1fq9gIEpaI0vrZww8tlZ94iEL75QoaIbVqAAAAAFYAAIAAAACAAQAAgAAAAAAAAAAAARcgUJKbdMGgSVS3i0tgNel6XgeKWg8o7JbVR7/ums6AOsAAAAA="
},
{
"outpoint": {
@@ -562,8 +492,7 @@ var (
"spentBy": "23c3a885f0ea05f7bdf83f3bf7f8ac9dc3f791ad292f4e63a6f53fa5e4935ab0",
"expireAt": "1726503895",
"swept": false,
"pending": false,
"pendingData": null
"redeemTx": ""
}
]
}`

View File

@@ -14,7 +14,6 @@ import (
"github.com/ark-network/ark/common"
"github.com/ark-network/ark/common/tree"
"github.com/ark-network/ark/pkg/client-sdk/client"
"github.com/ark-network/ark/pkg/client-sdk/explorer"
"github.com/ark-network/ark/pkg/client-sdk/internal/utils"
"github.com/ark-network/ark/pkg/client-sdk/redemption"
"github.com/ark-network/ark/pkg/client-sdk/types"
@@ -251,26 +250,6 @@ func (a *covenantArkClient) listenForBoardingUtxos(
//are multiple boarding inputs + spent vtxo with change in spendable + received in the same round
}
func (a *covenantArkClient) ListVtxos(
ctx context.Context,
) (spendableVtxos, spentVtxos []client.Vtxo, err error) {
offchainAddrs, _, _, err := a.wallet.GetAddresses(ctx)
if err != nil {
return
}
for _, addr := range offchainAddrs {
spendable, spent, err := a.client.ListVtxos(ctx, addr.Address)
if err != nil {
return nil, nil, err
}
spendableVtxos = append(spendableVtxos, spendable...)
spentVtxos = append(spentVtxos, spent...)
}
return
}
func (a *covenantArkClient) Balance(
ctx context.Context, computeVtxoExpiration bool,
) (*Balance, error) {
@@ -285,14 +264,13 @@ func (a *covenantArkClient) Balance(
chRes := make(chan balanceRes, nbWorkers*len(offchainAddrs))
for i := range offchainAddrs {
offchainAddr := offchainAddrs[i]
boardingAddr := boardingAddrs[i]
redeemAddr := redeemAddrs[i]
go func(addr string) {
go func() {
defer wg.Done()
balance, amountByExpiration, err := a.getOffchainBalance(
ctx, addr, computeVtxoExpiration,
ctx, computeVtxoExpiration,
)
if err != nil {
chRes <- balanceRes{err: err}
@@ -303,7 +281,7 @@ func (a *covenantArkClient) Balance(
offchainBalance: balance,
offchainBalanceByExpiration: amountByExpiration,
}
}(offchainAddr.Address)
}()
getDelayedBalance := func(addr string) {
defer wg.Done()
@@ -448,20 +426,11 @@ func (a *covenantArkClient) UnilateralRedeem(ctx context.Context) error {
return fmt.Errorf("wallet is locked")
}
offchainAddrs, _, _, err := a.wallet.GetAddresses(ctx)
vtxos, err := a.getVtxos(ctx, false, nil)
if err != nil {
return err
}
vtxos := make([]client.Vtxo, 0)
for _, offchainAddr := range offchainAddrs {
fetchedVtxos, _, err := a.client.ListVtxos(ctx, offchainAddr.Address)
if err != nil {
return err
}
vtxos = append(vtxos, fetchedVtxos...)
}
totalVtxosAmount := uint64(0)
for _, vtxo := range vtxos {
totalVtxosAmount += vtxo.Amount
@@ -519,22 +488,13 @@ func (a *covenantArkClient) CollaborativeRedeem(
return "", fmt.Errorf("wallet is locked")
}
// validate liquid address
if _, err := address.ToOutputScript(addr); err != nil {
return "", fmt.Errorf("invalid onchain address")
}
addrNet, err := address.NetworkForAddress(addr)
if err != nil {
return "", fmt.Errorf("invalid onchain address: unknown network")
}
net := utils.ToElementsNetwork(a.Network)
if net.Name != addrNet.Name {
return "", fmt.Errorf("invalid onchain address: must be for %s network", net.Name)
}
if isConf, _ := address.IsConfidential(addr); isConf {
info, _ := address.FromConfidential(addr)
addr = info.Address
if isConf, err := address.IsConfidential(addr); err != nil || isConf {
return "", fmt.Errorf("confidential onchain address not supported")
}
offchainAddrs, _, _, err := a.wallet.GetAddresses(ctx)
@@ -550,22 +510,34 @@ func (a *covenantArkClient) CollaborativeRedeem(
}
vtxos := make([]client.DescriptorVtxo, 0)
for _, offchainAddr := range offchainAddrs {
spendableVtxos, err := a.getVtxos(ctx, offchainAddr.Address, withExpiryCoinselect)
if err != nil {
return "", err
}
spendableVtxos, err := a.getVtxos(ctx, false, nil)
if err != nil {
return "", err
}
for _, vtxo := range spendableVtxos {
vtxos = append(vtxos, client.DescriptorVtxo{
Vtxo: vtxo,
Descriptor: offchainAddr.Descriptor,
})
for _, offchainAddr := range offchainAddrs {
for _, v := range spendableVtxos {
vtxoAddr, err := v.Address(a.AspPubkey, a.Network)
if err != nil {
return "", err
}
if vtxoAddr == offchainAddr.Address {
vtxos = append(vtxos, client.DescriptorVtxo{
Vtxo: v,
Descriptor: offchainAddr.Descriptor,
})
}
}
}
selectedCoins, changeAmount, err := utils.CoinSelect(
vtxos, amount, a.Dust, withExpiryCoinselect,
boardingUtxos, err := a.getClaimableBoardingUtxos(ctx, nil)
if err != nil {
return "", err
}
selectedBoardingUtxos, selectedCoins, changeAmount, err := utils.CoinSelect(
boardingUtxos, vtxos, amount, a.Dust, withExpiryCoinselect,
)
if err != nil {
return "", err
@@ -583,7 +555,7 @@ func (a *covenantArkClient) CollaborativeRedeem(
})
}
inputs := make([]client.Input, 0, len(selectedCoins))
inputs := make([]client.Input, 0, len(selectedCoins)+len(selectedBoardingUtxos))
for _, coin := range selectedCoins {
inputs = append(inputs, client.Input{
@@ -594,8 +566,17 @@ func (a *covenantArkClient) CollaborativeRedeem(
Descriptor: coin.Descriptor,
})
}
for _, coin := range selectedBoardingUtxos {
inputs = append(inputs, client.Input{
Outpoint: client.Outpoint{
Txid: coin.Txid,
VOut: coin.VOut,
},
Descriptor: coin.Descriptor,
})
}
paymentID, err := a.client.RegisterInputsForNextRound(ctx, inputs, "") // ephemeralPublicKey is not required for covenant
paymentID, err := a.client.RegisterInputsForNextRound(ctx, inputs, "")
if err != nil {
return "", err
}
@@ -604,9 +585,7 @@ func (a *covenantArkClient) CollaborativeRedeem(
return "", err
}
poolTxID, err := a.handleRoundStream(
ctx, paymentID, selectedCoins, nil, "", receivers,
)
poolTxID, err := a.handleRoundStream(ctx, paymentID, selectedCoins, selectedBoardingUtxos, receivers)
if err != nil {
return "", err
}
@@ -621,36 +600,10 @@ func (a *covenantArkClient) SendAsync(
return "", fmt.Errorf("not implemented")
}
func (a *covenantArkClient) Claim(ctx context.Context) (string, error) {
myselfOffchain, boardingAddr, err := a.wallet.NewAddress(ctx, false)
if err != nil {
return "", err
}
boardingUtxos, err := a.getClaimableBoardingUtxos(ctx)
if err != nil {
return "", err
}
var pendingBalance uint64
for _, vtxo := range boardingUtxos {
pendingBalance += vtxo.Amount
}
if pendingBalance == 0 {
return "", fmt.Errorf("no funds to claim")
}
receiver := client.Output{
Address: myselfOffchain.Address,
Amount: pendingBalance,
}
return a.selfTransferAllPendingPayments(
ctx,
boardingUtxos,
receiver,
boardingAddr.Descriptor,
)
func (a *covenantArkClient) Settle(
ctx context.Context,
) (string, error) {
return a.sendOffchain(ctx, false, nil)
}
func (a *covenantArkClient) GetTransactionHistory(
@@ -675,13 +628,13 @@ func (a *covenantArkClient) GetTransactionHistory(
return vtxosToTxsCovenant(config.RoundLifetime, spendableVtxos, spentVtxos, boardingTxs)
}
func (a *covenantArkClient) getAllBoardingUtxos(ctx context.Context) ([]explorer.Utxo, error) {
func (a *covenantArkClient) getAllBoardingUtxos(ctx context.Context) ([]types.Utxo, error) {
_, boardingAddrs, _, err := a.wallet.GetAddresses(ctx)
if err != nil {
return nil, err
}
utxos := []explorer.Utxo{}
utxos := make([]types.Utxo, 0)
for _, addr := range boardingAddrs {
txs, err := a.explorer.GetTxs(addr.Address)
if err != nil {
@@ -694,11 +647,12 @@ func (a *covenantArkClient) getAllBoardingUtxos(ctx context.Context) ([]explorer
if tx.Status.Confirmed {
createdAt = time.Unix(tx.Status.Blocktime, 0)
}
utxos = append(utxos, explorer.Utxo{
Txid: tx.Txid,
Vout: uint32(i),
Amount: vout.Amount,
CreatedAt: createdAt,
utxos = append(utxos, types.Utxo{
Txid: tx.Txid,
VOut: uint32(i),
Amount: vout.Amount,
CreatedAt: createdAt,
Descriptor: addr.Descriptor,
})
}
}
@@ -708,13 +662,13 @@ func (a *covenantArkClient) getAllBoardingUtxos(ctx context.Context) ([]explorer
return utxos, nil
}
func (a *covenantArkClient) getClaimableBoardingUtxos(ctx context.Context) ([]explorer.Utxo, error) {
func (a *covenantArkClient) getClaimableBoardingUtxos(ctx context.Context, opts *CoinSelectOptions) ([]types.Utxo, error) {
_, boardingAddrs, _, err := a.wallet.GetAddresses(ctx)
if err != nil {
return nil, err
}
claimable := make([]explorer.Utxo, 0)
claimable := make([]types.Utxo, 0)
for _, addr := range boardingAddrs {
boardingScript, err := tree.ParseVtxoScript(addr.Descriptor)
@@ -738,8 +692,25 @@ func (a *covenantArkClient) getClaimableBoardingUtxos(ctx context.Context) ([]ex
now := time.Now()
for _, utxo := range boardingUtxos {
u := utxo.ToUtxo(boardingTimeout)
if opts != nil && len(opts.OutpointsFilter) > 0 {
utxoOutpoint := client.Outpoint{
Txid: utxo.Txid,
VOut: utxo.Vout,
}
found := false
for _, outpoint := range opts.OutpointsFilter {
if outpoint.Equals(utxoOutpoint) {
found = true
break
}
}
if !found {
continue
}
}
u := utxo.ToUtxo(boardingTimeout, addr.Descriptor)
if u.SpendableAt.Before(now) {
continue
}
@@ -906,84 +877,123 @@ func (a *covenantArkClient) sendOnchain(
}
func (a *covenantArkClient) sendOffchain(
ctx context.Context, withExpiryCoinselect bool, receivers []Receiver,
ctx context.Context,
withExpiryCoinselect bool, receivers []Receiver,
) (string, error) {
if a.wallet.IsLocked() {
return "", fmt.Errorf("wallet is locked")
}
expectedAspPubKey := schnorr.SerializePubKey(a.AspPubkey)
outputs := make([]client.Output, 0)
sumOfReceivers := uint64(0)
// validate receivers and create outputs
for _, receiver := range receivers {
rcvAddr, err := common.DecodeAddress(receiver.To())
if err != nil {
return "", fmt.Errorf("invalid receiver address: %s", err)
}
rcvAspPubKey := schnorr.SerializePubKey(rcvAddr.Asp)
if !bytes.Equal(expectedAspPubKey, rcvAspPubKey) {
return "", fmt.Errorf("invalid receiver address '%s': expected ASP %s, got %s", receiver.To(), hex.EncodeToString(expectedAspPubKey), hex.EncodeToString(rcvAspPubKey))
}
if receiver.Amount() < a.Dust {
return "", fmt.Errorf("invalid amount (%d), must be greater than dust %d", receiver.Amount(), a.Dust)
}
outputs = append(outputs, client.Output{
Address: receiver.To(),
Amount: receiver.Amount(),
})
sumOfReceivers += receiver.Amount()
}
offchainAddrs, _, _, err := a.wallet.GetAddresses(ctx)
if err != nil {
return "", err
}
if len(offchainAddrs) <= 0 {
return "", fmt.Errorf("no funds detected")
}
expectedAspPubkey := schnorr.SerializePubKey(a.AspPubkey)
receiversOutput := make([]client.Output, 0)
sumOfReceivers := uint64(0)
for _, receiver := range receivers {
rcvAddr, err := common.DecodeAddress(receiver.To())
if err != nil {
return "", fmt.Errorf("invalid receiver address: %s", err)
}
rcvAspPubkey := schnorr.SerializePubKey(rcvAddr.Asp)
if !bytes.Equal(rcvAspPubkey, expectedAspPubkey) {
return "", fmt.Errorf("invalid receiver address '%s': expected ASP %s, got %s", receiver.To(), hex.EncodeToString(expectedAspPubkey), hex.EncodeToString(rcvAspPubkey))
}
if receiver.Amount() < a.Dust {
return "", fmt.Errorf("invalid amount (%d), must be greater than dust %d", receiver.Amount(), a.Dust)
}
receiversOutput = append(receiversOutput, client.Output{
Address: receiver.To(),
Amount: receiver.Amount(),
})
sumOfReceivers += receiver.Amount()
return "", fmt.Errorf("no offchain addresses found")
}
vtxos := make([]client.DescriptorVtxo, 0)
for _, offchainAddr := range offchainAddrs {
spendableVtxos, err := a.getVtxos(ctx, offchainAddr.Address, withExpiryCoinselect)
if err != nil {
return "", err
}
for _, vtxo := range spendableVtxos {
vtxos = append(vtxos, client.DescriptorVtxo{
Vtxo: vtxo,
Descriptor: offchainAddr.Descriptor,
})
spendableVtxos, err := a.getVtxos(ctx, withExpiryCoinselect, nil)
if err != nil {
return "", err
}
for _, offchainAddr := range offchainAddrs {
for _, v := range spendableVtxos {
vtxoAddr, err := v.Address(a.AspPubkey, a.Network)
if err != nil {
return "", err
}
if vtxoAddr == offchainAddr.Address {
vtxos = append(vtxos, client.DescriptorVtxo{
Vtxo: v,
Descriptor: offchainAddr.Descriptor,
})
}
}
}
selectedCoins, changeAmount, err := utils.CoinSelect(
vtxos, sumOfReceivers, a.Dust, withExpiryCoinselect,
)
boardingUtxos, err := a.getClaimableBoardingUtxos(ctx, nil)
if err != nil {
return "", err
}
var selectedBoardingCoins []types.Utxo
var selectedCoins []client.DescriptorVtxo
var changeAmount uint64
// if no receivers, self send all selected coins
if len(outputs) <= 0 {
selectedBoardingCoins = boardingUtxos
selectedCoins = vtxos
amount := uint64(0)
for _, utxo := range boardingUtxos {
amount += utxo.Amount
}
for _, utxo := range vtxos {
amount += utxo.Amount
}
outputs = append(outputs, client.Output{
Address: offchainAddrs[0].Address,
Amount: amount,
})
changeAmount = 0
} else {
selectedBoardingCoins, selectedCoins, changeAmount, err = utils.CoinSelect(
boardingUtxos, vtxos, sumOfReceivers, a.Dust, withExpiryCoinselect,
)
if err != nil {
return "", err
}
}
if changeAmount > 0 {
offchainAddr, _, err := a.wallet.NewAddress(ctx, true)
if err != nil {
return "", err
}
changeReceiver := client.Output{
outputs = append(outputs, client.Output{
Address: offchainAddr.Address,
Amount: changeAmount,
}
receiversOutput = append(receiversOutput, changeReceiver)
})
}
inputs := make([]client.Input, 0, len(selectedCoins))
inputs := make([]client.Input, 0, len(selectedCoins)+len(selectedBoardingCoins))
for _, coin := range selectedCoins {
inputs = append(inputs, client.Input{
Outpoint: client.Outpoint{
@@ -993,16 +1003,23 @@ func (a *covenantArkClient) sendOffchain(
Descriptor: coin.Descriptor,
})
}
for _, coin := range selectedBoardingCoins {
inputs = append(inputs, client.Input{
Outpoint: client.Outpoint{
Txid: coin.Txid,
VOut: coin.VOut,
},
Descriptor: coin.Descriptor,
})
}
paymentID, err := a.client.RegisterInputsForNextRound(
ctx, inputs, "", // ephemeralPublicKey is not required for covenant
)
paymentID, err := a.client.RegisterInputsForNextRound(ctx, inputs, "")
if err != nil {
return "", err
}
if err := a.client.RegisterOutputsForNextRound(
ctx, paymentID, receiversOutput,
ctx, paymentID, outputs,
); err != nil {
return "", err
}
@@ -1010,7 +1027,7 @@ func (a *covenantArkClient) sendOffchain(
log.Infof("payment registered with id: %s", paymentID)
poolTxID, err := a.handleRoundStream(
ctx, paymentID, selectedCoins, nil, "", receiversOutput,
ctx, paymentID, selectedCoins, boardingUtxos, outputs,
)
if err != nil {
return "", err
@@ -1023,7 +1040,7 @@ func (a *covenantArkClient) sendOffchain(
func (a *covenantArkClient) addInputs(
ctx context.Context,
updater *psetv2.Updater,
utxos []explorer.Utxo,
utxos []types.Utxo,
) error {
// TODO works only with single-key wallet
offchain, _, err := a.wallet.NewAddress(ctx, false)
@@ -1055,7 +1072,7 @@ func (a *covenantArkClient) addInputs(
if err := updater.AddInputs([]psetv2.InputArgs{
{
Txid: utxo.Txid,
TxIndex: utxo.Vout,
TxIndex: utxo.VOut,
Sequence: sequence,
},
}); err != nil {
@@ -1113,8 +1130,7 @@ func (a *covenantArkClient) handleRoundStream(
ctx context.Context,
paymentID string,
vtxosToSign []client.DescriptorVtxo,
boardingUtxos []explorer.Utxo,
boardingDescriptor string,
boardingUtxos []types.Utxo,
receivers []client.Output,
) (string, error) {
eventsCh, close, err := a.client.GetEventStream(ctx, paymentID)
@@ -1151,7 +1167,7 @@ func (a *covenantArkClient) handleRoundStream(
log.Info("a round finalization started")
signedForfeitTxs, signedRoundTx, err := a.handleRoundFinalization(
ctx, event.(client.RoundFinalizationEvent), vtxosToSign, boardingUtxos, boardingDescriptor, receivers,
ctx, event.(client.RoundFinalizationEvent), vtxosToSign, boardingUtxos, receivers,
)
if err != nil {
return "", err
@@ -1178,8 +1194,7 @@ func (a *covenantArkClient) handleRoundFinalization(
ctx context.Context,
event client.RoundFinalizationEvent,
vtxos []client.DescriptorVtxo,
boardingUtxos []explorer.Utxo,
boardingDescriptor string,
boardingUtxos []types.Utxo,
receivers []client.Output,
) (signedForfeits []string, signedRoundTx string, err error) {
if err = a.validateCongestionTree(event, receivers); err != nil {
@@ -1193,13 +1208,23 @@ func (a *covenantArkClient) handleRoundFinalization(
}
}
if len(boardingUtxos) > 0 {
boardingVtxoScript, err := tree.ParseVtxoScript(boardingDescriptor)
if err != nil {
return nil, "", err
}
// if no boarding utxos inputs, we don't need to sign the round transaction
if len(boardingUtxos) <= 0 {
return
}
roundPtx, err := psetv2.NewPsetFromBase64(event.Tx)
roundPtx, err := psetv2.NewPsetFromBase64(event.Tx)
if err != nil {
return nil, "", err
}
updater, err := psetv2.NewUpdater(roundPtx)
if err != nil {
return nil, "", err
}
for _, boardingUtxo := range boardingUtxos {
boardingVtxoScript, err := tree.ParseVtxoScript(boardingUtxo.Descriptor)
if err != nil {
return nil, "", err
}
@@ -1213,7 +1238,7 @@ func (a *covenantArkClient) handleRoundFinalization(
AspPubkey: a.AspPubkey,
}
default:
return nil, "", fmt.Errorf("unsupported boarding descriptor: %s", boardingDescriptor)
return nil, "", fmt.Errorf("unsupported boarding descriptor: %s", boardingUtxo.Descriptor)
}
forfeitLeaf, err := forfeitClosure.Leaf()
@@ -1241,31 +1266,25 @@ func (a *covenantArkClient) handleRoundFinalization(
ControlBlock: *ctrlBlock,
}
updater, err := psetv2.NewUpdater(roundPtx)
if err != nil {
return nil, "", err
}
for i, input := range updater.Pset.Inputs {
for _, boardingUtxo := range boardingUtxos {
if chainhash.Hash(input.PreviousTxid).String() == boardingUtxo.Txid && boardingUtxo.Vout == input.PreviousTxIndex {
if err := updater.AddInTapLeafScript(i, tapscript); err != nil {
return nil, "", err
}
break
if chainhash.Hash(input.PreviousTxid).String() == boardingUtxo.Txid && boardingUtxo.VOut == input.PreviousTxIndex {
if err := updater.AddInTapLeafScript(i, tapscript); err != nil {
return nil, "", err
}
break
}
}
b64, err := updater.Pset.ToBase64()
if err != nil {
return nil, "", err
}
}
signedRoundTx, err = a.wallet.SignTransaction(ctx, a.explorer, b64)
if err != nil {
return nil, "", err
}
b64, err := updater.Pset.ToBase64()
if err != nil {
return nil, "", err
}
signedRoundTx, err = a.wallet.SignTransaction(ctx, a.explorer, b64)
if err != nil {
return nil, "", err
}
return signedForfeits, signedRoundTx, nil
@@ -1518,8 +1537,8 @@ func (a *covenantArkClient) createAndSignForfeits(
}
func (a *covenantArkClient) coinSelectOnchain(
ctx context.Context, targetAmount uint64, exclude []explorer.Utxo,
) ([]explorer.Utxo, uint64, error) {
ctx context.Context, targetAmount uint64, exclude []types.Utxo,
) ([]types.Utxo, uint64, error) {
_, boardingAddrs, redemptionAddrs, err := a.wallet.GetAddresses(ctx)
if err != nil {
return nil, 0, err
@@ -1527,7 +1546,7 @@ func (a *covenantArkClient) coinSelectOnchain(
now := time.Now()
fetchedUtxos := make([]explorer.Utxo, 0)
fetchedUtxos := make([]types.Utxo, 0)
for _, addr := range boardingAddrs {
boardingDescriptor := addr.Descriptor
@@ -1550,14 +1569,14 @@ func (a *covenantArkClient) coinSelectOnchain(
}
for _, utxo := range utxos {
u := utxo.ToUtxo(boardingTimeout)
u := utxo.ToUtxo(boardingTimeout, addr.Descriptor)
if u.SpendableAt.Before(now) {
fetchedUtxos = append(fetchedUtxos, u)
}
}
}
selected := make([]explorer.Utxo, 0)
selected := make([]types.Utxo, 0)
selectedAmount := uint64(0)
for _, utxo := range fetchedUtxos {
if selectedAmount >= targetAmount {
@@ -1565,7 +1584,7 @@ func (a *covenantArkClient) coinSelectOnchain(
}
for _, excluded := range exclude {
if utxo.Txid == excluded.Txid && utxo.Vout == excluded.Vout {
if utxo.Txid == excluded.Txid && utxo.VOut == excluded.VOut {
continue
}
}
@@ -1578,7 +1597,7 @@ func (a *covenantArkClient) coinSelectOnchain(
return selected, selectedAmount - targetAmount, nil
}
fetchedUtxos = make([]explorer.Utxo, 0)
fetchedUtxos = make([]types.Utxo, 0)
for _, addr := range redemptionAddrs {
utxos, err := a.explorer.GetUtxos(addr.Address)
if err != nil {
@@ -1586,7 +1605,7 @@ func (a *covenantArkClient) coinSelectOnchain(
}
for _, utxo := range utxos {
u := utxo.ToUtxo(uint(a.UnilateralExitDelay))
u := utxo.ToUtxo(uint(a.UnilateralExitDelay), addr.Descriptor)
if u.SpendableAt.Before(now) {
fetchedUtxos = append(fetchedUtxos, u)
}
@@ -1599,7 +1618,7 @@ func (a *covenantArkClient) coinSelectOnchain(
}
for _, excluded := range exclude {
if utxo.Txid == excluded.Txid && utxo.Vout == excluded.Vout {
if utxo.Txid == excluded.Txid && utxo.VOut == excluded.VOut {
continue
}
}
@@ -1648,11 +1667,11 @@ func (a *covenantArkClient) getRedeemBranches(
}
func (a *covenantArkClient) getOffchainBalance(
ctx context.Context, addr string, computeVtxoExpiration bool,
ctx context.Context, computeVtxoExpiration bool,
) (uint64, map[int64]uint64, error) {
amountByExpiration := make(map[int64]uint64, 0)
vtxos, err := a.getVtxos(ctx, addr, computeVtxoExpiration)
vtxos, err := a.getVtxos(ctx, computeVtxoExpiration, nil)
if err != nil {
return 0, nil, err
}
@@ -1675,18 +1694,24 @@ func (a *covenantArkClient) getOffchainBalance(
}
func (a *covenantArkClient) getVtxos(
ctx context.Context, addr string, computeVtxoExpiration bool,
ctx context.Context,
withExpiryCoinselect bool, opts *CoinSelectOptions,
) ([]client.Vtxo, error) {
vtxos, _, err := a.client.ListVtxos(ctx, addr)
spendableVtxos, _, err := a.ListVtxos(ctx)
if err != nil {
return nil, err
}
if !computeVtxoExpiration {
return vtxos, nil
if opts != nil && len(opts.OutpointsFilter) > 0 {
spendableVtxos = filterByOutpoints(spendableVtxos, opts.OutpointsFilter)
}
redeemBranches, err := a.getRedeemBranches(ctx, vtxos)
if opts == nil || !opts.WithExpirySorting {
return spendableVtxos, nil
}
// if sorting by expiry is required, we need to get the expiration date of each vtxo
redeemBranches, err := a.getRedeemBranches(ctx, spendableVtxos)
if err != nil {
return nil, err
}
@@ -1697,75 +1722,24 @@ func (a *covenantArkClient) getVtxos(
return nil, err
}
for i, vtxo := range vtxos {
for i, vtxo := range spendableVtxos {
if vtxo.Txid == vtxoTxid {
vtxos[i].ExpiresAt = expiration
spendableVtxos[i].ExpiresAt = expiration
break
}
}
}
return vtxos, nil
}
func (a *covenantArkClient) selfTransferAllPendingPayments(
ctx context.Context, boardingUtxos []explorer.Utxo, myself client.Output, boardingDescriptor string,
) (string, error) {
inputs := make([]client.Input, 0, len(boardingUtxos))
for _, utxo := range boardingUtxos {
inputs = append(inputs, client.Input{
Outpoint: client.Outpoint{
Txid: utxo.Txid,
VOut: utxo.Vout,
},
Descriptor: boardingDescriptor,
})
}
outputs := []client.Output{myself}
paymentID, err := a.client.RegisterInputsForNextRound(ctx, inputs, "") // ephemeralPublicKey is not required for covenant
if err != nil {
return "", err
}
if err := a.client.RegisterOutputsForNextRound(ctx, paymentID, outputs); err != nil {
return "", err
}
roundTxid, err := a.handleRoundStream(
ctx, paymentID, make([]client.DescriptorVtxo, 0), boardingUtxos, boardingDescriptor, outputs,
)
if err != nil {
return "", err
}
return roundTxid, nil
return spendableVtxos, nil
}
func (a *covenantArkClient) getBoardingTxs(ctx context.Context) (transactions []types.Transaction) {
utxos, err := a.getClaimableBoardingUtxos(ctx)
if err != nil {
return nil
}
isPending := make(map[string]bool)
for _, u := range utxos {
isPending[u.Txid] = true
}
allUtxos, err := a.getAllBoardingUtxos(ctx)
if err != nil {
return nil
}
for _, u := range allUtxos {
pending := false
if isPending[u.Txid] {
pending = true
}
transactions = append(transactions, types.Transaction{
TransactionKey: types.TransactionKey{
BoardingTxid: u.Txid,
@@ -1773,7 +1747,6 @@ func (a *covenantArkClient) getBoardingTxs(ctx context.Context) (transactions []
Amount: u.Amount,
Type: types.TxReceived,
CreatedAt: u.CreatedAt,
IsPending: pending,
})
}
return
@@ -1796,9 +1769,7 @@ func vtxosToTxsCovenant(
for _, v := range append(spendable, spent...) {
// get vtxo amount
amount := int(v.Amount)
if !v.Pending {
continue
}
// find other spent vtxos that spent this one
relatedVtxos := findVtxosBySpentBy(spent, v.Txid)
for _, r := range relatedVtxos {

File diff suppressed because it is too large Load Diff

View File

@@ -55,8 +55,8 @@ func main() {
onboardAmount := uint64(1_0000_0000) // 1 BTC
log.Infof("alice is onboarding with %d sats offchain...", onboardAmount)
log.Infof("alice claiming onboarding funds...")
txid, err := aliceArkClient.Claim(ctx)
log.Infof("alice settled the onboard funds...")
txid, err := aliceArkClient.Settle(ctx)
if err != nil {
log.Fatal(err)
}

View File

@@ -84,13 +84,13 @@ func main() {
log.Infof("alice onchain balance: %d", aliceBalance.OnchainBalance.SpendableAmount)
log.Infof("alice offchain balance: %d", aliceBalance.OffchainBalance.Total)
log.Infof("alice claiming onboarding funds...")
txid, err := aliceArkClient.Claim(ctx)
log.Infof("alice is settling the onboard funds...")
txid, err := aliceArkClient.Settle(ctx)
if err != nil {
log.Fatal(err)
}
log.Infof("alice claimed onboarding funds in round %s", txid)
log.Infof("alice settled the onboard funds in round %s", txid)
fmt.Println("")
log.Info("bob is setting up his ark wallet...")
@@ -128,7 +128,7 @@ func main() {
fmt.Println("")
log.Infof("alice is sending %d sats to bob offchain...", amount)
if _, err = aliceArkClient.SendAsync(ctx, false, receivers); err != nil {
if _, err = aliceArkClient.SendOffChain(ctx, false, receivers); err != nil {
log.Fatal(err)
}
@@ -158,13 +158,13 @@ func main() {
log.Infof("bob offchain balance: %d", bobBalance.OffchainBalance.Total)
fmt.Println("")
log.Info("bob is claiming the incoming payment...")
roundTxid, err := bobArkClient.Claim(ctx)
log.Info("bob is settling the received funds...")
roundTxid, err := bobArkClient.Settle(ctx)
if err != nil {
log.Fatal(err)
}
log.Infof("bob claimed the incoming payment in round %s", roundTxid)
log.Infof("bob settled the received funds in round %s", roundTxid)
time.Sleep(500 * time.Second)
}

View File

@@ -90,19 +90,19 @@
}
}
async function claimVtxos() {
async function settleVtxos() {
const password = document.getElementById("c_password").value;
if (!password) {
logMessage("Claim error: password is required");
logMessage("Settle error: password is required");
return;
}
try {
await unlock(password);
const txID = await claim();
logMessage("Claimed money with tx ID: " + txID);
const txID = await settle();
logMessage("Settled money with tx ID: " + txID);
} catch (err) {
logMessage("Claim error: " + err.message);
logMessage("Settle error: " + err.message);
} finally {
await lock(password);
}
@@ -166,7 +166,7 @@
<input type="password" id="s_password" placeholder="password">
</div>
<div>
<button onclick="claimVtxos()">Claim</button>
<button onclick="settleVtxos()">Settle</button>
<input type="password" id="c_password" placeholder="password">
</div>
<div>

View File

@@ -13,6 +13,7 @@ import (
"github.com/ark-network/ark/common"
"github.com/ark-network/ark/pkg/client-sdk/internal/utils"
"github.com/ark-network/ark/pkg/client-sdk/types"
"github.com/btcsuite/btcd/btcutil/psbt"
"github.com/btcsuite/btcd/wire"
"github.com/vulpemventures/go-elements/psetv2"
@@ -24,39 +25,6 @@ const (
LiquidExplorer = "liquid"
)
type Utxo struct {
Txid string
Vout uint32
Amount uint64
Asset string // liquid only
Delay uint
SpendableAt time.Time
CreatedAt time.Time
}
func (u *Utxo) Sequence() (uint32, error) {
return common.BIP68Sequence(u.Delay)
}
func newUtxo(explorerUtxo ExplorerUtxo, delay uint) Utxo {
utxoTime := explorerUtxo.Status.Blocktime
createdAt := time.Unix(utxoTime, 0)
if utxoTime == 0 {
createdAt = time.Time{}
utxoTime = time.Now().Unix()
}
return Utxo{
Txid: explorerUtxo.Txid,
Vout: explorerUtxo.Vout,
Amount: explorerUtxo.Amount,
Asset: explorerUtxo.Asset,
Delay: delay,
SpendableAt: time.Unix(utxoTime, 0).Add(time.Duration(delay) * time.Second),
CreatedAt: createdAt,
}
}
type ExplorerTx struct {
Txid string `json:"txid"`
Vout []struct {
@@ -85,8 +53,8 @@ type SpentStatus struct {
SpentBy string `json:"txid,omitempty"`
}
func (e ExplorerUtxo) ToUtxo(delay uint) Utxo {
return newUtxo(e, delay)
func (e ExplorerUtxo) ToUtxo(delay uint, descriptor string) types.Utxo {
return newUtxo(e, delay, descriptor)
}
type Explorer interface {
@@ -446,3 +414,23 @@ func parseBitcoinTx(txStr string) (string, string, error) {
return txhex, txid, nil
}
func newUtxo(explorerUtxo ExplorerUtxo, delay uint, descriptor string) types.Utxo {
utxoTime := explorerUtxo.Status.Blocktime
createdAt := time.Unix(utxoTime, 0)
if utxoTime == 0 {
createdAt = time.Time{}
utxoTime = time.Now().Unix()
}
return types.Utxo{
Txid: explorerUtxo.Txid,
VOut: explorerUtxo.Vout,
Amount: explorerUtxo.Amount,
Asset: explorerUtxo.Asset,
Delay: delay,
SpendableAt: time.Unix(utxoTime, 0).Add(time.Duration(delay) * time.Second),
CreatedAt: createdAt,
Descriptor: descriptor,
}
}

View File

@@ -12,6 +12,7 @@ import (
"github.com/ark-network/ark/common"
"github.com/ark-network/ark/pkg/client-sdk/client"
"github.com/ark-network/ark/pkg/client-sdk/types"
"github.com/btcsuite/btcd/btcec/v2"
"github.com/btcsuite/btcd/btcutil"
"github.com/btcsuite/btcd/chaincfg"
@@ -23,10 +24,14 @@ import (
)
func CoinSelect(
vtxos []client.DescriptorVtxo, amount, dust uint64, sortByExpirationTime bool,
) ([]client.DescriptorVtxo, uint64, error) {
selected := make([]client.DescriptorVtxo, 0)
notSelected := make([]client.DescriptorVtxo, 0)
boardingUtxos []types.Utxo,
vtxos []client.DescriptorVtxo,
amount,
dust uint64,
sortByExpirationTime bool,
) ([]types.Utxo, []client.DescriptorVtxo, uint64, error) {
selected, notSelected := make([]client.DescriptorVtxo, 0), make([]client.DescriptorVtxo, 0)
selectedBoarding, notSelectedBoarding := make([]types.Utxo, 0), make([]types.Utxo, 0)
selectedAmount := uint64(0)
if sortByExpirationTime {
@@ -38,6 +43,20 @@ func CoinSelect(
return vtxos[i].ExpiresAt.Before(*vtxos[j].ExpiresAt)
})
sort.SliceStable(boardingUtxos, func(i, j int) bool {
return boardingUtxos[i].SpendableAt.Before(boardingUtxos[j].SpendableAt)
})
}
for _, boardingUtxo := range boardingUtxos {
if selectedAmount >= amount {
notSelectedBoarding = append(notSelectedBoarding, boardingUtxo)
break
}
selectedBoarding = append(selectedBoarding, boardingUtxo)
selectedAmount += boardingUtxo.Amount
}
for _, vtxo := range vtxos {
@@ -51,7 +70,7 @@ func CoinSelect(
}
if selectedAmount < amount {
return nil, 0, fmt.Errorf("not enough funds to cover amount %d", amount)
return nil, nil, 0, fmt.Errorf("not enough funds to cover amount %d", amount)
}
change := selectedAmount - amount
@@ -60,10 +79,13 @@ func CoinSelect(
if len(notSelected) > 0 {
selected = append(selected, notSelected[0])
change += notSelected[0].Amount
} else if len(notSelectedBoarding) > 0 {
selectedBoarding = append(selectedBoarding, notSelectedBoarding[0])
change += notSelectedBoarding[0].Amount
}
}
return selected, change, nil
return selectedBoarding, selected, change, nil
}
func ParseLiquidAddress(addr string) (

View File

@@ -3,6 +3,7 @@ package arksdk
import (
"fmt"
"github.com/ark-network/ark/pkg/client-sdk/client"
grpcclient "github.com/ark-network/ark/pkg/client-sdk/client/grpc"
restclient "github.com/ark-network/ark/pkg/client-sdk/client/rest"
"github.com/ark-network/ark/pkg/client-sdk/internal/utils"
@@ -123,3 +124,10 @@ type balanceRes struct {
offchainBalanceByExpiration map[int64]uint64
err error
}
type CoinSelectOptions struct {
// If true, coin selector will select coins closest to expiry first.
WithExpirySorting bool
// If specified, coin selector will select only coins in the list.
OutpointsFilter []client.Outpoint
}

View File

@@ -73,7 +73,7 @@ type Transaction struct {
TransactionKey
Amount uint64
Type TxType
IsPending bool
Settled bool
CreatedAt time.Time
}
@@ -103,3 +103,19 @@ type TransactionEvent struct {
Tx Transaction
Event EventType
}
type Utxo struct {
Txid string
VOut uint32
Amount uint64
Asset string // liquid only
Delay uint
SpendableAt time.Time
CreatedAt time.Time
Descriptor string
Spent bool
}
func (u *Utxo) Sequence() (uint32, error) {
return common.BIP68Sequence(u.Delay)
}

View File

@@ -31,7 +31,7 @@ func init() {
js.Global().Set("sendOnChain", SendOnChainWrapper())
js.Global().Set("sendOffChain", SendOffChainWrapper())
js.Global().Set("sendAsync", SendAsyncWrapper())
js.Global().Set("claim", ClaimWrapper())
js.Global().Set("settle", SettleWrapper())
js.Global().Set("unilateralRedeem", UnilateralRedeemWrapper())
js.Global().Set("collaborativeRedeem", CollaborativeRedeemWrapper())
js.Global().Set("getTransactionHistory", GetTransactionHistoryWrapper())

View File

@@ -14,6 +14,7 @@ import (
"time"
arksdk "github.com/ark-network/ark/pkg/client-sdk"
"github.com/ark-network/ark/pkg/client-sdk/client"
"github.com/ark-network/ark/pkg/client-sdk/wallet"
singlekeywallet "github.com/ark-network/ark/pkg/client-sdk/wallet/singlekey"
)
@@ -186,17 +187,17 @@ func SendOnChainWrapper() js.Func {
if len(args) != 1 {
return nil, errors.New("invalid number of args")
}
receivers := make([]arksdk.Receiver, args[0].Length())
for i := 0; i < args[0].Length(); i++ {
receiver := args[0].Index(i)
receivers[i] = arksdk.NewBitcoinReceiver(
receiver.Get("To").String(), uint64(receiver.Get("Amount").Int()),
)
receivers, err := parseReceivers(args[0])
if err != nil {
return nil, err
}
txID, err := arkSdkClient.SendOnChain(
context.Background(), receivers,
)
if receivers == nil || len(receivers) == 0 {
return nil, errors.New("no receivers specified")
}
txID, err := arkSdkClient.SendOnChain(context.Background(), receivers)
if err != nil {
return nil, err
}
@@ -209,13 +210,11 @@ func SendOffChainWrapper() js.Func {
if len(args) != 2 {
return nil, errors.New("invalid number of args")
}
withExpiryCoinselect := args[0].Bool()
receivers := make([]arksdk.Receiver, args[1].Length())
for i := 0; i < args[1].Length(); i++ {
receiver := args[1].Index(i)
receivers[i] = arksdk.NewBitcoinReceiver(
receiver.Get("To").String(), uint64(receiver.Get("Amount").Int()),
)
receivers, err := parseReceivers(args[0])
if err != nil {
return nil, err
}
txID, err := arkSdkClient.SendOffChain(
@@ -233,13 +232,15 @@ func SendAsyncWrapper() js.Func {
if len(args) != 2 {
return nil, errors.New("invalid number of args")
}
withExpiryCoinselect := args[0].Bool()
receivers := make([]arksdk.Receiver, args[1].Length())
for i := 0; i < args[1].Length(); i++ {
receiver := args[1].Index(i)
receivers[i] = arksdk.NewBitcoinReceiver(
receiver.Get("To").String(), uint64(receiver.Get("Amount").Int()),
)
receivers, err := parseReceivers(args[0])
if err != nil {
return nil, err
}
if receivers == nil || len(receivers) == 0 {
return nil, errors.New("no receivers specified")
}
txID, err := arkSdkClient.SendAsync(
@@ -252,13 +253,13 @@ func SendAsyncWrapper() js.Func {
})
}
func ClaimWrapper() js.Func {
func SettleWrapper() js.Func {
return JSPromise(func(args []js.Value) (interface{}, error) {
if len(args) != 0 {
return nil, errors.New("invalid number of args")
}
resp, err := arkSdkClient.Claim(context.Background())
resp, err := arkSdkClient.Settle(context.Background())
if err != nil {
return nil, err
}
@@ -306,7 +307,7 @@ func GetTransactionHistoryWrapper() js.Func {
"redeemTxid": record.RedeemTxid,
"amount": strconv.Itoa(int(record.Amount)),
"type": record.Type,
"isPending": record.IsPending,
"settled": record.Settled,
"createdAt": record.CreatedAt.Format(time.RFC3339),
})
}
@@ -433,3 +434,63 @@ func JSPromise(fn promise) js.Func {
return promiseConstructor.New(handler)
})
}
func parseReceivers(jsReceivers js.Value) ([]arksdk.Receiver, error) {
if jsReceivers.IsNull() || jsReceivers.IsUndefined() {
return nil, nil // Return nil slice if input is null or undefined
}
if jsReceivers.Type() != js.TypeObject || jsReceivers.Get("length").Type() != js.TypeNumber {
return nil, errors.New("invalid receivers argument: expected array")
}
length := jsReceivers.Length()
if length == 0 {
return []arksdk.Receiver{}, nil // Return empty slice if input array is empty
}
receivers := make([]arksdk.Receiver, length)
for i := 0; i < length; i++ {
receiver := jsReceivers.Index(i)
if receiver.Type() != js.TypeObject {
return nil, fmt.Errorf("invalid receiver at index %d: expected object", i)
}
to := receiver.Get("To")
amount := receiver.Get("Amount")
if to.Type() != js.TypeString || amount.Type() != js.TypeNumber {
return nil, fmt.Errorf("invalid receiver at index %d: expected 'To' (string) and 'Amount' (number)", i)
}
receivers[i] = arksdk.NewBitcoinReceiver(to.String(), uint64(amount.Int()))
}
return receivers, nil
}
func parseOutpoints(jsOutpoints js.Value) ([]client.Outpoint, error) {
if jsOutpoints.Length() == 0 {
return nil, nil
}
outpoints := make([]client.Outpoint, jsOutpoints.Length())
for i := 0; i < jsOutpoints.Length(); i++ {
jsOutpoint := jsOutpoints.Index(i)
if jsOutpoint.Type() != js.TypeObject {
return nil, fmt.Errorf("invalid outpoint at index %d: expected object", i)
}
txid := jsOutpoint.Get("Txid")
vout := jsOutpoint.Get("Vout")
if txid.Type() != js.TypeString || vout.Type() != js.TypeNumber {
return nil, fmt.Errorf("invalid outpoint at index %d: expected 'Txid' (string) and 'Vout' (number)", i)
}
outpoints[i] = client.Outpoint{
Txid: txid.String(),
VOut: uint32(vout.Int()),
}
}
return outpoints, nil
}

View File

@@ -39,5 +39,3 @@ func PrintBuildInfo() {
func GetVersion() string {
return Version
}
// You can add more build-related functions here as needed

View File

@@ -21,8 +21,3 @@ func main() {
println("ARK SDK WebAssembly module initialized")
<-c
}
func init() {
// You can add any additional initialization here if needed
// This runs before the main function
}

View File

@@ -23,8 +23,8 @@ help:
## intergrationtest: runs integration tests
integrationtest:
@echo "Running integration tests..."
@go test -v -count 1 -timeout 400s github.com/ark-network/ark/server/test/e2e/covenant
@go test -v -count 1 -timeout 400s github.com/ark-network/ark/server/test/e2e/covenantless
@go test -v -count 1 -timeout 500s github.com/ark-network/ark/server/test/e2e/covenant
@go test -v -count 1 -timeout 500s github.com/ark-network/ark/server/test/e2e/covenantless
## lint: lint codebase
lint:

View File

@@ -51,10 +51,7 @@ type covenantlessService struct {
currentRoundLock sync.Mutex
currentRound *domain.Round
treeSigningSessions map[string]*musigSigningSession
asyncPaymentsCache map[string]struct { // redeem txid -> receivers
receivers []domain.Receiver
expireAt int64
}
asyncPaymentsCache map[string]asyncPaymentData
}
func NewCovenantlessService(
@@ -69,11 +66,6 @@ func NewCovenantlessService(
return nil, fmt.Errorf("failed to fetch pubkey: %s", err)
}
asyncPaymentsCache := make(map[string]struct {
receivers []domain.Receiver
expireAt int64
})
svc := &covenantlessService{
network: network,
pubkey: pubkey,
@@ -90,7 +82,7 @@ func NewCovenantlessService(
eventsCh: make(chan domain.RoundEvent),
transactionEventsCh: make(chan TransactionEvent),
currentRoundLock: sync.Mutex{},
asyncPaymentsCache: asyncPaymentsCache,
asyncPaymentsCache: make(map[string]asyncPaymentData),
treeSigningSessions: make(map[string]*musigSigningSession),
boardingExitDelay: boardingExitDelay,
}
@@ -269,19 +261,16 @@ func (s *covenantlessService) CompleteAsyncPayment(
vtxoPubkey := hex.EncodeToString(schnorr.SerializePubKey(vtxoTapKey))
// all pending except the last one
isPending := outIndex < len(asyncPayData.receivers)-1
vtxos = append(vtxos, domain.Vtxo{
VtxoKey: domain.VtxoKey{
Txid: redeemTxid,
VOut: uint32(outIndex),
},
Pubkey: vtxoPubkey,
Amount: uint64(out.Value),
ExpireAt: asyncPayData.expireAt,
RedeemTx: redeemTx,
Pending: isPending,
Pubkey: vtxoPubkey,
Amount: uint64(out.Value),
ExpireAt: asyncPayData.expireAt,
RoundTxid: asyncPayData.roundTxid,
RedeemTx: redeemTx,
})
}
@@ -338,6 +327,8 @@ func (s *covenantlessService) CreateAsyncPayment(
vtxosInputs := make([]domain.Vtxo, 0, len(inputs))
expiration := vtxos[0].ExpireAt
roundTxid := vtxos[0].RoundTxid
for _, vtxo := range vtxos {
if vtxo.Spent {
return "", fmt.Errorf("all vtxos must be unspent")
@@ -350,11 +341,9 @@ func (s *covenantlessService) CreateAsyncPayment(
if vtxo.Swept {
return "", fmt.Errorf("all vtxos must be swept")
}
if vtxo.Pending {
return "", fmt.Errorf("all vtxos must be claimed")
}
if vtxo.ExpireAt < expiration {
roundTxid = vtxo.RoundTxid
expiration = vtxo.ExpireAt
}
@@ -373,12 +362,10 @@ func (s *covenantlessService) CreateAsyncPayment(
return "", fmt.Errorf("failed to parse redeem tx: %s", err)
}
s.asyncPaymentsCache[redeemPtx.UnsignedTx.TxID()] = struct {
receivers []domain.Receiver
expireAt int64
}{
s.asyncPaymentsCache[redeemPtx.UnsignedTx.TxID()] = asyncPaymentData{
receivers: receivers,
expireAt: expiration,
roundTxid: roundTxid,
}
return redeemTx, nil
@@ -1588,6 +1575,12 @@ func findForfeitTxBitcoin(
return "", fmt.Errorf("forfeit tx not found")
}
type asyncPaymentData struct {
receivers []domain.Receiver
expireAt int64
roundTxid string
}
// musigSigningSession holds the state of ephemeral nonces and signatures in order to coordinate the signing of the tree
type musigSigningSession struct {
lock sync.Mutex

View File

@@ -166,6 +166,24 @@ func (m *paymentsMap) update(payment domain.Payment) error {
return fmt.Errorf("payment %s not found", payment.Id)
}
sumOfInputs := uint64(0)
for _, input := range payment.Inputs {
sumOfInputs += input.Amount
}
for _, boardingInput := range p.boardingInputs {
sumOfInputs += boardingInput.Amount
}
sumOfOutputs := uint64(0)
for _, receiver := range payment.Receivers {
sumOfOutputs += receiver.Amount
}
if sumOfInputs != sumOfOutputs {
return fmt.Errorf("sum of inputs %d does not match sum of outputs %d", sumOfInputs, sumOfOutputs)
}
p.Payment = payment
return nil

View File

@@ -123,5 +123,4 @@ type Vtxo struct {
Swept bool
ExpireAt int64
RedeemTx string // empty if in-round vtxo
Pending bool
}

View File

@@ -54,7 +54,6 @@ CREATE TABLE IF NOT EXISTS vtxo (
expire_at INTEGER NOT NULL,
payment_id TEXT,
redeem_tx TEXT,
pending BOOLEAN NOT NULL,
PRIMARY KEY (txid, vout),
FOREIGN KEY (payment_id) REFERENCES payment(id)
);

View File

@@ -33,7 +33,6 @@ type PaymentVtxoVw struct {
ExpireAt sql.NullInt64
PaymentID sql.NullString
RedeemTx sql.NullString
Pending sql.NullBool
}
type Receiver struct {
@@ -100,5 +99,4 @@ type Vtxo struct {
ExpireAt int64
PaymentID sql.NullString
RedeemTx sql.NullString
Pending bool
}

View File

@@ -54,7 +54,7 @@ func (q *Queries) MarkVtxoAsSwept(ctx context.Context, arg MarkVtxoAsSweptParams
}
const selectNotRedeemedVtxos = `-- name: SelectNotRedeemedVtxos :many
SELECT vtxo.txid, vtxo.vout, vtxo.pubkey, vtxo.amount, vtxo.pool_tx, vtxo.spent_by, vtxo.spent, vtxo.redeemed, vtxo.swept, vtxo.expire_at, vtxo.payment_id, vtxo.redeem_tx, vtxo.pending FROM vtxo
SELECT vtxo.txid, vtxo.vout, vtxo.pubkey, vtxo.amount, vtxo.pool_tx, vtxo.spent_by, vtxo.spent, vtxo.redeemed, vtxo.swept, vtxo.expire_at, vtxo.payment_id, vtxo.redeem_tx FROM vtxo
WHERE redeemed = false
`
@@ -84,7 +84,6 @@ func (q *Queries) SelectNotRedeemedVtxos(ctx context.Context) ([]SelectNotRedeem
&i.Vtxo.ExpireAt,
&i.Vtxo.PaymentID,
&i.Vtxo.RedeemTx,
&i.Vtxo.Pending,
); err != nil {
return nil, err
}
@@ -100,7 +99,7 @@ func (q *Queries) SelectNotRedeemedVtxos(ctx context.Context) ([]SelectNotRedeem
}
const selectNotRedeemedVtxosWithPubkey = `-- name: SelectNotRedeemedVtxosWithPubkey :many
SELECT vtxo.txid, vtxo.vout, vtxo.pubkey, vtxo.amount, vtxo.pool_tx, vtxo.spent_by, vtxo.spent, vtxo.redeemed, vtxo.swept, vtxo.expire_at, vtxo.payment_id, vtxo.redeem_tx, vtxo.pending FROM vtxo
SELECT vtxo.txid, vtxo.vout, vtxo.pubkey, vtxo.amount, vtxo.pool_tx, vtxo.spent_by, vtxo.spent, vtxo.redeemed, vtxo.swept, vtxo.expire_at, vtxo.payment_id, vtxo.redeem_tx FROM vtxo
WHERE redeemed = false AND pubkey = ?
`
@@ -130,7 +129,6 @@ func (q *Queries) SelectNotRedeemedVtxosWithPubkey(ctx context.Context, pubkey s
&i.Vtxo.ExpireAt,
&i.Vtxo.PaymentID,
&i.Vtxo.RedeemTx,
&i.Vtxo.Pending,
); err != nil {
return nil, err
}
@@ -209,7 +207,7 @@ SELECT round.id, round.starting_timestamp, round.ending_timestamp, round.ended,
round_payment_vw.id, round_payment_vw.round_id,
round_tx_vw.id, round_tx_vw.tx, round_tx_vw.round_id, round_tx_vw.type, round_tx_vw.position, round_tx_vw.txid, round_tx_vw.tree_level, round_tx_vw.parent_txid, round_tx_vw.is_leaf,
payment_receiver_vw.payment_id, payment_receiver_vw.pubkey, payment_receiver_vw.onchain_address, payment_receiver_vw.amount,
payment_vtxo_vw.txid, payment_vtxo_vw.vout, payment_vtxo_vw.pubkey, payment_vtxo_vw.amount, payment_vtxo_vw.pool_tx, payment_vtxo_vw.spent_by, payment_vtxo_vw.spent, payment_vtxo_vw.redeemed, payment_vtxo_vw.swept, payment_vtxo_vw.expire_at, payment_vtxo_vw.payment_id, payment_vtxo_vw.redeem_tx, payment_vtxo_vw.pending
payment_vtxo_vw.txid, payment_vtxo_vw.vout, payment_vtxo_vw.pubkey, payment_vtxo_vw.amount, payment_vtxo_vw.pool_tx, payment_vtxo_vw.spent_by, payment_vtxo_vw.spent, payment_vtxo_vw.redeemed, payment_vtxo_vw.swept, payment_vtxo_vw.expire_at, payment_vtxo_vw.payment_id, payment_vtxo_vw.redeem_tx
FROM round
LEFT OUTER JOIN round_payment_vw ON round.id=round_payment_vw.round_id
LEFT OUTER JOIN round_tx_vw ON round.id=round_tx_vw.round_id
@@ -275,7 +273,6 @@ func (q *Queries) SelectRoundWithRoundId(ctx context.Context, id string) ([]Sele
&i.PaymentVtxoVw.ExpireAt,
&i.PaymentVtxoVw.PaymentID,
&i.PaymentVtxoVw.RedeemTx,
&i.PaymentVtxoVw.Pending,
); err != nil {
return nil, err
}
@@ -295,7 +292,7 @@ SELECT round.id, round.starting_timestamp, round.ending_timestamp, round.ended,
round_payment_vw.id, round_payment_vw.round_id,
round_tx_vw.id, round_tx_vw.tx, round_tx_vw.round_id, round_tx_vw.type, round_tx_vw.position, round_tx_vw.txid, round_tx_vw.tree_level, round_tx_vw.parent_txid, round_tx_vw.is_leaf,
payment_receiver_vw.payment_id, payment_receiver_vw.pubkey, payment_receiver_vw.onchain_address, payment_receiver_vw.amount,
payment_vtxo_vw.txid, payment_vtxo_vw.vout, payment_vtxo_vw.pubkey, payment_vtxo_vw.amount, payment_vtxo_vw.pool_tx, payment_vtxo_vw.spent_by, payment_vtxo_vw.spent, payment_vtxo_vw.redeemed, payment_vtxo_vw.swept, payment_vtxo_vw.expire_at, payment_vtxo_vw.payment_id, payment_vtxo_vw.redeem_tx, payment_vtxo_vw.pending
payment_vtxo_vw.txid, payment_vtxo_vw.vout, payment_vtxo_vw.pubkey, payment_vtxo_vw.amount, payment_vtxo_vw.pool_tx, payment_vtxo_vw.spent_by, payment_vtxo_vw.spent, payment_vtxo_vw.redeemed, payment_vtxo_vw.swept, payment_vtxo_vw.expire_at, payment_vtxo_vw.payment_id, payment_vtxo_vw.redeem_tx
FROM round
LEFT OUTER JOIN round_payment_vw ON round.id=round_payment_vw.round_id
LEFT OUTER JOIN round_tx_vw ON round.id=round_tx_vw.round_id
@@ -361,7 +358,6 @@ func (q *Queries) SelectRoundWithRoundTxId(ctx context.Context, txid string) ([]
&i.PaymentVtxoVw.ExpireAt,
&i.PaymentVtxoVw.PaymentID,
&i.PaymentVtxoVw.RedeemTx,
&i.PaymentVtxoVw.Pending,
); err != nil {
return nil, err
}
@@ -381,7 +377,7 @@ SELECT round.id, round.starting_timestamp, round.ending_timestamp, round.ended,
round_payment_vw.id, round_payment_vw.round_id,
round_tx_vw.id, round_tx_vw.tx, round_tx_vw.round_id, round_tx_vw.type, round_tx_vw.position, round_tx_vw.txid, round_tx_vw.tree_level, round_tx_vw.parent_txid, round_tx_vw.is_leaf,
payment_receiver_vw.payment_id, payment_receiver_vw.pubkey, payment_receiver_vw.onchain_address, payment_receiver_vw.amount,
payment_vtxo_vw.txid, payment_vtxo_vw.vout, payment_vtxo_vw.pubkey, payment_vtxo_vw.amount, payment_vtxo_vw.pool_tx, payment_vtxo_vw.spent_by, payment_vtxo_vw.spent, payment_vtxo_vw.redeemed, payment_vtxo_vw.swept, payment_vtxo_vw.expire_at, payment_vtxo_vw.payment_id, payment_vtxo_vw.redeem_tx, payment_vtxo_vw.pending
payment_vtxo_vw.txid, payment_vtxo_vw.vout, payment_vtxo_vw.pubkey, payment_vtxo_vw.amount, payment_vtxo_vw.pool_tx, payment_vtxo_vw.spent_by, payment_vtxo_vw.spent, payment_vtxo_vw.redeemed, payment_vtxo_vw.swept, payment_vtxo_vw.expire_at, payment_vtxo_vw.payment_id, payment_vtxo_vw.redeem_tx
FROM round
LEFT OUTER JOIN round_payment_vw ON round.id=round_payment_vw.round_id
LEFT OUTER JOIN round_tx_vw ON round.id=round_tx_vw.round_id
@@ -447,7 +443,6 @@ func (q *Queries) SelectSweepableRounds(ctx context.Context) ([]SelectSweepableR
&i.PaymentVtxoVw.ExpireAt,
&i.PaymentVtxoVw.PaymentID,
&i.PaymentVtxoVw.RedeemTx,
&i.PaymentVtxoVw.Pending,
); err != nil {
return nil, err
}
@@ -463,7 +458,7 @@ func (q *Queries) SelectSweepableRounds(ctx context.Context) ([]SelectSweepableR
}
const selectSweepableVtxos = `-- name: SelectSweepableVtxos :many
SELECT vtxo.txid, vtxo.vout, vtxo.pubkey, vtxo.amount, vtxo.pool_tx, vtxo.spent_by, vtxo.spent, vtxo.redeemed, vtxo.swept, vtxo.expire_at, vtxo.payment_id, vtxo.redeem_tx, vtxo.pending FROM vtxo
SELECT vtxo.txid, vtxo.vout, vtxo.pubkey, vtxo.amount, vtxo.pool_tx, vtxo.spent_by, vtxo.spent, vtxo.redeemed, vtxo.swept, vtxo.expire_at, vtxo.payment_id, vtxo.redeem_tx FROM vtxo
WHERE redeemed = false AND swept = false
`
@@ -493,7 +488,6 @@ func (q *Queries) SelectSweepableVtxos(ctx context.Context) ([]SelectSweepableVt
&i.Vtxo.ExpireAt,
&i.Vtxo.PaymentID,
&i.Vtxo.RedeemTx,
&i.Vtxo.Pending,
); err != nil {
return nil, err
}
@@ -513,7 +507,7 @@ SELECT round.id, round.starting_timestamp, round.ending_timestamp, round.ended,
round_payment_vw.id, round_payment_vw.round_id,
round_tx_vw.id, round_tx_vw.tx, round_tx_vw.round_id, round_tx_vw.type, round_tx_vw.position, round_tx_vw.txid, round_tx_vw.tree_level, round_tx_vw.parent_txid, round_tx_vw.is_leaf,
payment_receiver_vw.payment_id, payment_receiver_vw.pubkey, payment_receiver_vw.onchain_address, payment_receiver_vw.amount,
payment_vtxo_vw.txid, payment_vtxo_vw.vout, payment_vtxo_vw.pubkey, payment_vtxo_vw.amount, payment_vtxo_vw.pool_tx, payment_vtxo_vw.spent_by, payment_vtxo_vw.spent, payment_vtxo_vw.redeemed, payment_vtxo_vw.swept, payment_vtxo_vw.expire_at, payment_vtxo_vw.payment_id, payment_vtxo_vw.redeem_tx, payment_vtxo_vw.pending
payment_vtxo_vw.txid, payment_vtxo_vw.vout, payment_vtxo_vw.pubkey, payment_vtxo_vw.amount, payment_vtxo_vw.pool_tx, payment_vtxo_vw.spent_by, payment_vtxo_vw.spent, payment_vtxo_vw.redeemed, payment_vtxo_vw.swept, payment_vtxo_vw.expire_at, payment_vtxo_vw.payment_id, payment_vtxo_vw.redeem_tx
FROM round
LEFT OUTER JOIN round_payment_vw ON round.id=round_payment_vw.round_id
LEFT OUTER JOIN round_tx_vw ON round.id=round_tx_vw.round_id
@@ -579,7 +573,6 @@ func (q *Queries) SelectSweptRounds(ctx context.Context) ([]SelectSweptRoundsRow
&i.PaymentVtxoVw.ExpireAt,
&i.PaymentVtxoVw.PaymentID,
&i.PaymentVtxoVw.RedeemTx,
&i.PaymentVtxoVw.Pending,
); err != nil {
return nil, err
}
@@ -595,7 +588,7 @@ func (q *Queries) SelectSweptRounds(ctx context.Context) ([]SelectSweptRoundsRow
}
const selectVtxoByOutpoint = `-- name: SelectVtxoByOutpoint :one
SELECT vtxo.txid, vtxo.vout, vtxo.pubkey, vtxo.amount, vtxo.pool_tx, vtxo.spent_by, vtxo.spent, vtxo.redeemed, vtxo.swept, vtxo.expire_at, vtxo.payment_id, vtxo.redeem_tx, vtxo.pending FROM vtxo
SELECT vtxo.txid, vtxo.vout, vtxo.pubkey, vtxo.amount, vtxo.pool_tx, vtxo.spent_by, vtxo.spent, vtxo.redeemed, vtxo.swept, vtxo.expire_at, vtxo.payment_id, vtxo.redeem_tx FROM vtxo
WHERE txid = ? AND vout = ?
`
@@ -624,13 +617,12 @@ func (q *Queries) SelectVtxoByOutpoint(ctx context.Context, arg SelectVtxoByOutp
&i.Vtxo.ExpireAt,
&i.Vtxo.PaymentID,
&i.Vtxo.RedeemTx,
&i.Vtxo.Pending,
)
return i, err
}
const selectVtxosByPoolTxid = `-- name: SelectVtxosByPoolTxid :many
SELECT vtxo.txid, vtxo.vout, vtxo.pubkey, vtxo.amount, vtxo.pool_tx, vtxo.spent_by, vtxo.spent, vtxo.redeemed, vtxo.swept, vtxo.expire_at, vtxo.payment_id, vtxo.redeem_tx, vtxo.pending FROM vtxo
SELECT vtxo.txid, vtxo.vout, vtxo.pubkey, vtxo.amount, vtxo.pool_tx, vtxo.spent_by, vtxo.spent, vtxo.redeemed, vtxo.swept, vtxo.expire_at, vtxo.payment_id, vtxo.redeem_tx FROM vtxo
WHERE pool_tx = ?
`
@@ -660,7 +652,6 @@ func (q *Queries) SelectVtxosByPoolTxid(ctx context.Context, poolTx string) ([]S
&i.Vtxo.ExpireAt,
&i.Vtxo.PaymentID,
&i.Vtxo.RedeemTx,
&i.Vtxo.Pending,
); err != nil {
return nil, err
}
@@ -847,8 +838,8 @@ func (q *Queries) UpsertTransaction(ctx context.Context, arg UpsertTransactionPa
}
const upsertVtxo = `-- name: UpsertVtxo :exec
INSERT INTO vtxo (txid, vout, pubkey, amount, pool_tx, spent_by, spent, redeemed, swept, expire_at, redeem_tx, pending)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) ON CONFLICT(txid, vout) DO UPDATE SET
INSERT INTO vtxo (txid, vout, pubkey, amount, pool_tx, spent_by, spent, redeemed, swept, expire_at, redeem_tx)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) ON CONFLICT(txid, vout) DO UPDATE SET
pubkey = EXCLUDED.pubkey,
amount = EXCLUDED.amount,
pool_tx = EXCLUDED.pool_tx,
@@ -857,8 +848,7 @@ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) ON CONFLICT(txid, vout) DO UPDATE SE
redeemed = EXCLUDED.redeemed,
swept = EXCLUDED.swept,
expire_at = EXCLUDED.expire_at,
redeem_tx = EXCLUDED.redeem_tx,
pending = EXCLUDED.pending
redeem_tx = EXCLUDED.redeem_tx
`
type UpsertVtxoParams struct {
@@ -873,7 +863,6 @@ type UpsertVtxoParams struct {
Swept bool
ExpireAt int64
RedeemTx sql.NullString
Pending bool
}
func (q *Queries) UpsertVtxo(ctx context.Context, arg UpsertVtxoParams) error {
@@ -889,7 +878,6 @@ func (q *Queries) UpsertVtxo(ctx context.Context, arg UpsertVtxoParams) error {
arg.Swept,
arg.ExpireAt,
arg.RedeemTx,
arg.Pending,
)
return err
}

View File

@@ -112,8 +112,8 @@ SELECT id FROM round WHERE starting_timestamp > ? AND starting_timestamp < ?;
SELECT id FROM round;
-- name: UpsertVtxo :exec
INSERT INTO vtxo (txid, vout, pubkey, amount, pool_tx, spent_by, spent, redeemed, swept, expire_at, redeem_tx, pending)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) ON CONFLICT(txid, vout) DO UPDATE SET
INSERT INTO vtxo (txid, vout, pubkey, amount, pool_tx, spent_by, spent, redeemed, swept, expire_at, redeem_tx)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) ON CONFLICT(txid, vout) DO UPDATE SET
pubkey = EXCLUDED.pubkey,
amount = EXCLUDED.amount,
pool_tx = EXCLUDED.pool_tx,
@@ -122,8 +122,7 @@ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) ON CONFLICT(txid, vout) DO UPDATE SE
redeemed = EXCLUDED.redeemed,
swept = EXCLUDED.swept,
expire_at = EXCLUDED.expire_at,
redeem_tx = EXCLUDED.redeem_tx,
pending = EXCLUDED.pending;
redeem_tx = EXCLUDED.redeem_tx;
-- name: SelectSweepableVtxos :many
SELECT sqlc.embed(vtxo) FROM vtxo

View File

@@ -51,7 +51,6 @@ func (v *vxtoRepository) AddVtxos(ctx context.Context, vtxos []domain.Vtxo) erro
Swept: vtxo.Swept,
ExpireAt: vtxo.ExpireAt,
RedeemTx: sql.NullString{String: vtxo.RedeemTx, Valid: true},
Pending: vtxo.Pending,
},
); err != nil {
return err
@@ -259,7 +258,6 @@ func rowToVtxo(row queries.Vtxo) domain.Vtxo {
Swept: row.Swept,
ExpireAt: row.ExpireAt,
RedeemTx: row.RedeemTx.String,
Pending: row.Pending,
}
}

View File

@@ -782,9 +782,9 @@ func (s *service) Status(ctx context.Context) (ports.WalletStatus, error) {
w := s.wallet.InternalWallet()
return status{
true,
!w.Manager.IsLocked(),
w.ChainSynced(),
initialized: true,
unlocked: !w.Manager.IsLocked(),
synced: s.isSynced,
}, nil
}

View File

@@ -124,7 +124,7 @@ func (v vtxoList) toProto() []*arkv1.Vtxo {
SpentBy: vv.SpentBy,
Swept: vv.Swept,
RedeemTx: vv.RedeemTx,
Pending: vv.Pending,
IsOor: len(vv.RedeemTx) > 0,
Pubkey: vv.Pubkey,
})
}

View File

@@ -74,7 +74,7 @@ func TestSendOffchain(t *testing.T) {
time.Sleep(5 * time.Second)
_, err = runArkCommand("claim", "--password", utils.Password)
_, err = runArkCommand("settle", "--password", utils.Password)
require.NoError(t, err)
time.Sleep(3 * time.Second)
@@ -101,7 +101,7 @@ func TestUnilateralExit(t *testing.T) {
time.Sleep(5 * time.Second)
_, err = runArkCommand("claim", "--password", utils.Password)
_, err = runArkCommand("settle", "--password", utils.Password)
require.NoError(t, err)
time.Sleep(3 * time.Second)
@@ -142,7 +142,7 @@ func TestCollaborativeExit(t *testing.T) {
time.Sleep(5 * time.Second)
_, err = runArkCommand("claim", "--password", utils.Password)
_, err = runArkCommand("settle", "--password", utils.Password)
require.NoError(t, err)
time.Sleep(3 * time.Second)
@@ -164,7 +164,7 @@ func TestReactToSpentVtxosRedemption(t *testing.T) {
time.Sleep(5 * time.Second)
_, err = client.Claim(ctx)
_, err = client.Settle(ctx)
require.NoError(t, err)
time.Sleep(3 * time.Second)
@@ -216,7 +216,7 @@ func TestSweep(t *testing.T) {
time.Sleep(5 * time.Second)
_, err = runArkCommand("claim", "--password", utils.Password)
_, err = runArkCommand("settle", "--password", utils.Password)
require.NoError(t, err)
time.Sleep(3 * time.Second)

View File

@@ -77,7 +77,7 @@ func TestSendOffchain(t *testing.T) {
time.Sleep(5 * time.Second)
_, err = runClarkCommand("claim", "--password", utils.Password)
_, err = runClarkCommand("settle", "--password", utils.Password)
require.NoError(t, err)
time.Sleep(3 * time.Second)
@@ -91,7 +91,7 @@ func TestSendOffchain(t *testing.T) {
require.NoError(t, json.Unmarshal([]byte(balanceStr), &balance))
require.NotZero(t, balance.Offchain.Total)
_, err = runClarkCommand("claim", "--password", utils.Password)
_, err = runClarkCommand("settle", "--password", utils.Password)
require.NoError(t, err)
balanceStr, err = runClarkCommand("balance")
@@ -113,7 +113,7 @@ func TestUnilateralExit(t *testing.T) {
time.Sleep(5 * time.Second)
_, err = runClarkCommand("claim", "--password", utils.Password)
_, err = runClarkCommand("settle", "--password", utils.Password)
require.NoError(t, err)
time.Sleep(3 * time.Second)
@@ -155,11 +155,6 @@ func TestCollaborativeExit(t *testing.T) {
time.Sleep(5 * time.Second)
_, err = runClarkCommand("claim", "--password", utils.Password)
require.NoError(t, err)
time.Sleep(3 * time.Second)
_, err = runClarkCommand("redeem", "--amount", "1000", "--address", redeemAddress, "--password", utils.Password)
require.NoError(t, err)
}
@@ -177,7 +172,7 @@ func TestReactToSpentVtxosRedemption(t *testing.T) {
time.Sleep(5 * time.Second)
_, err = client.Claim(ctx)
_, err = client.Settle(ctx)
require.NoError(t, err)
_, err = client.SendOffChain(ctx, false, []arksdk.Receiver{arksdk.NewBitcoinReceiver(offchainAddress, 1000)})
@@ -217,71 +212,105 @@ func TestReactToSpentVtxosRedemption(t *testing.T) {
}
func TestReactToAsyncSpentVtxosRedemption(t *testing.T) {
t.Run("receiver claimed funds", func(t *testing.T) {
ctx := context.Background()
sdkClient, grpcClient := setupArkSDK(t)
defer grpcClient.Close()
ctx := context.Background()
sdkClient, grpcClient := setupArkSDK(t)
defer grpcClient.Close()
offchainAddress, boardingAddress, err := sdkClient.Receive(ctx)
require.NoError(t, err)
offchainAddress, boardingAddress, err := sdkClient.Receive(ctx)
require.NoError(t, err)
_, err = utils.RunCommand("nigiri", "faucet", boardingAddress)
require.NoError(t, err)
_, err = utils.RunCommand("nigiri", "faucet", boardingAddress)
require.NoError(t, err)
time.Sleep(5 * time.Second)
time.Sleep(5 * time.Second)
roundId, err := sdkClient.Claim(ctx)
require.NoError(t, err)
roundId, err := sdkClient.Settle(ctx)
require.NoError(t, err)
err = utils.GenerateBlock()
require.NoError(t, err)
err = utils.GenerateBlock()
require.NoError(t, err)
_, err = sdkClient.SendAsync(ctx, false, []arksdk.Receiver{arksdk.NewBitcoinReceiver(offchainAddress, 1000)})
require.NoError(t, err)
_, err = sdkClient.SendOffChain(ctx, false, []arksdk.Receiver{arksdk.NewBitcoinReceiver(offchainAddress, 1000)})
require.NoError(t, err)
_, err = sdkClient.Claim(ctx)
require.NoError(t, err)
_, err = sdkClient.Settle(ctx)
require.NoError(t, err)
time.Sleep(5 * time.Second)
time.Sleep(5 * time.Second)
_, spentVtxos, err := sdkClient.ListVtxos(ctx)
require.NoError(t, err)
require.NotEmpty(t, spentVtxos)
_, spentVtxos, err := sdkClient.ListVtxos(ctx)
require.NoError(t, err)
require.NotEmpty(t, spentVtxos)
var vtxo client.Vtxo
var vtxo client.Vtxo
for _, v := range spentVtxos {
if v.RoundTxid == roundId {
vtxo = v
break
}
for _, v := range spentVtxos {
if v.RoundTxid == roundId {
vtxo = v
break
}
require.NotEmpty(t, vtxo)
}
require.NotEmpty(t, vtxo)
round, err := grpcClient.GetRound(ctx, vtxo.RoundTxid)
round, err := grpcClient.GetRound(ctx, vtxo.RoundTxid)
require.NoError(t, err)
expl := explorer.NewExplorer("http://localhost:3000", common.BitcoinRegTest)
branch, err := redemption.NewCovenantlessRedeemBranch(expl, round.Tree, vtxo)
require.NoError(t, err)
txs, err := branch.RedeemPath()
require.NoError(t, err)
for _, tx := range txs {
_, err := expl.Broadcast(tx)
require.NoError(t, err)
}
expl := explorer.NewExplorer("http://localhost:3000", common.BitcoinRegTest)
// give time for the ASP to detect and process the fraud
time.Sleep(50 * time.Second)
branch, err := redemption.NewCovenantlessRedeemBranch(expl, round.Tree, vtxo)
require.NoError(t, err)
balance, err := sdkClient.Balance(ctx, false)
require.NoError(t, err)
txs, err := branch.RedeemPath()
require.NoError(t, err)
require.Empty(t, balance.OnchainBalance.LockedAmount)
}
for _, tx := range txs {
_, err := expl.Broadcast(tx)
require.NoError(t, err)
}
func TestChainAsyncPayments(t *testing.T) {
var receive utils.ArkReceive
receiveStr, err := runClarkCommand("receive")
require.NoError(t, err)
// give time for the ASP to detect and process the fraud
time.Sleep(50 * time.Second)
err = json.Unmarshal([]byte(receiveStr), &receive)
require.NoError(t, err)
balance, err := sdkClient.Balance(ctx, false)
require.NoError(t, err)
_, err = utils.RunCommand("nigiri", "faucet", receive.Boarding)
require.NoError(t, err)
require.Empty(t, balance.OnchainBalance.LockedAmount)
})
time.Sleep(5 * time.Second)
_, err = runClarkCommand("settle", "--password", utils.Password)
require.NoError(t, err)
time.Sleep(3 * time.Second)
_, err = runClarkCommand("send", "--amount", "10000", "--to", receive.Offchain, "--password", utils.Password)
require.NoError(t, err)
var balance utils.ArkBalance
balanceStr, err := runClarkCommand("balance")
require.NoError(t, err)
require.NoError(t, json.Unmarshal([]byte(balanceStr), &balance))
require.NotZero(t, balance.Offchain.Total)
_, err = runClarkCommand("send", "--amount", "10000", "--to", receive.Offchain, "--password", utils.Password)
require.NoError(t, err)
balanceStr, err = runClarkCommand("balance")
require.NoError(t, err)
require.NoError(t, json.Unmarshal([]byte(balanceStr), &balance))
require.NotZero(t, balance.Offchain.Total)
}
func TestAliceSeveralPaymentsToBob(t *testing.T) {
@@ -300,9 +329,6 @@ func TestAliceSeveralPaymentsToBob(t *testing.T) {
time.Sleep(5 * time.Second)
_, err = alice.Claim(ctx)
require.NoError(t, err)
bobAddress, _, err := bob.Receive(ctx)
require.NoError(t, err)
@@ -315,12 +341,6 @@ func TestAliceSeveralPaymentsToBob(t *testing.T) {
require.NoError(t, err)
require.Len(t, bobVtxos, 1)
_, err = bob.Claim(ctx)
require.NoError(t, err)
_, err = alice.Claim(ctx)
require.NoError(t, err)
_, err = alice.SendOffChain(ctx, false, []arksdk.Receiver{arksdk.NewBitcoinReceiver(bobAddress, 10000)})
require.NoError(t, err)
@@ -339,7 +359,7 @@ func TestAliceSeveralPaymentsToBob(t *testing.T) {
require.NoError(t, err)
require.Len(t, bobVtxos, 3)
_, err = alice.SendAsync(ctx, false, []arksdk.Receiver{arksdk.NewBitcoinReceiver(bobAddress, 10000)})
_, err = alice.SendOffChain(ctx, false, []arksdk.Receiver{arksdk.NewBitcoinReceiver(bobAddress, 10000)})
require.NoError(t, err)
time.Sleep(2 * time.Second)
@@ -348,9 +368,6 @@ func TestAliceSeveralPaymentsToBob(t *testing.T) {
require.NoError(t, err)
require.Len(t, bobVtxos, 4)
_, err = alice.Claim(ctx)
require.NoError(t, err)
// bobVtxos should be unique
uniqueVtxos := make(map[string]struct{})
for _, v := range bobVtxos {
@@ -358,9 +375,7 @@ func TestAliceSeveralPaymentsToBob(t *testing.T) {
}
require.Len(t, uniqueVtxos, 4)
_, err = bob.Claim(ctx)
require.NoError(t, err)
}
func TestSweep(t *testing.T) {
@@ -376,7 +391,7 @@ func TestSweep(t *testing.T) {
time.Sleep(5 * time.Second)
_, err = runClarkCommand("claim", "--password", utils.Password)
_, err = runClarkCommand("settle", "--password", utils.Password)
require.NoError(t, err)
time.Sleep(3 * time.Second)