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

Commit

Permalink
Payment acceptance debugging
Browse files Browse the repository at this point in the history
  • Loading branch information
dr-orlovsky committed Mar 7, 2021
1 parent 1e6381b commit c632e60
Show file tree
Hide file tree
Showing 10 changed files with 245 additions and 141 deletions.
153 changes: 76 additions & 77 deletions Cargo.lock

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion libmycitadel/libmycitadel.h
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@

#define BECH32_RGB_CONSIGNMENT 800

#define BECH32_RGB20_ASSET 800
#define BECH32_RGB20_ASSET 816

#define SUCCESS 0

Expand Down
46 changes: 44 additions & 2 deletions libmycitadel/src/capi/bech32.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ use std::str::FromStr;

use invoice::Invoice;
use rgb::bech32::Error;
use rgb::Bech32;
use rgb::{Bech32, Consignment};
use rgb20::Asset;

use crate::{TryAsStr, TryIntoRaw, TryIntoString};
Expand Down Expand Up @@ -49,7 +49,7 @@ pub const BECH32_RGB_SCHEMA: c_int = 0x0310;
pub const BECH32_RGB_GENESIS: c_int = 0x0311;
pub const BECH32_RGB_CONSIGNMENT: c_int = 0x0320;

pub const BECH32_RGB20_ASSET: c_int = 0x0320;
pub const BECH32_RGB20_ASSET: c_int = 0x0330;

#[allow(non_camel_case_types)]
#[repr(C)]
Expand Down Expand Up @@ -190,6 +190,35 @@ impl From<Invoice> for InvoiceInfo {
}
}

#[serde_as]
#[derive(Clone, Debug, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ConsignmentInfo {
pub version: u16,
pub asset: rgb20::Asset,
pub schema_id: rgb::SchemaId,
pub endpoints_count: usize,
pub transactions_count: usize,
pub transitions_count: usize,
pub extensions_count: usize,
}

impl TryFrom<Consignment> for ConsignmentInfo {
type Error = rgb20::Error;

fn try_from(consignment: Consignment) -> Result<Self, Self::Error> {
Ok(ConsignmentInfo {
version: 0, // TODO: Use consignment.version()
asset: rgb20::Asset::try_from(consignment.genesis.clone())?,
schema_id: consignment.genesis.schema_id(),
endpoints_count: consignment.endpoints.len(),
transactions_count: consignment.txids().len(),
transitions_count: consignment.state_transitions.len(),
extensions_count: consignment.state_extensions.len(),
})
}
}

#[no_mangle]
pub extern "C" fn lnpbp_bech32_release(info: bech32_info_t) {
(info.details as *mut c_char).try_into_string();
Expand All @@ -216,6 +245,19 @@ pub extern "C" fn lnpbp_bech32_info(bech_str: *const c_char) -> bech32_info_t {
Err(err) => bech32_info_t::from(err),
}
}
Ok(Bech32::Other(hrp, _)) if &hrp == "consignment" => {
match Consignment::from_str(s)
.map_err(bech32_info_t::from)
.and_then(|consignment| {
ConsignmentInfo::try_from(consignment)
.map_err(|_| bech32_info_t::unsuported())
}) {
Ok(info) => {
bech32_info_t::with_value(BECH32_RGB_CONSIGNMENT, &info)
}
Err(err) => err,
}
}
Ok(_) => bech32_info_t::unsuported(),
Err(err) => bech32_info_t::from(err),
})
Expand Down
62 changes: 35 additions & 27 deletions packages/MyCitadelKit/CAPI.swift
Original file line number Diff line number Diff line change
Expand Up @@ -73,21 +73,21 @@ struct UTXOJson: Codable {
}
}

struct RGB20Json: Codable {
let genesis: String
let id: String
let ticker: String
let name: String
let description: String?
let decimalPrecision: UInt8
let date: String
let knownCirculating: UInt64
let issueLimit: UInt64?
public struct RGB20Json: Codable {
public let genesis: String
public let id: String
public let ticker: String
public let name: String
public let description: String?
public let decimalPrecision: UInt8
public let date: String
public let knownCirculating: UInt64
public let issueLimit: UInt64?
}

struct Transfer {
let psbt: String
let consignment: String?
public struct Transfer {
public let psbt: String
public let consignment: String?
}

extension CitadelVault {
Expand All @@ -101,7 +101,7 @@ extension CitadelVault {

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

extension CitadelVault {
internal func create(singleSig derivation: String, name: String, descriptorType: DescriptorType) throws -> ContractJson {
try self.createSeed()
let pubkeyChain = try self.createScopedChain(derivation: derivation)
print("Creating seed")
try createSeed()
let pubkeyChain = try createScopedChain(derivation: derivation)
let response = mycitadel_single_sig_create(rpcClient, name, pubkeyChain, descriptorType.cDescriptorType());
return try JSONDecoder().decode(ContractJson.self, from: self.processResponse(response))
return try JSONDecoder().decode(ContractJson.self, from: processResponse(response))
}

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

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

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

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

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

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

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

internal func invoice(usingFormat format: InvoiceType, receiveTo contractId: String, nominatedIn assetId: String?, value: UInt64?, useLegacySegWit legacy: Bool = false) throws -> String {
let invoice = mycitadel_invoice_create(rpcClient, format.cType(), contractId, assetId ?? nil, value ?? 0, nil, nil, false, legacy)
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 {
print("Creating invoice")
let invoice = mycitadel_invoice_create(rpcClient, format.cType(), contractId, assetId ?? nil, value ?? 0, merchant ?? nil, purpose ?? nil, false, legacy)
return try processResponseToString(invoice)
}

Expand All @@ -207,9 +212,10 @@ extension CitadelVault {
*/

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

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

internal func accept(consignment: String) throws -> String {
print("Accepting consignment")
let status = mycitadel_invoice_accept(rpcClient, consignment)
return try processResponseToString(status)
}
Expand Down
54 changes: 45 additions & 9 deletions packages/MyCitadelKit/Parser.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,16 @@

import Foundation

public struct ConsignmentInfo: Codable {
public let version: UInt16
public let asset: RGB20Json
public let schemaId: String
public let endpointsCount: UInt16
public let transactionsCount: UInt32
public let transitionsCount: UInt32
public let extensionsCount: UInt32
}

open class UniversalParser {
public enum ParsedData {
case unknown
Expand All @@ -31,7 +41,7 @@ open class UniversalParser {
case rgbContractId
case rgbSchema
case rgbGenesis
case rgbConsignment
case rgbConsignment(ConsignmentInfo)
case rgb20Asset(RGB20Asset)

case outpoint(OutPoint)
Expand Down Expand Up @@ -92,11 +102,39 @@ open class UniversalParser {
parsedData = .unknown
parseStatus = parseError.type
parseReport = parseError.message
} catch DecodingError.keyNotFound(let key, let context) {
parsedData = .unknown
parseStatus = .invalidJSON
let path = context.codingPath.count == 0 ? "self" : "\\.\(context.codingPath.map{"\($0)"}.joined(separator: "."))"
let details = "key `\(key.stringValue)` is not found at path `\(path)`"
parseReport = "Unable to recognize data from backend: \(details)"
print(details)
} catch DecodingError.typeMismatch(let type, let context) {
parsedData = .unknown
parseStatus = .invalidJSON
let path = context.codingPath.count == 0 ? "self" : "\\.\(context.codingPath.map{"\($0)"}.joined(separator: "."))"
let details = "key at `\(path)` must be of `\(type)` type"
parseReport = "Unable to recognize data from backend: \(details)"
print(details)
} catch DecodingError.valueNotFound(let type, let context) {
parsedData = .unknown
parseStatus = .invalidJSON
let path = context.codingPath.count == 0 ? "self" : "\\.\(context.codingPath.map{"\($0)"}.joined(separator: "."))"
let details = "value at `\(path)` of `\(type)` type is not found"
parseReport = "Unable to recognize data from backend: \(details)"
print(details)
} catch DecodingError.dataCorrupted(let context) {
parsedData = .unknown
parseStatus = .invalidJSON
let path = context.codingPath.count == 0 ? "self" : "\\.\(context.codingPath.map{"\($0)"}.joined(separator: "."))"
let details = "data corrupted at `\(path)`"
parseReport = "Unable to recognize data from backend: \(details)"
print(details)
} catch {
parsedData = .unknown
parseStatus = .invalidJSON
parseReport = "Unable to recognize details from the provided JSON data"
print("Bech32 parse error \(error.localizedDescription)")
parseReport = "Internal error"
print("Other \(error.localizedDescription)")
}

// TODO: Parse descriptors
Expand All @@ -121,12 +159,7 @@ open class UniversalParser {
let jsonData = Data(jsonString.utf8)
let decoder = JSONDecoder();
print("Parsing JSON address data: \(jsonString)")
do {
return try decoder.decode(AddressInfo.self, from: jsonData)
} catch {
print("Error parsing address: \(error.localizedDescription)")
throw error
}
return try decoder.decode(AddressInfo.self, from: jsonData)
}

public static func parse(bech32: String) throws -> ParsedData {
Expand All @@ -148,6 +181,9 @@ open class UniversalParser {
case BECH32_LNPBP_INVOICE:
let invoice = try decoder.decode(Invoice.self, from: jsonData)
return ParsedData.lnbpInvoice(invoice)
case BECH32_RGB_CONSIGNMENT:
let info = try decoder.decode(ConsignmentInfo.self, from: jsonData)
return ParsedData.rgbConsignment(info)
default: return ParsedData.unknown
}
}
Expand Down
4 changes: 2 additions & 2 deletions packages/MyCitadelKit/Wallet.swift
Original file line number Diff line number Diff line change
Expand Up @@ -107,10 +107,10 @@ public class WalletContract: Identifiable {
}
}

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

/*
Expand Down
14 changes: 8 additions & 6 deletions src/cache/driver.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
// along with this software.
// If not, see <https://www.gnu.org/licenses/agpl-3.0-standalone.html>.

use std::collections::{BTreeMap, HashSet};
use std::collections::{BTreeMap, BTreeSet, HashSet};

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

fn unspent_bitcoin_only(
&self,
contract_id: ContractId,
) -> Result<Vec<Utxo>, Error>;
) -> Result<HashSet<Utxo>, Error>;

fn allocations(
&self,
contract_id: ContractId,
) -> Result<Allocations, Error>;

fn utxo(&self, contract_id: ContractId)
-> Result<HashSet<OutPoint>, Error>;
fn utxo(
&self,
contract_id: ContractId,
) -> Result<BTreeSet<OutPoint>, Error>;

fn update(
&mut self,
contract_id: ContractId,
mine_info: BTreeMap<(u32, u16), Txid>,
updated_height: Option<u32>,
utxo: Vec<OutPoint>,
utxo: BTreeSet<OutPoint>,
unspent: BTreeMap<rgb::ContractId, Vec<Utxo>>,
) -> Result<(), Error>;

Expand Down
Loading

0 comments on commit c632e60

Please sign in to comment.