diff --git a/api-spec/openapi/swagger/ark/v1/service.swagger.json b/api-spec/openapi/swagger/ark/v1/service.swagger.json index 5937a588..805e625c 100644 --- a/api-spec/openapi/swagger/ark/v1/service.swagger.json +++ b/api-spec/openapi/swagger/ark/v1/service.swagger.json @@ -1082,6 +1082,10 @@ }, "pubkey": { "type": "string" + }, + "createdAt": { + "type": "string", + "format": "int64" } } } diff --git a/api-spec/protobuf/ark/v1/service.proto b/api-spec/protobuf/ark/v1/service.proto index cb44c0d8..841bec3d 100755 --- a/api-spec/protobuf/ark/v1/service.proto +++ b/api-spec/protobuf/ark/v1/service.proto @@ -322,6 +322,7 @@ message Vtxo { string redeem_tx = 8; uint64 amount = 9; string pubkey = 10; + int64 created_at = 11; } message GetTransactionsStreamRequest {} diff --git a/api-spec/protobuf/gen/ark/v1/service.pb.go b/api-spec/protobuf/gen/ark/v1/service.pb.go index 2625f4a2..1f3963cb 100644 --- a/api-spec/protobuf/gen/ark/v1/service.pb.go +++ b/api-spec/protobuf/gen/ark/v1/service.pb.go @@ -2440,6 +2440,7 @@ type Vtxo struct { RedeemTx string `protobuf:"bytes,8,opt,name=redeem_tx,json=redeemTx,proto3" json:"redeem_tx,omitempty"` Amount uint64 `protobuf:"varint,9,opt,name=amount,proto3" json:"amount,omitempty"` Pubkey string `protobuf:"bytes,10,opt,name=pubkey,proto3" json:"pubkey,omitempty"` + CreatedAt int64 `protobuf:"varint,11,opt,name=created_at,json=createdAt,proto3" json:"created_at,omitempty"` } func (x *Vtxo) Reset() { @@ -2544,6 +2545,13 @@ func (x *Vtxo) GetPubkey() string { return "" } +func (x *Vtxo) GetCreatedAt() int64 { + if x != nil { + return x.CreatedAt + } + return 0 +} + type GetTransactionsStreamRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -3067,7 +3075,7 @@ var file_ark_v1_service_proto_rawDesc = []byte{ 0x52, 0x04, 0x74, 0x78, 0x69, 0x64, 0x12, 0x0e, 0x0a, 0x02, 0x74, 0x78, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x74, 0x78, 0x12, 0x1f, 0x0a, 0x0b, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x5f, 0x74, 0x78, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x70, 0x61, 0x72, - 0x65, 0x6e, 0x74, 0x54, 0x78, 0x69, 0x64, 0x22, 0x9b, 0x02, 0x0a, 0x04, 0x56, 0x74, 0x78, 0x6f, + 0x65, 0x6e, 0x74, 0x54, 0x78, 0x69, 0x64, 0x22, 0xba, 0x02, 0x0a, 0x04, 0x56, 0x74, 0x78, 0x6f, 0x12, 0x2c, 0x0a, 0x08, 0x6f, 0x75, 0x74, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x4f, 0x75, 0x74, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x52, 0x08, 0x6f, 0x75, 0x74, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x12, 0x14, @@ -3085,171 +3093,173 @@ var file_ark_v1_service_proto_rawDesc = []byte{ 0x65, 0x65, 0x6d, 0x54, 0x78, 0x12, 0x16, 0x0a, 0x06, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x09, 0x20, 0x01, 0x28, 0x04, 0x52, 0x06, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x70, 0x75, 0x62, 0x6b, 0x65, 0x79, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x70, - 0x75, 0x62, 0x6b, 0x65, 0x79, 0x22, 0x1e, 0x0a, 0x1c, 0x47, 0x65, 0x74, 0x54, 0x72, 0x61, 0x6e, + 0x75, 0x62, 0x6b, 0x65, 0x79, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, + 0x5f, 0x61, 0x74, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x63, 0x72, 0x65, 0x61, 0x74, + 0x65, 0x64, 0x41, 0x74, 0x22, 0x1e, 0x0a, 0x1c, 0x47, 0x65, 0x74, 0x54, 0x72, 0x61, 0x6e, 0x73, + 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x22, 0x8c, 0x01, 0x0a, 0x1d, 0x47, 0x65, 0x74, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x8c, 0x01, 0x0a, 0x1d, 0x47, 0x65, 0x74, 0x54, 0x72, 0x61, - 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x30, 0x0a, 0x05, 0x72, 0x6f, 0x75, 0x6e, 0x64, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, - 0x52, 0x6f, 0x75, 0x6e, 0x64, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, - 0x48, 0x00, 0x52, 0x05, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x12, 0x33, 0x0a, 0x06, 0x72, 0x65, 0x64, - 0x65, 0x65, 0x6d, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x61, 0x72, 0x6b, 0x2e, - 0x76, 0x31, 0x2e, 0x52, 0x65, 0x64, 0x65, 0x65, 0x6d, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, - 0x74, 0x69, 0x6f, 0x6e, 0x48, 0x00, 0x52, 0x06, 0x72, 0x65, 0x64, 0x65, 0x65, 0x6d, 0x42, 0x04, - 0x0a, 0x02, 0x74, 0x78, 0x22, 0xd8, 0x01, 0x0a, 0x10, 0x52, 0x6f, 0x75, 0x6e, 0x64, 0x54, 0x72, - 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x78, 0x69, - 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x78, 0x69, 0x64, 0x12, 0x31, 0x0a, - 0x0b, 0x73, 0x70, 0x65, 0x6e, 0x74, 0x5f, 0x76, 0x74, 0x78, 0x6f, 0x73, 0x18, 0x02, 0x20, 0x03, - 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x4f, 0x75, 0x74, 0x70, - 0x6f, 0x69, 0x6e, 0x74, 0x52, 0x0a, 0x73, 0x70, 0x65, 0x6e, 0x74, 0x56, 0x74, 0x78, 0x6f, 0x73, - 0x12, 0x35, 0x0a, 0x0f, 0x73, 0x70, 0x65, 0x6e, 0x64, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x76, 0x74, - 0x78, 0x6f, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x61, 0x72, 0x6b, 0x2e, - 0x76, 0x31, 0x2e, 0x56, 0x74, 0x78, 0x6f, 0x52, 0x0e, 0x73, 0x70, 0x65, 0x6e, 0x64, 0x61, 0x62, - 0x6c, 0x65, 0x56, 0x74, 0x78, 0x6f, 0x73, 0x12, 0x46, 0x0a, 0x16, 0x63, 0x6c, 0x61, 0x69, 0x6d, - 0x65, 0x64, 0x5f, 0x62, 0x6f, 0x61, 0x72, 0x64, 0x69, 0x6e, 0x67, 0x5f, 0x75, 0x74, 0x78, 0x6f, - 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, - 0x2e, 0x4f, 0x75, 0x74, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x52, 0x14, 0x63, 0x6c, 0x61, 0x69, 0x6d, - 0x65, 0x64, 0x42, 0x6f, 0x61, 0x72, 0x64, 0x69, 0x6e, 0x67, 0x55, 0x74, 0x78, 0x6f, 0x73, 0x22, - 0x91, 0x01, 0x0a, 0x11, 0x52, 0x65, 0x64, 0x65, 0x65, 0x6d, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, - 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x78, 0x69, 0x64, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x78, 0x69, 0x64, 0x12, 0x31, 0x0a, 0x0b, 0x73, 0x70, 0x65, - 0x6e, 0x74, 0x5f, 0x76, 0x74, 0x78, 0x6f, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x10, - 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x4f, 0x75, 0x74, 0x70, 0x6f, 0x69, 0x6e, 0x74, - 0x52, 0x0a, 0x73, 0x70, 0x65, 0x6e, 0x74, 0x56, 0x74, 0x78, 0x6f, 0x73, 0x12, 0x35, 0x0a, 0x0f, - 0x73, 0x70, 0x65, 0x6e, 0x64, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x76, 0x74, 0x78, 0x6f, 0x73, 0x18, - 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x56, - 0x74, 0x78, 0x6f, 0x52, 0x0e, 0x73, 0x70, 0x65, 0x6e, 0x64, 0x61, 0x62, 0x6c, 0x65, 0x56, 0x74, - 0x78, 0x6f, 0x73, 0x2a, 0x98, 0x01, 0x0a, 0x0a, 0x52, 0x6f, 0x75, 0x6e, 0x64, 0x53, 0x74, 0x61, - 0x67, 0x65, 0x12, 0x1b, 0x0a, 0x17, 0x52, 0x4f, 0x55, 0x4e, 0x44, 0x5f, 0x53, 0x54, 0x41, 0x47, - 0x45, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, - 0x1c, 0x0a, 0x18, 0x52, 0x4f, 0x55, 0x4e, 0x44, 0x5f, 0x53, 0x54, 0x41, 0x47, 0x45, 0x5f, 0x52, - 0x45, 0x47, 0x49, 0x53, 0x54, 0x52, 0x41, 0x54, 0x49, 0x4f, 0x4e, 0x10, 0x01, 0x12, 0x1c, 0x0a, - 0x18, 0x52, 0x4f, 0x55, 0x4e, 0x44, 0x5f, 0x53, 0x54, 0x41, 0x47, 0x45, 0x5f, 0x46, 0x49, 0x4e, - 0x41, 0x4c, 0x49, 0x5a, 0x41, 0x54, 0x49, 0x4f, 0x4e, 0x10, 0x02, 0x12, 0x19, 0x0a, 0x15, 0x52, - 0x4f, 0x55, 0x4e, 0x44, 0x5f, 0x53, 0x54, 0x41, 0x47, 0x45, 0x5f, 0x46, 0x49, 0x4e, 0x41, 0x4c, - 0x49, 0x5a, 0x45, 0x44, 0x10, 0x03, 0x12, 0x16, 0x0a, 0x12, 0x52, 0x4f, 0x55, 0x4e, 0x44, 0x5f, - 0x53, 0x54, 0x41, 0x47, 0x45, 0x5f, 0x46, 0x41, 0x49, 0x4c, 0x45, 0x44, 0x10, 0x04, 0x32, 0xeb, - 0x0d, 0x0a, 0x0a, 0x41, 0x72, 0x6b, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x4c, 0x0a, - 0x07, 0x47, 0x65, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x16, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, - 0x31, 0x2e, 0x47, 0x65, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x1a, 0x17, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x49, 0x6e, 0x66, - 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x10, 0x82, 0xd3, 0xe4, 0x93, 0x02, - 0x0a, 0x12, 0x08, 0x2f, 0x76, 0x31, 0x2f, 0x69, 0x6e, 0x66, 0x6f, 0x12, 0x74, 0x0a, 0x12, 0x47, - 0x65, 0x74, 0x42, 0x6f, 0x61, 0x72, 0x64, 0x69, 0x6e, 0x67, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, - 0x73, 0x12, 0x21, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x42, 0x6f, - 0x61, 0x72, 0x64, 0x69, 0x6e, 0x67, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x1a, 0x22, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x30, 0x0a, 0x05, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x52, + 0x6f, 0x75, 0x6e, 0x64, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x48, + 0x00, 0x52, 0x05, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x12, 0x33, 0x0a, 0x06, 0x72, 0x65, 0x64, 0x65, + 0x65, 0x6d, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, + 0x31, 0x2e, 0x52, 0x65, 0x64, 0x65, 0x65, 0x6d, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, + 0x69, 0x6f, 0x6e, 0x48, 0x00, 0x52, 0x06, 0x72, 0x65, 0x64, 0x65, 0x65, 0x6d, 0x42, 0x04, 0x0a, + 0x02, 0x74, 0x78, 0x22, 0xd8, 0x01, 0x0a, 0x10, 0x52, 0x6f, 0x75, 0x6e, 0x64, 0x54, 0x72, 0x61, + 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x78, 0x69, 0x64, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x78, 0x69, 0x64, 0x12, 0x31, 0x0a, 0x0b, + 0x73, 0x70, 0x65, 0x6e, 0x74, 0x5f, 0x76, 0x74, 0x78, 0x6f, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, + 0x0b, 0x32, 0x10, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x4f, 0x75, 0x74, 0x70, 0x6f, + 0x69, 0x6e, 0x74, 0x52, 0x0a, 0x73, 0x70, 0x65, 0x6e, 0x74, 0x56, 0x74, 0x78, 0x6f, 0x73, 0x12, + 0x35, 0x0a, 0x0f, 0x73, 0x70, 0x65, 0x6e, 0x64, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x76, 0x74, 0x78, + 0x6f, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, + 0x31, 0x2e, 0x56, 0x74, 0x78, 0x6f, 0x52, 0x0e, 0x73, 0x70, 0x65, 0x6e, 0x64, 0x61, 0x62, 0x6c, + 0x65, 0x56, 0x74, 0x78, 0x6f, 0x73, 0x12, 0x46, 0x0a, 0x16, 0x63, 0x6c, 0x61, 0x69, 0x6d, 0x65, + 0x64, 0x5f, 0x62, 0x6f, 0x61, 0x72, 0x64, 0x69, 0x6e, 0x67, 0x5f, 0x75, 0x74, 0x78, 0x6f, 0x73, + 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, + 0x4f, 0x75, 0x74, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x52, 0x14, 0x63, 0x6c, 0x61, 0x69, 0x6d, 0x65, + 0x64, 0x42, 0x6f, 0x61, 0x72, 0x64, 0x69, 0x6e, 0x67, 0x55, 0x74, 0x78, 0x6f, 0x73, 0x22, 0x91, + 0x01, 0x0a, 0x11, 0x52, 0x65, 0x64, 0x65, 0x65, 0x6d, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, + 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x78, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x04, 0x74, 0x78, 0x69, 0x64, 0x12, 0x31, 0x0a, 0x0b, 0x73, 0x70, 0x65, 0x6e, + 0x74, 0x5f, 0x76, 0x74, 0x78, 0x6f, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x10, 0x2e, + 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x4f, 0x75, 0x74, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x52, + 0x0a, 0x73, 0x70, 0x65, 0x6e, 0x74, 0x56, 0x74, 0x78, 0x6f, 0x73, 0x12, 0x35, 0x0a, 0x0f, 0x73, + 0x70, 0x65, 0x6e, 0x64, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x76, 0x74, 0x78, 0x6f, 0x73, 0x18, 0x03, + 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x56, 0x74, + 0x78, 0x6f, 0x52, 0x0e, 0x73, 0x70, 0x65, 0x6e, 0x64, 0x61, 0x62, 0x6c, 0x65, 0x56, 0x74, 0x78, + 0x6f, 0x73, 0x2a, 0x98, 0x01, 0x0a, 0x0a, 0x52, 0x6f, 0x75, 0x6e, 0x64, 0x53, 0x74, 0x61, 0x67, + 0x65, 0x12, 0x1b, 0x0a, 0x17, 0x52, 0x4f, 0x55, 0x4e, 0x44, 0x5f, 0x53, 0x54, 0x41, 0x47, 0x45, + 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x1c, + 0x0a, 0x18, 0x52, 0x4f, 0x55, 0x4e, 0x44, 0x5f, 0x53, 0x54, 0x41, 0x47, 0x45, 0x5f, 0x52, 0x45, + 0x47, 0x49, 0x53, 0x54, 0x52, 0x41, 0x54, 0x49, 0x4f, 0x4e, 0x10, 0x01, 0x12, 0x1c, 0x0a, 0x18, + 0x52, 0x4f, 0x55, 0x4e, 0x44, 0x5f, 0x53, 0x54, 0x41, 0x47, 0x45, 0x5f, 0x46, 0x49, 0x4e, 0x41, + 0x4c, 0x49, 0x5a, 0x41, 0x54, 0x49, 0x4f, 0x4e, 0x10, 0x02, 0x12, 0x19, 0x0a, 0x15, 0x52, 0x4f, + 0x55, 0x4e, 0x44, 0x5f, 0x53, 0x54, 0x41, 0x47, 0x45, 0x5f, 0x46, 0x49, 0x4e, 0x41, 0x4c, 0x49, + 0x5a, 0x45, 0x44, 0x10, 0x03, 0x12, 0x16, 0x0a, 0x12, 0x52, 0x4f, 0x55, 0x4e, 0x44, 0x5f, 0x53, + 0x54, 0x41, 0x47, 0x45, 0x5f, 0x46, 0x41, 0x49, 0x4c, 0x45, 0x44, 0x10, 0x04, 0x32, 0xeb, 0x0d, + 0x0a, 0x0a, 0x41, 0x72, 0x6b, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x4c, 0x0a, 0x07, + 0x47, 0x65, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x16, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, + 0x2e, 0x47, 0x65, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, + 0x17, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x49, 0x6e, 0x66, 0x6f, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x10, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x0a, + 0x12, 0x08, 0x2f, 0x76, 0x31, 0x2f, 0x69, 0x6e, 0x66, 0x6f, 0x12, 0x74, 0x0a, 0x12, 0x47, 0x65, 0x74, 0x42, 0x6f, 0x61, 0x72, 0x64, 0x69, 0x6e, 0x67, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x17, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x11, - 0x3a, 0x01, 0x2a, 0x22, 0x0c, 0x2f, 0x76, 0x31, 0x2f, 0x62, 0x6f, 0x61, 0x72, 0x64, 0x69, 0x6e, - 0x67, 0x12, 0x98, 0x01, 0x0a, 0x1a, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x49, 0x6e, + 0x12, 0x21, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x42, 0x6f, 0x61, + 0x72, 0x64, 0x69, 0x6e, 0x67, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x1a, 0x22, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, + 0x42, 0x6f, 0x61, 0x72, 0x64, 0x69, 0x6e, 0x67, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x17, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x11, 0x3a, + 0x01, 0x2a, 0x22, 0x0c, 0x2f, 0x76, 0x31, 0x2f, 0x62, 0x6f, 0x61, 0x72, 0x64, 0x69, 0x6e, 0x67, + 0x12, 0x98, 0x01, 0x0a, 0x1a, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x49, 0x6e, 0x70, + 0x75, 0x74, 0x73, 0x46, 0x6f, 0x72, 0x4e, 0x65, 0x78, 0x74, 0x52, 0x6f, 0x75, 0x6e, 0x64, 0x12, + 0x29, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, + 0x72, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x73, 0x46, 0x6f, 0x72, 0x4e, 0x65, 0x78, 0x74, 0x52, 0x6f, + 0x75, 0x6e, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2a, 0x2e, 0x61, 0x72, 0x6b, + 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x49, 0x6e, 0x70, 0x75, + 0x74, 0x73, 0x46, 0x6f, 0x72, 0x4e, 0x65, 0x78, 0x74, 0x52, 0x6f, 0x75, 0x6e, 0x64, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x23, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1d, 0x3a, 0x01, + 0x2a, 0x22, 0x18, 0x2f, 0x76, 0x31, 0x2f, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x2f, 0x72, 0x65, 0x67, + 0x69, 0x73, 0x74, 0x65, 0x72, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x73, 0x12, 0x9c, 0x01, 0x0a, 0x1b, + 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x4f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x73, 0x46, + 0x6f, 0x72, 0x4e, 0x65, 0x78, 0x74, 0x52, 0x6f, 0x75, 0x6e, 0x64, 0x12, 0x2a, 0x2e, 0x61, 0x72, + 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x4f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x73, 0x46, 0x6f, 0x72, 0x4e, 0x65, 0x78, 0x74, 0x52, 0x6f, 0x75, 0x6e, 0x64, - 0x12, 0x29, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, - 0x65, 0x72, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x73, 0x46, 0x6f, 0x72, 0x4e, 0x65, 0x78, 0x74, 0x52, - 0x6f, 0x75, 0x6e, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2a, 0x2e, 0x61, 0x72, - 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x49, 0x6e, 0x70, - 0x75, 0x74, 0x73, 0x46, 0x6f, 0x72, 0x4e, 0x65, 0x78, 0x74, 0x52, 0x6f, 0x75, 0x6e, 0x64, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x23, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1d, 0x3a, - 0x01, 0x2a, 0x22, 0x18, 0x2f, 0x76, 0x31, 0x2f, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x2f, 0x72, 0x65, - 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x73, 0x12, 0x9c, 0x01, 0x0a, - 0x1b, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x4f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x73, - 0x46, 0x6f, 0x72, 0x4e, 0x65, 0x78, 0x74, 0x52, 0x6f, 0x75, 0x6e, 0x64, 0x12, 0x2a, 0x2e, 0x61, - 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x4f, 0x75, - 0x74, 0x70, 0x75, 0x74, 0x73, 0x46, 0x6f, 0x72, 0x4e, 0x65, 0x78, 0x74, 0x52, 0x6f, 0x75, 0x6e, - 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2b, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, - 0x31, 0x2e, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x4f, 0x75, 0x74, 0x70, 0x75, 0x74, - 0x73, 0x46, 0x6f, 0x72, 0x4e, 0x65, 0x78, 0x74, 0x52, 0x6f, 0x75, 0x6e, 0x64, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x24, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1e, 0x3a, 0x01, 0x2a, - 0x22, 0x19, 0x2f, 0x76, 0x31, 0x2f, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x2f, 0x72, 0x65, 0x67, 0x69, - 0x73, 0x74, 0x65, 0x72, 0x4f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x73, 0x12, 0x7d, 0x0a, 0x10, 0x53, - 0x75, 0x62, 0x6d, 0x69, 0x74, 0x54, 0x72, 0x65, 0x65, 0x4e, 0x6f, 0x6e, 0x63, 0x65, 0x73, 0x12, - 0x1f, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x54, - 0x72, 0x65, 0x65, 0x4e, 0x6f, 0x6e, 0x63, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x1a, 0x20, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x75, 0x62, 0x6d, 0x69, 0x74, - 0x54, 0x72, 0x65, 0x65, 0x4e, 0x6f, 0x6e, 0x63, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x22, 0x26, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x20, 0x3a, 0x01, 0x2a, 0x22, 0x1b, 0x2f, - 0x76, 0x31, 0x2f, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x2f, 0x74, 0x72, 0x65, 0x65, 0x2f, 0x73, 0x75, - 0x62, 0x6d, 0x69, 0x74, 0x4e, 0x6f, 0x6e, 0x63, 0x65, 0x73, 0x12, 0x8d, 0x01, 0x0a, 0x14, 0x53, - 0x75, 0x62, 0x6d, 0x69, 0x74, 0x54, 0x72, 0x65, 0x65, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, - 0x72, 0x65, 0x73, 0x12, 0x23, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x75, 0x62, - 0x6d, 0x69, 0x74, 0x54, 0x72, 0x65, 0x65, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, - 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x24, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, - 0x31, 0x2e, 0x53, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x54, 0x72, 0x65, 0x65, 0x53, 0x69, 0x67, 0x6e, - 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x2a, - 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x24, 0x3a, 0x01, 0x2a, 0x22, 0x1f, 0x2f, 0x76, 0x31, 0x2f, 0x72, - 0x6f, 0x75, 0x6e, 0x64, 0x2f, 0x74, 0x72, 0x65, 0x65, 0x2f, 0x73, 0x75, 0x62, 0x6d, 0x69, 0x74, - 0x53, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x12, 0x8e, 0x01, 0x0a, 0x16, 0x53, - 0x75, 0x62, 0x6d, 0x69, 0x74, 0x53, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x46, 0x6f, 0x72, 0x66, 0x65, - 0x69, 0x74, 0x54, 0x78, 0x73, 0x12, 0x25, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x53, - 0x75, 0x62, 0x6d, 0x69, 0x74, 0x53, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x46, 0x6f, 0x72, 0x66, 0x65, - 0x69, 0x74, 0x54, 0x78, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x26, 0x2e, 0x61, - 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x53, 0x69, 0x67, 0x6e, - 0x65, 0x64, 0x46, 0x6f, 0x72, 0x66, 0x65, 0x69, 0x74, 0x54, 0x78, 0x73, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x25, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1f, 0x3a, 0x01, 0x2a, 0x22, - 0x1a, 0x2f, 0x76, 0x31, 0x2f, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x2f, 0x73, 0x75, 0x62, 0x6d, 0x69, - 0x74, 0x46, 0x6f, 0x72, 0x66, 0x65, 0x69, 0x74, 0x54, 0x78, 0x73, 0x12, 0x6b, 0x0a, 0x0e, 0x47, - 0x65, 0x74, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x12, 0x1d, 0x2e, - 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x53, - 0x74, 0x72, 0x65, 0x61, 0x6d, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x61, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2b, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, + 0x2e, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x4f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x73, + 0x46, 0x6f, 0x72, 0x4e, 0x65, 0x78, 0x74, 0x52, 0x6f, 0x75, 0x6e, 0x64, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x24, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1e, 0x3a, 0x01, 0x2a, 0x22, + 0x19, 0x2f, 0x76, 0x31, 0x2f, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x2f, 0x72, 0x65, 0x67, 0x69, 0x73, + 0x74, 0x65, 0x72, 0x4f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x73, 0x12, 0x7d, 0x0a, 0x10, 0x53, 0x75, + 0x62, 0x6d, 0x69, 0x74, 0x54, 0x72, 0x65, 0x65, 0x4e, 0x6f, 0x6e, 0x63, 0x65, 0x73, 0x12, 0x1f, + 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x54, 0x72, + 0x65, 0x65, 0x4e, 0x6f, 0x6e, 0x63, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, + 0x20, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x54, + 0x72, 0x65, 0x65, 0x4e, 0x6f, 0x6e, 0x63, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x22, 0x26, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x20, 0x3a, 0x01, 0x2a, 0x22, 0x1b, 0x2f, 0x76, + 0x31, 0x2f, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x2f, 0x74, 0x72, 0x65, 0x65, 0x2f, 0x73, 0x75, 0x62, + 0x6d, 0x69, 0x74, 0x4e, 0x6f, 0x6e, 0x63, 0x65, 0x73, 0x12, 0x8d, 0x01, 0x0a, 0x14, 0x53, 0x75, + 0x62, 0x6d, 0x69, 0x74, 0x54, 0x72, 0x65, 0x65, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, + 0x65, 0x73, 0x12, 0x23, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x75, 0x62, 0x6d, + 0x69, 0x74, 0x54, 0x72, 0x65, 0x65, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x24, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, + 0x2e, 0x53, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x54, 0x72, 0x65, 0x65, 0x53, 0x69, 0x67, 0x6e, 0x61, + 0x74, 0x75, 0x72, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x2a, 0x82, + 0xd3, 0xe4, 0x93, 0x02, 0x24, 0x3a, 0x01, 0x2a, 0x22, 0x1f, 0x2f, 0x76, 0x31, 0x2f, 0x72, 0x6f, + 0x75, 0x6e, 0x64, 0x2f, 0x74, 0x72, 0x65, 0x65, 0x2f, 0x73, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x53, + 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x12, 0x8e, 0x01, 0x0a, 0x16, 0x53, 0x75, + 0x62, 0x6d, 0x69, 0x74, 0x53, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x46, 0x6f, 0x72, 0x66, 0x65, 0x69, + 0x74, 0x54, 0x78, 0x73, 0x12, 0x25, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x75, + 0x62, 0x6d, 0x69, 0x74, 0x53, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x46, 0x6f, 0x72, 0x66, 0x65, 0x69, + 0x74, 0x54, 0x78, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x26, 0x2e, 0x61, 0x72, + 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x53, 0x69, 0x67, 0x6e, 0x65, + 0x64, 0x46, 0x6f, 0x72, 0x66, 0x65, 0x69, 0x74, 0x54, 0x78, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x22, 0x25, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1f, 0x3a, 0x01, 0x2a, 0x22, 0x1a, + 0x2f, 0x76, 0x31, 0x2f, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x2f, 0x73, 0x75, 0x62, 0x6d, 0x69, 0x74, + 0x46, 0x6f, 0x72, 0x66, 0x65, 0x69, 0x74, 0x54, 0x78, 0x73, 0x12, 0x6b, 0x0a, 0x0e, 0x47, 0x65, + 0x74, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x12, 0x1d, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x53, 0x74, - 0x72, 0x65, 0x61, 0x6d, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x18, 0x82, 0xd3, - 0xe4, 0x93, 0x02, 0x12, 0x12, 0x10, 0x2f, 0x76, 0x31, 0x2f, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x2f, - 0x65, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x30, 0x01, 0x12, 0x56, 0x0a, 0x04, 0x50, 0x69, 0x6e, 0x67, - 0x12, 0x13, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x69, 0x6e, 0x67, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x14, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x50, - 0x69, 0x6e, 0x67, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x23, 0x82, 0xd3, 0xe4, - 0x93, 0x02, 0x1d, 0x12, 0x1b, 0x2f, 0x76, 0x31, 0x2f, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x2f, 0x70, - 0x69, 0x6e, 0x67, 0x2f, 0x7b, 0x70, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x5f, 0x69, 0x64, 0x7d, - 0x12, 0x64, 0x0a, 0x0d, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, - 0x74, 0x12, 0x1c, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, - 0x65, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, - 0x1d, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x50, - 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x16, - 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x10, 0x3a, 0x01, 0x2a, 0x22, 0x0b, 0x2f, 0x76, 0x31, 0x2f, 0x70, - 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x12, 0x73, 0x0a, 0x0f, 0x43, 0x6f, 0x6d, 0x70, 0x6c, 0x65, - 0x74, 0x65, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x12, 0x1e, 0x2e, 0x61, 0x72, 0x6b, 0x2e, - 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x50, 0x61, 0x79, 0x6d, 0x65, - 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x61, 0x72, 0x6b, 0x2e, - 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x50, 0x61, 0x79, 0x6d, 0x65, - 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1f, 0x82, 0xd3, 0xe4, 0x93, - 0x02, 0x19, 0x3a, 0x01, 0x2a, 0x22, 0x14, 0x2f, 0x76, 0x31, 0x2f, 0x70, 0x61, 0x79, 0x6d, 0x65, - 0x6e, 0x74, 0x2f, 0x63, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x12, 0x57, 0x0a, 0x08, 0x47, - 0x65, 0x74, 0x52, 0x6f, 0x75, 0x6e, 0x64, 0x12, 0x17, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, - 0x2e, 0x47, 0x65, 0x74, 0x52, 0x6f, 0x75, 0x6e, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x1a, 0x18, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x52, 0x6f, 0x75, - 0x6e, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x18, 0x82, 0xd3, 0xe4, 0x93, - 0x02, 0x12, 0x12, 0x10, 0x2f, 0x76, 0x31, 0x2f, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x2f, 0x7b, 0x74, - 0x78, 0x69, 0x64, 0x7d, 0x12, 0x64, 0x0a, 0x0c, 0x47, 0x65, 0x74, 0x52, 0x6f, 0x75, 0x6e, 0x64, - 0x42, 0x79, 0x49, 0x64, 0x12, 0x1b, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, - 0x74, 0x52, 0x6f, 0x75, 0x6e, 0x64, 0x42, 0x79, 0x49, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x1a, 0x1c, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x52, 0x6f, - 0x75, 0x6e, 0x64, 0x42, 0x79, 0x49, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, - 0x19, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x13, 0x12, 0x11, 0x2f, 0x76, 0x31, 0x2f, 0x72, 0x6f, 0x75, - 0x6e, 0x64, 0x2f, 0x69, 0x64, 0x2f, 0x7b, 0x69, 0x64, 0x7d, 0x12, 0x5d, 0x0a, 0x09, 0x4c, 0x69, - 0x73, 0x74, 0x56, 0x74, 0x78, 0x6f, 0x73, 0x12, 0x18, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, - 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x56, 0x74, 0x78, 0x6f, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x1a, 0x19, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x56, - 0x74, 0x78, 0x6f, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1b, 0x82, 0xd3, - 0xe4, 0x93, 0x02, 0x15, 0x12, 0x13, 0x2f, 0x76, 0x31, 0x2f, 0x76, 0x74, 0x78, 0x6f, 0x73, 0x2f, - 0x7b, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x7d, 0x12, 0x80, 0x01, 0x0a, 0x15, 0x47, 0x65, - 0x74, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x53, 0x74, 0x72, - 0x65, 0x61, 0x6d, 0x12, 0x24, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, + 0x72, 0x65, 0x61, 0x6d, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x61, 0x72, + 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x53, 0x74, 0x72, + 0x65, 0x61, 0x6d, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x18, 0x82, 0xd3, 0xe4, + 0x93, 0x02, 0x12, 0x12, 0x10, 0x2f, 0x76, 0x31, 0x2f, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x2f, 0x65, + 0x76, 0x65, 0x6e, 0x74, 0x73, 0x30, 0x01, 0x12, 0x56, 0x0a, 0x04, 0x50, 0x69, 0x6e, 0x67, 0x12, + 0x13, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x69, 0x6e, 0x67, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x1a, 0x14, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x69, + 0x6e, 0x67, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x23, 0x82, 0xd3, 0xe4, 0x93, + 0x02, 0x1d, 0x12, 0x1b, 0x2f, 0x76, 0x31, 0x2f, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x2f, 0x70, 0x69, + 0x6e, 0x67, 0x2f, 0x7b, 0x70, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x5f, 0x69, 0x64, 0x7d, 0x12, + 0x64, 0x0a, 0x0d, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, + 0x12, 0x1c, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, + 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, + 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x50, 0x61, + 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x16, 0x82, + 0xd3, 0xe4, 0x93, 0x02, 0x10, 0x3a, 0x01, 0x2a, 0x22, 0x0b, 0x2f, 0x76, 0x31, 0x2f, 0x70, 0x61, + 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x12, 0x73, 0x0a, 0x0f, 0x43, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, + 0x65, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x12, 0x1e, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, + 0x31, 0x2e, 0x43, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, + 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, + 0x31, 0x2e, 0x43, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, + 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1f, 0x82, 0xd3, 0xe4, 0x93, 0x02, + 0x19, 0x3a, 0x01, 0x2a, 0x22, 0x14, 0x2f, 0x76, 0x31, 0x2f, 0x70, 0x61, 0x79, 0x6d, 0x65, 0x6e, + 0x74, 0x2f, 0x63, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x12, 0x57, 0x0a, 0x08, 0x47, 0x65, + 0x74, 0x52, 0x6f, 0x75, 0x6e, 0x64, 0x12, 0x17, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, + 0x47, 0x65, 0x74, 0x52, 0x6f, 0x75, 0x6e, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, + 0x18, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x52, 0x6f, 0x75, 0x6e, + 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x18, 0x82, 0xd3, 0xe4, 0x93, 0x02, + 0x12, 0x12, 0x10, 0x2f, 0x76, 0x31, 0x2f, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x2f, 0x7b, 0x74, 0x78, + 0x69, 0x64, 0x7d, 0x12, 0x64, 0x0a, 0x0c, 0x47, 0x65, 0x74, 0x52, 0x6f, 0x75, 0x6e, 0x64, 0x42, + 0x79, 0x49, 0x64, 0x12, 0x1b, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, + 0x52, 0x6f, 0x75, 0x6e, 0x64, 0x42, 0x79, 0x49, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x1a, 0x1c, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x52, 0x6f, 0x75, + 0x6e, 0x64, 0x42, 0x79, 0x49, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x19, + 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x13, 0x12, 0x11, 0x2f, 0x76, 0x31, 0x2f, 0x72, 0x6f, 0x75, 0x6e, + 0x64, 0x2f, 0x69, 0x64, 0x2f, 0x7b, 0x69, 0x64, 0x7d, 0x12, 0x5d, 0x0a, 0x09, 0x4c, 0x69, 0x73, + 0x74, 0x56, 0x74, 0x78, 0x6f, 0x73, 0x12, 0x18, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, + 0x4c, 0x69, 0x73, 0x74, 0x56, 0x74, 0x78, 0x6f, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x1a, 0x19, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x56, 0x74, + 0x78, 0x6f, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1b, 0x82, 0xd3, 0xe4, + 0x93, 0x02, 0x15, 0x12, 0x13, 0x2f, 0x76, 0x31, 0x2f, 0x76, 0x74, 0x78, 0x6f, 0x73, 0x2f, 0x7b, + 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x7d, 0x12, 0x80, 0x01, 0x0a, 0x15, 0x47, 0x65, 0x74, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x53, 0x74, 0x72, 0x65, - 0x61, 0x6d, 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, + 0x61, 0x6d, 0x12, 0x24, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x54, + 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x53, 0x74, 0x72, 0x65, 0x61, + 0x6d, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x25, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, + 0x31, 0x2e, 0x47, 0x65, 0x74, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, + 0x73, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, + 0x18, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x12, 0x12, 0x10, 0x2f, 0x76, 0x31, 0x2f, 0x74, 0x72, 0x61, + 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x30, 0x01, 0x42, 0x92, 0x01, 0x0a, 0x0a, + 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x72, 0x6b, 0x2e, 0x76, 0x31, 0x42, 0x0c, 0x53, 0x65, 0x72, 0x76, + 0x69, 0x63, 0x65, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x3d, 0x67, 0x69, 0x74, 0x68, + 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x61, 0x72, 0x6b, 0x2d, 0x6e, 0x65, 0x74, 0x77, 0x6f, + 0x72, 0x6b, 0x2f, 0x61, 0x72, 0x6b, 0x2f, 0x61, 0x70, 0x69, 0x2d, 0x73, 0x70, 0x65, 0x63, 0x2f, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x67, 0x65, 0x6e, 0x2f, 0x61, 0x72, 0x6b, + 0x2f, 0x76, 0x31, 0x3b, 0x61, 0x72, 0x6b, 0x76, 0x31, 0xa2, 0x02, 0x03, 0x41, 0x58, 0x58, 0xaa, + 0x02, 0x06, 0x41, 0x72, 0x6b, 0x2e, 0x56, 0x31, 0xca, 0x02, 0x06, 0x41, 0x72, 0x6b, 0x5c, 0x56, + 0x31, 0xe2, 0x02, 0x12, 0x41, 0x72, 0x6b, 0x5c, 0x56, 0x31, 0x5c, 0x47, 0x50, 0x42, 0x4d, 0x65, + 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0xea, 0x02, 0x07, 0x41, 0x72, 0x6b, 0x3a, 0x3a, 0x56, 0x31, + 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( diff --git a/pkg/client-sdk/client/client.go b/pkg/client-sdk/client/client.go index e29175ae..ce1adcd3 100644 --- a/pkg/client-sdk/client/client.go +++ b/pkg/client-sdk/client/client.go @@ -97,7 +97,8 @@ type Vtxo struct { Pubkey string Amount uint64 RoundTxid string - ExpiresAt *time.Time + ExpiresAt time.Time + CreatedAt time.Time RedeemTx string IsOOR bool SpentBy string diff --git a/pkg/client-sdk/client/grpc/types.go b/pkg/client-sdk/client/grpc/types.go index 6070af74..d0ab1a99 100644 --- a/pkg/client-sdk/client/grpc/types.go +++ b/pkg/client-sdk/client/grpc/types.go @@ -112,11 +112,6 @@ type vtxo struct { } func (v vtxo) toVtxo() client.Vtxo { - var expiresAt *time.Time - if v.GetExpireAt() > 0 { - t := time.Unix(v.GetExpireAt(), 0) - expiresAt = &t - } return client.Vtxo{ Outpoint: client.Outpoint{ Txid: v.GetOutpoint().GetTxid(), @@ -124,11 +119,12 @@ func (v vtxo) toVtxo() client.Vtxo { }, Amount: v.GetAmount(), RoundTxid: v.GetRoundTxid(), - ExpiresAt: expiresAt, + ExpiresAt: time.Unix(v.GetExpireAt(), 0), IsOOR: v.GetIsOor(), RedeemTx: v.GetRedeemTx(), SpentBy: v.GetSpentBy(), Pubkey: v.GetPubkey(), + CreatedAt: time.Unix(v.GetCreatedAt(), 0), } } diff --git a/pkg/client-sdk/client/rest/client.go b/pkg/client-sdk/client/rest/client.go index ec8a6f86..e9dabfce 100644 --- a/pkg/client-sdk/client/rest/client.go +++ b/pkg/client-sdk/client/rest/client.go @@ -474,67 +474,8 @@ func (a *restClient) ListVtxos( return nil, nil, err } - spendableVtxos := make([]client.Vtxo, 0, len(resp.Payload.SpendableVtxos)) - for _, v := range resp.Payload.SpendableVtxos { - var expiresAt *time.Time - if v.ExpireAt != "" && v.ExpireAt != "0" { - expAt, err := strconv.Atoi(v.ExpireAt) - if err != nil { - return nil, nil, err - } - t := time.Unix(int64(expAt), 0) - expiresAt = &t - } - - amount, err := strconv.Atoi(v.Amount) - if err != nil { - return nil, nil, err - } - - spendableVtxos = append(spendableVtxos, client.Vtxo{ - Outpoint: client.Outpoint{ - Txid: v.Outpoint.Txid, - VOut: uint32(v.Outpoint.Vout), - }, - Amount: uint64(amount), - RoundTxid: v.RoundTxid, - ExpiresAt: expiresAt, - IsOOR: v.IsOor, - RedeemTx: v.RedeemTx, - SpentBy: v.SpentBy, - Pubkey: v.Pubkey, - }) - } - - spentVtxos := make([]client.Vtxo, 0, len(resp.Payload.SpentVtxos)) - for _, v := range resp.Payload.SpentVtxos { - var expiresAt *time.Time - if v.ExpireAt != "" && v.ExpireAt != "0" { - expAt, err := strconv.Atoi(v.ExpireAt) - if err != nil { - return nil, nil, err - } - t := time.Unix(int64(expAt), 0) - expiresAt = &t - } - - amount, err := strconv.Atoi(v.Amount) - if err != nil { - return nil, nil, err - } - - spentVtxos = append(spentVtxos, client.Vtxo{ - Outpoint: client.Outpoint{ - Txid: v.Outpoint.Txid, - VOut: uint32(v.Outpoint.Vout), - }, - Amount: uint64(amount), - RoundTxid: v.RoundTxid, - ExpiresAt: expiresAt, - SpentBy: v.SpentBy, - Pubkey: v.Pubkey, - }) - } + spendableVtxos := vtxosFromRest(resp.Payload.SpendableVtxos) + spentVtxos := vtxosFromRest(resp.Payload.SpentVtxos) return spendableVtxos, spentVtxos, nil } @@ -676,14 +617,21 @@ func outpointsFromRest(restOutpoints []*models.V1Outpoint) []client.Outpoint { func vtxosFromRest(restVtxos []*models.V1Vtxo) []client.Vtxo { vtxos := make([]client.Vtxo, len(restVtxos)) for i, v := range restVtxos { - var expiresAt *time.Time + var expiresAt, createdAt time.Time if v.ExpireAt != "" && v.ExpireAt != "0" { expAt, err := strconv.Atoi(v.ExpireAt) if err != nil { return nil } - t := time.Unix(int64(expAt), 0) - expiresAt = &t + expiresAt = time.Unix(int64(expAt), 0) + } + + if v.CreatedAt != "" && v.CreatedAt != "0" { + creaAt, err := strconv.Atoi(v.CreatedAt) + if err != nil { + return nil + } + createdAt = time.Unix(int64(creaAt), 0) } amount, err := strconv.Atoi(v.Amount) @@ -703,6 +651,7 @@ func vtxosFromRest(restVtxos []*models.V1Vtxo) []client.Vtxo { RedeemTx: v.RedeemTx, IsOOR: v.IsOor, SpentBy: v.SpentBy, + CreatedAt: createdAt, } } return vtxos diff --git a/pkg/client-sdk/client/rest/service/models/v1_vtxo.go b/pkg/client-sdk/client/rest/service/models/v1_vtxo.go index 5ae090e1..09f2a7ae 100644 --- a/pkg/client-sdk/client/rest/service/models/v1_vtxo.go +++ b/pkg/client-sdk/client/rest/service/models/v1_vtxo.go @@ -21,6 +21,9 @@ type V1Vtxo struct { // amount Amount string `json:"amount,omitempty"` + // created at + CreatedAt string `json:"createdAt,omitempty"` + // expire at ExpireAt string `json:"expireAt,omitempty"` diff --git a/pkg/client-sdk/client_test.go b/pkg/client-sdk/client_test.go index aa889feb..a11c7334 100644 --- a/pkg/client-sdk/client_test.go +++ b/pkg/client-sdk/client_test.go @@ -3,6 +3,7 @@ package arksdk import ( "encoding/json" "fmt" + "os" "strconv" "testing" "time" @@ -12,488 +13,143 @@ import ( "github.com/stretchr/testify/require" ) +type fixture struct { + name string + ignoreTxs map[string]struct{} + spendableVtxos []client.Vtxo + spentVtxos []client.Vtxo + expectedTxHistory []sdktypes.Transaction +} + func TestVtxosToTxs(t *testing.T) { - tests := []struct { - name string - fixture string - want []sdktypes.Transaction - }{ - { - name: "Alice Before Sending Async", - fixture: aliceBeforeSendingAsync, - want: []sdktypes.Transaction{}, - }, - { - name: "Alice After Sending Async", - fixture: aliceAfterSendingAsync, - want: []sdktypes.Transaction{ - { - TransactionKey: sdktypes.TransactionKey{ - RedeemTxid: "94fa598302f17f00c8881e742ec0ce2f8c8d16f3d54fe6ba0fb7d13a493d84ad", - }, - Amount: 1000, - Type: sdktypes.TxSent, - CreatedAt: time.Unix(1726054898, 0), - }, - }, - }, - { - name: "Bob Before Claiming Async", - fixture: bobBeforeClaimingAsync, - want: []sdktypes.Transaction{ - { - TransactionKey: sdktypes.TransactionKey{ - RedeemTxid: "94fa598302f17f00c8881e742ec0ce2f8c8d16f3d54fe6ba0fb7d13a493d84ad", - }, - Amount: 1000, - Type: sdktypes.TxReceived, - CreatedAt: time.Unix(1726054898, 0), - }, - { - TransactionKey: sdktypes.TransactionKey{ - RedeemTxid: "766fc46ba5c2da41cd4c4bc0566e0f4e0f24c184c41acd3bead5cd7b11120367", - }, - Amount: 2000, - Type: sdktypes.TxReceived, - CreatedAt: time.Unix(1726486359, 0), - }, - }, - }, - { - name: "Bob After Claiming Async", - fixture: bobAfterClaimingAsync, - want: []sdktypes.Transaction{ - { - TransactionKey: sdktypes.TransactionKey{ - RedeemTxid: "94fa598302f17f00c8881e742ec0ce2f8c8d16f3d54fe6ba0fb7d13a493d84ad", - }, - Amount: 1000, - Type: sdktypes.TxReceived, - CreatedAt: time.Unix(1726054898, 0), - }, - { - TransactionKey: sdktypes.TransactionKey{ - RedeemTxid: "766fc46ba5c2da41cd4c4bc0566e0f4e0f24c184c41acd3bead5cd7b11120367", - }, - Amount: 2000, - Type: sdktypes.TxReceived, - CreatedAt: time.Unix(1726486359, 0), - }, - }, - }, - { - name: "Bob After Sending Async", - fixture: bobAfterSendingAsync, - want: []sdktypes.Transaction{ - { - TransactionKey: sdktypes.TransactionKey{ - RedeemTxid: "94fa598302f17f00c8881e742ec0ce2f8c8d16f3d54fe6ba0fb7d13a493d84ad", - }, - Amount: 1000, - Type: sdktypes.TxReceived, - CreatedAt: time.Unix(1726054898, 0), - }, - { - TransactionKey: sdktypes.TransactionKey{ - RedeemTxid: "766fc46ba5c2da41cd4c4bc0566e0f4e0f24c184c41acd3bead5cd7b11120367", - }, - Amount: 2000, - Type: sdktypes.TxReceived, - CreatedAt: time.Unix(1726486359, 0), - }, - { - TransactionKey: sdktypes.TransactionKey{ - RedeemTxid: "23c3a885f0ea05f7bdf83f3bf7f8ac9dc3f791ad292f4e63a6f53fa5e4935ab0", - }, - Amount: 2100, - Type: sdktypes.TxSent, - CreatedAt: time.Unix(1726503865, 0), - }, - }, - }, - } + fixtures, err := loadFixtures() + require.NoError(t, err) - for _, tt := range tests { + for _, tt := range fixtures { t.Run(tt.name, func(t *testing.T) { - vtxos, ignoreTxs, err := loadFixtures(tt.fixture) - if err != nil { - t.Fatalf("failed to load fixture: %s", err) - } - got, err := vtxosToTxsCovenantless(30, vtxos.spendable, vtxos.spent, ignoreTxs) + txHistory, err := vtxosToTxsCovenantless(tt.spendableVtxos, tt.spentVtxos) require.NoError(t, err) - require.Len(t, got, len(tt.want)) + require.Len(t, txHistory, len(tt.expectedTxHistory)) // Check each expected transaction, excluding CreatedAt - for i, wantTx := range tt.want { - gotTx := got[i] - require.Equal(t, wantTx.RoundTxid, gotTx.RoundTxid) - require.Equal(t, wantTx.RedeemTxid, gotTx.RedeemTxid) + for i, wantTx := range tt.expectedTxHistory { + gotTx := txHistory[i] + require.Equal(t, wantTx.TransactionKey, gotTx.TransactionKey) require.Equal(t, int(wantTx.Amount), int(gotTx.Amount)) require.Equal(t, wantTx.Type, gotTx.Type) + require.Equal(t, wantTx.Settled, gotTx.Settled) + require.Equal(t, wantTx.CreatedAt, gotTx.CreatedAt) } }) } } -type vtxos struct { - spendable []client.Vtxo - spent []client.Vtxo +type vtxo struct { + Outpoint struct { + Txid string `json:"txid"` + VOut uint32 `json:"vout"` + } `json:"outpoint"` + Amount string `json:"amount"` + Spent bool `json:"spent"` + RoundTxid string `json:"roundTxid"` + SpentBy string `json:"spentBy"` + ExpiresAt string `json:"expireAt"` + Swept bool `json:"swept"` + RedeemTx string `json:"redeemTx"` + CreatedAt string `json:"createdAt"` + IsOOR bool `json:"isOor"` } -func loadFixtures(jsonStr string) (vtxos, map[string]struct{}, error) { - var data struct { - IgnoreTxs []string `json:"ignoreTxs"` - SpendableVtxos []struct { - Outpoint struct { - Txid string `json:"txid"` - Vout uint32 `json:"vout"` - } `json:"outpoint"` - Receiver struct { - Address string `json:"address"` - Amount string `json:"amount"` - } `json:"receiver"` - Spent bool `json:"spent"` - PoolTxid string `json:"poolTxid"` - SpentBy string `json:"spentBy"` - ExpireAt string `json:"expireAt"` - Swept bool `json:"swept"` - RedeemTx string `json:"redeemTx"` - } `json:"spendableVtxos"` - SpentVtxos []struct { - Outpoint struct { - Txid string `json:"txid"` - Vout uint32 `json:"vout"` - } `json:"outpoint"` - Receiver struct { - Address string `json:"address"` - Amount string `json:"amount"` - } `json:"receiver"` - Spent bool `json:"spent"` - PoolTxid string `json:"poolTxid"` - SpentBy string `json:"spentBy"` - ExpireAt string `json:"expireAt"` - Swept bool `json:"swept"` - RedeemTx string `json:"redeemTx"` - } `json:"spentVtxos"` - } - - if err := json.Unmarshal([]byte(jsonStr), &data); err != nil { - return vtxos{}, nil, err - } +type vtxos []vtxo - spendable := make([]client.Vtxo, len(data.SpendableVtxos)) - for i, vtxo := range data.SpendableVtxos { - expireAt, err := parseTimestamp(vtxo.ExpireAt) - if err != nil { - return vtxos{}, nil, err - } - amount, err := parseAmount(vtxo.Receiver.Amount) - if err != nil { - return vtxos{}, nil, err - } - spendable[i] = client.Vtxo{ +func (v vtxos) parse() []client.Vtxo { + list := make([]client.Vtxo, 0, len(v)) + for _, vv := range v { + list = append(list, client.Vtxo{ Outpoint: client.Outpoint{ - Txid: vtxo.Outpoint.Txid, - VOut: vtxo.Outpoint.Vout, + Txid: vv.Outpoint.Txid, + VOut: vv.Outpoint.VOut, }, - Amount: amount, - RoundTxid: vtxo.PoolTxid, - ExpiresAt: &expireAt, - RedeemTx: vtxo.RedeemTx, - SpentBy: vtxo.SpentBy, - } + Amount: parseAmount(vv.Amount), + RoundTxid: vv.RoundTxid, + ExpiresAt: parseTimestamp(vv.ExpiresAt), + CreatedAt: parseTimestamp(vv.CreatedAt), + RedeemTx: vv.RedeemTx, + SpentBy: vv.SpentBy, + IsOOR: vv.IsOOR, + }) } + return list +} - spent := make([]client.Vtxo, len(data.SpentVtxos)) - for i, vtxo := range data.SpentVtxos { - expireAt, err := parseTimestamp(vtxo.ExpireAt) - if err != nil { - return vtxos{}, nil, err - } - amount, err := parseAmount(vtxo.Receiver.Amount) - if err != nil { - return vtxos{}, nil, err - } - spent[i] = client.Vtxo{ - Outpoint: client.Outpoint{ - Txid: vtxo.Outpoint.Txid, - VOut: vtxo.Outpoint.Vout, - }, - Amount: amount, - RoundTxid: vtxo.PoolTxid, - ExpiresAt: &expireAt, - RedeemTx: vtxo.RedeemTx, - SpentBy: vtxo.SpentBy, - } - } +type tx struct { + RoundTxid string `json:"roundTxid"` + RedeemTxid string `json:"redeemTxid"` + Amount string `json:"amount"` + Type string `json:"type"` + Settled bool `json:"settled"` + CreatedAt string `json:"createdAt"` +} - vtxos := vtxos{ - spendable: spendable, - spent: spent, - } +type txs []tx - ignoreTxs := make(map[string]struct{}) - for _, tx := range data.IgnoreTxs { - ignoreTxs[tx] = struct{}{} +func (t txs) parse() []sdktypes.Transaction { + list := make([]sdktypes.Transaction, 0, len(t)) + for _, tx := range t { + list = append(list, sdktypes.Transaction{ + TransactionKey: sdktypes.TransactionKey{ + RedeemTxid: tx.RedeemTxid, + RoundTxid: tx.RoundTxid, + }, + Amount: parseAmount(tx.Amount), + Type: sdktypes.TxType(tx.Type), + Settled: tx.Settled, + CreatedAt: parseTimestamp(tx.CreatedAt), + }, + ) } - - return vtxos, ignoreTxs, nil + return list } -func parseAmount(amountStr string) (uint64, error) { - amount, err := strconv.ParseUint(amountStr, 10, 64) +func loadFixtures() ([]fixture, error) { + data := make([]struct { + Name string `json:"name"` + IgnoreTxs []string `json:"ignoreTxs"` + SpendableVtxos vtxos `json:"spendableVtxos"` + SpentVtxos vtxos `json:"spentVtxos"` + ExpectedTxHistory txs `json:"expectedTxHistory"` + }, 0) + buf, err := os.ReadFile("test_data.json") if err != nil { - return 0, err + return nil, fmt.Errorf("failed to read fixtures: %s", err) } - - return amount, nil -} - -func parseTimestamp(timestamp string) (time.Time, error) { - seconds, err := strconv.ParseInt(timestamp, 10, 64) - if err != nil { - return time.Time{}, fmt.Errorf("invalid timestamp format: %w", err) + if err := json.Unmarshal(buf, &data); err != nil { + return nil, fmt.Errorf("failed to unmarshal fixtures: %s", err) } - return time.Unix(seconds, 0), nil + fixtures := make([]fixture, 0, len(data)) + for _, r := range data { + indexedTxs := make(map[string]struct{}) + for _, tx := range r.IgnoreTxs { + indexedTxs[tx] = struct{}{} + } + fixtures = append(fixtures, fixture{ + name: r.Name, + ignoreTxs: indexedTxs, + spendableVtxos: r.SpendableVtxos.parse(), + spentVtxos: r.SpentVtxos.parse(), + expectedTxHistory: r.ExpectedTxHistory.parse(), + }) + } + return fixtures, nil } -// bellow fixtures are used in bellow scenario: -// 1. Alice boards with 20OOO -// 2. Alice sends 1000 to Bob -// 3. Bob claims 1000 -var ( - aliceBeforeSendingAsync = ` - { - "ignoreTxs": [ - "377fa2fbd27c82bdbc095478384c88b6c75432c0ef464189e49c965194446cdf" - ], - "spendableVtxos": [ - { - "outpoint": { - "txid": "69ccb6520e0b91ac1cbaa459b16ec1e3ff5f6349990b0d149dd8e6c6485d316c", - "vout": 0 - }, - "receiver": { - "address": "tark1qwnakvl59d5wckz9lqhhdav0uvns6uu3zkc6hg65gh0kgh6wve9pwqa0qjq9ajm57ss4m7wutyhp3vexxzgkn2r5awtzytp8qfk8exfn4vm5d8ff", - "amount": "20000" - }, - "spent": false, - "poolTxid": "377fa2fbd27c82bdbc095478384c88b6c75432c0ef464189e49c965194446cdf", - "spentBy": "", - "expireAt": "1726054928", - "swept": false - } - ], - "spentVtxos": [] - }` - - aliceAfterSendingAsync = ` - { - "ignoreTxs": [ - "377fa2fbd27c82bdbc095478384c88b6c75432c0ef464189e49c965194446cdf" - ], - "spendableVtxos": [ - { - "outpoint": { - "txid": "94fa598302f17f00c8881e742ec0ce2f8c8d16f3d54fe6ba0fb7d13a493d84ad", - "vout": 1 - }, - "receiver": { - "address": "tark1qwnakvl59d5wckz9lqhhdav0uvns6uu3zkc6hg65gh0kgh6wve9pwqa0qjq9ajm57ss4m7wutyhp3vexxzgkn2r5awtzytp8qfk8exfn4vm5d8ff", - "amount": "19000" - }, - "spent": false, - "poolTxid": "", - "spentBy": "", - "expireAt": "1726054928", - "swept": false, - "redeemTx": "cHNidP8BAIkCAAAAAWwxXUjG5tidFA0LmUljX//jwW6xWaS6HKyRCw5StsxpAAAAAAD/////AugDAAAAAAAAIlEgt2eR8LtqTP7yUcQtSydeGrRiHnVmHHnZwYjdC23G7MZwSQAAAAAAACJRIKfUzf/o9h+r0v9y4nmyOt9qO8EkDumQPQZGTbEv8fSFAAAAAAABASsgTgAAAAAAACJRIKfUzf/o9h+r0v9y4nmyOt9qO8EkDumQPQZGTbEv8fSFIgYDp9sz9Cto7FhF+C929Y/jJw1zkRWxq6NURd9kX05mShcYAAAAAFYAAIAAAACAAQAAgAAAAAAAAAAAQRSvBIBey3T0IV353FkuGLMmMJFpqHTrliIsJwJsfJkzq7J0B8bQ0j9842h5lUfOWcbj2TeoFx6OCpgoHIqWIBhHQAFqkBLiRmP3AZ8MS77s1QIWZswMV3L72D9gN0f0MbD6XHkmzZeC1clF3uzxr+13wsF0vcFe29Zl3e2gAhMNGYVCFcFQkpt0waBJVLeLS2A16XpeB4paDyjsltVHv+6azoA6wKRtST8P7teUpSF4DAEbfJj5OIXITx5QGbZns/AtxqGyRSCn2zP0K2jsWEX4L3b1j+MnDXORFbGro1RF32RfTmZKF60grwSAXst09CFd+dxZLhizJjCRaah065YiLCcCbHyZM6uswCEWp9sz9Cto7FhF+C929Y/jJw1zkRWxq6NURd9kX05mShc5AbJ0B8bQ0j9842h5lUfOWcbj2TeoFx6OCpgoHIqWIBhHAAAAAFYAAIAAAACAAQAAgAAAAAAAAAAAARcgUJKbdMGgSVS3i0tgNel6XgeKWg8o7JbVR7/ums6AOsAAAAA=" - } - ], - "spentVtxos": [ - { - "outpoint": { - "txid": "69ccb6520e0b91ac1cbaa459b16ec1e3ff5f6349990b0d149dd8e6c6485d316c", - "vout": 0 - }, - "receiver": { - "address": "tark1qwnakvl59d5wckz9lqhhdav0uvns6uu3zkc6hg65gh0kgh6wve9pwqa0qjq9ajm57ss4m7wutyhp3vexxzgkn2r5awtzytp8qfk8exfn4vm5d8ff", - "amount": "20000" - }, - "spent": true, - "poolTxid": "377fa2fbd27c82bdbc095478384c88b6c75432c0ef464189e49c965194446cdf", - "spentBy": "94fa598302f17f00c8881e742ec0ce2f8c8d16f3d54fe6ba0fb7d13a493d84ad", - "expireAt": "1726054928", - "swept": false, - "redeemTx": "" - } - ] - }` +func parseAmount(amountStr string) uint64 { + amount, _ := strconv.ParseUint(amountStr, 10, 64) + return amount +} - bobBeforeClaimingAsync = ` - { - "spendableVtxos": [ - { - "outpoint": { - "txid": "94fa598302f17f00c8881e742ec0ce2f8c8d16f3d54fe6ba0fb7d13a493d84ad", - "vout": 0 - }, - "receiver": { - "address": "tark1qwnakvl59d5wckz9lqhhdav0uvns6uu3zkc6hg65gh0kgh6wve9pwqa8vzms5xcr7pqgt0sw88vc287dse5rw6fnxuk9f08frf8amxjcrya0tkgt", - "amount": "1000" - }, - "spent": false, - "poolTxid": "", - "spentBy": "", - "expireAt": "1726054928", - "swept": false, - "redeemTx": "cHNidP8BAIkCAAAAAWwxXUjG5tidFA0LmUljX//jwW6xWaS6HKyRCw5StsxpAAAAAAD/////AugDAAAAAAAAIlEgt2eR8LtqTP7yUcQtSydeGrRiHnVmHHnZwYjdC23G7MZwSQAAAAAAACJRIKfUzf/o9h+r0v9y4nmyOt9qO8EkDumQPQZGTbEv8fSFAAAAAAABASsgTgAAAAAAACJRIKfUzf/o9h+r0v9y4nmyOt9qO8EkDumQPQZGTbEv8fSFIgYDp9sz9Cto7FhF+C929Y/jJw1zkRWxq6NURd9kX05mShcYAAAAAFYAAIAAAACAAQAAgAAAAAAAAAAAQRSvBIBey3T0IV353FkuGLMmMJFpqHTrliIsJwJsfJkzq7J0B8bQ0j9842h5lUfOWcbj2TeoFx6OCpgoHIqWIBhHQAFqkBLiRmP3AZ8MS77s1QIWZswMV3L72D9gN0f0MbD6XHkmzZeC1clF3uzxr+13wsF0vcFe29Zl3e2gAhMNGYVCFcFQkpt0waBJVLeLS2A16XpeB4paDyjsltVHv+6azoA6wKRtST8P7teUpSF4DAEbfJj5OIXITx5QGbZns/AtxqGyRSCn2zP0K2jsWEX4L3b1j+MnDXORFbGro1RF32RfTmZKF60grwSAXst09CFd+dxZLhizJjCRaah065YiLCcCbHyZM6uswCEWp9sz9Cto7FhF+C929Y/jJw1zkRWxq6NURd9kX05mShc5AbJ0B8bQ0j9842h5lUfOWcbj2TeoFx6OCpgoHIqWIBhHAAAAAFYAAIAAAACAAQAAgAAAAAAAAAAAARcgUJKbdMGgSVS3i0tgNel6XgeKWg8o7JbVR7/ums6AOsAAAAA=" - }, - { - "outpoint": { - "txid": "766fc46ba5c2da41cd4c4bc0566e0f4e0f24c184c41acd3bead5cd7b11120367", - "vout": 0 - }, - "receiver": { - "address": "tark1qgw4gpt40zet7q399hv78z7pdak5sxlcgzhy6y6qq7hw3syeudc6xqsws4tegt5r88eahx7g5try2ua4n9rflsncpresjfwcrq80k0d3systnm98", - "amount": "2000" - }, - "spent": false, - "poolTxid": "", - "spentBy": "", - "expireAt": "1726486389", - "swept": false, - "redeemTx": "cHNidP8BAIkCAAAAARH6LJRGP/pFIkD/o5bBp8fXAhjl8yjfN7MhJsxdt5lrAQAAAAD/////AtAHAAAAAAAAIlEguuBh3KQUVZp+NHV2sixQ/mrsngCuLCGXzsgJPC1FzY7ANQ8AAAAAACJRIP7uXXtl4jLUcVVQU+sX7WFXmx2H6iCMzn7gye0v1Y8JAAAAAAABAStYPg8AAAAAACJRIP7uXXtl4jLUcVVQU+sX7WFXmx2H6iCMzn7gye0v1Y8JIgYCHVQFdXiyvwIlLdnji8FvbUgb+ECuTRNAB67owJnjcaMYAAAAAFYAAIAAAACAAQAAgAAAAAAAAAAAQRSc1yjQ/vHRHev23fKGANLvbOhkNYmGtmRWt8fSszlOJUzRFbnfxd1fq9gIEpaI0vrZww8tlZ94iEL75QoaIbVqQJsPdLYf7fAoXO82VoqwYHu1WevE4g6LxUGBPzfd96q5EEZkoW5qqg+v5dWJUEY467Q6qZLFHwziUaB3KEY8yEpCFcBQkpt0waBJVLeLS2A16XpeB4paDyjsltVHv+6azoA6wO5D2Mh3x0XNGxFCS67GNughkENFodpFeVpZjn76chI8RSAdVAV1eLK/AiUt2eOLwW9tSBv4QK5NE0AHrujAmeNxo60gnNco0P7x0R3r9t3yhgDS72zoZDWJhrZkVrfH0rM5TiWswCEWHVQFdXiyvwIlLdnji8FvbUgb+ECuTRNAB67owJnjcaM5AUzRFbnfxd1fq9gIEpaI0vrZww8tlZ94iEL75QoaIbVqAAAAAFYAAIAAAACAAQAAgAAAAAAAAAAAARcgUJKbdMGgSVS3i0tgNel6XgeKWg8o7JbVR7/ums6AOsAAAAA=" - } - ], - "spentVtxos": [] - }` - bobAfterClaimingAsync = ` - { - "spendableVtxos": [ - { - "outpoint": { - "txid": "11cba4cbb06290fb7426157efe439940e1e4143d51bdd20567d7bfd28f0d9090", - "vout": 0 - }, - "receiver": { - "address": "tark1qgw4gpt40zet7q399hv78z7pdak5sxlcgzhy6y6qq7hw3syeudc6xqsws4tegt5r88eahx7g5try2ua4n9rflsncpresjfwcrq80k0d3systnm98", - "amount": "3000" - }, - "spent": false, - "poolTxid": "d6684a5b9e6939dccdf07d1f0eaf7fdd7b31de4d123e63e400d23de739800d4e", - "spentBy": "", - "expireAt": "1726503895", - "swept": false, - "redeemTx": "" - } - ], - "spentVtxos": [ - { - "outpoint": { - "txid": "94fa598302f17f00c8881e742ec0ce2f8c8d16f3d54fe6ba0fb7d13a493d84ad", - "vout": 0 - }, - "receiver": { - "address": "tark1qwnakvl59d5wckz9lqhhdav0uvns6uu3zkc6hg65gh0kgh6wve9pwqa8vzms5xcr7pqgt0sw88vc287dse5rw6fnxuk9f08frf8amxjcrya0tkgt", - "amount": "1000" - }, - "spent": true, - "poolTxid": "", - "spentBy": "d6684a5b9e6939dccdf07d1f0eaf7fdd7b31de4d123e63e400d23de739800d4e", - "expireAt": "1726054928", - "swept": false, - "redeemTx": "cHNidP8BAIkCAAAAAWwxXUjG5tidFA0LmUljX//jwW6xWaS6HKyRCw5StsxpAAAAAAD/////AugDAAAAAAAAIlEgt2eR8LtqTP7yUcQtSydeGrRiHnVmHHnZwYjdC23G7MZwSQAAAAAAACJRIKfUzf/o9h+r0v9y4nmyOt9qO8EkDumQPQZGTbEv8fSFAAAAAAABASsgTgAAAAAAACJRIKfUzf/o9h+r0v9y4nmyOt9qO8EkDumQPQZGTbEv8fSFIgYDp9sz9Cto7FhF+C929Y/jJw1zkRWxq6NURd9kX05mShcYAAAAAFYAAIAAAACAAQAAgAAAAAAAAAAAQRSvBIBey3T0IV353FkuGLMmMJFpqHTrliIsJwJsfJkzq7J0B8bQ0j9842h5lUfOWcbj2TeoFx6OCpgoHIqWIBhHQAFqkBLiRmP3AZ8MS77s1QIWZswMV3L72D9gN0f0MbD6XHkmzZeC1clF3uzxr+13wsF0vcFe29Zl3e2gAhMNGYVCFcFQkpt0waBJVLeLS2A16XpeB4paDyjsltVHv+6azoA6wKRtST8P7teUpSF4DAEbfJj5OIXITx5QGbZns/AtxqGyRSCn2zP0K2jsWEX4L3b1j+MnDXORFbGro1RF32RfTmZKF60grwSAXst09CFd+dxZLhizJjCRaah065YiLCcCbHyZM6uswCEWp9sz9Cto7FhF+C929Y/jJw1zkRWxq6NURd9kX05mShc5AbJ0B8bQ0j9842h5lUfOWcbj2TeoFx6OCpgoHIqWIBhHAAAAAFYAAIAAAACAAQAAgAAAAAAAAAAAARcgUJKbdMGgSVS3i0tgNel6XgeKWg8o7JbVR7/ums6AOsAAAAA=" - }, - { - "outpoint": { - "txid": "766fc46ba5c2da41cd4c4bc0566e0f4e0f24c184c41acd3bead5cd7b11120367", - "vout": 0 - }, - "receiver": { - "address": "tark1qgw4gpt40zet7q399hv78z7pdak5sxlcgzhy6y6qq7hw3syeudc6xqsws4tegt5r88eahx7g5try2ua4n9rflsncpresjfwcrq80k0d3systnm98", - "amount": "2000" - }, - "spent": true, - "poolTxid": "", - "spentBy": "d6684a5b9e6939dccdf07d1f0eaf7fdd7b31de4d123e63e400d23de739800d4e", - "expireAt": "1726486389", - "swept": false, - "redeemTx": "cHNidP8BAIkCAAAAARH6LJRGP/pFIkD/o5bBp8fXAhjl8yjfN7MhJsxdt5lrAQAAAAD/////AtAHAAAAAAAAIlEguuBh3KQUVZp+NHV2sixQ/mrsngCuLCGXzsgJPC1FzY7ANQ8AAAAAACJRIP7uXXtl4jLUcVVQU+sX7WFXmx2H6iCMzn7gye0v1Y8JAAAAAAABAStYPg8AAAAAACJRIP7uXXtl4jLUcVVQU+sX7WFXmx2H6iCMzn7gye0v1Y8JIgYCHVQFdXiyvwIlLdnji8FvbUgb+ECuTRNAB67owJnjcaMYAAAAAFYAAIAAAACAAQAAgAAAAAAAAAAAQRSc1yjQ/vHRHev23fKGANLvbOhkNYmGtmRWt8fSszlOJUzRFbnfxd1fq9gIEpaI0vrZww8tlZ94iEL75QoaIbVqQJsPdLYf7fAoXO82VoqwYHu1WevE4g6LxUGBPzfd96q5EEZkoW5qqg+v5dWJUEY467Q6qZLFHwziUaB3KEY8yEpCFcBQkpt0waBJVLeLS2A16XpeB4paDyjsltVHv+6azoA6wO5D2Mh3x0XNGxFCS67GNughkENFodpFeVpZjn76chI8RSAdVAV1eLK/AiUt2eOLwW9tSBv4QK5NE0AHrujAmeNxo60gnNco0P7x0R3r9t3yhgDS72zoZDWJhrZkVrfH0rM5TiWswCEWHVQFdXiyvwIlLdnji8FvbUgb+ECuTRNAB67owJnjcaM5AUzRFbnfxd1fq9gIEpaI0vrZww8tlZ94iEL75QoaIbVqAAAAAFYAAIAAAACAAQAAgAAAAAAAAAAAARcgUJKbdMGgSVS3i0tgNel6XgeKWg8o7JbVR7/ums6AOsAAAAA=" - } - ] - }` - bobAfterSendingAsync = ` - { - "spendableVtxos": [ - { - "outpoint": { - "txid": "23c3a885f0ea05f7bdf83f3bf7f8ac9dc3f791ad292f4e63a6f53fa5e4935ab0", - "vout": 0 - }, - "receiver": { - "address": "tark1qwnakvl59d5wckz9lqhhdav0uvns6uu3zkc6hg65gh0kgh6wve9pwqa8vzms5xcr7pqgt0sw88vc287dse5rw6fnxuk9f08frf8amxjcrya0tkgt", - "amount": "900" - }, - "spent": false, - "poolTxid": "", - "spentBy": "", - "expireAt": "1726503895", - "swept": false, - "redeemTx": "cHNidP8BAIkCAAAAAdOK9YzYw1ceJznqJxtRXGe0KeHj6CLcLtqLVwcbMCivAAAAAAD/////ArgLAAAAAAAAIlEgC39Vxhw3dIa4heHgFS6X4XwDl1mBggsKLVTBwF1h3qEgegEAAAAAACJRIMkktfIFxFNTtAmy3K0p+7JqVn2kcA0P6y2vJ1QX2zysAAAAAAABASughgEAAAAAACJRIMkktfIFxFNTtAmy3K0p+7JqVn2kcA0P6y2vJ1QX2zysIgYDjGeMfnNwCrU45iB3iRqiFdWTADaiJ968+w3ruFuq1F0YAAAAAFYAAIAAAACAAQAAgAAAAAAAAAAAQRTYEOuHJ0hyLBGzY8nSHpD2F1nby5/XQ5Sh2Je+cQ5Wsx0ZucLmB/LLspxMRN9JcJn3Q2KJRMhhg7415cCg1d0gQNSvgaBk/1WLYqQxCKxCfv8ViVJ7vjBxvNO5tc2FEDy27V9cIrfL1jPJoVrhgPZT0GwY7dkVZS7saIKI03CbipBCFcBQkpt0waBJVLeLS2A16XpeB4paDyjsltVHv+6azoA6wPKiQ0JM6aw2kcUByijEbOydM3gTIVCGN/69q+dmyxcqRSCMZ4x+c3AKtTjmIHeJGqIV1ZMANqIn3rz7Deu4W6rUXa0g2BDrhydIciwRs2PJ0h6Q9hdZ28uf10OUodiXvnEOVrOswCEWjGeMfnNwCrU45iB3iRqiFdWTADaiJ968+w3ruFuq1F05AR0ZucLmB/LLspxMRN9JcJn3Q2KJRMhhg7415cCg1d0gAAAAAFYAAIAAAACAAQAAgAAAAAAAAAAAARcgUJKbdMGgSVS3i0tgNel6XgeKWg8o7JbVR7/ums6AOsAAAAA=" - } - ], - "spentVtxos": [ - { - "outpoint": { - "txid": "94fa598302f17f00c8881e742ec0ce2f8c8d16f3d54fe6ba0fb7d13a493d84ad", - "vout": 0 - }, - "receiver": { - "address": "tark1qwnakvl59d5wckz9lqhhdav0uvns6uu3zkc6hg65gh0kgh6wve9pwqa8vzms5xcr7pqgt0sw88vc287dse5rw6fnxuk9f08frf8amxjcrya0tkgt", - "amount": "1000" - }, - "spent": true, - "poolTxid": "", - "spentBy": "d6684a5b9e6939dccdf07d1f0eaf7fdd7b31de4d123e63e400d23de739800d4e", - "expireAt": "1726054928", - "swept": false, - "redeemTx": "cHNidP8BAIkCAAAAAWwxXUjG5tidFA0LmUljX//jwW6xWaS6HKyRCw5StsxpAAAAAAD/////AugDAAAAAAAAIlEgt2eR8LtqTP7yUcQtSydeGrRiHnVmHHnZwYjdC23G7MZwSQAAAAAAACJRIKfUzf/o9h+r0v9y4nmyOt9qO8EkDumQPQZGTbEv8fSFAAAAAAABASsgTgAAAAAAACJRIKfUzf/o9h+r0v9y4nmyOt9qO8EkDumQPQZGTbEv8fSFIgYDp9sz9Cto7FhF+C929Y/jJw1zkRWxq6NURd9kX05mShcYAAAAAFYAAIAAAACAAQAAgAAAAAAAAAAAQRSvBIBey3T0IV353FkuGLMmMJFpqHTrliIsJwJsfJkzq7J0B8bQ0j9842h5lUfOWcbj2TeoFx6OCpgoHIqWIBhHQAFqkBLiRmP3AZ8MS77s1QIWZswMV3L72D9gN0f0MbD6XHkmzZeC1clF3uzxr+13wsF0vcFe29Zl3e2gAhMNGYVCFcFQkpt0waBJVLeLS2A16XpeB4paDyjsltVHv+6azoA6wKRtST8P7teUpSF4DAEbfJj5OIXITx5QGbZns/AtxqGyRSCn2zP0K2jsWEX4L3b1j+MnDXORFbGro1RF32RfTmZKF60grwSAXst09CFd+dxZLhizJjCRaah065YiLCcCbHyZM6uswCEWp9sz9Cto7FhF+C929Y/jJw1zkRWxq6NURd9kX05mShc5AbJ0B8bQ0j9842h5lUfOWcbj2TeoFx6OCpgoHIqWIBhHAAAAAFYAAIAAAACAAQAAgAAAAAAAAAAAARcgUJKbdMGgSVS3i0tgNel6XgeKWg8o7JbVR7/ums6AOsAAAAA=" - }, - { - "outpoint": { - "txid": "766fc46ba5c2da41cd4c4bc0566e0f4e0f24c184c41acd3bead5cd7b11120367", - "vout": 0 - }, - "receiver": { - "address": "tark1qgw4gpt40zet7q399hv78z7pdak5sxlcgzhy6y6qq7hw3syeudc6xqsws4tegt5r88eahx7g5try2ua4n9rflsncpresjfwcrq80k0d3systnm98", - "amount": "2000" - }, - "spent": true, - "poolTxid": "", - "spentBy": "d6684a5b9e6939dccdf07d1f0eaf7fdd7b31de4d123e63e400d23de739800d4e", - "expireAt": "1726486389", - "swept": false, - "pending": true, - "redeemTx": "cHNidP8BAIkCAAAAARH6LJRGP/pFIkD/o5bBp8fXAhjl8yjfN7MhJsxdt5lrAQAAAAD/////AtAHAAAAAAAAIlEguuBh3KQUVZp+NHV2sixQ/mrsngCuLCGXzsgJPC1FzY7ANQ8AAAAAACJRIP7uXXtl4jLUcVVQU+sX7WFXmx2H6iCMzn7gye0v1Y8JAAAAAAABAStYPg8AAAAAACJRIP7uXXtl4jLUcVVQU+sX7WFXmx2H6iCMzn7gye0v1Y8JIgYCHVQFdXiyvwIlLdnji8FvbUgb+ECuTRNAB67owJnjcaMYAAAAAFYAAIAAAACAAQAAgAAAAAAAAAAAQRSc1yjQ/vHRHev23fKGANLvbOhkNYmGtmRWt8fSszlOJUzRFbnfxd1fq9gIEpaI0vrZww8tlZ94iEL75QoaIbVqQJsPdLYf7fAoXO82VoqwYHu1WevE4g6LxUGBPzfd96q5EEZkoW5qqg+v5dWJUEY467Q6qZLFHwziUaB3KEY8yEpCFcBQkpt0waBJVLeLS2A16XpeB4paDyjsltVHv+6azoA6wO5D2Mh3x0XNGxFCS67GNughkENFodpFeVpZjn76chI8RSAdVAV1eLK/AiUt2eOLwW9tSBv4QK5NE0AHrujAmeNxo60gnNco0P7x0R3r9t3yhgDS72zoZDWJhrZkVrfH0rM5TiWswCEWHVQFdXiyvwIlLdnji8FvbUgb+ECuTRNAB67owJnjcaM5AUzRFbnfxd1fq9gIEpaI0vrZww8tlZ94iEL75QoaIbVqAAAAAFYAAIAAAACAAQAAgAAAAAAAAAAAARcgUJKbdMGgSVS3i0tgNel6XgeKWg8o7JbVR7/ums6AOsAAAAA=" - }, - { - "outpoint": { - "txid": "11cba4cbb06290fb7426157efe439940e1e4143d51bdd20567d7bfd28f0d9090", - "vout": 0 - }, - "receiver": { - "address": "tark1qgw4gpt40zet7q399hv78z7pdak5sxlcgzhy6y6qq7hw3syeudc6xqsws4tegt5r88eahx7g5try2ua4n9rflsncpresjfwcrq80k0d3systnm98", - "amount": "3000" - }, - "spent": false, - "poolTxid": "d6684a5b9e6939dccdf07d1f0eaf7fdd7b31de4d123e63e400d23de739800d4e", - "spentBy": "23c3a885f0ea05f7bdf83f3bf7f8ac9dc3f791ad292f4e63a6f53fa5e4935ab0", - "expireAt": "1726503895", - "swept": false, - "redeemTx": "" - } - ] - }` -) +func parseTimestamp(timestamp string) time.Time { + seconds, _ := strconv.ParseInt(timestamp, 10, 64) + return time.Unix(seconds, 0) +} diff --git a/pkg/client-sdk/covenant_client.go b/pkg/client-sdk/covenant_client.go index 2af5c0df..495d2a72 100644 --- a/pkg/client-sdk/covenant_client.go +++ b/pkg/client-sdk/covenant_client.go @@ -1679,7 +1679,7 @@ func (a *covenantArkClient) getOffchainBalance( for _, vtxo := range vtxos { balance += vtxo.Amount - if vtxo.ExpiresAt != nil { + if !vtxo.ExpiresAt.IsZero() { expiration := vtxo.ExpiresAt.Unix() if _, ok := amountByExpiration[expiration]; !ok { @@ -1724,7 +1724,7 @@ func (a *covenantArkClient) getVtxos( for i, vtxo := range spendableVtxos { if vtxo.Txid == vtxoTxid { - spendableVtxos[i].ExpiresAt = expiration + spendableVtxos[i].ExpiresAt = *expiration break } } @@ -1800,7 +1800,7 @@ func vtxosToTxsCovenant( }, Amount: uint64(math.Abs(float64(amount))), Type: txType, - CreatedAt: getCreatedAtFromExpiry(roundLifetime, *v.ExpiresAt), + CreatedAt: getCreatedAtFromExpiry(roundLifetime, v.ExpiresAt), }) } diff --git a/pkg/client-sdk/covenantless_client.go b/pkg/client-sdk/covenantless_client.go index 447f9c00..d7c7e34b 100644 --- a/pkg/client-sdk/covenantless_client.go +++ b/pkg/client-sdk/covenantless_client.go @@ -399,6 +399,7 @@ func (a *covenantlessArkClient) processTransactionEvent( }, Amount: v.Amount, ExpiresAt: v.ExpiresAt, + CreatedAt: v.CreatedAt, RedeemTx: event.Round.Txid, Pending: false, SpentBy: v.SpentBy, @@ -412,7 +413,7 @@ func (a *covenantlessArkClient) processTransactionEvent( }, Amount: v.Amount, Type: types.TxReceived, - CreatedAt: time.Now(), //TODO is this ok? + CreatedAt: v.CreatedAt, }) } } @@ -477,6 +478,7 @@ func (a *covenantlessArkClient) processTransactionEvent( }, Amount: v.Amount, ExpiresAt: v.ExpiresAt, + CreatedAt: v.CreatedAt, RedeemTx: event.Redeem.Txid, Pending: true, SpentBy: v.SpentBy, @@ -492,7 +494,7 @@ func (a *covenantlessArkClient) processTransactionEvent( }, Amount: inputAmount - outputAmount, Type: types.TxSent, - CreatedAt: time.Now(), //TODO is this ok? + CreatedAt: time.Now(), } if err := a.store.TransactionStore(). @@ -509,6 +511,7 @@ func (a *covenantlessArkClient) processTransactionEvent( }, Amount: v.Amount, ExpiresAt: v.ExpiresAt, + CreatedAt: v.CreatedAt, RedeemTx: event.Redeem.Txid, Pending: true, SpentBy: v.SpentBy, @@ -521,7 +524,7 @@ func (a *covenantlessArkClient) processTransactionEvent( }, Amount: v.Amount, Type: types.TxReceived, - CreatedAt: time.Now(), //TODO is this ok? + CreatedAt: v.CreatedAt, } if err := a.store.TransactionStore(). AddTransactions(context.Background(), []types.Transaction{tx}); err != nil { @@ -1047,34 +1050,32 @@ func (a *covenantlessArkClient) Settle(ctx context.Context) (string, error) { func (a *covenantlessArkClient) GetTransactionHistory( ctx context.Context, ) ([]types.Transaction, error) { - if a.Config.WithTransactionFeed { - return a.store.TransactionStore().GetAllTransactions(ctx) - } - if a.Config == nil { return nil, fmt.Errorf("client not initialized") } + if a.Config.WithTransactionFeed { + return a.store.TransactionStore().GetAllTransactions(ctx) + } + spendableVtxos, spentVtxos, err := a.ListVtxos(ctx) if err != nil { return nil, err } - boardingTxs, ignoreVtxos, err := a.getBoardingTxs(ctx) + boardingTxs, _, err := a.getBoardingTxs(ctx) if err != nil { return nil, err } - offchainTxs, err := vtxosToTxsCovenantless( - a.Config.RoundLifetime, spendableVtxos, spentVtxos, ignoreVtxos, - ) + offchainTxs, err := vtxosToTxsCovenantless(spendableVtxos, spentVtxos) if err != nil { return nil, err } txs := append(boardingTxs, offchainTxs...) // Sort the slice by age - sort.Slice(txs, func(i, j int) bool { + sort.SliceStable(txs, func(i, j int) bool { txi := txs[i] txj := txs[j] if txi.CreatedAt.Equal(txj.CreatedAt) { @@ -1486,6 +1487,11 @@ func (a *covenantlessArkClient) handleRoundStream( receivers []client.Output, roundEphemeralKey *secp256k1.PrivateKey, ) (string, error) { + round, err := a.client.GetRound(ctx, "") + if err != nil { + return "", err + } + eventsCh, close, err := a.client.GetEventStream(ctx, paymentID) if err != nil { return "", err @@ -1520,14 +1526,32 @@ func (a *covenantlessArkClient) handleRoundStream( if notify.Err != nil { return "", notify.Err } + if notify.Event == nil { + if step != roundFinalization { + continue + } + res, err := a.client.Ping(ctx, paymentID) + if err != nil { + return "", err + } + if e, ok := res.(client.RoundFinalizedEvent); ok { + log.Infof("round completed %s", e.Txid) + return e.Txid, nil + } + time.Sleep(time.Second) + } switch event := notify.Event; event.(type) { case client.RoundFinalizedEvent: if step != roundFinalization { continue } + log.Infof("round completed %s", event.(client.RoundFinalizedEvent).Txid) return event.(client.RoundFinalizedEvent).Txid, nil case client.RoundFailedEvent: - return "", fmt.Errorf("round failed: %s", event.(client.RoundFailedEvent).Reason) + if event.(client.RoundFailedEvent).ID == round.ID { + return "", fmt.Errorf("round failed: %s", event.(client.RoundFailedEvent).Reason) + } + continue case client.RoundSigningStartedEvent: pingStop() if step != start { @@ -2170,7 +2194,7 @@ func (a *covenantlessArkClient) getOffchainBalance( for _, vtxo := range vtxos { balance += vtxo.Amount - if vtxo.ExpiresAt != nil { + if !vtxo.ExpiresAt.IsZero() { expiration := vtxo.ExpiresAt.Unix() if _, ok := amountByExpiration[expiration]; !ok { @@ -2321,7 +2345,7 @@ func (a *covenantlessArkClient) getVtxos( for i, vtxo := range spendableVtxos { if vtxo.Txid == vtxoTxid { - spendableVtxos[i].ExpiresAt = expiration + spendableVtxos[i].ExpiresAt = *expiration break } } @@ -2373,120 +2397,76 @@ func findVtxosBySpentBy(allVtxos []client.Vtxo, txid string) (vtxos []client.Vtx } func vtxosToTxsCovenantless( - roundLifetime int64, spendable, spent []client.Vtxo, ignoreVtxos map[string]struct{}, + spendable, spent []client.Vtxo, ) ([]types.Transaction, error) { - transactions := make([]types.Transaction, 0) - indexedTxs := make(map[string]types.Transaction) - settledVtxos := make(map[string]struct{}) - - // First, loop over all vtxos to find those that have been settled - // (they have round txid instead of redeem tx) + txs := make([]types.Transaction, 0) + vtxosByRound := make(map[string][]client.Vtxo) for _, v := range append(spendable, spent...) { - _, ok1 := ignoreVtxos[v.Txid] - _, ok2 := ignoreVtxos[v.RoundTxid] - if ok1 || ok2 { - continue - } - if len(v.RoundTxid) > 0 { - settledVtxos[v.RoundTxid] = struct{}{} + if _, ok := vtxosByRound[v.RoundTxid]; !ok { + vtxosByRound[v.RoundTxid] = make([]client.Vtxo, 0) } + vtxosByRound[v.RoundTxid] = append(vtxosByRound[v.RoundTxid], v) + } + + for round := range vtxosByRound { + sort.SliceStable(vtxosByRound[round], func(i, j int) bool { + return vtxosByRound[round][i].CreatedAt.Before(vtxosByRound[round][j].CreatedAt) + }) } - for _, v := range spent { - // If the vtxo is settled, add the record to the tx history. - if _, ok := settledVtxos[v.SpentBy]; ok { - transactions = append(transactions, types.Transaction{ + for _, vtxos := range vtxosByRound { + v := vtxos[0] + if v.IsOOR { + txs = append(txs, types.Transaction{ TransactionKey: types.TransactionKey{ RedeemTxid: v.Txid, }, Amount: v.Amount, Type: types.TxReceived, - CreatedAt: getCreatedAtFromExpiry(roundLifetime, *v.ExpiresAt), - Settled: true, + CreatedAt: v.CreatedAt, }) - // Delete any duplicate in the indexed list. - delete(indexedTxs, v.SpentBy) - // Ignore the spendable vtxo created by the settlement. - ignoreVtxos[v.SpentBy] = struct{}{} - continue - } - - // If this vtxo spent another one => subtract the amount to find the sent amount. - if tx, ok := indexedTxs[v.Txid]; ok { - tx.Amount -= v.Amount - if v.RedeemTx == "" { - tx.RedeemTxid = "" - } else { - tx.RoundTxid = "" - } - indexedTxs[v.Txid] = tx } - - // Add a transaction to the indexed list if not existing. - // This is an intermediate tx state that is updated in the next iterations. - tx, ok := indexedTxs[v.SpentBy] - if !ok { - indexedTxs[v.SpentBy] = types.Transaction{ - TransactionKey: types.TransactionKey{ - RedeemTxid: v.SpentBy, - RoundTxid: v.SpentBy, - }, - Amount: v.Amount, - Type: types.TxSent, - CreatedAt: getCreatedAtFromExpiry(roundLifetime, *v.ExpiresAt), + if len(vtxos) > 1 { + for _, v := range vtxos[1:] { + var tx types.Transaction + if v.VOut > 0 { + var spentAmount uint64 + for _, vv := range vtxos { + if vv.SpentBy == v.Txid { + spentAmount += vv.Amount + } + } + tx = types.Transaction{ + TransactionKey: types.TransactionKey{ + RedeemTxid: v.Txid, + }, + Amount: spentAmount - v.Amount, + Type: types.TxSent, + CreatedAt: v.CreatedAt, + } + } else { + tx = types.Transaction{ + TransactionKey: types.TransactionKey{ + RedeemTxid: v.Txid, + }, + Amount: v.Amount, + Type: types.TxReceived, + CreatedAt: v.CreatedAt, + } + } + txs = append(txs, tx) } - continue } - - // Otherwise add the amount of this vtxo to the one of the tx in the indexed list. - tx.Amount += v.Amount - indexedTxs[v.SpentBy] = tx } - for _, v := range spendable { - // Ignore the vtxo eventually. - _, ok1 := ignoreVtxos[v.Txid] - _, ok2 := ignoreVtxos[v.RoundTxid] - if ok1 || ok2 { - continue - } - txid := v.RoundTxid - if txid == "" { - txid = v.Txid - } - - tx, ok := indexedTxs[txid] - // If there is no track of records, add a received tx record in the history. - if !ok { - redeemTxid := "" - if v.RoundTxid == "" { - redeemTxid = v.Txid - } - transactions = append(transactions, types.Transaction{ - TransactionKey: types.TransactionKey{ - RedeemTxid: redeemTxid, - RoundTxid: v.RoundTxid, - }, - Amount: v.Amount, - Type: types.TxReceived, - CreatedAt: getCreatedAtFromExpiry(roundLifetime, *v.ExpiresAt), - }) - continue - } - - // Otherwise subtract the amount to find the actual spent amount. - tx.Amount -= v.Amount - if v.RedeemTx == "" { - tx.RedeemTxid = "" - } else { - tx.RoundTxid = "" + sort.SliceStable(txs, func(i, j int) bool { + txi := txs[i] + txj := txs[j] + if txi.CreatedAt.Equal(txj.CreatedAt) { + return txi.Type > txj.Type } - indexedTxs[txid] = tx - } - - for _, tx := range indexedTxs { - transactions = append(transactions, tx) - } + return txi.CreatedAt.After(txj.CreatedAt) + }) - return transactions, nil + return txs, nil } diff --git a/pkg/client-sdk/example/covenantless/alice_to_bob.go b/pkg/client-sdk/example/covenantless/alice_to_bob.go index 04d2c655..13066ae0 100644 --- a/pkg/client-sdk/example/covenantless/alice_to_bob.go +++ b/pkg/client-sdk/example/covenantless/alice_to_bob.go @@ -128,7 +128,7 @@ func main() { fmt.Println("") log.Infof("alice is sending %d sats to bob offchain...", amount) - if _, err = aliceArkClient.SendOffChain(ctx, false, receivers); err != nil { + if _, err = aliceArkClient.SendAsync(ctx, false, receivers); err != nil { log.Fatal(err) } diff --git a/pkg/client-sdk/internal/utils/utils.go b/pkg/client-sdk/internal/utils/utils.go index 256ca867..eaeabbe4 100644 --- a/pkg/client-sdk/internal/utils/utils.go +++ b/pkg/client-sdk/internal/utils/utils.go @@ -6,7 +6,6 @@ import ( "crypto/rand" "crypto/sha256" "fmt" - "runtime/debug" "sort" "sync" @@ -20,7 +19,7 @@ import ( "github.com/decred/dcrd/dcrec/secp256k1/v4" "github.com/vulpemventures/go-elements/address" "github.com/vulpemventures/go-elements/network" - "golang.org/x/crypto/scrypt" + "golang.org/x/crypto/pbkdf2" ) func CoinSelect( @@ -37,11 +36,7 @@ func CoinSelect( if sortByExpirationTime { // sort vtxos by expiration (older first) sort.SliceStable(vtxos, func(i, j int) bool { - if vtxos[i].ExpiresAt == nil || vtxos[j].ExpiresAt == nil { - return false - } - - return vtxos[i].ExpiresAt.Before(*vtxos[j].ExpiresAt) + return vtxos[i].ExpiresAt.Before(vtxos[j].ExpiresAt) }) sort.SliceStable(boardingUtxos, func(i, j int) bool { @@ -190,12 +185,7 @@ func HashPassword(password []byte) []byte { return hash[:] } -func EncryptAES128(privateKey, password []byte) ([]byte, error) { - // Due to https://github.com/golang/go/issues/7168. - // This call makes sure that memory is freed in case the GC doesn't do that - // right after the encryption/decryption. - defer debug.FreeOSMemory() - +func EncryptAES256(privateKey, password []byte) ([]byte, error) { if len(privateKey) == 0 { return nil, fmt.Errorf("missing plaintext private key") } @@ -227,9 +217,7 @@ func EncryptAES128(privateKey, password []byte) ([]byte, error) { return ciphertext, nil } -func DecryptAES128(encrypted, password []byte) ([]byte, error) { - defer debug.FreeOSMemory() - +func DecryptAES256(encrypted, password []byte) ([]byte, error) { if len(encrypted) == 0 { return nil, fmt.Errorf("missing encrypted mnemonic") } @@ -275,12 +263,8 @@ func deriveKey(password, salt []byte) ([]byte, []byte, error) { return nil, nil, err } } - // 2^20 = 1048576 recommended length for key-stretching - // check the doc for other recommended values: - // https://godoc.org/golang.org/x/crypto/scrypt - key, err := scrypt.Key(password, salt, 1048576, 8, 1, 32) - if err != nil { - return nil, nil, err - } + iterations := 10000 + keySize := 32 + key := pbkdf2.Key(password, salt, iterations, keySize, sha256.New) return key, salt, nil } diff --git a/pkg/client-sdk/test_data.json b/pkg/client-sdk/test_data.json new file mode 100644 index 00000000..364e9d6a --- /dev/null +++ b/pkg/client-sdk/test_data.json @@ -0,0 +1,297 @@ +[ + { + "name": "alice before sending", + "ignoreTxs": [ + "c16ae0d917ac400790da18456015975521bec6e1d1962ad728c0070808c564e8" + ], + "spendableVtxos": [ + { + "outpoint": { + "txid": "2646aea682389e1739a33a617d1f3ee28ccc7e4e16210936cece7a823e37527e", + "vout": 0 + }, + "spent": false, + "roundTxid": "c16ae0d917ac400790da18456015975521bec6e1d1962ad728c0070808c564e8", + "spentBy": "", + "expireAt": "1730934927", + "swept": false, + "isOor": false, + "redeemTx": "", + "amount": "20000", + "pubkey": "fc3ed4822401bc75858c6a7e08a974c68a777bcf87e6ba535d48afab7d00cf5f", + "createdAt": "1730330127" + } + ], + "spentVtxos": [], + "expectedTxHistory": [] + }, + { + "name": "alice after sending", + "ignoreTxs": [ + "c16ae0d917ac400790da18456015975521bec6e1d1962ad728c0070808c564e8" + ], + "spendableVtxos": [ + { + "outpoint": { + "txid": "33fd8ca9ea9cfb53802c42be10ae428573e19fb89484dfe536d06d43efa82034", + "vout": 1 + }, + "spent": false, + "roundTxid": "c16ae0d917ac400790da18456015975521bec6e1d1962ad728c0070808c564e8", + "spentBy": "", + "expireAt": "1730934927", + "swept": false, + "isOor": true, + "redeemTx": "cHNidP8BAIkCAAAAAX5SNz6Ces7ONgkhFk5+zIziPh99YTqjOReeOIKmrkYmAAAAAAD/////AugDAAAAAAAAIlEgqlhusFLr8eZ29t9OrzWRyQ1WqXmHGu+ptn5WGMt8DyVgSQAAAAAAACJRIPw+1IIkAbx1hYxqfgipdMaKd3vPh+a6U11Ir6t9AM9fAAAAAAABASsgTgAAAAAAACJRIPw+1IIkAbx1hYxqfgipdMaKd3vPh+a6U11Ir6t9AM9fQRRzba+JiJXvEqldJ7Squ3RSy3Tygj+kNcbZ9sdwtY13kLn0kC6mChU9UVqsaZC/ptUXmqKnA1BqwBIdRoXN3UIQQPXRlndrrsGBbJZ0P1+K+UWTytCU+O1AZbfg/APqOYkI+e7ne0BRtQVTKu2V8mEMydDRmpy5UCGcr9ZhfRRr6d1BFL+NVPnBYGBLoEcv5/9swwUPUfu0KQNTf5WWzvL0XQGlufSQLqYKFT1RWqxpkL+m1ReaoqcDUGrAEh1Ghc3dQhBANfxQ5evXPI16w88zj7VkcarvA6MUPKjTKmOTzAnYGHUb2GkQa6Ixdg+s/z+Nt5jMyL+KUsiLsuoFeat5dn6r3UIVwVCSm3TBoElUt4tLYDXpel4HiloPKOyW1Ue/7prOgDrASdfU5FE/s1XZ2TstJV7kIKp/CP0Z2eTjaXuvl4qCX/NFIHNtr4mIle8SqV0ntKq7dFLLdPKCP6Q1xtn2x3C1jXeQrSC/jVT5wWBgS6BHL+f/bMMFD1H7tCkDU3+Vls7y9F0BpazAAAAA", + "amount": "18784", + "pubkey": "fc3ed4822401bc75858c6a7e08a974c68a777bcf87e6ba535d48afab7d00cf5f", + "createdAt": "1730330256" + } + ], + "spentVtxos": [ + { + "outpoint": { + "txid": "2646aea682389e1739a33a617d1f3ee28ccc7e4e16210936cece7a823e37527e", + "vout": 0 + }, + "spent": true, + "roundTxid": "c16ae0d917ac400790da18456015975521bec6e1d1962ad728c0070808c564e8", + "spentBy": "33fd8ca9ea9cfb53802c42be10ae428573e19fb89484dfe536d06d43efa82034", + "expireAt": "1730934927", + "swept": false, + "isOor": false, + "redeemTx": "", + "amount": "20000", + "pubkey": "fc3ed4822401bc75858c6a7e08a974c68a777bcf87e6ba535d48afab7d00cf5f", + "createdAt": "1730330127" + } + ], + "expectedTxHistory": [ + { + "redeemTxid": "33fd8ca9ea9cfb53802c42be10ae428573e19fb89484dfe536d06d43efa82034", + "amount": "1216", + "type": "SENT", + "createdAt": "1730330256" + } + ] + }, + { + "name": "bob before settling", + "spendableVtxos": [ + { + "outpoint": { + "txid": "33fd8ca9ea9cfb53802c42be10ae428573e19fb89484dfe536d06d43efa82034", + "vout": 0 + }, + "spent": false, + "roundTxid": "c16ae0d917ac400790da18456015975521bec6e1d1962ad728c0070808c564e8", + "spentBy": "", + "expireAt": "1730934927", + "swept": false, + "isOor": true, + "redeemTx": "cHNidP8BAIkCAAAAAX5SNz6Ces7ONgkhFk5+zIziPh99YTqjOReeOIKmrkYmAAAAAAD/////AugDAAAAAAAAIlEgqlhusFLr8eZ29t9OrzWRyQ1WqXmHGu+ptn5WGMt8DyVgSQAAAAAAACJRIPw+1IIkAbx1hYxqfgipdMaKd3vPh+a6U11Ir6t9AM9fAAAAAAABASsgTgAAAAAAACJRIPw+1IIkAbx1hYxqfgipdMaKd3vPh+a6U11Ir6t9AM9fQRRzba+JiJXvEqldJ7Squ3RSy3Tygj+kNcbZ9sdwtY13kLn0kC6mChU9UVqsaZC/ptUXmqKnA1BqwBIdRoXN3UIQQPXRlndrrsGBbJZ0P1+K+UWTytCU+O1AZbfg/APqOYkI+e7ne0BRtQVTKu2V8mEMydDRmpy5UCGcr9ZhfRRr6d1BFL+NVPnBYGBLoEcv5/9swwUPUfu0KQNTf5WWzvL0XQGlufSQLqYKFT1RWqxpkL+m1ReaoqcDUGrAEh1Ghc3dQhBANfxQ5evXPI16w88zj7VkcarvA6MUPKjTKmOTzAnYGHUb2GkQa6Ixdg+s/z+Nt5jMyL+KUsiLsuoFeat5dn6r3UIVwVCSm3TBoElUt4tLYDXpel4HiloPKOyW1Ue/7prOgDrASdfU5FE/s1XZ2TstJV7kIKp/CP0Z2eTjaXuvl4qCX/NFIHNtr4mIle8SqV0ntKq7dFLLdPKCP6Q1xtn2x3C1jXeQrSC/jVT5wWBgS6BHL+f/bMMFD1H7tCkDU3+Vls7y9F0BpazAAAAA", + "amount": "1000", + "pubkey": "aa586eb052ebf1e676f6df4eaf3591c90d56a979871aefa9b67e5618cb7c0f25", + "createdAt": "1730330256" + }, + { + "outpoint": { + "txid": "884d85c0db6b52139c39337d54c1f20cd8c5c0d2e83109d69246a345ccc9d169", + "vout": 0 + }, + "spent": false, + "roundTxid": "a4e91c211398e0be0edad322fb74a739b1c77bb82b9e4ea94b0115b8e4dfe645", + "spentBy": "", + "expireAt": "1730935548", + "swept": false, + "isOor": true, + "redeemTx": "cHNidP8BAIkCAAAAAT7y41Cb5k0SMpEYaB/3NLlJ8leksHt08k6sK2gRlx3/AAAAAAD/////AtAHAAAAAAAAIlEgqlhusFLr8eZ29t9OrzWRyQ1WqXmHGu+ptn5WGMt8DyVoHgAAAAAAACJRIHnxG3UzOEn7H8oZrxQCVXCjoUVhUEBBgzDLu8ARqongAAAAAAABASsQJwAAAAAAACJRIHnxG3UzOEn7H8oZrxQCVXCjoUVhUEBBgzDLu8ARqongQRRzba+JiJXvEqldJ7Squ3RSy3Tygj+kNcbZ9sdwtY13kH1QsQK/Pk7/PqAmUThCuTCfbTo69ePAgzsvSuR97VgUQBSvpq/lJ7+uc8nyWwV5sCRukn5TnOybRHCjCOUPviykP6C+ue768mRDK6PxQ5FpNJhHmNLpfdTbIQwGCNIJr7pBFOchVKNhwXJqAhCx+u7ObLBb4YqW5vA1iW45rGgxtmP7fVCxAr8+Tv8+oCZROEK5MJ9tOjr148CDOy9K5H3tWBRALxMyiBhy6eGAHjj0OJ+LRFYI8PCIplSLl+SqfMLoSHZzsXkDIyDcLdV6w4Vvq4oBQN+lfKAX2IKZGB0WUGavn0IVwVCSm3TBoElUt4tLYDXpel4HiloPKOyW1Ue/7prOgDrAL6zdBmWt8+odVYaSKdWl60i5qQGel8jvirsvt2ageslFIHNtr4mIle8SqV0ntKq7dFLLdPKCP6Q1xtn2x3C1jXeQrSDnIVSjYcFyagIQsfruzmywW+GKlubwNYluOaxoMbZj+6zAAAAA", + "amount": "2000", + "pubkey": "aa586eb052ebf1e676f6df4eaf3591c90d56a979871aefa9b67e5618cb7c0f25", + "createdAt": "1730330748" + } + ], + "spentVtxos": [], + "expectedTxHistory": [ + { + "redeemTxid": "884d85c0db6b52139c39337d54c1f20cd8c5c0d2e83109d69246a345ccc9d169", + "amount": "2000", + "type": "RECEIVED", + "createdAt": "1730330748", + "settled": false + }, + { + "redeemTxid": "33fd8ca9ea9cfb53802c42be10ae428573e19fb89484dfe536d06d43efa82034", + "amount": "1000", + "type": "RECEIVED", + "createdAt": "1730330256", + "settled": false + } + ] + }, + { + "name": "bob after settling", + "spendableVtxos": [ + { + "outpoint": { + "txid": "d9c95372c0c419fd007005edd54e21dabac0375a37fc5f17c313bc1e5f483af9", + "vout": 0 + }, + "spent": false, + "roundTxid": "7fd65ce87e0f9a7af583593d5b0124aabd65c97e05159525d0a98201d6ae95a4", + "spentBy": "", + "expireAt": "1730935835", + "swept": false, + "isOor": false, + "redeemTx": "", + "amount": "3000", + "pubkey": "aa586eb052ebf1e676f6df4eaf3591c90d56a979871aefa9b67e5618cb7c0f25", + "createdAt": "1730331035" + } + ], + "spentVtxos": [ + { + "outpoint": { + "txid": "33fd8ca9ea9cfb53802c42be10ae428573e19fb89484dfe536d06d43efa82034", + "vout": 0 + }, + "spent": true, + "roundTxid": "c16ae0d917ac400790da18456015975521bec6e1d1962ad728c0070808c564e8", + "spentBy": "7fd65ce87e0f9a7af583593d5b0124aabd65c97e05159525d0a98201d6ae95a4", + "expireAt": "1730934927", + "swept": false, + "isOor": true, + "redeemTx": "cHNidP8BAIkCAAAAAX5SNz6Ces7ONgkhFk5+zIziPh99YTqjOReeOIKmrkYmAAAAAAD/////AugDAAAAAAAAIlEgqlhusFLr8eZ29t9OrzWRyQ1WqXmHGu+ptn5WGMt8DyVgSQAAAAAAACJRIPw+1IIkAbx1hYxqfgipdMaKd3vPh+a6U11Ir6t9AM9fAAAAAAABASsgTgAAAAAAACJRIPw+1IIkAbx1hYxqfgipdMaKd3vPh+a6U11Ir6t9AM9fQRRzba+JiJXvEqldJ7Squ3RSy3Tygj+kNcbZ9sdwtY13kLn0kC6mChU9UVqsaZC/ptUXmqKnA1BqwBIdRoXN3UIQQPXRlndrrsGBbJZ0P1+K+UWTytCU+O1AZbfg/APqOYkI+e7ne0BRtQVTKu2V8mEMydDRmpy5UCGcr9ZhfRRr6d1BFL+NVPnBYGBLoEcv5/9swwUPUfu0KQNTf5WWzvL0XQGlufSQLqYKFT1RWqxpkL+m1ReaoqcDUGrAEh1Ghc3dQhBANfxQ5evXPI16w88zj7VkcarvA6MUPKjTKmOTzAnYGHUb2GkQa6Ixdg+s/z+Nt5jMyL+KUsiLsuoFeat5dn6r3UIVwVCSm3TBoElUt4tLYDXpel4HiloPKOyW1Ue/7prOgDrASdfU5FE/s1XZ2TstJV7kIKp/CP0Z2eTjaXuvl4qCX/NFIHNtr4mIle8SqV0ntKq7dFLLdPKCP6Q1xtn2x3C1jXeQrSC/jVT5wWBgS6BHL+f/bMMFD1H7tCkDU3+Vls7y9F0BpazAAAAA", + "amount": "1000", + "pubkey": "aa586eb052ebf1e676f6df4eaf3591c90d56a979871aefa9b67e5618cb7c0f25", + "createdAt": "1730330256" + }, + { + "outpoint": { + "txid": "884d85c0db6b52139c39337d54c1f20cd8c5c0d2e83109d69246a345ccc9d169", + "vout": 0 + }, + "spent": true, + "roundTxid": "a4e91c211398e0be0edad322fb74a739b1c77bb82b9e4ea94b0115b8e4dfe645", + "spentBy": "7fd65ce87e0f9a7af583593d5b0124aabd65c97e05159525d0a98201d6ae95a4", + "expireAt": "1730935548", + "swept": false, + "isOor": true, + "redeemTx": "cHNidP8BAIkCAAAAAT7y41Cb5k0SMpEYaB/3NLlJ8leksHt08k6sK2gRlx3/AAAAAAD/////AtAHAAAAAAAAIlEgqlhusFLr8eZ29t9OrzWRyQ1WqXmHGu+ptn5WGMt8DyVoHgAAAAAAACJRIHnxG3UzOEn7H8oZrxQCVXCjoUVhUEBBgzDLu8ARqongAAAAAAABASsQJwAAAAAAACJRIHnxG3UzOEn7H8oZrxQCVXCjoUVhUEBBgzDLu8ARqongQRRzba+JiJXvEqldJ7Squ3RSy3Tygj+kNcbZ9sdwtY13kH1QsQK/Pk7/PqAmUThCuTCfbTo69ePAgzsvSuR97VgUQBSvpq/lJ7+uc8nyWwV5sCRukn5TnOybRHCjCOUPviykP6C+ue768mRDK6PxQ5FpNJhHmNLpfdTbIQwGCNIJr7pBFOchVKNhwXJqAhCx+u7ObLBb4YqW5vA1iW45rGgxtmP7fVCxAr8+Tv8+oCZROEK5MJ9tOjr148CDOy9K5H3tWBRALxMyiBhy6eGAHjj0OJ+LRFYI8PCIplSLl+SqfMLoSHZzsXkDIyDcLdV6w4Vvq4oBQN+lfKAX2IKZGB0WUGavn0IVwVCSm3TBoElUt4tLYDXpel4HiloPKOyW1Ue/7prOgDrAL6zdBmWt8+odVYaSKdWl60i5qQGel8jvirsvt2ageslFIHNtr4mIle8SqV0ntKq7dFLLdPKCP6Q1xtn2x3C1jXeQrSDnIVSjYcFyagIQsfruzmywW+GKlubwNYluOaxoMbZj+6zAAAAA", + "amount": "2000", + "pubkey": "aa586eb052ebf1e676f6df4eaf3591c90d56a979871aefa9b67e5618cb7c0f25", + "createdAt": "1730330748" + } + ], + "expectedTxHistory": [ + { + "redeemTxid": "884d85c0db6b52139c39337d54c1f20cd8c5c0d2e83109d69246a345ccc9d169", + "amount": "2000", + "type": "RECEIVED", + "createdAt": "1730330748", + "settled": false + }, + { + "redeemTxid": "33fd8ca9ea9cfb53802c42be10ae428573e19fb89484dfe536d06d43efa82034", + "amount": "1000", + "type": "RECEIVED", + "createdAt": "1730330256", + "settled": false + } + ] + }, + { + "name": "bob after sending", + "spendableVtxos": [ + { + "outpoint": { + "txid": "c59004f8c468a922216f513ec7d63d9b6a13571af0bacd51910709351d27fe55", + "vout": 1 + }, + "spent": false, + "roundTxid": "7fd65ce87e0f9a7af583593d5b0124aabd65c97e05159525d0a98201d6ae95a4", + "spentBy": "", + "expireAt": "1730935835", + "swept": false, + "isOor": true, + "redeemTx": "cHNidP8BAIkCAAAAAfk6SF8evBPDF1/8N1o3wLraIU7V7QVwAP0ZxMByU8nZAAAAAAD/////AjQIAAAAAAAAIlEgefEbdTM4SfsfyhmvFAJVcKOhRWFQQEGDMMu7wBGqieCsAgAAAAAAACJRIKpYbrBS6/HmdvbfTq81kckNVql5hxrvqbZ+VhjLfA8lAAAAAAABASu4CwAAAAAAACJRIKpYbrBS6/HmdvbfTq81kckNVql5hxrvqbZ+VhjLfA8lQRRzba+JiJXvEqldJ7Squ3RSy3Tygj+kNcbZ9sdwtY13kMkuxL2rXufbKxVtT1EaM9Vz7X2fpReM0c3VdBGWLTt6QOQzMhmzDjjLlb76u3ZS/xfu4DdmpxClsIAtAjvKhMycpjoPpLqdFMZkfRR3hM6rSUHpED+NY2UdqCyyh4EhZKVBFIdwPOfQj0BBcg2i+i3lRh1pA4SOHkW+q0rabgNGEdfiyS7Evate59srFW1PURoz1XPtfZ+lF4zRzdV0EZYtO3pAVlbcq0Z0Fh/BHSNd6IDksw8RC0fitTYPdnaWOmAlUHmH9d343v25QSc6q/2HdE8VoQi3+sQ6cS3Xm+EWBClZAUIVwFCSm3TBoElUt4tLYDXpel4HiloPKOyW1Ue/7prOgDrAUNUCehfUMhBKXlquGl4TQ7nsvjlxxe9WfEPi4eN3DDtFIHNtr4mIle8SqV0ntKq7dFLLdPKCP6Q1xtn2x3C1jXeQrSCHcDzn0I9AQXINovot5UYdaQOEjh5FvqtK2m4DRhHX4qzAAAAA", + "amount": "684", + "pubkey": "aa586eb052ebf1e676f6df4eaf3591c90d56a979871aefa9b67e5618cb7c0f25", + "createdAt": "1730331198" + } + ], + "spentVtxos": [ + { + "outpoint": { + "txid": "33fd8ca9ea9cfb53802c42be10ae428573e19fb89484dfe536d06d43efa82034", + "vout": 0 + }, + "spent": true, + "roundTxid": "c16ae0d917ac400790da18456015975521bec6e1d1962ad728c0070808c564e8", + "spentBy": "7fd65ce87e0f9a7af583593d5b0124aabd65c97e05159525d0a98201d6ae95a4", + "expireAt": "1730934927", + "swept": false, + "isOor": true, + "redeemTx": "cHNidP8BAIkCAAAAAX5SNz6Ces7ONgkhFk5+zIziPh99YTqjOReeOIKmrkYmAAAAAAD/////AugDAAAAAAAAIlEgqlhusFLr8eZ29t9OrzWRyQ1WqXmHGu+ptn5WGMt8DyVgSQAAAAAAACJRIPw+1IIkAbx1hYxqfgipdMaKd3vPh+a6U11Ir6t9AM9fAAAAAAABASsgTgAAAAAAACJRIPw+1IIkAbx1hYxqfgipdMaKd3vPh+a6U11Ir6t9AM9fQRRzba+JiJXvEqldJ7Squ3RSy3Tygj+kNcbZ9sdwtY13kLn0kC6mChU9UVqsaZC/ptUXmqKnA1BqwBIdRoXN3UIQQPXRlndrrsGBbJZ0P1+K+UWTytCU+O1AZbfg/APqOYkI+e7ne0BRtQVTKu2V8mEMydDRmpy5UCGcr9ZhfRRr6d1BFL+NVPnBYGBLoEcv5/9swwUPUfu0KQNTf5WWzvL0XQGlufSQLqYKFT1RWqxpkL+m1ReaoqcDUGrAEh1Ghc3dQhBANfxQ5evXPI16w88zj7VkcarvA6MUPKjTKmOTzAnYGHUb2GkQa6Ixdg+s/z+Nt5jMyL+KUsiLsuoFeat5dn6r3UIVwVCSm3TBoElUt4tLYDXpel4HiloPKOyW1Ue/7prOgDrASdfU5FE/s1XZ2TstJV7kIKp/CP0Z2eTjaXuvl4qCX/NFIHNtr4mIle8SqV0ntKq7dFLLdPKCP6Q1xtn2x3C1jXeQrSC/jVT5wWBgS6BHL+f/bMMFD1H7tCkDU3+Vls7y9F0BpazAAAAA", + "amount": "1000", + "pubkey": "aa586eb052ebf1e676f6df4eaf3591c90d56a979871aefa9b67e5618cb7c0f25", + "createdAt": "1730330256" + }, + { + "outpoint": { + "txid": "884d85c0db6b52139c39337d54c1f20cd8c5c0d2e83109d69246a345ccc9d169", + "vout": 0 + }, + "spent": true, + "roundTxid": "a4e91c211398e0be0edad322fb74a739b1c77bb82b9e4ea94b0115b8e4dfe645", + "spentBy": "7fd65ce87e0f9a7af583593d5b0124aabd65c97e05159525d0a98201d6ae95a4", + "expireAt": "1730935548", + "swept": false, + "isOor": true, + "redeemTx": "cHNidP8BAIkCAAAAAT7y41Cb5k0SMpEYaB/3NLlJ8leksHt08k6sK2gRlx3/AAAAAAD/////AtAHAAAAAAAAIlEgqlhusFLr8eZ29t9OrzWRyQ1WqXmHGu+ptn5WGMt8DyVoHgAAAAAAACJRIHnxG3UzOEn7H8oZrxQCVXCjoUVhUEBBgzDLu8ARqongAAAAAAABASsQJwAAAAAAACJRIHnxG3UzOEn7H8oZrxQCVXCjoUVhUEBBgzDLu8ARqongQRRzba+JiJXvEqldJ7Squ3RSy3Tygj+kNcbZ9sdwtY13kH1QsQK/Pk7/PqAmUThCuTCfbTo69ePAgzsvSuR97VgUQBSvpq/lJ7+uc8nyWwV5sCRukn5TnOybRHCjCOUPviykP6C+ue768mRDK6PxQ5FpNJhHmNLpfdTbIQwGCNIJr7pBFOchVKNhwXJqAhCx+u7ObLBb4YqW5vA1iW45rGgxtmP7fVCxAr8+Tv8+oCZROEK5MJ9tOjr148CDOy9K5H3tWBRALxMyiBhy6eGAHjj0OJ+LRFYI8PCIplSLl+SqfMLoSHZzsXkDIyDcLdV6w4Vvq4oBQN+lfKAX2IKZGB0WUGavn0IVwVCSm3TBoElUt4tLYDXpel4HiloPKOyW1Ue/7prOgDrAL6zdBmWt8+odVYaSKdWl60i5qQGel8jvirsvt2ageslFIHNtr4mIle8SqV0ntKq7dFLLdPKCP6Q1xtn2x3C1jXeQrSDnIVSjYcFyagIQsfruzmywW+GKlubwNYluOaxoMbZj+6zAAAAA", + "amount": "2000", + "pubkey": "aa586eb052ebf1e676f6df4eaf3591c90d56a979871aefa9b67e5618cb7c0f25", + "createdAt": "1730330748" + }, + { + "outpoint": { + "txid": "d9c95372c0c419fd007005edd54e21dabac0375a37fc5f17c313bc1e5f483af9", + "vout": 0 + }, + "spent": true, + "roundTxid": "7fd65ce87e0f9a7af583593d5b0124aabd65c97e05159525d0a98201d6ae95a4", + "spentBy": "c59004f8c468a922216f513ec7d63d9b6a13571af0bacd51910709351d27fe55", + "expireAt": "1730935835", + "swept": false, + "isOor": false, + "redeemTx": "", + "amount": "3000", + "pubkey": "aa586eb052ebf1e676f6df4eaf3591c90d56a979871aefa9b67e5618cb7c0f25", + "createdAt": "1730331035" + } + ], + "expectedTxHistory": [ + { + "redeemTxid": "c59004f8c468a922216f513ec7d63d9b6a13571af0bacd51910709351d27fe55", + "amount": "2316", + "type": "SENT", + "createdAt": "1730331198", + "settled": false + }, + { + "redeemTxid": "884d85c0db6b52139c39337d54c1f20cd8c5c0d2e83109d69246a345ccc9d169", + "amount": "2000", + "type": "RECEIVED", + "createdAt": "1730330748", + "settled": false + }, + { + "redeemTxid": "33fd8ca9ea9cfb53802c42be10ae428573e19fb89484dfe536d06d43efa82034", + "amount": "1000", + "type": "RECEIVED", + "createdAt": "1730330256", + "settled": false + } + ] + } +] \ No newline at end of file diff --git a/pkg/client-sdk/types/types.go b/pkg/client-sdk/types/types.go index cb7b14ea..b034c050 100644 --- a/pkg/client-sdk/types/types.go +++ b/pkg/client-sdk/types/types.go @@ -44,7 +44,8 @@ type Vtxo struct { VtxoKey Amount uint64 RoundTxid string - ExpiresAt *time.Time + ExpiresAt time.Time + CreatedAt time.Time RedeemTx string UnconditionalForfeitTxs []string Pending bool diff --git a/pkg/client-sdk/wallet/singlekey/wallet.go b/pkg/client-sdk/wallet/singlekey/wallet.go index 04fe9236..4dceb17a 100644 --- a/pkg/client-sdk/wallet/singlekey/wallet.go +++ b/pkg/client-sdk/wallet/singlekey/wallet.go @@ -47,7 +47,7 @@ func (w *singlekeyWallet) Create( passwordHash := utils.HashPassword(pwd) pubkey := privateKey.PubKey() buf := privateKey.Serialize() - encryptedPrivateKey, err := utils.EncryptAES128(buf, pwd) + encryptedPrivateKey, err := utils.EncryptAES256(buf, pwd) if err != nil { return "", err } @@ -104,7 +104,7 @@ func (w *singlekeyWallet) Unlock( return false, fmt.Errorf("invalid password") } - privateKeyBytes, err := utils.DecryptAES128(w.walletData.EncryptedPrvkey, pwd) + privateKeyBytes, err := utils.DecryptAES256(w.walletData.EncryptedPrvkey, pwd) if err != nil { return false, err } diff --git a/pkg/client-sdk/wasm/browser/config_store.go b/pkg/client-sdk/wasm/browser/config_store.go index a35c8cd4..83e293c8 100644 --- a/pkg/client-sdk/wasm/browser/config_store.go +++ b/pkg/client-sdk/wasm/browser/config_store.go @@ -91,7 +91,7 @@ func (s *configStore) GetData(ctx context.Context) (*types.Config, error) { roundLifetime, _ := strconv.Atoi(s.store.Call("getItem", "round_lifetime").String()) roundInterval, _ := strconv.Atoi(s.store.Call("getItem", "round_interval").String()) unilateralExitDelay, _ := strconv.Atoi(s.store.Call("getItem", "unilateral_exit_delay").String()) - dust, _ := strconv.Atoi(s.store.Call("getItem", "min_relay_fee").String()) + dust, _ := strconv.Atoi(s.store.Call("getItem", "dust").String()) withTxFeed, _ := strconv.ParseBool(s.store.Call("getItem", "with_transaction_feed").String()) return &types.Config{ diff --git a/pkg/client-sdk/wasm/browser/exports.go b/pkg/client-sdk/wasm/browser/exports.go index c601ddb4..b08a7e2e 100644 --- a/pkg/client-sdk/wasm/browser/exports.go +++ b/pkg/client-sdk/wasm/browser/exports.go @@ -37,6 +37,7 @@ func init() { js.Global().Set("getTransactionHistory", GetTransactionHistoryWrapper()) js.Global().Set("log", LogWrapper()) js.Global().Set("dump", DumpWrapper()) + js.Global().Set("listVtxos", ListVtxosWrapper()) js.Global().Set("getAspUrl", GetAspUrlWrapper()) js.Global().Set("getAspPubKeyHex", GetAspPubkeyWrapper()) diff --git a/pkg/client-sdk/wasm/browser/wrappers.go b/pkg/client-sdk/wasm/browser/wrappers.go index 8062e9be..43926800 100644 --- a/pkg/client-sdk/wasm/browser/wrappers.go +++ b/pkg/client-sdk/wasm/browser/wrappers.go @@ -182,6 +182,27 @@ func DumpWrapper() js.Func { }) } +func ListVtxosWrapper() js.Func { + return JSPromise(func(args []js.Value) (interface{}, error) { + if arkSdkClient == nil { + return nil, errors.New("ARK SDK client is not initialized") + } + spendable, spent, err := arkSdkClient.ListVtxos(context.Background()) + if err != nil { + return nil, err + } + rawList := map[string]interface{}{ + "spendable": spendable, + "spent": spent, + } + result, err := json.Marshal(rawList) + if err != nil { + return nil, err + } + return js.ValueOf(string(result)), nil + }) +} + func SendOnChainWrapper() js.Func { return JSPromise(func(args []js.Value) (interface{}, error) { if len(args) != 1 { @@ -234,7 +255,7 @@ func SendAsyncWrapper() js.Func { } withExpiryCoinselect := args[0].Bool() - receivers, err := parseReceivers(args[0]) + receivers, err := parseReceivers(args[1]) if err != nil { return nil, err } diff --git a/server/internal/core/application/covenant.go b/server/internal/core/application/covenant.go index d1e56152..e4b5f674 100644 --- a/server/internal/core/application/covenant.go +++ b/server/internal/core/application/covenant.go @@ -931,6 +931,8 @@ func (s *covenantService) getNewVtxos(round *domain.Round) []domain.Vtxo { return nil } + createdAt := time.Now().Unix() + leaves := round.CongestionTree.Leaves() vtxos := make([]domain.Vtxo, 0) for _, node := range leaves { @@ -953,6 +955,7 @@ func (s *covenantService) getNewVtxos(round *domain.Round) []domain.Vtxo { Pubkey: vtxoPubkey, Amount: uint64(out.Value), RoundTxid: round.Txid, + CreatedAt: createdAt, }) } } diff --git a/server/internal/core/application/covenantless.go b/server/internal/core/application/covenantless.go index 2273b851..81cc6a31 100644 --- a/server/internal/core/application/covenantless.go +++ b/server/internal/core/application/covenantless.go @@ -271,6 +271,7 @@ func (s *covenantlessService) CompleteAsyncPayment( ExpireAt: asyncPayData.expireAt, RoundTxid: asyncPayData.roundTxid, RedeemTx: redeemTx, + CreatedAt: time.Now().Unix(), }) } @@ -1328,6 +1329,8 @@ func (s *covenantlessService) getNewVtxos(round *domain.Round) []domain.Vtxo { return nil } + createdAt := time.Now().Unix() + leaves := round.CongestionTree.Leaves() vtxos := make([]domain.Vtxo, 0) for _, node := range leaves { @@ -1348,6 +1351,7 @@ func (s *covenantlessService) getNewVtxos(round *domain.Round) []domain.Vtxo { Pubkey: hex.EncodeToString(schnorr.SerializePubKey(vtxoTapKey)), Amount: uint64(out.Value), RoundTxid: round.Txid, + CreatedAt: createdAt, }) } } diff --git a/server/internal/core/domain/payment.go b/server/internal/core/domain/payment.go index 3af59840..9211aeb7 100644 --- a/server/internal/core/domain/payment.go +++ b/server/internal/core/domain/payment.go @@ -123,4 +123,5 @@ type Vtxo struct { Swept bool ExpireAt int64 RedeemTx string // empty if in-round vtxo + CreatedAt int64 } diff --git a/server/internal/infrastructure/db/sqlite/migration/20240703120550_init.up.sql b/server/internal/infrastructure/db/sqlite/migration/20240703120550_init.up.sql index 96d3dd5b..f23d66ea 100644 --- a/server/internal/infrastructure/db/sqlite/migration/20240703120550_init.up.sql +++ b/server/internal/infrastructure/db/sqlite/migration/20240703120550_init.up.sql @@ -52,6 +52,7 @@ CREATE TABLE IF NOT EXISTS vtxo ( redeemed BOOLEAN NOT NULL, swept BOOLEAN NOT NULL, expire_at INTEGER NOT NULL, + created_at INTEGER NOT NULL, payment_id TEXT, redeem_tx TEXT, PRIMARY KEY (txid, vout), diff --git a/server/internal/infrastructure/db/sqlite/sqlc/queries/models.go b/server/internal/infrastructure/db/sqlite/sqlc/queries/models.go index 9e9d2bb7..bca7b1d3 100644 --- a/server/internal/infrastructure/db/sqlite/sqlc/queries/models.go +++ b/server/internal/infrastructure/db/sqlite/sqlc/queries/models.go @@ -31,6 +31,7 @@ type PaymentVtxoVw struct { Redeemed sql.NullBool Swept sql.NullBool ExpireAt sql.NullInt64 + CreatedAt sql.NullInt64 PaymentID sql.NullString RedeemTx sql.NullString } @@ -97,6 +98,7 @@ type Vtxo struct { Redeemed bool Swept bool ExpireAt int64 + CreatedAt int64 PaymentID sql.NullString RedeemTx sql.NullString } diff --git a/server/internal/infrastructure/db/sqlite/sqlc/queries/query.sql.go b/server/internal/infrastructure/db/sqlite/sqlc/queries/query.sql.go index 84b99ed7..1f72aebb 100644 --- a/server/internal/infrastructure/db/sqlite/sqlc/queries/query.sql.go +++ b/server/internal/infrastructure/db/sqlite/sqlc/queries/query.sql.go @@ -54,7 +54,7 @@ func (q *Queries) MarkVtxoAsSwept(ctx context.Context, arg MarkVtxoAsSweptParams } const selectNotRedeemedVtxos = `-- name: SelectNotRedeemedVtxos :many -SELECT vtxo.txid, vtxo.vout, vtxo.pubkey, vtxo.amount, vtxo.pool_tx, vtxo.spent_by, vtxo.spent, vtxo.redeemed, vtxo.swept, vtxo.expire_at, vtxo.payment_id, vtxo.redeem_tx 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.created_at, vtxo.payment_id, vtxo.redeem_tx FROM vtxo WHERE redeemed = false ` @@ -82,6 +82,7 @@ func (q *Queries) SelectNotRedeemedVtxos(ctx context.Context) ([]SelectNotRedeem &i.Vtxo.Redeemed, &i.Vtxo.Swept, &i.Vtxo.ExpireAt, + &i.Vtxo.CreatedAt, &i.Vtxo.PaymentID, &i.Vtxo.RedeemTx, ); err != nil { @@ -99,7 +100,7 @@ func (q *Queries) SelectNotRedeemedVtxos(ctx context.Context) ([]SelectNotRedeem } const selectNotRedeemedVtxosWithPubkey = `-- name: SelectNotRedeemedVtxosWithPubkey :many -SELECT vtxo.txid, vtxo.vout, vtxo.pubkey, vtxo.amount, vtxo.pool_tx, vtxo.spent_by, vtxo.spent, vtxo.redeemed, vtxo.swept, vtxo.expire_at, vtxo.payment_id, vtxo.redeem_tx 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.created_at, vtxo.payment_id, vtxo.redeem_tx FROM vtxo WHERE redeemed = false AND pubkey = ? ` @@ -127,6 +128,7 @@ func (q *Queries) SelectNotRedeemedVtxosWithPubkey(ctx context.Context, pubkey s &i.Vtxo.Redeemed, &i.Vtxo.Swept, &i.Vtxo.ExpireAt, + &i.Vtxo.CreatedAt, &i.Vtxo.PaymentID, &i.Vtxo.RedeemTx, ); err != nil { @@ -207,7 +209,7 @@ SELECT round.id, round.starting_timestamp, round.ending_timestamp, round.ended, round_payment_vw.id, round_payment_vw.round_id, round_tx_vw.id, round_tx_vw.tx, round_tx_vw.round_id, round_tx_vw.type, round_tx_vw.position, round_tx_vw.txid, round_tx_vw.tree_level, round_tx_vw.parent_txid, round_tx_vw.is_leaf, payment_receiver_vw.payment_id, payment_receiver_vw.pubkey, payment_receiver_vw.onchain_address, payment_receiver_vw.amount, - payment_vtxo_vw.txid, payment_vtxo_vw.vout, payment_vtxo_vw.pubkey, payment_vtxo_vw.amount, payment_vtxo_vw.pool_tx, payment_vtxo_vw.spent_by, payment_vtxo_vw.spent, payment_vtxo_vw.redeemed, payment_vtxo_vw.swept, payment_vtxo_vw.expire_at, payment_vtxo_vw.payment_id, payment_vtxo_vw.redeem_tx + payment_vtxo_vw.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.created_at, payment_vtxo_vw.payment_id, payment_vtxo_vw.redeem_tx FROM round LEFT OUTER JOIN round_payment_vw ON round.id=round_payment_vw.round_id LEFT OUTER JOIN round_tx_vw ON round.id=round_tx_vw.round_id @@ -271,6 +273,7 @@ func (q *Queries) SelectRoundWithRoundId(ctx context.Context, id string) ([]Sele &i.PaymentVtxoVw.Redeemed, &i.PaymentVtxoVw.Swept, &i.PaymentVtxoVw.ExpireAt, + &i.PaymentVtxoVw.CreatedAt, &i.PaymentVtxoVw.PaymentID, &i.PaymentVtxoVw.RedeemTx, ); err != nil { @@ -292,7 +295,7 @@ SELECT round.id, round.starting_timestamp, round.ending_timestamp, round.ended, round_payment_vw.id, round_payment_vw.round_id, round_tx_vw.id, round_tx_vw.tx, round_tx_vw.round_id, round_tx_vw.type, round_tx_vw.position, round_tx_vw.txid, round_tx_vw.tree_level, round_tx_vw.parent_txid, round_tx_vw.is_leaf, payment_receiver_vw.payment_id, payment_receiver_vw.pubkey, payment_receiver_vw.onchain_address, payment_receiver_vw.amount, - payment_vtxo_vw.txid, payment_vtxo_vw.vout, payment_vtxo_vw.pubkey, payment_vtxo_vw.amount, payment_vtxo_vw.pool_tx, payment_vtxo_vw.spent_by, payment_vtxo_vw.spent, payment_vtxo_vw.redeemed, payment_vtxo_vw.swept, payment_vtxo_vw.expire_at, payment_vtxo_vw.payment_id, payment_vtxo_vw.redeem_tx + payment_vtxo_vw.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.created_at, payment_vtxo_vw.payment_id, payment_vtxo_vw.redeem_tx FROM round LEFT OUTER JOIN round_payment_vw ON round.id=round_payment_vw.round_id LEFT OUTER JOIN round_tx_vw ON round.id=round_tx_vw.round_id @@ -356,6 +359,7 @@ func (q *Queries) SelectRoundWithRoundTxId(ctx context.Context, txid string) ([] &i.PaymentVtxoVw.Redeemed, &i.PaymentVtxoVw.Swept, &i.PaymentVtxoVw.ExpireAt, + &i.PaymentVtxoVw.CreatedAt, &i.PaymentVtxoVw.PaymentID, &i.PaymentVtxoVw.RedeemTx, ); err != nil { @@ -377,7 +381,7 @@ SELECT round.id, round.starting_timestamp, round.ending_timestamp, round.ended, round_payment_vw.id, round_payment_vw.round_id, round_tx_vw.id, round_tx_vw.tx, round_tx_vw.round_id, round_tx_vw.type, round_tx_vw.position, round_tx_vw.txid, round_tx_vw.tree_level, round_tx_vw.parent_txid, round_tx_vw.is_leaf, payment_receiver_vw.payment_id, payment_receiver_vw.pubkey, payment_receiver_vw.onchain_address, payment_receiver_vw.amount, - payment_vtxo_vw.txid, payment_vtxo_vw.vout, payment_vtxo_vw.pubkey, payment_vtxo_vw.amount, payment_vtxo_vw.pool_tx, payment_vtxo_vw.spent_by, payment_vtxo_vw.spent, payment_vtxo_vw.redeemed, payment_vtxo_vw.swept, payment_vtxo_vw.expire_at, payment_vtxo_vw.payment_id, payment_vtxo_vw.redeem_tx + payment_vtxo_vw.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.created_at, payment_vtxo_vw.payment_id, payment_vtxo_vw.redeem_tx FROM round LEFT OUTER JOIN round_payment_vw ON round.id=round_payment_vw.round_id LEFT OUTER JOIN round_tx_vw ON round.id=round_tx_vw.round_id @@ -441,6 +445,7 @@ func (q *Queries) SelectSweepableRounds(ctx context.Context) ([]SelectSweepableR &i.PaymentVtxoVw.Redeemed, &i.PaymentVtxoVw.Swept, &i.PaymentVtxoVw.ExpireAt, + &i.PaymentVtxoVw.CreatedAt, &i.PaymentVtxoVw.PaymentID, &i.PaymentVtxoVw.RedeemTx, ); err != nil { @@ -458,7 +463,7 @@ func (q *Queries) SelectSweepableRounds(ctx context.Context) ([]SelectSweepableR } const selectSweepableVtxos = `-- name: SelectSweepableVtxos :many -SELECT vtxo.txid, vtxo.vout, vtxo.pubkey, vtxo.amount, vtxo.pool_tx, vtxo.spent_by, vtxo.spent, vtxo.redeemed, vtxo.swept, vtxo.expire_at, vtxo.payment_id, vtxo.redeem_tx 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.created_at, vtxo.payment_id, vtxo.redeem_tx FROM vtxo WHERE redeemed = false AND swept = false ` @@ -486,6 +491,7 @@ func (q *Queries) SelectSweepableVtxos(ctx context.Context) ([]SelectSweepableVt &i.Vtxo.Redeemed, &i.Vtxo.Swept, &i.Vtxo.ExpireAt, + &i.Vtxo.CreatedAt, &i.Vtxo.PaymentID, &i.Vtxo.RedeemTx, ); err != nil { @@ -507,7 +513,7 @@ SELECT round.id, round.starting_timestamp, round.ending_timestamp, round.ended, round_payment_vw.id, round_payment_vw.round_id, round_tx_vw.id, round_tx_vw.tx, round_tx_vw.round_id, round_tx_vw.type, round_tx_vw.position, round_tx_vw.txid, round_tx_vw.tree_level, round_tx_vw.parent_txid, round_tx_vw.is_leaf, payment_receiver_vw.payment_id, payment_receiver_vw.pubkey, payment_receiver_vw.onchain_address, payment_receiver_vw.amount, - payment_vtxo_vw.txid, payment_vtxo_vw.vout, payment_vtxo_vw.pubkey, payment_vtxo_vw.amount, payment_vtxo_vw.pool_tx, payment_vtxo_vw.spent_by, payment_vtxo_vw.spent, payment_vtxo_vw.redeemed, payment_vtxo_vw.swept, payment_vtxo_vw.expire_at, payment_vtxo_vw.payment_id, payment_vtxo_vw.redeem_tx + payment_vtxo_vw.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.created_at, payment_vtxo_vw.payment_id, payment_vtxo_vw.redeem_tx FROM round LEFT OUTER JOIN round_payment_vw ON round.id=round_payment_vw.round_id LEFT OUTER JOIN round_tx_vw ON round.id=round_tx_vw.round_id @@ -571,6 +577,7 @@ func (q *Queries) SelectSweptRounds(ctx context.Context) ([]SelectSweptRoundsRow &i.PaymentVtxoVw.Redeemed, &i.PaymentVtxoVw.Swept, &i.PaymentVtxoVw.ExpireAt, + &i.PaymentVtxoVw.CreatedAt, &i.PaymentVtxoVw.PaymentID, &i.PaymentVtxoVw.RedeemTx, ); err != nil { @@ -588,7 +595,7 @@ func (q *Queries) SelectSweptRounds(ctx context.Context) ([]SelectSweptRoundsRow } const selectVtxoByOutpoint = `-- name: SelectVtxoByOutpoint :one -SELECT vtxo.txid, vtxo.vout, vtxo.pubkey, vtxo.amount, vtxo.pool_tx, vtxo.spent_by, vtxo.spent, vtxo.redeemed, vtxo.swept, vtxo.expire_at, vtxo.payment_id, vtxo.redeem_tx 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.created_at, vtxo.payment_id, vtxo.redeem_tx FROM vtxo WHERE txid = ? AND vout = ? ` @@ -615,6 +622,7 @@ func (q *Queries) SelectVtxoByOutpoint(ctx context.Context, arg SelectVtxoByOutp &i.Vtxo.Redeemed, &i.Vtxo.Swept, &i.Vtxo.ExpireAt, + &i.Vtxo.CreatedAt, &i.Vtxo.PaymentID, &i.Vtxo.RedeemTx, ) @@ -622,7 +630,7 @@ func (q *Queries) SelectVtxoByOutpoint(ctx context.Context, arg SelectVtxoByOutp } 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 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.created_at, vtxo.payment_id, vtxo.redeem_tx FROM vtxo WHERE pool_tx = ? ` @@ -650,6 +658,7 @@ func (q *Queries) SelectVtxosByPoolTxid(ctx context.Context, poolTx string) ([]S &i.Vtxo.Redeemed, &i.Vtxo.Swept, &i.Vtxo.ExpireAt, + &i.Vtxo.CreatedAt, &i.Vtxo.PaymentID, &i.Vtxo.RedeemTx, ); err != nil { @@ -838,8 +847,8 @@ func (q *Queries) UpsertTransaction(ctx context.Context, arg UpsertTransactionPa } const upsertVtxo = `-- name: UpsertVtxo :exec -INSERT INTO vtxo (txid, vout, pubkey, amount, pool_tx, spent_by, spent, redeemed, swept, expire_at, redeem_tx) -VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) ON CONFLICT(txid, vout) DO UPDATE SET +INSERT INTO vtxo (txid, vout, pubkey, amount, pool_tx, spent_by, spent, redeemed, swept, expire_at, created_at, redeem_tx) +VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) ON CONFLICT(txid, vout) DO UPDATE SET pubkey = EXCLUDED.pubkey, amount = EXCLUDED.amount, pool_tx = EXCLUDED.pool_tx, @@ -848,21 +857,23 @@ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) ON CONFLICT(txid, vout) DO UPDATE SET redeemed = EXCLUDED.redeemed, swept = EXCLUDED.swept, expire_at = EXCLUDED.expire_at, + created_at = EXCLUDED.created_at, redeem_tx = EXCLUDED.redeem_tx ` type UpsertVtxoParams struct { - Txid string - Vout int64 - Pubkey string - Amount int64 - PoolTx string - SpentBy string - Spent bool - Redeemed bool - Swept bool - ExpireAt int64 - RedeemTx sql.NullString + Txid string + Vout int64 + Pubkey string + Amount int64 + PoolTx string + SpentBy string + Spent bool + Redeemed bool + Swept bool + ExpireAt int64 + CreatedAt int64 + RedeemTx sql.NullString } func (q *Queries) UpsertVtxo(ctx context.Context, arg UpsertVtxoParams) error { @@ -877,6 +888,7 @@ func (q *Queries) UpsertVtxo(ctx context.Context, arg UpsertVtxoParams) error { arg.Redeemed, arg.Swept, arg.ExpireAt, + arg.CreatedAt, arg.RedeemTx, ) return err diff --git a/server/internal/infrastructure/db/sqlite/sqlc/query.sql b/server/internal/infrastructure/db/sqlite/sqlc/query.sql index 2f4e83ee..1df332d2 100644 --- a/server/internal/infrastructure/db/sqlite/sqlc/query.sql +++ b/server/internal/infrastructure/db/sqlite/sqlc/query.sql @@ -112,8 +112,8 @@ SELECT id FROM round WHERE starting_timestamp > ? AND starting_timestamp < ?; SELECT id FROM round; -- name: UpsertVtxo :exec -INSERT INTO vtxo (txid, vout, pubkey, amount, pool_tx, spent_by, spent, redeemed, swept, expire_at, redeem_tx) -VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) ON CONFLICT(txid, vout) DO UPDATE SET +INSERT INTO vtxo (txid, vout, pubkey, amount, pool_tx, spent_by, spent, redeemed, swept, expire_at, created_at, redeem_tx) +VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) ON CONFLICT(txid, vout) DO UPDATE SET pubkey = EXCLUDED.pubkey, amount = EXCLUDED.amount, pool_tx = EXCLUDED.pool_tx, @@ -122,6 +122,7 @@ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) ON CONFLICT(txid, vout) DO UPDATE SET redeemed = EXCLUDED.redeemed, swept = EXCLUDED.swept, expire_at = EXCLUDED.expire_at, + created_at = EXCLUDED.created_at, redeem_tx = EXCLUDED.redeem_tx; -- name: SelectSweepableVtxos :many diff --git a/server/internal/infrastructure/db/sqlite/vtxo_repo.go b/server/internal/infrastructure/db/sqlite/vtxo_repo.go index 7284cf73..f61b3ed8 100644 --- a/server/internal/infrastructure/db/sqlite/vtxo_repo.go +++ b/server/internal/infrastructure/db/sqlite/vtxo_repo.go @@ -40,17 +40,18 @@ func (v *vxtoRepository) AddVtxos(ctx context.Context, vtxos []domain.Vtxo) erro if err := querierWithTx.UpsertVtxo( ctx, queries.UpsertVtxoParams{ - Txid: vtxo.Txid, - Vout: int64(vtxo.VOut), - Pubkey: vtxo.Pubkey, - Amount: int64(vtxo.Amount), - PoolTx: vtxo.RoundTxid, - SpentBy: vtxo.SpentBy, - Spent: vtxo.Spent, - Redeemed: vtxo.Redeemed, - Swept: vtxo.Swept, - ExpireAt: vtxo.ExpireAt, - RedeemTx: sql.NullString{String: vtxo.RedeemTx, Valid: true}, + Txid: vtxo.Txid, + Vout: int64(vtxo.VOut), + Pubkey: vtxo.Pubkey, + Amount: int64(vtxo.Amount), + PoolTx: vtxo.RoundTxid, + SpentBy: vtxo.SpentBy, + Spent: vtxo.Spent, + Redeemed: vtxo.Redeemed, + Swept: vtxo.Swept, + ExpireAt: vtxo.ExpireAt, + CreatedAt: vtxo.CreatedAt, + RedeemTx: sql.NullString{String: vtxo.RedeemTx, Valid: true}, }, ); err != nil { return err @@ -258,6 +259,7 @@ func rowToVtxo(row queries.Vtxo) domain.Vtxo { Swept: row.Swept, ExpireAt: row.ExpireAt, RedeemTx: row.RedeemTx.String, + CreatedAt: row.CreatedAt, } } diff --git a/server/internal/infrastructure/tx-builder/covenantless/builder.go b/server/internal/infrastructure/tx-builder/covenantless/builder.go index db723262..4536579c 100644 --- a/server/internal/infrastructure/tx-builder/covenantless/builder.go +++ b/server/internal/infrastructure/tx-builder/covenantless/builder.go @@ -1222,7 +1222,6 @@ func extractSweepLeaf(input psbt.PInput) (sweepLeaf *psbt.TaprootTapLeafScript, return nil, nil, 0, err } - fmt.Println("closure", valid) if valid && closure.Seconds > 0 { sweepLeaf = leaf lifetime = int64(closure.Seconds) diff --git a/server/internal/interface/grpc/handlers/parser.go b/server/internal/interface/grpc/handlers/parser.go index c134474d..cba04360 100644 --- a/server/internal/interface/grpc/handlers/parser.go +++ b/server/internal/interface/grpc/handlers/parser.go @@ -126,6 +126,7 @@ func (v vtxoList) toProto() []*arkv1.Vtxo { RedeemTx: vv.RedeemTx, IsOor: len(vv.RedeemTx) > 0, Pubkey: vv.Pubkey, + CreatedAt: vv.CreatedAt, }) }