Skip to content

Commit

Permalink
Partial UDLify
Browse files Browse the repository at this point in the history
  • Loading branch information
DanGould committed Nov 4, 2024
1 parent 3bf8a7e commit 79cc692
Show file tree
Hide file tree
Showing 13 changed files with 238 additions and 186 deletions.
36 changes: 23 additions & 13 deletions src/bitcoin.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use std::str::FromStr;

use std::sync::Arc;
use payjoin::bitcoin;

/// A reference to a transaction output.
Expand Down Expand Up @@ -29,15 +29,15 @@ impl From<bitcoin::OutPoint> for OutPoint {
#[derive(Debug, Clone)]
pub struct PsbtInput {
pub witness_utxo: Option<TxOut>,
pub redeem_script: Option<ScriptBuf>,
pub witness_script: Option<ScriptBuf>,
pub redeem_script: Option<Arc<Script>>,
pub witness_script: Option<Arc<Script>>,
}

impl PsbtInput {
pub fn new(
witness_utxo: Option<TxOut>,
redeem_script: Option<ScriptBuf>,
witness_script: Option<ScriptBuf>,
redeem_script: Option<Arc<Script>>,
witness_script: Option<Arc<Script>>,
) -> Self {
Self { witness_utxo, redeem_script, witness_script }
}
Expand All @@ -47,8 +47,8 @@ impl From<bitcoin::psbt::Input> for PsbtInput {
fn from(psbt_input: bitcoin::psbt::Input) -> Self {
Self {
witness_utxo: psbt_input.witness_utxo.map(|s| s.into()),
redeem_script: psbt_input.redeem_script.clone().map(|s| s.into()),
witness_script: psbt_input.witness_script.clone().map(|s| s.into()),
redeem_script: psbt_input.redeem_script.clone().map(|s| Arc::new(s.into())),
witness_script: psbt_input.witness_script.clone().map(|s| Arc::new(s.into())),
}
}
}
Expand All @@ -57,8 +57,8 @@ impl From<PsbtInput> for bitcoin::psbt::Input {
fn from(psbt_input: PsbtInput) -> Self {
Self {
witness_utxo: psbt_input.witness_utxo.map(|s| s.into()),
redeem_script: psbt_input.redeem_script.map(|s| s.into()),
witness_script: psbt_input.witness_script.map(|s| s.into()),
redeem_script: psbt_input.redeem_script.map(|s| Arc::unwrap_or_clone(s).into()),
witness_script: psbt_input.witness_script.map(|s| Arc::unwrap_or_clone(s).into()),
..Default::default()
}
}
Expand Down Expand Up @@ -129,16 +129,26 @@ impl From<Network> for bitcoin::Network {
}

#[derive(Clone, Debug)]
pub struct ScriptBuf(pub payjoin::bitcoin::ScriptBuf);
pub struct Script(pub payjoin::bitcoin::ScriptBuf);

impl Script {
pub fn new(script: Vec<u8>) -> Self {
Self(payjoin::bitcoin::ScriptBuf::from_bytes(script))
}

pub fn to_bytes(&self) -> Vec<u8> {
self.0.to_bytes()
}
}

impl From<payjoin::bitcoin::ScriptBuf> for ScriptBuf {
impl From<payjoin::bitcoin::ScriptBuf> for Script {
fn from(value: payjoin::bitcoin::ScriptBuf) -> Self {
Self(value)
}
}

impl From<ScriptBuf> for payjoin::bitcoin::ScriptBuf {
fn from(value: ScriptBuf) -> Self {
impl From<Script> for payjoin::bitcoin::ScriptBuf {
fn from(value: Script) -> Self {
value.0
}
}
1 change: 1 addition & 0 deletions src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ use payjoin::receive::{
use payjoin::send::{CreateRequestError, ResponseError as PdkResponseError, ValidationError};

#[derive(Debug, PartialEq, Eq, thiserror::Error)]
#[cfg_attr(feature = "uniffi", derive(uniffi::Error))]
pub enum PayjoinError {
#[error("Error while parsing the string: {message} ")]
InvalidAddress { message: String },
Expand Down
4 changes: 4 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@ pub mod send;
pub mod uri;

pub use crate::bitcoin::*;
pub use crate::receive::*;
pub use crate::send::v1::*;
pub use crate::send::v2::*;
pub use crate::send::Context;
use crate::error::PayjoinError;
pub use crate::ohttp::*;
pub use crate::request::Request;
Expand Down
183 changes: 106 additions & 77 deletions src/payjoin_ffi.udl
Original file line number Diff line number Diff line change
Expand Up @@ -21,17 +21,17 @@ interface PayjoinError {
OhttpError(string message);
UrlError(string message);
IoError(string message);
OutputSubstitutionError(string message);
InputContributionError(string message);
InputPairError(string message);
};


callback interface CanBroadcast {
[Throws=PayjoinError]
boolean callback(sequence<u8> tx);
};
callback interface GenerateScript {
[Throws=PayjoinError]
sequence<u8> callback();
};

callback interface IsScriptOwned {
[Throws=PayjoinError]
boolean callback(sequence<u8> script);
Expand All @@ -50,12 +50,29 @@ dictionary OutPoint {
u32 vout;
};

dictionary PsbtInput {
TxOut? witness_utxo;
Script? redeem_script;
Script? witness_script;
};

interface Script {
constructor(sequence<u8> raw_output_script);

sequence<u8> to_bytes();
};

dictionary TxIn {
OutPoint previous_output;
};

dictionary TxOut{
u64 value;
sequence<u8> script_pubkey;
};
dictionary Request {
Url url;
string content_type;
sequence<u8> body;
};
enum Network {
Expand Down Expand Up @@ -105,6 +122,12 @@ dictionary RequestResponse {
Request request;
ClientResponse client_response;
};

interface InputPair {
[Throws=PayjoinError]
constructor(TxIn txin, PsbtInput psbtin);
};

interface UncheckedProposal{
[Name=from_request, Throws=PayjoinError]
constructor(sequence<u8> body, string query, Headers headers);
Expand All @@ -113,139 +136,145 @@ interface UncheckedProposal{
MaybeInputsOwned check_broadcast_suitability(u64? min_fee_rate,CanBroadcast can_broadcast);
MaybeInputsOwned assume_interactive_receiver();
};

interface MaybeInputsOwned{
[Throws=PayjoinError]
MaybeMixedInputScripts check_inputs_not_owned(IsScriptOwned is_owned);
};
interface MaybeMixedInputScripts{
[Throws=PayjoinError]
MaybeInputsSeen check_no_mixed_input_scripts();
MaybeInputsSeen check_inputs_not_owned(IsScriptOwned is_owned);
};

interface MaybeInputsSeen{
[Throws=PayjoinError]
OutputsUnknown check_no_inputs_seen_before(IsOutputKnown is_known);
};

interface OutputsUnknown {
[Throws=PayjoinError]
ProvisionalProposal identify_receiver_outputs(IsScriptOwned is_receiver_output);
WantsOutputs identify_receiver_outputs(IsScriptOwned is_receiver_output);
};

interface WantsOutputs {
[Throws=PayjoinError]
WantsOutputs replace_receiver_outputs(sequence<TxOut> replacement_outputs, Script drain_script);

[Throws=PayjoinError]
WantsOutputs substitute_receiver_script(sequence<u8> output_script);

WantsInputs commit_outputs();
};

interface WantsInputs {
[Throws=PayjoinError]
WantsInputs contribute_inputs(sequence<InputPair> replacement_inputs);

ProvisionalProposal commit_inputs();

[Throws=PayjoinError]
InputPair try_preserving_privacy(sequence<InputPair> candidate_inputs);
};

interface ProvisionalProposal{
[Throws=PayjoinError]
void contribute_witness_input(TxOut txout, OutPoint outpoint);
[Throws=PayjoinError]
void try_substitute_receiver_output(GenerateScript generate_script);
[Throws=PayjoinError]
OutPoint try_preserving_privacy(record<u64, OutPoint> candidate_inputs);
[Throws=PayjoinError]
PayjoinProposal finalize_proposal(ProcessPartiallySignedTransaction process_psbt, u64? min_feerate_sat_per_vb);
PayjoinProposal finalize_proposal(ProcessPartiallySignedTransaction process_psbt, u64? min_feerate_sat_per_vb, u64 max_feerate_sat_per_vb);
};
interface PayjoinProposal{
sequence<OutPoint> utxos_to_be_locked();
boolean is_output_substitution_disabled();
sequence<u64> owned_vouts();
string psbt();
};
interface RequestBuilder{
[Throws=PayjoinError, Name=from_psbt_and_uri]
constructor(string psbt, PjUri uri);
RequestBuilder always_disable_output_substitution( boolean disable );
[Throws=PayjoinError]
RequestContext build_recommended( u64 min_fee_rate);
[Throws=PayjoinError]
RequestContext build_with_additional_fee( u64 max_fee_contribution, u8? change_index, u64 min_fee_rate, boolean clamp_fee_contribution );
[Throws=PayjoinError]
RequestContext build_non_incentivizing( u64 min_fee_rate );

dictionary RequestV1Context{
Request request;
V1Context context;
};

interface RequestContext{
[Throws=PayjoinError]
RequestContextV1 extract_v1();
interface V1Context{
[Throws=PayjoinError]
RequestContextV2 extract_v2( Url ohttp_proxy_url);
string process_response( sequence<u8> response );
};

dictionary RequestContextV1{
Request request;
ContextV1 context_v1;
interface V2PostContext{
[Throws=PayjoinError]
V2GetContext process_response( sequence<u8> response );
};

dictionary RequestContextV2{
dictionary RequestOhttpContext{
Request request;
ContextV2 context_v2;
ClientResponse ohttp_ctx;
};

interface ContextV1{
interface V2GetContext{
[Throws=PayjoinError]
string process_response( sequence<u8> response );
};

interface ContextV2{
RequestOhttpContext extract_req(Url ohttp_relay);
[Throws=PayjoinError]
string? process_response( sequence<u8> response );
string? process_response(sequence<u8> response, ClientResponse ohttp_ctx);
};

interface OhttpKeys{
[Throws=PayjoinError, Name=decode]
constructor( sequence<u8> bytes );
};

interface SessionInitializer{
[Throws=PayjoinError]
constructor( string address, u64? expire_after, Network network, Url directory, OhttpKeys ohttp_keys, Url ohttp_relay);
[Throws=PayjoinError]
RequestResponse extract_req();
[Throws=PayjoinError]
ActiveSession process_res(sequence<u8> body, ClientResponse ctx);
};

interface ActiveSession {
[Throws=PayjoinError]
RequestResponse extract_req();
Url pj_url();
string public_key();
PjUriBuilder pj_uri_builder();
[Throws=PayjoinError]
V2UncheckedProposal? process_res(sequence<u8> body, ClientResponse ctx);
interface Receiver {
[Throws=PayjoinError]
constructor(string address, Network network, Url directory, OhttpKeys ohttp_keys, Url ohttp_relay, u64? expire_after);
[Throws=PayjoinError]
RequestResponse extract_req();
[Throws=PayjoinError]
V2UncheckedProposal? process_res(sequence<u8> body, ClientResponse ctx);
PjUriBuilder pj_uri_builder();
Url pj_url();
sequence<u8> id();
};

interface V2UncheckedProposal{
sequence<u8> extract_tx_to_schedule_broadcast();
V2MaybeInputsOwned assume_interactive_receiver();
[Throws=PayjoinError]
V2MaybeInputsOwned check_broadcast_suitability(u64? min_fee_rate,CanBroadcast can_broadcast);
V2MaybeInputsOwned assume_interactive_receiver();
};

interface V2MaybeInputsOwned{
[Throws=PayjoinError]
V2MaybeMixedInputScripts check_inputs_not_owned(IsScriptOwned is_owned);
};
interface V2MaybeMixedInputScripts{
[Throws=PayjoinError]
V2MaybeInputsSeen check_no_mixed_input_scripts();
V2MaybeInputsSeen check_inputs_not_owned(IsScriptOwned is_owned);
};

interface V2MaybeInputsSeen{
[Throws=PayjoinError]
V2OutputsUnknown check_no_inputs_seen_before(IsOutputKnown is_known);
};
interface V2OutputsUnknown{
[Throws=PayjoinError]
V2ProvisionalProposal identify_receiver_outputs(IsScriptOwned is_receiver_output);
V2WantsOutputs identify_receiver_outputs(IsScriptOwned is_receiver_output);
};

interface V2ProvisionalProposal{
[Throws=PayjoinError]
void contribute_witness_input(TxOut txout, OutPoint outpoint);
[Throws=PayjoinError]
void try_substitute_receiver_output(GenerateScript generate_script);
[Throws=PayjoinError]
OutPoint try_preserving_privacy(record<u64, OutPoint> candidate_inputs);
[Throws=PayjoinError]
V2PayjoinProposal finalize_proposal(ProcessPartiallySignedTransaction process_psbt, u64? min_feerate_sat_per_vb);
interface V2WantsOutputs {
[Throws=PayjoinError]
V2WantsOutputs replace_receiver_outputs(sequence<TxOut> replacement_outputs, Script drain_script);

[Throws=PayjoinError]
V2WantsOutputs substitute_receiver_script(sequence<u8> output_script);

V2WantsInputs commit_outputs();
};

interface V2WantsInputs {
[Throws=PayjoinError]
V2WantsInputs contribute_inputs(sequence<InputPair> replacement_inputs);

V2ProvisionalProposal commit_inputs();

[Throws=PayjoinError]
InputPair try_preserving_privacy(sequence<InputPair> candidate_inputs);
};

interface V2ProvisionalProposal {
[Throws=PayjoinError]
V2PayjoinProposal finalize_proposal(ProcessPartiallySignedTransaction process_psbt, u64? min_feerate_sat_per_vb, u64 max_fee_rate_sat_per_vb);
};

interface V2PayjoinProposal{
sequence<OutPoint> utxos_to_be_locked();
boolean is_output_substitution_disabled();
sequence<u64> owned_vouts();
string psbt();
string extract_v1_req();
[Throws=PayjoinError]
Expand Down
3 changes: 3 additions & 0 deletions src/receive/mod.rs
Original file line number Diff line number Diff line change
@@ -1,2 +1,5 @@
pub mod v1;
pub mod v2;

pub use v1::*;
pub use v2::*;
Loading

0 comments on commit 79cc692

Please sign in to comment.