Skip to content

Commit

Permalink
add support for mixed transactions, including those with SCIs in them
Browse files Browse the repository at this point in the history
  • Loading branch information
cbeck88 committed Mar 12, 2023
1 parent dd24518 commit 82b04fa
Show file tree
Hide file tree
Showing 17 changed files with 902 additions and 90 deletions.
6 changes: 6 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,7 @@ members = [
"util/telemetry",
"util/test-helper",
"util/test-vector",
"util/u64-ratio",
"util/uri",
"util/vec-map",
"util/zip-exact",
Expand Down
2 changes: 1 addition & 1 deletion mobilecoind-json/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ serde_derive = "1.0"

[dev-dependencies]
mc-crypto-keys = { path = "../crypto/keys" }
mc-ledger-db = { path = "../ledger/db" }
mc-ledger-db = { path = "../ledger/db", features = ["test_utils"] }
mc-transaction-core = { path = "../transaction/core" }
mc-transaction-core-test-utils = { path = "../transaction/core/test-utils" }
mc-transaction-extra = { path = "../transaction/extra" }
Expand Down
50 changes: 44 additions & 6 deletions mobilecoind-json/src/data_types.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright (c) 2018-2022 The MobileCoin Foundation
// Copyright (c) 2018-2023 The MobileCoin Foundation

//! Serializeable data types that wrap the mobilecoind API.
Expand Down Expand Up @@ -486,6 +486,39 @@ impl TryFrom<&JsonOutlay> for api::Outlay {
}
}

#[derive(Deserialize, Serialize, Default, Debug, Clone)]
pub struct JsonOutlayV2 {
pub value: JsonU64,
pub receiver: JsonPublicAddress,
pub token_id: JsonU64,
}

impl From<&api::OutlayV2> for JsonOutlayV2 {
fn from(src: &api::OutlayV2) -> Self {
Self {
value: JsonU64(src.get_value()),
token_id: JsonU64(src.get_token_id()),
receiver: src.get_receiver().into(),
}
}
}

impl TryFrom<&JsonOutlayV2> for api::OutlayV2 {
type Error = String;

fn try_from(src: &JsonOutlayV2) -> Result<api::OutlayV2, String> {
let mut outlay = api::OutlayV2::new();
outlay.set_value(src.value.into());
outlay.set_token_id(src.token_id.into());
outlay.set_receiver(
PublicAddress::try_from(&src.receiver)
.map_err(|err| format!("Could not convert receiver: {err}"))?,
);

Ok(outlay)
}
}

#[derive(Deserialize, Serialize, Default, Debug, Clone)]
pub struct JsonMaskedAmount {
pub commitment: String,
Expand Down Expand Up @@ -1019,10 +1052,11 @@ impl TryFrom<&JsonTx> for Tx {
}
}

// FIXME: Add sci's to this?
#[derive(Deserialize, Serialize, Default, Debug)]
pub struct JsonTxProposal {
pub input_list: Vec<JsonUnspentTxOut>,
pub outlay_list: Vec<JsonOutlay>,
pub outlay_list: Vec<JsonOutlayV2>,
pub tx: JsonTx,
pub fee: u64,
pub outlay_index_to_tx_out_index: Vec<(usize, usize)>,
Expand All @@ -1042,7 +1076,11 @@ impl From<&api::TxProposal> for JsonTxProposal {
.iter()
.map(JsonUnspentTxOut::from)
.collect(),
outlay_list: src.get_outlay_list().iter().map(JsonOutlay::from).collect(),
outlay_list: src
.get_outlay_list()
.iter()
.map(JsonOutlayV2::from)
.collect(),
tx: src.get_tx().into(),
fee: src.get_fee(),
outlay_index_to_tx_out_index: outlay_map,
Expand All @@ -1063,9 +1101,9 @@ impl TryFrom<&JsonTxProposal> for api::TxProposal {
inputs.push(utxo);
}

let mut outlays: Vec<api::Outlay> = Vec::new();
let mut outlays: Vec<api::OutlayV2> = Vec::new();
for outlay in src.outlay_list.iter() {
let out = api::Outlay::try_from(outlay)
let out = api::OutlayV2::try_from(outlay)
.map_err(|err| format!("Failed to convert outlay: {err}"))?;
outlays.push(out);
}
Expand Down Expand Up @@ -1444,7 +1482,7 @@ mod test {
};

// Make proto outlay
let mut outlay = api::Outlay::new();
let mut outlay = api::OutlayV2::new();
let public_addr = AccountKey::random(&mut rng).default_subaddress();
outlay.set_receiver(mc_api::external::PublicAddress::from(&public_addr));
outlay.set_value(1234);
Expand Down
68 changes: 67 additions & 1 deletion mobilecoind/api/proto/mobilecoind_api.proto
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ service MobilecoindAPI {

// Swaps
rpc GenerateSwap (GenerateSwapRequest) returns (GenerateSwapResponse) {}
rpc GenerateMixedTx (GenerateMixedTxRequest) returns (GenerateMixedTxResponse) {}

// Databases
rpc GetLedgerInfo (google.protobuf.Empty) returns (GetLedgerInfoResponse) {}
Expand Down Expand Up @@ -108,11 +109,20 @@ enum TxStatus {
}

// Structure used in specifying the list of outputs when generating a transaction.
// Here the token id is implied from context, and matches the fee token id.
message Outlay {
uint64 value = 1;
external.PublicAddress receiver = 2;
}

// Structure used in specifying the list of outputs in a transaction.
// Here the token id is explicit.
message OutlayV2 {
uint64 value = 1;
external.PublicAddress receiver = 2;
uint64 token_id = 3;
}

// Structure used to refer to a TxOut in the ledger that is presumed to be spendable.
// The structure is annotated with extra information needed to spend the TxOut in a payment, calculated using the private keys that control the TxOut.
message UnspentTxOut {
Expand Down Expand Up @@ -142,14 +152,24 @@ message UnspentTxOut {
bytes monitor_id = 10;
}

// Structure used to refer to an SCI that we want to add to a transaction.
// The structure has additional information -- if it's a partial fill SCI, we need to know the partial fill amount.
message SciForTx {
/// The signed input we want to add
external.SignedContingentInput sci = 1;

/// If it's a partial fill SCI, the value we wish to fill it for
uint64 partial_fill_value = 2;
}

// Structure used to refer to a prepared transaction
message TxProposal {
// List of inputs being spent.
repeated UnspentTxOut input_list = 1;

// List of outputs being created.
// This excludes the fee output.
repeated Outlay outlay_list = 2;
repeated OutlayV2 outlay_list = 2;

// The actual transaction object.
// Together with the private view/spend keys, this structure contains all information in existence about the transaction.
Expand All @@ -167,6 +187,9 @@ message TxProposal {
/// A list of the confirmation numbers, in the same order
/// as the outlays.
repeated bytes outlay_confirmation_numbers = 6;

/// A list of the scis that were incorporated into this transaction, if any
repeated SciForTx scis = 7;
}

// Structure used to check transaction status as a Sender.
Expand Down Expand Up @@ -493,12 +516,55 @@ message GenerateTxRequest {

// Token id to use for the transaction.
uint64 token_id = 7;

// List of SCIs to be added to the transaction
repeated SciForTx scis = 8;
}

message GenerateTxResponse {
TxProposal tx_proposal = 1;
}

// Generate a transaction proposal object with mixed token types.
// Notes:
// - Sum of inputs needs to be greater than sum of outlays and fee.
// - The set of inputs to use would be chosen automatically by mobilecoind.
// - The fee field could be set to zero, in which case mobilecoind would choose a fee.
// Right now that fee is hardcoded.
message GenerateMixedTxRequest {
// Monitor id sending the funds.
bytes sender_monitor_id = 1;

// Subaddress to return change to.
uint64 change_subaddress = 2;

// List of UnspentTxOuts to be spent by the transaction.
// All UnspentTxOuts must belong to the same sender_monitor_id.
// mobilecoind would choose a subset of these inputs to construct the transaction.
// Total input amount must be >= sum of outlays + fees.
repeated UnspentTxOut input_list = 3;

// List of SCIs to be added to the transaction
repeated SciForTx scis = 4;

// Outputs to be generated by the transaction. This excludes change and fee.
repeated OutlayV2 outlay_list = 5;

// Fee in picoMOB (setting to 0 causes mobilecoind to choose a value).
// The value used can be checked (but not changed) in tx_proposal.tx.prefix.fee
uint64 fee = 6;

// Token id to use for the transaction.
uint64 fee_token_id = 7;

// Tombstone block (setting to 0 causes mobilecoind to choose a value).
// The value used can be checked (but not changed) in tx_proposal.tx.prefix.tombstone_block
uint64 tombstone = 8;
}

message GenerateMixedTxResponse {
TxProposal tx_proposal = 1;
}
// Generate a transaction that merges a few UnspentTxOuts into one, in order to reduce wallet fragmentation.
message GenerateOptimizationTxRequest {
// Monitor Id to operate on.
Expand Down
11 changes: 10 additions & 1 deletion mobilecoind/api/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright (c) 2018-2022 The MobileCoin Foundation
// Copyright (c) 2018-2023 The MobileCoin Foundation

//! mobilecoind gRPC API.
Expand All @@ -21,6 +21,15 @@ mod autogenerated_code {

pub use autogenerated_code::{mobilecoind_api::*, *};

// This is used for some tests, we want to test that the fields that appeared
// in an outlay in a request match the corresponding fields in the outlay in the
// response.
impl PartialEq<Outlay> for OutlayV2 {
fn eq(&self, other: &Outlay) -> bool {
self.value == other.value && self.receiver == other.receiver
}
}

pub type MobilecoindUri = Uri<MobilecoindScheme>;

/// Mobilecoind Uri Scheme
Expand Down
Loading

0 comments on commit 82b04fa

Please sign in to comment.