Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

mobilecoind: add support for mixed transactions, including those with SCIs in them #3214

Merged
merged 16 commits into from
Mar 14, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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,
}
nick-mobilecoin marked this conversation as resolved.
Show resolved Hide resolved

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
85 changes: 82 additions & 3 deletions 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 {
cbeck88 marked this conversation as resolved.
Show resolved Hide resolved
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,32 @@ 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
cbeck88 marked this conversation as resolved.
Show resolved Hide resolved
/// This is the amount that, we, the counter-party, take from the SCI originator,
/// in exchange for a corresponding fraction of the partial fill outputs in the SCI.
///
/// This is a u64 value corresponding to an amount less or equal to the partial_fill_change amount
/// in the SCI.
///
/// In case of a non partial fill SCI (without partial_fill_change output),
/// this must be zero.
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;
nick-mobilecoin marked this conversation as resolved.
Show resolved Hide resolved

// 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 +195,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 @@ -466,7 +497,7 @@ message GetMembershipProofsResponse {
// - 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.
// Right now that fee is the network-reported minimum fee for the given token id.
message GenerateTxRequest {
// Monitor id sending the funds.
bytes sender_monitor_id = 1;
Expand All @@ -483,7 +514,9 @@ message GenerateTxRequest {
// Outputs to be generated by the transaction. This excludes change and fee.
repeated Outlay outlay_list = 4;

// Fee in picoMOB (setting to 0 causes mobilecoind to choose a value).
// Fee value, in smallest representable units (u64).
// (For MOB this is 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 = 5;

Expand All @@ -493,12 +526,58 @@ 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 or equal to the 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 the network-reported minimum fee for the fee token id.
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, for each token id involved.
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;
nick-mobilecoin marked this conversation as resolved.
Show resolved Hide resolved

// Fee value, in smallest representable units (u64).
// (For MOB this is 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 fee.
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
13 changes: 12 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,17 @@ mod autogenerated_code {

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

impl OutlayV2 {
/// This is used for some tests
pub fn new_from_outlay_and_token_id(outlay: &Outlay, token_id: u64) -> Self {
let mut result = Self::new();
result.set_value(outlay.value);
result.set_token_id(token_id);
result.set_receiver(outlay.get_receiver().clone());
result
}
}

pub type MobilecoindUri = Uri<MobilecoindScheme>;

/// Mobilecoind Uri Scheme
Expand Down
Loading