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": { "swept": {
"type": "boolean" "type": "boolean"
}, },
"pending": { "isOor": {
"type": "boolean" "type": "boolean"
}, },
"redeemTx": { "redeemTx": {

View File

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

View File

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

View File

@@ -15,22 +15,19 @@ import (
type VtxoScript common.VtxoScript[bitcoinTapTree] type VtxoScript common.VtxoScript[bitcoinTapTree]
func ParseVtxoScript(desc string) (VtxoScript, error) { func ParseVtxoScript(desc string) (VtxoScript, error) {
v := &DefaultVtxoScript{} types := []VtxoScript{
// TODO add other type &DefaultVtxoScript{},
err := v.FromDescriptor(desc) }
if err != nil {
v := &ReversibleVtxoScript{} for _, v := range types {
err = v.FromDescriptor(desc) if err := v.FromDescriptor(desc); err == nil {
if err != nil { return v, nil
}
}
return nil, fmt.Errorf("invalid vtxo descriptor: %s", desc) return nil, fmt.Errorf("invalid vtxo descriptor: %s", desc)
} }
return v, nil
}
return v, nil
}
/* /*
* DefaultVtxoScript is the default implementation of VTXO with 2 closures * DefaultVtxoScript is the default implementation of VTXO with 2 closures
* - Owner and ASP (forfeit) * - Owner and ASP (forfeit)
@@ -101,94 +98,6 @@ func (v *DefaultVtxoScript) TapTree() (*secp256k1.PublicKey, bitcoinTapTree, err
return taprootKey, bitcoinTapTree{tapTree}, nil 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 // bitcoinTapTree is a wrapper around txscript.IndexedTapScriptTree to implement the common.TaprootTree interface
type bitcoinTapTree struct { type bitcoinTapTree struct {
*txscript.IndexedTapScriptTree *txscript.IndexedTapScriptTree

View File

@@ -19,12 +19,8 @@ func TestParseDescriptor(t *testing.T) {
aliceKey, err := secp256k1.GeneratePrivateKey() aliceKey, err := secp256k1.GeneratePrivateKey()
require.NoError(t, err) require.NoError(t, err)
bobKey, err := secp256k1.GeneratePrivateKey()
require.NoError(t, err)
aspPubKey := hex.EncodeToString(schnorr.SerializePubKey(aspKey.PubKey())) aspPubKey := hex.EncodeToString(schnorr.SerializePubKey(aspKey.PubKey()))
alicePubKey := hex.EncodeToString(schnorr.SerializePubKey(aliceKey.PubKey())) alicePubKey := hex.EncodeToString(schnorr.SerializePubKey(aliceKey.PubKey()))
bobPubKey := hex.EncodeToString(schnorr.SerializePubKey(bobKey.PubKey()))
unspendableKey := hex.EncodeToString(bitcointree.UnspendableKey().SerializeCompressed()) unspendableKey := hex.EncodeToString(bitcointree.UnspendableKey().SerializeCompressed())
@@ -44,24 +40,4 @@ func TestParseDescriptor(t *testing.T) {
require.Equal(t, defaultScriptDescriptor, vtxo.ToDescriptor()) require.Equal(t, defaultScriptDescriptor, vtxo.ToDescriptor())
require.Equal(t, alicePubKey, hex.EncodeToString(schnorr.SerializePubKey(vtxo.(*bitcointree.DefaultVtxoScript).Owner))) require.Equal(t, alicePubKey, hex.EncodeToString(schnorr.SerializePubKey(vtxo.(*bitcointree.DefaultVtxoScript).Owner)))
require.Equal(t, aspPubKey, hex.EncodeToString(schnorr.SerializePubKey(vtxo.(*bitcointree.DefaultVtxoScript).Asp))) 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)) }) // 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)) })" 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( func ParseDefaultVtxoDescriptor(
descriptor string, descriptor string,
) (user, asp *secp256k1.PublicKey, timeout uint, err error) { ) (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, ctx context.Context, addr string, amount uint64, withExpiryCoinselect bool,
) (string, error) ) (string, error)
SendAsync(ctx context.Context, withExpiryCoinselect bool, receivers []Receiver) (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) ListVtxos(ctx context.Context) (spendable, spent []client.Vtxo, err error)
Dump(ctx context.Context) (seed string, err error) Dump(ctx context.Context) (seed string, err error)
GetTransactionHistory(ctx context.Context) ([]types.Transaction, error) GetTransactionHistory(ctx context.Context) ([]types.Transaction, error)

View File

@@ -270,6 +270,27 @@ func (a *arkClient) ping(
return ticker.Stop 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( func getClient(
supportedClients utils.SupportedType[utils.ClientFactory], clientType, aspUrl string, supportedClients utils.SupportedType[utils.ClientFactory], clientType, aspUrl string,
) (client.ASPClient, error) { ) (client.ASPClient, error) {
@@ -329,3 +350,15 @@ func getWalletStore(storeType, datadir string) (walletstore.WalletStore, error)
func getCreatedAtFromExpiry(roundLifetime int64, expiry time.Time) time.Time { func getCreatedAtFromExpiry(roundLifetime int64, expiry time.Time) time.Time {
return expiry.Add(-time.Duration(roundLifetime) * time.Second) 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 VOut uint32
} }
func (o Outpoint) Equals(other Outpoint) bool {
return o.Txid == other.Txid && o.VOut == other.VOut
}
type Input struct { type Input struct {
Outpoint Outpoint
Descriptor string Descriptor string
@@ -95,7 +99,7 @@ type Vtxo struct {
RoundTxid string RoundTxid string
ExpiresAt *time.Time ExpiresAt *time.Time
RedeemTx string RedeemTx string
Pending bool IsOOR bool
SpentBy string SpentBy string
} }

View File

@@ -359,7 +359,7 @@ func (c *grpcClient) GetTransactionsStream(
Round: &client.RoundTransaction{ Round: &client.RoundTransaction{
Txid: tx.Round.Txid, Txid: tx.Round.Txid,
SpentVtxos: outpointsFromProto(tx.Round.SpentVtxos), SpentVtxos: outpointsFromProto(tx.Round.SpentVtxos),
SpendableVtxos: vtxosFromProto(tx.Round.SpendableVtxos), SpendableVtxos: vtxos(tx.Round.SpendableVtxos).toVtxos(),
ClaimedBoardingUtxos: outpointsFromProto(tx.Round.ClaimedBoardingUtxos), ClaimedBoardingUtxos: outpointsFromProto(tx.Round.ClaimedBoardingUtxos),
}, },
} }
@@ -368,7 +368,7 @@ func (c *grpcClient) GetTransactionsStream(
Redeem: &client.RedeemTransaction{ Redeem: &client.RedeemTransaction{
Txid: tx.Redeem.Txid, Txid: tx.Redeem.Txid,
SpentVtxos: outpointsFromProto(tx.Redeem.SpentVtxos), 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 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(), Amount: v.GetAmount(),
RoundTxid: v.GetRoundTxid(), RoundTxid: v.GetRoundTxid(),
ExpiresAt: expiresAt, ExpiresAt: expiresAt,
Pending: v.GetPending(), IsOOR: v.GetIsOor(),
RedeemTx: v.GetRedeemTx(), RedeemTx: v.GetRedeemTx(),
SpentBy: v.GetSpentBy(), SpentBy: v.GetSpentBy(),
Pubkey: v.GetPubkey(), Pubkey: v.GetPubkey(),

View File

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

View File

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

View File

@@ -33,7 +33,6 @@ func TestVtxosToTxs(t *testing.T) {
}, },
Amount: 1000, Amount: 1000,
Type: sdktypes.TxSent, Type: sdktypes.TxSent,
IsPending: false,
CreatedAt: time.Unix(1726054898, 0), CreatedAt: time.Unix(1726054898, 0),
}, },
}, },
@@ -48,7 +47,6 @@ func TestVtxosToTxs(t *testing.T) {
}, },
Amount: 1000, Amount: 1000,
Type: sdktypes.TxReceived, Type: sdktypes.TxReceived,
IsPending: true,
CreatedAt: time.Unix(1726054898, 0), CreatedAt: time.Unix(1726054898, 0),
}, },
{ {
@@ -57,7 +55,6 @@ func TestVtxosToTxs(t *testing.T) {
}, },
Amount: 2000, Amount: 2000,
Type: sdktypes.TxReceived, Type: sdktypes.TxReceived,
IsPending: true,
CreatedAt: time.Unix(1726486359, 0), CreatedAt: time.Unix(1726486359, 0),
}, },
}, },
@@ -72,7 +69,6 @@ func TestVtxosToTxs(t *testing.T) {
}, },
Amount: 1000, Amount: 1000,
Type: sdktypes.TxReceived, Type: sdktypes.TxReceived,
IsPending: false,
CreatedAt: time.Unix(1726054898, 0), CreatedAt: time.Unix(1726054898, 0),
}, },
{ {
@@ -81,7 +77,6 @@ func TestVtxosToTxs(t *testing.T) {
}, },
Amount: 2000, Amount: 2000,
Type: sdktypes.TxReceived, Type: sdktypes.TxReceived,
IsPending: false,
CreatedAt: time.Unix(1726486359, 0), CreatedAt: time.Unix(1726486359, 0),
}, },
}, },
@@ -96,7 +91,6 @@ func TestVtxosToTxs(t *testing.T) {
}, },
Amount: 1000, Amount: 1000,
Type: sdktypes.TxReceived, Type: sdktypes.TxReceived,
IsPending: false,
CreatedAt: time.Unix(1726054898, 0), CreatedAt: time.Unix(1726054898, 0),
}, },
{ {
@@ -105,7 +99,6 @@ func TestVtxosToTxs(t *testing.T) {
}, },
Amount: 2000, Amount: 2000,
Type: sdktypes.TxReceived, Type: sdktypes.TxReceived,
IsPending: false,
CreatedAt: time.Unix(1726486359, 0), CreatedAt: time.Unix(1726486359, 0),
}, },
{ {
@@ -114,7 +107,6 @@ func TestVtxosToTxs(t *testing.T) {
}, },
Amount: 2100, Amount: 2100,
Type: sdktypes.TxSent, Type: sdktypes.TxSent,
IsPending: false,
CreatedAt: time.Unix(1726503865, 0), CreatedAt: time.Unix(1726503865, 0),
}, },
}, },
@@ -138,7 +130,6 @@ func TestVtxosToTxs(t *testing.T) {
require.Equal(t, wantTx.RedeemTxid, gotTx.RedeemTxid) require.Equal(t, wantTx.RedeemTxid, gotTx.RedeemTxid)
require.Equal(t, int(wantTx.Amount), int(gotTx.Amount)) require.Equal(t, int(wantTx.Amount), int(gotTx.Amount))
require.Equal(t, wantTx.Type, gotTx.Type) require.Equal(t, wantTx.Type, gotTx.Type)
require.Equal(t, wantTx.IsPending, gotTx.IsPending)
} }
}) })
} }
@@ -166,11 +157,7 @@ func loadFixtures(jsonStr string) (vtxos, map[string]struct{}, error) {
SpentBy string `json:"spentBy"` SpentBy string `json:"spentBy"`
ExpireAt string `json:"expireAt"` ExpireAt string `json:"expireAt"`
Swept bool `json:"swept"` Swept bool `json:"swept"`
Pending bool `json:"pending"`
PendingData struct {
RedeemTx string `json:"redeemTx"` RedeemTx string `json:"redeemTx"`
UnconditionalForfeitTxs []string `json:"unconditionalForfeitTxs"`
} `json:"pendingData"`
} `json:"spendableVtxos"` } `json:"spendableVtxos"`
SpentVtxos []struct { SpentVtxos []struct {
Outpoint struct { Outpoint struct {
@@ -186,11 +173,7 @@ func loadFixtures(jsonStr string) (vtxos, map[string]struct{}, error) {
SpentBy string `json:"spentBy"` SpentBy string `json:"spentBy"`
ExpireAt string `json:"expireAt"` ExpireAt string `json:"expireAt"`
Swept bool `json:"swept"` Swept bool `json:"swept"`
Pending bool `json:"pending"`
PendingData struct {
RedeemTx string `json:"redeemTx"` RedeemTx string `json:"redeemTx"`
UnconditionalForfeitTxs []string `json:"unconditionalForfeitTxs"`
} `json:"pendingData"`
} `json:"spentVtxos"` } `json:"spentVtxos"`
} }
@@ -216,8 +199,7 @@ func loadFixtures(jsonStr string) (vtxos, map[string]struct{}, error) {
Amount: amount, Amount: amount,
RoundTxid: vtxo.PoolTxid, RoundTxid: vtxo.PoolTxid,
ExpiresAt: &expireAt, ExpiresAt: &expireAt,
RedeemTx: vtxo.PendingData.RedeemTx, RedeemTx: vtxo.RedeemTx,
Pending: vtxo.Pending,
SpentBy: vtxo.SpentBy, SpentBy: vtxo.SpentBy,
} }
} }
@@ -240,8 +222,7 @@ func loadFixtures(jsonStr string) (vtxos, map[string]struct{}, error) {
Amount: amount, Amount: amount,
RoundTxid: vtxo.PoolTxid, RoundTxid: vtxo.PoolTxid,
ExpiresAt: &expireAt, ExpiresAt: &expireAt,
RedeemTx: vtxo.PendingData.RedeemTx, RedeemTx: vtxo.RedeemTx,
Pending: vtxo.Pending,
SpentBy: vtxo.SpentBy, SpentBy: vtxo.SpentBy,
} }
} }
@@ -301,9 +282,7 @@ var (
"poolTxid": "377fa2fbd27c82bdbc095478384c88b6c75432c0ef464189e49c965194446cdf", "poolTxid": "377fa2fbd27c82bdbc095478384c88b6c75432c0ef464189e49c965194446cdf",
"spentBy": "", "spentBy": "",
"expireAt": "1726054928", "expireAt": "1726054928",
"swept": false, "swept": false
"pending": false,
"pendingData": null
} }
], ],
"spentVtxos": [] "spentVtxos": []
@@ -329,13 +308,7 @@ var (
"spentBy": "", "spentBy": "",
"expireAt": "1726054928", "expireAt": "1726054928",
"swept": false, "swept": false,
"pending": false, "redeemTx": "cHNidP8BAIkCAAAAAWwxXUjG5tidFA0LmUljX//jwW6xWaS6HKyRCw5StsxpAAAAAAD/////AugDAAAAAAAAIlEgt2eR8LtqTP7yUcQtSydeGrRiHnVmHHnZwYjdC23G7MZwSQAAAAAAACJRIKfUzf/o9h+r0v9y4nmyOt9qO8EkDumQPQZGTbEv8fSFAAAAAAABASsgTgAAAAAAACJRIKfUzf/o9h+r0v9y4nmyOt9qO8EkDumQPQZGTbEv8fSFIgYDp9sz9Cto7FhF+C929Y/jJw1zkRWxq6NURd9kX05mShcYAAAAAFYAAIAAAACAAQAAgAAAAAAAAAAAQRSvBIBey3T0IV353FkuGLMmMJFpqHTrliIsJwJsfJkzq7J0B8bQ0j9842h5lUfOWcbj2TeoFx6OCpgoHIqWIBhHQAFqkBLiRmP3AZ8MS77s1QIWZswMV3L72D9gN0f0MbD6XHkmzZeC1clF3uzxr+13wsF0vcFe29Zl3e2gAhMNGYVCFcFQkpt0waBJVLeLS2A16XpeB4paDyjsltVHv+6azoA6wKRtST8P7teUpSF4DAEbfJj5OIXITx5QGbZns/AtxqGyRSCn2zP0K2jsWEX4L3b1j+MnDXORFbGro1RF32RfTmZKF60grwSAXst09CFd+dxZLhizJjCRaah065YiLCcCbHyZM6uswCEWp9sz9Cto7FhF+C929Y/jJw1zkRWxq6NURd9kX05mShc5AbJ0B8bQ0j9842h5lUfOWcbj2TeoFx6OCpgoHIqWIBhHAAAAAFYAAIAAAACAAQAAgAAAAAAAAAAAARcgUJKbdMGgSVS3i0tgNel6XgeKWg8o7JbVR7/ums6AOsAAAAA="
"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=="
]
}
} }
], ],
"spentVtxos": [ "spentVtxos": [
@@ -353,8 +326,7 @@ var (
"spentBy": "94fa598302f17f00c8881e742ec0ce2f8c8d16f3d54fe6ba0fb7d13a493d84ad", "spentBy": "94fa598302f17f00c8881e742ec0ce2f8c8d16f3d54fe6ba0fb7d13a493d84ad",
"expireAt": "1726054928", "expireAt": "1726054928",
"swept": false, "swept": false,
"pending": false, "redeemTx": ""
"pendingData": null
} }
] ]
}` }`
@@ -376,13 +348,7 @@ var (
"spentBy": "", "spentBy": "",
"expireAt": "1726054928", "expireAt": "1726054928",
"swept": false, "swept": false,
"pending": true, "redeemTx": "cHNidP8BAIkCAAAAAWwxXUjG5tidFA0LmUljX//jwW6xWaS6HKyRCw5StsxpAAAAAAD/////AugDAAAAAAAAIlEgt2eR8LtqTP7yUcQtSydeGrRiHnVmHHnZwYjdC23G7MZwSQAAAAAAACJRIKfUzf/o9h+r0v9y4nmyOt9qO8EkDumQPQZGTbEv8fSFAAAAAAABASsgTgAAAAAAACJRIKfUzf/o9h+r0v9y4nmyOt9qO8EkDumQPQZGTbEv8fSFIgYDp9sz9Cto7FhF+C929Y/jJw1zkRWxq6NURd9kX05mShcYAAAAAFYAAIAAAACAAQAAgAAAAAAAAAAAQRSvBIBey3T0IV353FkuGLMmMJFpqHTrliIsJwJsfJkzq7J0B8bQ0j9842h5lUfOWcbj2TeoFx6OCpgoHIqWIBhHQAFqkBLiRmP3AZ8MS77s1QIWZswMV3L72D9gN0f0MbD6XHkmzZeC1clF3uzxr+13wsF0vcFe29Zl3e2gAhMNGYVCFcFQkpt0waBJVLeLS2A16XpeB4paDyjsltVHv+6azoA6wKRtST8P7teUpSF4DAEbfJj5OIXITx5QGbZns/AtxqGyRSCn2zP0K2jsWEX4L3b1j+MnDXORFbGro1RF32RfTmZKF60grwSAXst09CFd+dxZLhizJjCRaah065YiLCcCbHyZM6uswCEWp9sz9Cto7FhF+C929Y/jJw1zkRWxq6NURd9kX05mShc5AbJ0B8bQ0j9842h5lUfOWcbj2TeoFx6OCpgoHIqWIBhHAAAAAFYAAIAAAACAAQAAgAAAAAAAAAAAARcgUJKbdMGgSVS3i0tgNel6XgeKWg8o7JbVR7/ums6AOsAAAAA="
"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=="
]
}
}, },
{ {
"outpoint": { "outpoint": {
@@ -398,13 +364,7 @@ var (
"spentBy": "", "spentBy": "",
"expireAt": "1726486389", "expireAt": "1726486389",
"swept": false, "swept": false,
"pending": true, "redeemTx": "cHNidP8BAIkCAAAAARH6LJRGP/pFIkD/o5bBp8fXAhjl8yjfN7MhJsxdt5lrAQAAAAD/////AtAHAAAAAAAAIlEguuBh3KQUVZp+NHV2sixQ/mrsngCuLCGXzsgJPC1FzY7ANQ8AAAAAACJRIP7uXXtl4jLUcVVQU+sX7WFXmx2H6iCMzn7gye0v1Y8JAAAAAAABAStYPg8AAAAAACJRIP7uXXtl4jLUcVVQU+sX7WFXmx2H6iCMzn7gye0v1Y8JIgYCHVQFdXiyvwIlLdnji8FvbUgb+ECuTRNAB67owJnjcaMYAAAAAFYAAIAAAACAAQAAgAAAAAAAAAAAQRSc1yjQ/vHRHev23fKGANLvbOhkNYmGtmRWt8fSszlOJUzRFbnfxd1fq9gIEpaI0vrZww8tlZ94iEL75QoaIbVqQJsPdLYf7fAoXO82VoqwYHu1WevE4g6LxUGBPzfd96q5EEZkoW5qqg+v5dWJUEY467Q6qZLFHwziUaB3KEY8yEpCFcBQkpt0waBJVLeLS2A16XpeB4paDyjsltVHv+6azoA6wO5D2Mh3x0XNGxFCS67GNughkENFodpFeVpZjn76chI8RSAdVAV1eLK/AiUt2eOLwW9tSBv4QK5NE0AHrujAmeNxo60gnNco0P7x0R3r9t3yhgDS72zoZDWJhrZkVrfH0rM5TiWswCEWHVQFdXiyvwIlLdnji8FvbUgb+ECuTRNAB67owJnjcaM5AUzRFbnfxd1fq9gIEpaI0vrZww8tlZ94iEL75QoaIbVqAAAAAFYAAIAAAACAAQAAgAAAAAAAAAAAARcgUJKbdMGgSVS3i0tgNel6XgeKWg8o7JbVR7/ums6AOsAAAAA="
"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=="
]
}
} }
], ],
"spentVtxos": [] "spentVtxos": []
@@ -426,8 +386,7 @@ var (
"spentBy": "", "spentBy": "",
"expireAt": "1726503895", "expireAt": "1726503895",
"swept": false, "swept": false,
"pending": false, "redeemTx": ""
"pendingData": null
} }
], ],
"spentVtxos": [ "spentVtxos": [
@@ -445,13 +404,7 @@ var (
"spentBy": "d6684a5b9e6939dccdf07d1f0eaf7fdd7b31de4d123e63e400d23de739800d4e", "spentBy": "d6684a5b9e6939dccdf07d1f0eaf7fdd7b31de4d123e63e400d23de739800d4e",
"expireAt": "1726054928", "expireAt": "1726054928",
"swept": false, "swept": false,
"pending": true, "redeemTx": "cHNidP8BAIkCAAAAAWwxXUjG5tidFA0LmUljX//jwW6xWaS6HKyRCw5StsxpAAAAAAD/////AugDAAAAAAAAIlEgt2eR8LtqTP7yUcQtSydeGrRiHnVmHHnZwYjdC23G7MZwSQAAAAAAACJRIKfUzf/o9h+r0v9y4nmyOt9qO8EkDumQPQZGTbEv8fSFAAAAAAABASsgTgAAAAAAACJRIKfUzf/o9h+r0v9y4nmyOt9qO8EkDumQPQZGTbEv8fSFIgYDp9sz9Cto7FhF+C929Y/jJw1zkRWxq6NURd9kX05mShcYAAAAAFYAAIAAAACAAQAAgAAAAAAAAAAAQRSvBIBey3T0IV353FkuGLMmMJFpqHTrliIsJwJsfJkzq7J0B8bQ0j9842h5lUfOWcbj2TeoFx6OCpgoHIqWIBhHQAFqkBLiRmP3AZ8MS77s1QIWZswMV3L72D9gN0f0MbD6XHkmzZeC1clF3uzxr+13wsF0vcFe29Zl3e2gAhMNGYVCFcFQkpt0waBJVLeLS2A16XpeB4paDyjsltVHv+6azoA6wKRtST8P7teUpSF4DAEbfJj5OIXITx5QGbZns/AtxqGyRSCn2zP0K2jsWEX4L3b1j+MnDXORFbGro1RF32RfTmZKF60grwSAXst09CFd+dxZLhizJjCRaah065YiLCcCbHyZM6uswCEWp9sz9Cto7FhF+C929Y/jJw1zkRWxq6NURd9kX05mShc5AbJ0B8bQ0j9842h5lUfOWcbj2TeoFx6OCpgoHIqWIBhHAAAAAFYAAIAAAACAAQAAgAAAAAAAAAAAARcgUJKbdMGgSVS3i0tgNel6XgeKWg8o7JbVR7/ums6AOsAAAAA="
"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=="
]
}
}, },
{ {
"outpoint": { "outpoint": {
@@ -467,13 +420,7 @@ var (
"spentBy": "d6684a5b9e6939dccdf07d1f0eaf7fdd7b31de4d123e63e400d23de739800d4e", "spentBy": "d6684a5b9e6939dccdf07d1f0eaf7fdd7b31de4d123e63e400d23de739800d4e",
"expireAt": "1726486389", "expireAt": "1726486389",
"swept": false, "swept": false,
"pending": true, "redeemTx": "cHNidP8BAIkCAAAAARH6LJRGP/pFIkD/o5bBp8fXAhjl8yjfN7MhJsxdt5lrAQAAAAD/////AtAHAAAAAAAAIlEguuBh3KQUVZp+NHV2sixQ/mrsngCuLCGXzsgJPC1FzY7ANQ8AAAAAACJRIP7uXXtl4jLUcVVQU+sX7WFXmx2H6iCMzn7gye0v1Y8JAAAAAAABAStYPg8AAAAAACJRIP7uXXtl4jLUcVVQU+sX7WFXmx2H6iCMzn7gye0v1Y8JIgYCHVQFdXiyvwIlLdnji8FvbUgb+ECuTRNAB67owJnjcaMYAAAAAFYAAIAAAACAAQAAgAAAAAAAAAAAQRSc1yjQ/vHRHev23fKGANLvbOhkNYmGtmRWt8fSszlOJUzRFbnfxd1fq9gIEpaI0vrZww8tlZ94iEL75QoaIbVqQJsPdLYf7fAoXO82VoqwYHu1WevE4g6LxUGBPzfd96q5EEZkoW5qqg+v5dWJUEY467Q6qZLFHwziUaB3KEY8yEpCFcBQkpt0waBJVLeLS2A16XpeB4paDyjsltVHv+6azoA6wO5D2Mh3x0XNGxFCS67GNughkENFodpFeVpZjn76chI8RSAdVAV1eLK/AiUt2eOLwW9tSBv4QK5NE0AHrujAmeNxo60gnNco0P7x0R3r9t3yhgDS72zoZDWJhrZkVrfH0rM5TiWswCEWHVQFdXiyvwIlLdnji8FvbUgb+ECuTRNAB67owJnjcaM5AUzRFbnfxd1fq9gIEpaI0vrZww8tlZ94iEL75QoaIbVqAAAAAFYAAIAAAACAAQAAgAAAAAAAAAAAARcgUJKbdMGgSVS3i0tgNel6XgeKWg8o7JbVR7/ums6AOsAAAAA="
"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=="
]
}
} }
] ]
}` }`
@@ -494,13 +441,7 @@ var (
"spentBy": "", "spentBy": "",
"expireAt": "1726503895", "expireAt": "1726503895",
"swept": false, "swept": false,
"pending": false, "redeemTx": "cHNidP8BAIkCAAAAAdOK9YzYw1ceJznqJxtRXGe0KeHj6CLcLtqLVwcbMCivAAAAAAD/////ArgLAAAAAAAAIlEgC39Vxhw3dIa4heHgFS6X4XwDl1mBggsKLVTBwF1h3qEgegEAAAAAACJRIMkktfIFxFNTtAmy3K0p+7JqVn2kcA0P6y2vJ1QX2zysAAAAAAABASughgEAAAAAACJRIMkktfIFxFNTtAmy3K0p+7JqVn2kcA0P6y2vJ1QX2zysIgYDjGeMfnNwCrU45iB3iRqiFdWTADaiJ968+w3ruFuq1F0YAAAAAFYAAIAAAACAAQAAgAAAAAAAAAAAQRTYEOuHJ0hyLBGzY8nSHpD2F1nby5/XQ5Sh2Je+cQ5Wsx0ZucLmB/LLspxMRN9JcJn3Q2KJRMhhg7415cCg1d0gQNSvgaBk/1WLYqQxCKxCfv8ViVJ7vjBxvNO5tc2FEDy27V9cIrfL1jPJoVrhgPZT0GwY7dkVZS7saIKI03CbipBCFcBQkpt0waBJVLeLS2A16XpeB4paDyjsltVHv+6azoA6wPKiQ0JM6aw2kcUByijEbOydM3gTIVCGN/69q+dmyxcqRSCMZ4x+c3AKtTjmIHeJGqIV1ZMANqIn3rz7Deu4W6rUXa0g2BDrhydIciwRs2PJ0h6Q9hdZ28uf10OUodiXvnEOVrOswCEWjGeMfnNwCrU45iB3iRqiFdWTADaiJ968+w3ruFuq1F05AR0ZucLmB/LLspxMRN9JcJn3Q2KJRMhhg7415cCg1d0gAAAAAFYAAIAAAACAAQAAgAAAAAAAAAAAARcgUJKbdMGgSVS3i0tgNel6XgeKWg8o7JbVR7/ums6AOsAAAAA="
"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=="
]
}
} }
], ],
"spentVtxos": [ "spentVtxos": [
@@ -518,13 +459,7 @@ var (
"spentBy": "d6684a5b9e6939dccdf07d1f0eaf7fdd7b31de4d123e63e400d23de739800d4e", "spentBy": "d6684a5b9e6939dccdf07d1f0eaf7fdd7b31de4d123e63e400d23de739800d4e",
"expireAt": "1726054928", "expireAt": "1726054928",
"swept": false, "swept": false,
"pending": true, "redeemTx": "cHNidP8BAIkCAAAAAWwxXUjG5tidFA0LmUljX//jwW6xWaS6HKyRCw5StsxpAAAAAAD/////AugDAAAAAAAAIlEgt2eR8LtqTP7yUcQtSydeGrRiHnVmHHnZwYjdC23G7MZwSQAAAAAAACJRIKfUzf/o9h+r0v9y4nmyOt9qO8EkDumQPQZGTbEv8fSFAAAAAAABASsgTgAAAAAAACJRIKfUzf/o9h+r0v9y4nmyOt9qO8EkDumQPQZGTbEv8fSFIgYDp9sz9Cto7FhF+C929Y/jJw1zkRWxq6NURd9kX05mShcYAAAAAFYAAIAAAACAAQAAgAAAAAAAAAAAQRSvBIBey3T0IV353FkuGLMmMJFpqHTrliIsJwJsfJkzq7J0B8bQ0j9842h5lUfOWcbj2TeoFx6OCpgoHIqWIBhHQAFqkBLiRmP3AZ8MS77s1QIWZswMV3L72D9gN0f0MbD6XHkmzZeC1clF3uzxr+13wsF0vcFe29Zl3e2gAhMNGYVCFcFQkpt0waBJVLeLS2A16XpeB4paDyjsltVHv+6azoA6wKRtST8P7teUpSF4DAEbfJj5OIXITx5QGbZns/AtxqGyRSCn2zP0K2jsWEX4L3b1j+MnDXORFbGro1RF32RfTmZKF60grwSAXst09CFd+dxZLhizJjCRaah065YiLCcCbHyZM6uswCEWp9sz9Cto7FhF+C929Y/jJw1zkRWxq6NURd9kX05mShc5AbJ0B8bQ0j9842h5lUfOWcbj2TeoFx6OCpgoHIqWIBhHAAAAAFYAAIAAAACAAQAAgAAAAAAAAAAAARcgUJKbdMGgSVS3i0tgNel6XgeKWg8o7JbVR7/ums6AOsAAAAA="
"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=="
]
}
}, },
{ {
"outpoint": { "outpoint": {
@@ -541,12 +476,7 @@ var (
"expireAt": "1726486389", "expireAt": "1726486389",
"swept": false, "swept": false,
"pending": true, "pending": true,
"pendingData": { "redeemTx": "cHNidP8BAIkCAAAAARH6LJRGP/pFIkD/o5bBp8fXAhjl8yjfN7MhJsxdt5lrAQAAAAD/////AtAHAAAAAAAAIlEguuBh3KQUVZp+NHV2sixQ/mrsngCuLCGXzsgJPC1FzY7ANQ8AAAAAACJRIP7uXXtl4jLUcVVQU+sX7WFXmx2H6iCMzn7gye0v1Y8JAAAAAAABAStYPg8AAAAAACJRIP7uXXtl4jLUcVVQU+sX7WFXmx2H6iCMzn7gye0v1Y8JIgYCHVQFdXiyvwIlLdnji8FvbUgb+ECuTRNAB67owJnjcaMYAAAAAFYAAIAAAACAAQAAgAAAAAAAAAAAQRSc1yjQ/vHRHev23fKGANLvbOhkNYmGtmRWt8fSszlOJUzRFbnfxd1fq9gIEpaI0vrZww8tlZ94iEL75QoaIbVqQJsPdLYf7fAoXO82VoqwYHu1WevE4g6LxUGBPzfd96q5EEZkoW5qqg+v5dWJUEY467Q6qZLFHwziUaB3KEY8yEpCFcBQkpt0waBJVLeLS2A16XpeB4paDyjsltVHv+6azoA6wO5D2Mh3x0XNGxFCS67GNughkENFodpFeVpZjn76chI8RSAdVAV1eLK/AiUt2eOLwW9tSBv4QK5NE0AHrujAmeNxo60gnNco0P7x0R3r9t3yhgDS72zoZDWJhrZkVrfH0rM5TiWswCEWHVQFdXiyvwIlLdnji8FvbUgb+ECuTRNAB67owJnjcaM5AUzRFbnfxd1fq9gIEpaI0vrZww8tlZ94iEL75QoaIbVqAAAAAFYAAIAAAACAAQAAgAAAAAAAAAAAARcgUJKbdMGgSVS3i0tgNel6XgeKWg8o7JbVR7/ums6AOsAAAAA="
"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=="
]
}
}, },
{ {
"outpoint": { "outpoint": {
@@ -562,8 +492,7 @@ var (
"spentBy": "23c3a885f0ea05f7bdf83f3bf7f8ac9dc3f791ad292f4e63a6f53fa5e4935ab0", "spentBy": "23c3a885f0ea05f7bdf83f3bf7f8ac9dc3f791ad292f4e63a6f53fa5e4935ab0",
"expireAt": "1726503895", "expireAt": "1726503895",
"swept": false, "swept": false,
"pending": false, "redeemTx": ""
"pendingData": null
} }
] ]
}` }`

View File

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

View File

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

View File

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

View File

@@ -13,6 +13,7 @@ import (
"github.com/ark-network/ark/common" "github.com/ark-network/ark/common"
"github.com/ark-network/ark/pkg/client-sdk/internal/utils" "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/btcutil/psbt"
"github.com/btcsuite/btcd/wire" "github.com/btcsuite/btcd/wire"
"github.com/vulpemventures/go-elements/psetv2" "github.com/vulpemventures/go-elements/psetv2"
@@ -24,39 +25,6 @@ const (
LiquidExplorer = "liquid" 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 { type ExplorerTx struct {
Txid string `json:"txid"` Txid string `json:"txid"`
Vout []struct { Vout []struct {
@@ -85,8 +53,8 @@ type SpentStatus struct {
SpentBy string `json:"txid,omitempty"` SpentBy string `json:"txid,omitempty"`
} }
func (e ExplorerUtxo) ToUtxo(delay uint) Utxo { func (e ExplorerUtxo) ToUtxo(delay uint, descriptor string) types.Utxo {
return newUtxo(e, delay) return newUtxo(e, delay, descriptor)
} }
type Explorer interface { type Explorer interface {
@@ -446,3 +414,23 @@ func parseBitcoinTx(txStr string) (string, string, error) {
return txhex, txid, nil 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/common"
"github.com/ark-network/ark/pkg/client-sdk/client" "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/btcec/v2"
"github.com/btcsuite/btcd/btcutil" "github.com/btcsuite/btcd/btcutil"
"github.com/btcsuite/btcd/chaincfg" "github.com/btcsuite/btcd/chaincfg"
@@ -23,10 +24,14 @@ import (
) )
func CoinSelect( func CoinSelect(
vtxos []client.DescriptorVtxo, amount, dust uint64, sortByExpirationTime bool, boardingUtxos []types.Utxo,
) ([]client.DescriptorVtxo, uint64, error) { vtxos []client.DescriptorVtxo,
selected := make([]client.DescriptorVtxo, 0) amount,
notSelected := make([]client.DescriptorVtxo, 0) 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) selectedAmount := uint64(0)
if sortByExpirationTime { if sortByExpirationTime {
@@ -38,6 +43,20 @@ func CoinSelect(
return vtxos[i].ExpiresAt.Before(*vtxos[j].ExpiresAt) 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 { for _, vtxo := range vtxos {
@@ -51,7 +70,7 @@ func CoinSelect(
} }
if selectedAmount < amount { 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 change := selectedAmount - amount
@@ -60,10 +79,13 @@ func CoinSelect(
if len(notSelected) > 0 { if len(notSelected) > 0 {
selected = append(selected, notSelected[0]) selected = append(selected, notSelected[0])
change += notSelected[0].Amount 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) ( func ParseLiquidAddress(addr string) (

View File

@@ -3,6 +3,7 @@ package arksdk
import ( import (
"fmt" "fmt"
"github.com/ark-network/ark/pkg/client-sdk/client"
grpcclient "github.com/ark-network/ark/pkg/client-sdk/client/grpc" grpcclient "github.com/ark-network/ark/pkg/client-sdk/client/grpc"
restclient "github.com/ark-network/ark/pkg/client-sdk/client/rest" restclient "github.com/ark-network/ark/pkg/client-sdk/client/rest"
"github.com/ark-network/ark/pkg/client-sdk/internal/utils" "github.com/ark-network/ark/pkg/client-sdk/internal/utils"
@@ -123,3 +124,10 @@ type balanceRes struct {
offchainBalanceByExpiration map[int64]uint64 offchainBalanceByExpiration map[int64]uint64
err error 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 TransactionKey
Amount uint64 Amount uint64
Type TxType Type TxType
IsPending bool Settled bool
CreatedAt time.Time CreatedAt time.Time
} }
@@ -103,3 +103,19 @@ type TransactionEvent struct {
Tx Transaction Tx Transaction
Event EventType 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("sendOnChain", SendOnChainWrapper())
js.Global().Set("sendOffChain", SendOffChainWrapper()) js.Global().Set("sendOffChain", SendOffChainWrapper())
js.Global().Set("sendAsync", SendAsyncWrapper()) js.Global().Set("sendAsync", SendAsyncWrapper())
js.Global().Set("claim", ClaimWrapper()) js.Global().Set("settle", SettleWrapper())
js.Global().Set("unilateralRedeem", UnilateralRedeemWrapper()) js.Global().Set("unilateralRedeem", UnilateralRedeemWrapper())
js.Global().Set("collaborativeRedeem", CollaborativeRedeemWrapper()) js.Global().Set("collaborativeRedeem", CollaborativeRedeemWrapper())
js.Global().Set("getTransactionHistory", GetTransactionHistoryWrapper()) js.Global().Set("getTransactionHistory", GetTransactionHistoryWrapper())

View File

@@ -14,6 +14,7 @@ import (
"time" "time"
arksdk "github.com/ark-network/ark/pkg/client-sdk" 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" "github.com/ark-network/ark/pkg/client-sdk/wallet"
singlekeywallet "github.com/ark-network/ark/pkg/client-sdk/wallet/singlekey" singlekeywallet "github.com/ark-network/ark/pkg/client-sdk/wallet/singlekey"
) )
@@ -186,17 +187,17 @@ func SendOnChainWrapper() js.Func {
if len(args) != 1 { if len(args) != 1 {
return nil, errors.New("invalid number of args") return nil, errors.New("invalid number of args")
} }
receivers := make([]arksdk.Receiver, args[0].Length())
for i := 0; i < args[0].Length(); i++ { receivers, err := parseReceivers(args[0])
receiver := args[0].Index(i) if err != nil {
receivers[i] = arksdk.NewBitcoinReceiver( return nil, err
receiver.Get("To").String(), uint64(receiver.Get("Amount").Int()),
)
} }
txID, err := arkSdkClient.SendOnChain( if receivers == nil || len(receivers) == 0 {
context.Background(), receivers, return nil, errors.New("no receivers specified")
) }
txID, err := arkSdkClient.SendOnChain(context.Background(), receivers)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -209,13 +210,11 @@ func SendOffChainWrapper() js.Func {
if len(args) != 2 { if len(args) != 2 {
return nil, errors.New("invalid number of args") return nil, errors.New("invalid number of args")
} }
withExpiryCoinselect := args[0].Bool() withExpiryCoinselect := args[0].Bool()
receivers := make([]arksdk.Receiver, args[1].Length()) receivers, err := parseReceivers(args[0])
for i := 0; i < args[1].Length(); i++ { if err != nil {
receiver := args[1].Index(i) return nil, err
receivers[i] = arksdk.NewBitcoinReceiver(
receiver.Get("To").String(), uint64(receiver.Get("Amount").Int()),
)
} }
txID, err := arkSdkClient.SendOffChain( txID, err := arkSdkClient.SendOffChain(
@@ -233,13 +232,15 @@ func SendAsyncWrapper() js.Func {
if len(args) != 2 { if len(args) != 2 {
return nil, errors.New("invalid number of args") return nil, errors.New("invalid number of args")
} }
withExpiryCoinselect := args[0].Bool() withExpiryCoinselect := args[0].Bool()
receivers := make([]arksdk.Receiver, args[1].Length()) receivers, err := parseReceivers(args[0])
for i := 0; i < args[1].Length(); i++ { if err != nil {
receiver := args[1].Index(i) return nil, err
receivers[i] = arksdk.NewBitcoinReceiver( }
receiver.Get("To").String(), uint64(receiver.Get("Amount").Int()),
) if receivers == nil || len(receivers) == 0 {
return nil, errors.New("no receivers specified")
} }
txID, err := arkSdkClient.SendAsync( 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) { return JSPromise(func(args []js.Value) (interface{}, error) {
if len(args) != 0 { if len(args) != 0 {
return nil, errors.New("invalid number of args") return nil, errors.New("invalid number of args")
} }
resp, err := arkSdkClient.Claim(context.Background()) resp, err := arkSdkClient.Settle(context.Background())
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -306,7 +307,7 @@ func GetTransactionHistoryWrapper() js.Func {
"redeemTxid": record.RedeemTxid, "redeemTxid": record.RedeemTxid,
"amount": strconv.Itoa(int(record.Amount)), "amount": strconv.Itoa(int(record.Amount)),
"type": record.Type, "type": record.Type,
"isPending": record.IsPending, "settled": record.Settled,
"createdAt": record.CreatedAt.Format(time.RFC3339), "createdAt": record.CreatedAt.Format(time.RFC3339),
}) })
} }
@@ -433,3 +434,63 @@ func JSPromise(fn promise) js.Func {
return promiseConstructor.New(handler) 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 { func GetVersion() string {
return Version 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") println("ARK SDK WebAssembly module initialized")
<-c <-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 ## intergrationtest: runs integration tests
integrationtest: integrationtest:
@echo "Running integration tests..." @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 500s 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/covenantless
## lint: lint codebase ## lint: lint codebase
lint: lint:

View File

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

View File

@@ -123,5 +123,4 @@ type Vtxo struct {
Swept bool Swept bool
ExpireAt int64 ExpireAt int64
RedeemTx string // empty if in-round vtxo 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, expire_at INTEGER NOT NULL,
payment_id TEXT, payment_id TEXT,
redeem_tx TEXT, redeem_tx TEXT,
pending BOOLEAN NOT NULL,
PRIMARY KEY (txid, vout), PRIMARY KEY (txid, vout),
FOREIGN KEY (payment_id) REFERENCES payment(id) FOREIGN KEY (payment_id) REFERENCES payment(id)
); );

View File

@@ -33,7 +33,6 @@ type PaymentVtxoVw struct {
ExpireAt sql.NullInt64 ExpireAt sql.NullInt64
PaymentID sql.NullString PaymentID sql.NullString
RedeemTx sql.NullString RedeemTx sql.NullString
Pending sql.NullBool
} }
type Receiver struct { type Receiver struct {
@@ -100,5 +99,4 @@ type Vtxo struct {
ExpireAt int64 ExpireAt int64
PaymentID sql.NullString PaymentID sql.NullString
RedeemTx 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 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 WHERE redeemed = false
` `
@@ -84,7 +84,6 @@ func (q *Queries) SelectNotRedeemedVtxos(ctx context.Context) ([]SelectNotRedeem
&i.Vtxo.ExpireAt, &i.Vtxo.ExpireAt,
&i.Vtxo.PaymentID, &i.Vtxo.PaymentID,
&i.Vtxo.RedeemTx, &i.Vtxo.RedeemTx,
&i.Vtxo.Pending,
); err != nil { ); err != nil {
return nil, err return nil, err
} }
@@ -100,7 +99,7 @@ func (q *Queries) SelectNotRedeemedVtxos(ctx context.Context) ([]SelectNotRedeem
} }
const selectNotRedeemedVtxosWithPubkey = `-- name: SelectNotRedeemedVtxosWithPubkey :many 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 = ? WHERE redeemed = false AND pubkey = ?
` `
@@ -130,7 +129,6 @@ func (q *Queries) SelectNotRedeemedVtxosWithPubkey(ctx context.Context, pubkey s
&i.Vtxo.ExpireAt, &i.Vtxo.ExpireAt,
&i.Vtxo.PaymentID, &i.Vtxo.PaymentID,
&i.Vtxo.RedeemTx, &i.Vtxo.RedeemTx,
&i.Vtxo.Pending,
); err != nil { ); err != nil {
return nil, err 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_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, 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_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 FROM round
LEFT OUTER JOIN round_payment_vw ON round.id=round_payment_vw.round_id 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 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.ExpireAt,
&i.PaymentVtxoVw.PaymentID, &i.PaymentVtxoVw.PaymentID,
&i.PaymentVtxoVw.RedeemTx, &i.PaymentVtxoVw.RedeemTx,
&i.PaymentVtxoVw.Pending,
); err != nil { ); err != nil {
return nil, err 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_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, 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_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 FROM round
LEFT OUTER JOIN round_payment_vw ON round.id=round_payment_vw.round_id 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 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.ExpireAt,
&i.PaymentVtxoVw.PaymentID, &i.PaymentVtxoVw.PaymentID,
&i.PaymentVtxoVw.RedeemTx, &i.PaymentVtxoVw.RedeemTx,
&i.PaymentVtxoVw.Pending,
); err != nil { ); err != nil {
return nil, err 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_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, 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_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 FROM round
LEFT OUTER JOIN round_payment_vw ON round.id=round_payment_vw.round_id 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 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.ExpireAt,
&i.PaymentVtxoVw.PaymentID, &i.PaymentVtxoVw.PaymentID,
&i.PaymentVtxoVw.RedeemTx, &i.PaymentVtxoVw.RedeemTx,
&i.PaymentVtxoVw.Pending,
); err != nil { ); err != nil {
return nil, err return nil, err
} }
@@ -463,7 +458,7 @@ func (q *Queries) SelectSweepableRounds(ctx context.Context) ([]SelectSweepableR
} }
const selectSweepableVtxos = `-- name: SelectSweepableVtxos :many 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 WHERE redeemed = false AND swept = false
` `
@@ -493,7 +488,6 @@ func (q *Queries) SelectSweepableVtxos(ctx context.Context) ([]SelectSweepableVt
&i.Vtxo.ExpireAt, &i.Vtxo.ExpireAt,
&i.Vtxo.PaymentID, &i.Vtxo.PaymentID,
&i.Vtxo.RedeemTx, &i.Vtxo.RedeemTx,
&i.Vtxo.Pending,
); err != nil { ); err != nil {
return nil, err 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_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, 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_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 FROM round
LEFT OUTER JOIN round_payment_vw ON round.id=round_payment_vw.round_id 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 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.ExpireAt,
&i.PaymentVtxoVw.PaymentID, &i.PaymentVtxoVw.PaymentID,
&i.PaymentVtxoVw.RedeemTx, &i.PaymentVtxoVw.RedeemTx,
&i.PaymentVtxoVw.Pending,
); err != nil { ); err != nil {
return nil, err return nil, err
} }
@@ -595,7 +588,7 @@ func (q *Queries) SelectSweptRounds(ctx context.Context) ([]SelectSweptRoundsRow
} }
const selectVtxoByOutpoint = `-- name: SelectVtxoByOutpoint :one 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 = ? WHERE txid = ? AND vout = ?
` `
@@ -624,13 +617,12 @@ func (q *Queries) SelectVtxoByOutpoint(ctx context.Context, arg SelectVtxoByOutp
&i.Vtxo.ExpireAt, &i.Vtxo.ExpireAt,
&i.Vtxo.PaymentID, &i.Vtxo.PaymentID,
&i.Vtxo.RedeemTx, &i.Vtxo.RedeemTx,
&i.Vtxo.Pending,
) )
return i, err return i, err
} }
const selectVtxosByPoolTxid = `-- name: SelectVtxosByPoolTxid :many 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 = ? WHERE pool_tx = ?
` `
@@ -660,7 +652,6 @@ func (q *Queries) SelectVtxosByPoolTxid(ctx context.Context, poolTx string) ([]S
&i.Vtxo.ExpireAt, &i.Vtxo.ExpireAt,
&i.Vtxo.PaymentID, &i.Vtxo.PaymentID,
&i.Vtxo.RedeemTx, &i.Vtxo.RedeemTx,
&i.Vtxo.Pending,
); err != nil { ); err != nil {
return nil, err return nil, err
} }
@@ -847,8 +838,8 @@ func (q *Queries) UpsertTransaction(ctx context.Context, arg UpsertTransactionPa
} }
const upsertVtxo = `-- name: UpsertVtxo :exec const upsertVtxo = `-- name: UpsertVtxo :exec
INSERT INTO vtxo (txid, vout, pubkey, amount, pool_tx, spent_by, spent, redeemed, swept, expire_at, redeem_tx, pending) 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 VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) ON CONFLICT(txid, vout) DO UPDATE SET
pubkey = EXCLUDED.pubkey, pubkey = EXCLUDED.pubkey,
amount = EXCLUDED.amount, amount = EXCLUDED.amount,
pool_tx = EXCLUDED.pool_tx, pool_tx = EXCLUDED.pool_tx,
@@ -857,8 +848,7 @@ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) ON CONFLICT(txid, vout) DO UPDATE SE
redeemed = EXCLUDED.redeemed, redeemed = EXCLUDED.redeemed,
swept = EXCLUDED.swept, swept = EXCLUDED.swept,
expire_at = EXCLUDED.expire_at, expire_at = EXCLUDED.expire_at,
redeem_tx = EXCLUDED.redeem_tx, redeem_tx = EXCLUDED.redeem_tx
pending = EXCLUDED.pending
` `
type UpsertVtxoParams struct { type UpsertVtxoParams struct {
@@ -873,7 +863,6 @@ type UpsertVtxoParams struct {
Swept bool Swept bool
ExpireAt int64 ExpireAt int64
RedeemTx sql.NullString RedeemTx sql.NullString
Pending bool
} }
func (q *Queries) UpsertVtxo(ctx context.Context, arg UpsertVtxoParams) error { 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.Swept,
arg.ExpireAt, arg.ExpireAt,
arg.RedeemTx, arg.RedeemTx,
arg.Pending,
) )
return err return err
} }

View File

@@ -112,8 +112,8 @@ SELECT id FROM round WHERE starting_timestamp > ? AND starting_timestamp < ?;
SELECT id FROM round; SELECT id FROM round;
-- name: UpsertVtxo :exec -- name: UpsertVtxo :exec
INSERT INTO vtxo (txid, vout, pubkey, amount, pool_tx, spent_by, spent, redeemed, swept, expire_at, redeem_tx, pending) 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 VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) ON CONFLICT(txid, vout) DO UPDATE SET
pubkey = EXCLUDED.pubkey, pubkey = EXCLUDED.pubkey,
amount = EXCLUDED.amount, amount = EXCLUDED.amount,
pool_tx = EXCLUDED.pool_tx, pool_tx = EXCLUDED.pool_tx,
@@ -122,8 +122,7 @@ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) ON CONFLICT(txid, vout) DO UPDATE SE
redeemed = EXCLUDED.redeemed, redeemed = EXCLUDED.redeemed,
swept = EXCLUDED.swept, swept = EXCLUDED.swept,
expire_at = EXCLUDED.expire_at, expire_at = EXCLUDED.expire_at,
redeem_tx = EXCLUDED.redeem_tx, redeem_tx = EXCLUDED.redeem_tx;
pending = EXCLUDED.pending;
-- name: SelectSweepableVtxos :many -- name: SelectSweepableVtxos :many
SELECT sqlc.embed(vtxo) FROM vtxo 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, Swept: vtxo.Swept,
ExpireAt: vtxo.ExpireAt, ExpireAt: vtxo.ExpireAt,
RedeemTx: sql.NullString{String: vtxo.RedeemTx, Valid: true}, RedeemTx: sql.NullString{String: vtxo.RedeemTx, Valid: true},
Pending: vtxo.Pending,
}, },
); err != nil { ); err != nil {
return err return err
@@ -259,7 +258,6 @@ func rowToVtxo(row queries.Vtxo) domain.Vtxo {
Swept: row.Swept, Swept: row.Swept,
ExpireAt: row.ExpireAt, ExpireAt: row.ExpireAt,
RedeemTx: row.RedeemTx.String, 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() w := s.wallet.InternalWallet()
return status{ return status{
true, initialized: true,
!w.Manager.IsLocked(), unlocked: !w.Manager.IsLocked(),
w.ChainSynced(), synced: s.isSynced,
}, nil }, nil
} }

View File

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

View File

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

View File

@@ -77,7 +77,7 @@ func TestSendOffchain(t *testing.T) {
time.Sleep(5 * time.Second) time.Sleep(5 * time.Second)
_, err = runClarkCommand("claim", "--password", utils.Password) _, err = runClarkCommand("settle", "--password", utils.Password)
require.NoError(t, err) require.NoError(t, err)
time.Sleep(3 * time.Second) time.Sleep(3 * time.Second)
@@ -91,7 +91,7 @@ func TestSendOffchain(t *testing.T) {
require.NoError(t, json.Unmarshal([]byte(balanceStr), &balance)) require.NoError(t, json.Unmarshal([]byte(balanceStr), &balance))
require.NotZero(t, balance.Offchain.Total) require.NotZero(t, balance.Offchain.Total)
_, err = runClarkCommand("claim", "--password", utils.Password) _, err = runClarkCommand("settle", "--password", utils.Password)
require.NoError(t, err) require.NoError(t, err)
balanceStr, err = runClarkCommand("balance") balanceStr, err = runClarkCommand("balance")
@@ -113,7 +113,7 @@ func TestUnilateralExit(t *testing.T) {
time.Sleep(5 * time.Second) time.Sleep(5 * time.Second)
_, err = runClarkCommand("claim", "--password", utils.Password) _, err = runClarkCommand("settle", "--password", utils.Password)
require.NoError(t, err) require.NoError(t, err)
time.Sleep(3 * time.Second) time.Sleep(3 * time.Second)
@@ -155,11 +155,6 @@ func TestCollaborativeExit(t *testing.T) {
time.Sleep(5 * time.Second) 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) _, err = runClarkCommand("redeem", "--amount", "1000", "--address", redeemAddress, "--password", utils.Password)
require.NoError(t, err) require.NoError(t, err)
} }
@@ -177,7 +172,7 @@ func TestReactToSpentVtxosRedemption(t *testing.T) {
time.Sleep(5 * time.Second) time.Sleep(5 * time.Second)
_, err = client.Claim(ctx) _, err = client.Settle(ctx)
require.NoError(t, err) require.NoError(t, err)
_, err = client.SendOffChain(ctx, false, []arksdk.Receiver{arksdk.NewBitcoinReceiver(offchainAddress, 1000)}) _, err = client.SendOffChain(ctx, false, []arksdk.Receiver{arksdk.NewBitcoinReceiver(offchainAddress, 1000)})
@@ -217,7 +212,6 @@ func TestReactToSpentVtxosRedemption(t *testing.T) {
} }
func TestReactToAsyncSpentVtxosRedemption(t *testing.T) { func TestReactToAsyncSpentVtxosRedemption(t *testing.T) {
t.Run("receiver claimed funds", func(t *testing.T) {
ctx := context.Background() ctx := context.Background()
sdkClient, grpcClient := setupArkSDK(t) sdkClient, grpcClient := setupArkSDK(t)
defer grpcClient.Close() defer grpcClient.Close()
@@ -230,16 +224,16 @@ func TestReactToAsyncSpentVtxosRedemption(t *testing.T) {
time.Sleep(5 * time.Second) time.Sleep(5 * time.Second)
roundId, err := sdkClient.Claim(ctx) roundId, err := sdkClient.Settle(ctx)
require.NoError(t, err) require.NoError(t, err)
err = utils.GenerateBlock() err = utils.GenerateBlock()
require.NoError(t, err) require.NoError(t, err)
_, err = sdkClient.SendAsync(ctx, false, []arksdk.Receiver{arksdk.NewBitcoinReceiver(offchainAddress, 1000)}) _, err = sdkClient.SendOffChain(ctx, false, []arksdk.Receiver{arksdk.NewBitcoinReceiver(offchainAddress, 1000)})
require.NoError(t, err) require.NoError(t, err)
_, err = sdkClient.Claim(ctx) _, err = sdkClient.Settle(ctx)
require.NoError(t, err) require.NoError(t, err)
time.Sleep(5 * time.Second) time.Sleep(5 * time.Second)
@@ -281,7 +275,42 @@ func TestReactToAsyncSpentVtxosRedemption(t *testing.T) {
require.NoError(t, err) require.NoError(t, err)
require.Empty(t, balance.OnchainBalance.LockedAmount) require.Empty(t, balance.OnchainBalance.LockedAmount)
}) }
func TestChainAsyncPayments(t *testing.T) {
var receive utils.ArkReceive
receiveStr, err := runClarkCommand("receive")
require.NoError(t, err)
err = json.Unmarshal([]byte(receiveStr), &receive)
require.NoError(t, err)
_, err = utils.RunCommand("nigiri", "faucet", receive.Boarding)
require.NoError(t, err)
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) { func TestAliceSeveralPaymentsToBob(t *testing.T) {
@@ -300,9 +329,6 @@ func TestAliceSeveralPaymentsToBob(t *testing.T) {
time.Sleep(5 * time.Second) time.Sleep(5 * time.Second)
_, err = alice.Claim(ctx)
require.NoError(t, err)
bobAddress, _, err := bob.Receive(ctx) bobAddress, _, err := bob.Receive(ctx)
require.NoError(t, err) require.NoError(t, err)
@@ -315,12 +341,6 @@ func TestAliceSeveralPaymentsToBob(t *testing.T) {
require.NoError(t, err) require.NoError(t, err)
require.Len(t, bobVtxos, 1) 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)}) _, err = alice.SendOffChain(ctx, false, []arksdk.Receiver{arksdk.NewBitcoinReceiver(bobAddress, 10000)})
require.NoError(t, err) require.NoError(t, err)
@@ -339,7 +359,7 @@ func TestAliceSeveralPaymentsToBob(t *testing.T) {
require.NoError(t, err) require.NoError(t, err)
require.Len(t, bobVtxos, 3) 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) require.NoError(t, err)
time.Sleep(2 * time.Second) time.Sleep(2 * time.Second)
@@ -348,9 +368,6 @@ func TestAliceSeveralPaymentsToBob(t *testing.T) {
require.NoError(t, err) require.NoError(t, err)
require.Len(t, bobVtxos, 4) require.Len(t, bobVtxos, 4)
_, err = alice.Claim(ctx)
require.NoError(t, err)
// bobVtxos should be unique // bobVtxos should be unique
uniqueVtxos := make(map[string]struct{}) uniqueVtxos := make(map[string]struct{})
for _, v := range bobVtxos { for _, v := range bobVtxos {
@@ -358,9 +375,7 @@ func TestAliceSeveralPaymentsToBob(t *testing.T) {
} }
require.Len(t, uniqueVtxos, 4) require.Len(t, uniqueVtxos, 4)
_, err = bob.Claim(ctx)
require.NoError(t, err) require.NoError(t, err)
} }
func TestSweep(t *testing.T) { func TestSweep(t *testing.T) {
@@ -376,7 +391,7 @@ func TestSweep(t *testing.T) {
time.Sleep(5 * time.Second) time.Sleep(5 * time.Second)
_, err = runClarkCommand("claim", "--password", utils.Password) _, err = runClarkCommand("settle", "--password", utils.Password)
require.NoError(t, err) require.NoError(t, err)
time.Sleep(3 * time.Second) time.Sleep(3 * time.Second)