Skip to content
This repository was archived by the owner on Feb 8, 2024. It is now read-only.

Commit c632e60

Browse files
committed
Payment acceptance debugging
1 parent 1e6381b commit c632e60

File tree

10 files changed

+245
-141
lines changed

10 files changed

+245
-141
lines changed

Cargo.lock

+76-77
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

libmycitadel/libmycitadel.h

+1-1
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@
5252

5353
#define BECH32_RGB_CONSIGNMENT 800
5454

55-
#define BECH32_RGB20_ASSET 800
55+
#define BECH32_RGB20_ASSET 816
5656

5757
#define SUCCESS 0
5858

libmycitadel/src/capi/bech32.rs

+44-2
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ use std::str::FromStr;
1818

1919
use invoice::Invoice;
2020
use rgb::bech32::Error;
21-
use rgb::Bech32;
21+
use rgb::{Bech32, Consignment};
2222
use rgb20::Asset;
2323

2424
use crate::{TryAsStr, TryIntoRaw, TryIntoString};
@@ -49,7 +49,7 @@ pub const BECH32_RGB_SCHEMA: c_int = 0x0310;
4949
pub const BECH32_RGB_GENESIS: c_int = 0x0311;
5050
pub const BECH32_RGB_CONSIGNMENT: c_int = 0x0320;
5151

52-
pub const BECH32_RGB20_ASSET: c_int = 0x0320;
52+
pub const BECH32_RGB20_ASSET: c_int = 0x0330;
5353

5454
#[allow(non_camel_case_types)]
5555
#[repr(C)]
@@ -190,6 +190,35 @@ impl From<Invoice> for InvoiceInfo {
190190
}
191191
}
192192

193+
#[serde_as]
194+
#[derive(Clone, Debug, Serialize, Deserialize)]
195+
#[serde(rename_all = "camelCase")]
196+
pub struct ConsignmentInfo {
197+
pub version: u16,
198+
pub asset: rgb20::Asset,
199+
pub schema_id: rgb::SchemaId,
200+
pub endpoints_count: usize,
201+
pub transactions_count: usize,
202+
pub transitions_count: usize,
203+
pub extensions_count: usize,
204+
}
205+
206+
impl TryFrom<Consignment> for ConsignmentInfo {
207+
type Error = rgb20::Error;
208+
209+
fn try_from(consignment: Consignment) -> Result<Self, Self::Error> {
210+
Ok(ConsignmentInfo {
211+
version: 0, // TODO: Use consignment.version()
212+
asset: rgb20::Asset::try_from(consignment.genesis.clone())?,
213+
schema_id: consignment.genesis.schema_id(),
214+
endpoints_count: consignment.endpoints.len(),
215+
transactions_count: consignment.txids().len(),
216+
transitions_count: consignment.state_transitions.len(),
217+
extensions_count: consignment.state_extensions.len(),
218+
})
219+
}
220+
}
221+
193222
#[no_mangle]
194223
pub extern "C" fn lnpbp_bech32_release(info: bech32_info_t) {
195224
(info.details as *mut c_char).try_into_string();
@@ -216,6 +245,19 @@ pub extern "C" fn lnpbp_bech32_info(bech_str: *const c_char) -> bech32_info_t {
216245
Err(err) => bech32_info_t::from(err),
217246
}
218247
}
248+
Ok(Bech32::Other(hrp, _)) if &hrp == "consignment" => {
249+
match Consignment::from_str(s)
250+
.map_err(bech32_info_t::from)
251+
.and_then(|consignment| {
252+
ConsignmentInfo::try_from(consignment)
253+
.map_err(|_| bech32_info_t::unsuported())
254+
}) {
255+
Ok(info) => {
256+
bech32_info_t::with_value(BECH32_RGB_CONSIGNMENT, &info)
257+
}
258+
Err(err) => err,
259+
}
260+
}
219261
Ok(_) => bech32_info_t::unsuported(),
220262
Err(err) => bech32_info_t::from(err),
221263
})

packages/MyCitadelKit/CAPI.swift

+35-27
Original file line numberDiff line numberDiff line change
@@ -73,21 +73,21 @@ struct UTXOJson: Codable {
7373
}
7474
}
7575

76-
struct RGB20Json: Codable {
77-
let genesis: String
78-
let id: String
79-
let ticker: String
80-
let name: String
81-
let description: String?
82-
let decimalPrecision: UInt8
83-
let date: String
84-
let knownCirculating: UInt64
85-
let issueLimit: UInt64?
76+
public struct RGB20Json: Codable {
77+
public let genesis: String
78+
public let id: String
79+
public let ticker: String
80+
public let name: String
81+
public let description: String?
82+
public let decimalPrecision: UInt8
83+
public let date: String
84+
public let knownCirculating: UInt64
85+
public let issueLimit: UInt64?
8686
}
8787

88-
struct Transfer {
89-
let psbt: String
90-
let consignment: String?
88+
public struct Transfer {
89+
public let psbt: String
90+
public let consignment: String?
9191
}
9292

9393
extension CitadelVault {
@@ -101,7 +101,7 @@ extension CitadelVault {
101101

102102
private func processResponseToString(_ response: UnsafePointer<Int8>?) throws -> String {
103103
guard let response = response else {
104-
guard let err = self.lastError() else {
104+
guard let err = lastError() else {
105105
throw CitadelError("MyCitadel C API is broken")
106106
}
107107
throw err
@@ -145,54 +145,59 @@ extension WalletContract {
145145

146146
extension CitadelVault {
147147
internal func create(singleSig derivation: String, name: String, descriptorType: DescriptorType) throws -> ContractJson {
148-
try self.createSeed()
149-
let pubkeyChain = try self.createScopedChain(derivation: derivation)
148+
print("Creating seed")
149+
try createSeed()
150+
let pubkeyChain = try createScopedChain(derivation: derivation)
150151
let response = mycitadel_single_sig_create(rpcClient, name, pubkeyChain, descriptorType.cDescriptorType());
151-
return try JSONDecoder().decode(ContractJson.self, from: self.processResponse(response))
152+
return try JSONDecoder().decode(ContractJson.self, from: processResponse(response))
152153
}
153154

154155
internal func listContracts() throws -> [ContractJson] {
155156
print("Listing contracts")
156157
let response = mycitadel_contract_list(rpcClient)
157-
return try JSONDecoder().decode([ContractJson].self, from: self.processResponse(response))
158+
return try JSONDecoder().decode([ContractJson].self, from: processResponse(response))
158159
}
159160

160161
internal func operations(walletId: String) throws -> [TransferOperation] {
161162
print("Listing operations")
162163
let response = mycitadel_contract_operations(rpcClient, walletId)
163-
return try JSONDecoder().decode([TransferOperation].self, from: self.processResponse(response))
164+
return try JSONDecoder().decode([TransferOperation].self, from: processResponse(response))
164165
}
165166

166167
internal func balance(walletId: String) throws -> [String: [UTXOJson]] {
167168
print("Requesting balance for \(walletId)")
168169
let response = mycitadel_contract_balance(rpcClient, walletId, true, 20)
169-
return try JSONDecoder().decode([String: [UTXOJson]].self, from: self.processResponse(response))
170+
return try JSONDecoder().decode([String: [UTXOJson]].self, from: processResponse(response))
170171
}
171172

172173
internal func listAssets() throws -> [RGB20Json] {
173174
print("Listing assets")
174175
let response = mycitadel_asset_list(rpcClient);
175-
return try JSONDecoder().decode([RGB20Json].self, from: self.processResponse(response))
176+
return try JSONDecoder().decode([RGB20Json].self, from: processResponse(response))
176177
}
177178

178179
internal func importRGB(genesisBech32 genesis: String) throws -> RGB20Json {
180+
print("Importing RGB asset")
179181
let response = mycitadel_asset_import(rpcClient, genesis);
180-
return try JSONDecoder().decode(RGB20Json.self, from: self.processResponse(response))
182+
return try JSONDecoder().decode(RGB20Json.self, from: processResponse(response))
181183
}
182184

183185
public func nextAddress(forContractId contractId: String, useLegacySegWit legacy: Bool = false) throws -> AddressDerivation {
186+
print("Generating next avaliable address")
184187
let response = mycitadel_address_create(rpcClient, contractId, false, legacy)
185-
return try JSONDecoder().decode(AddressDerivation.self, from: self.processResponse(response))
188+
return try JSONDecoder().decode(AddressDerivation.self, from: processResponse(response))
186189
}
187190

188191
internal func usedAddresses(forContractId contractId: String) throws -> [AddressDerivation] {
192+
print("Listing used addresses")
189193
let response = mycitadel_address_list(rpcClient, contractId, false, 0)
190-
return try JSONDecoder().decode([String: UInt32].self, from: self.processResponse(response))
194+
return try JSONDecoder().decode([String: UInt32].self, from: processResponse(response))
191195
.map { (address, index) in AddressDerivation(address: address, derivation: [index]) }
192196
}
193197

194-
internal func invoice(usingFormat format: InvoiceType, receiveTo contractId: String, nominatedIn assetId: String?, value: UInt64?, useLegacySegWit legacy: Bool = false) throws -> String {
195-
let invoice = mycitadel_invoice_create(rpcClient, format.cType(), contractId, assetId ?? nil, value ?? 0, nil, nil, false, legacy)
198+
internal func invoice(usingFormat format: InvoiceType, receiveTo contractId: String, nominatedIn assetId: String?, value: UInt64?, from merchant: String? = nil, purpose: String? = nil, useLegacySegWit legacy: Bool = false) throws -> String {
199+
print("Creating invoice")
200+
let invoice = mycitadel_invoice_create(rpcClient, format.cType(), contractId, assetId ?? nil, value ?? 0, merchant ?? nil, purpose ?? nil, false, legacy)
196201
return try processResponseToString(invoice)
197202
}
198203

@@ -207,9 +212,10 @@ extension CitadelVault {
207212
*/
208213

209214
internal func pay(from contractId: String, invoice: String, value: UInt64? = nil, fee: UInt64, giveaway: UInt64? = nil) throws -> Transfer {
215+
print("Paying invoice")
210216
let transfer = mycitadel_invoice_pay(rpcClient, contractId, invoice, value ?? 0, fee, giveaway ?? 0)
211217
if !transfer.success {
212-
guard let err = self.lastError() else {
218+
guard let err = lastError() else {
213219
throw CitadelError("MyCitadel C API is broken")
214220
}
215221
throw err
@@ -229,11 +235,13 @@ extension CitadelVault {
229235
}
230236

231237
internal func publish(psbt: String) throws -> String {
238+
print("Signing and publishing transaction")
232239
let txid = mycitadel_psbt_publish(rpcClient, psbt)
233240
return try processResponseToString(txid)
234241
}
235242

236243
internal func accept(consignment: String) throws -> String {
244+
print("Accepting consignment")
237245
let status = mycitadel_invoice_accept(rpcClient, consignment)
238246
return try processResponseToString(status)
239247
}

packages/MyCitadelKit/Parser.swift

+45-9
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,16 @@
77

88
import Foundation
99

10+
public struct ConsignmentInfo: Codable {
11+
public let version: UInt16
12+
public let asset: RGB20Json
13+
public let schemaId: String
14+
public let endpointsCount: UInt16
15+
public let transactionsCount: UInt32
16+
public let transitionsCount: UInt32
17+
public let extensionsCount: UInt32
18+
}
19+
1020
open class UniversalParser {
1121
public enum ParsedData {
1222
case unknown
@@ -31,7 +41,7 @@ open class UniversalParser {
3141
case rgbContractId
3242
case rgbSchema
3343
case rgbGenesis
34-
case rgbConsignment
44+
case rgbConsignment(ConsignmentInfo)
3545
case rgb20Asset(RGB20Asset)
3646

3747
case outpoint(OutPoint)
@@ -92,11 +102,39 @@ open class UniversalParser {
92102
parsedData = .unknown
93103
parseStatus = parseError.type
94104
parseReport = parseError.message
105+
} catch DecodingError.keyNotFound(let key, let context) {
106+
parsedData = .unknown
107+
parseStatus = .invalidJSON
108+
let path = context.codingPath.count == 0 ? "self" : "\\.\(context.codingPath.map{"\($0)"}.joined(separator: "."))"
109+
let details = "key `\(key.stringValue)` is not found at path `\(path)`"
110+
parseReport = "Unable to recognize data from backend: \(details)"
111+
print(details)
112+
} catch DecodingError.typeMismatch(let type, let context) {
113+
parsedData = .unknown
114+
parseStatus = .invalidJSON
115+
let path = context.codingPath.count == 0 ? "self" : "\\.\(context.codingPath.map{"\($0)"}.joined(separator: "."))"
116+
let details = "key at `\(path)` must be of `\(type)` type"
117+
parseReport = "Unable to recognize data from backend: \(details)"
118+
print(details)
119+
} catch DecodingError.valueNotFound(let type, let context) {
120+
parsedData = .unknown
121+
parseStatus = .invalidJSON
122+
let path = context.codingPath.count == 0 ? "self" : "\\.\(context.codingPath.map{"\($0)"}.joined(separator: "."))"
123+
let details = "value at `\(path)` of `\(type)` type is not found"
124+
parseReport = "Unable to recognize data from backend: \(details)"
125+
print(details)
126+
} catch DecodingError.dataCorrupted(let context) {
127+
parsedData = .unknown
128+
parseStatus = .invalidJSON
129+
let path = context.codingPath.count == 0 ? "self" : "\\.\(context.codingPath.map{"\($0)"}.joined(separator: "."))"
130+
let details = "data corrupted at `\(path)`"
131+
parseReport = "Unable to recognize data from backend: \(details)"
132+
print(details)
95133
} catch {
96134
parsedData = .unknown
97135
parseStatus = .invalidJSON
98-
parseReport = "Unable to recognize details from the provided JSON data"
99-
print("Bech32 parse error \(error.localizedDescription)")
136+
parseReport = "Internal error"
137+
print("Other \(error.localizedDescription)")
100138
}
101139

102140
// TODO: Parse descriptors
@@ -121,12 +159,7 @@ open class UniversalParser {
121159
let jsonData = Data(jsonString.utf8)
122160
let decoder = JSONDecoder();
123161
print("Parsing JSON address data: \(jsonString)")
124-
do {
125-
return try decoder.decode(AddressInfo.self, from: jsonData)
126-
} catch {
127-
print("Error parsing address: \(error.localizedDescription)")
128-
throw error
129-
}
162+
return try decoder.decode(AddressInfo.self, from: jsonData)
130163
}
131164

132165
public static func parse(bech32: String) throws -> ParsedData {
@@ -148,6 +181,9 @@ open class UniversalParser {
148181
case BECH32_LNPBP_INVOICE:
149182
let invoice = try decoder.decode(Invoice.self, from: jsonData)
150183
return ParsedData.lnbpInvoice(invoice)
184+
case BECH32_RGB_CONSIGNMENT:
185+
let info = try decoder.decode(ConsignmentInfo.self, from: jsonData)
186+
return ParsedData.rgbConsignment(info)
151187
default: return ParsedData.unknown
152188
}
153189
}

packages/MyCitadelKit/Wallet.swift

+2-2
Original file line numberDiff line numberDiff line change
@@ -107,10 +107,10 @@ public class WalletContract: Identifiable {
107107
}
108108
}
109109

110-
public func invoice(usingFormat format: InvoiceType, nominatedIn asset: Asset, amount: Double?, useLegacySegWit legacy: Bool = false) throws -> String {
110+
public func invoice(usingFormat format: InvoiceType, nominatedIn asset: Asset, amount: Double?, from merchant: String? = nil, purpose: String? = nil, useLegacySegWit legacy: Bool = false) throws -> String {
111111
let assetId = asset.isNative ? nil : asset.id
112112
let value = amount != nil ? asset.amount(toAtoms: amount!) : nil
113-
return try vault.invoice(usingFormat: format, receiveTo: id, nominatedIn: assetId, value: value, useLegacySegWit: legacy)
113+
return try vault.invoice(usingFormat: format, receiveTo: id, nominatedIn: assetId, value: value, from: merchant, purpose: purpose, useLegacySegWit: legacy)
114114
}
115115

116116
/*

src/cache/driver.rs

+8-6
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
// along with this software.
1212
// If not, see <https://www.gnu.org/licenses/agpl-3.0-standalone.html>.
1313

14-
use std::collections::{BTreeMap, HashSet};
14+
use std::collections::{BTreeMap, BTreeSet, HashSet};
1515

1616
use bitcoin::{Address, OutPoint, Txid};
1717
use wallet::bip32::UnhardenedIndex;
@@ -25,27 +25,29 @@ pub trait Driver {
2525
fn unspent(
2626
&self,
2727
contract_id: ContractId,
28-
) -> Result<BTreeMap<rgb::ContractId, Vec<Utxo>>, Error>;
28+
) -> Result<BTreeMap<rgb::ContractId, HashSet<Utxo>>, Error>;
2929

3030
fn unspent_bitcoin_only(
3131
&self,
3232
contract_id: ContractId,
33-
) -> Result<Vec<Utxo>, Error>;
33+
) -> Result<HashSet<Utxo>, Error>;
3434

3535
fn allocations(
3636
&self,
3737
contract_id: ContractId,
3838
) -> Result<Allocations, Error>;
3939

40-
fn utxo(&self, contract_id: ContractId)
41-
-> Result<HashSet<OutPoint>, Error>;
40+
fn utxo(
41+
&self,
42+
contract_id: ContractId,
43+
) -> Result<BTreeSet<OutPoint>, Error>;
4244

4345
fn update(
4446
&mut self,
4547
contract_id: ContractId,
4648
mine_info: BTreeMap<(u32, u16), Txid>,
4749
updated_height: Option<u32>,
48-
utxo: Vec<OutPoint>,
50+
utxo: BTreeSet<OutPoint>,
4951
unspent: BTreeMap<rgb::ContractId, Vec<Utxo>>,
5052
) -> Result<(), Error>;
5153

0 commit comments

Comments
 (0)