mirror of
https://github.com/mimblewimble/grin-wallet.git
synced 2025-01-20 19:11:09 +03:00
Compact slate merge (#404)
* Add support for sending compact slates (#366) * WIP add support for sending compact slates * add repopulate_tx function to internal API * first pass at compacted slate working * move slate compaction to separate function * test fixes * support compact slate inits in invoice workflow * add compress flags to send and invoice * attempting to remove is_compact and assume all V4 slates begin as compact * attempting to calculate offsets when full tx data isn't available * update calc_commit to use participant blind data * update doctests for compact slates * start to remove unneeded fields from serialization * make num_participants optional * remove other_version from slate * use grin master branch * remove message field * lock height assumed to be 0 if it doesn't exist * don't serialise receiver signature when null * don't serialize payment_info if not needed * remove participant id from participant info * add note on id field * fix finalize and receive doctests * finalize_tx tests, init_send_tx tests * doctests for process_invoice_tx, retrieve_tx, tx_lock_outputs * finished test changes * update from grin master * rebuild PR from diff (#380) * recreate PR from diff (#381) * serialize tx struct into top level coms object (#382) * remove height (#383) * Add State Slate (#384) * add state field to slate and SlateV4 * set slate state at each transaction stage, add check to tests * serialize slate status properly * V4 Slate field tweaks (#386) * various tweaks to V4 slate * field renaming * serialize slate v4 ID as base64 (#387) * remove amount and fee where not needed (#388) * Final Changes for compact Slate (#389) * add tests for all types of file output, remove message args * default range proof serialization * shorten output features serialization * rename payment proof fields in slate v4 * v4 payment proof serialization * Binary Slates (#385) * start test implementation * add experimental binary serialization to slate * serialize id * serialize fields that can be skipped as a separate struct * factor out sigs serialization * clean up sigs and coms serialization * completed v4 bin serialization * add manual de/ser traits for V4 bin slate * add simple byte array serializer * complete wiring in of bin slate serialization * clarify comment * clarify comment * update version * test output dir name fix * update slate v4 change description * add binary output to command line * Remove unneeded signature data during S2 and I2 stages (#390) * remove unneeded return signature data during S2 * remove unneeded sig data from I2 * Doctest Fixes for compact slate branch (#392) * begin to fix doctests * more doctest fixes * fix receive_tx * update get_stored_tx to accept an UUID instead of a tx object, and operate on a raw Transaction object (#394) * Fixes to async transaction posting (#395) * unstash post_tx changes * add offset during S3 and I3 * Revert slate id serialization to hex-string uuid (#396) * update from master (#397) * v3.x.x - v4.0.0 wallet compatibility fixes (#398) * changes to support http sending to v3 wallets * sending via http/tor TO 3.0.0 wallet works * receiving FROM 3.0.0 wallets works over http/tor * output converted V3 slate when needed * paying invoices from 3.0.0 wallets working * handle all participant info in slate states * sending and receiving standard file transactions between v3 and 4 wallets confirmed working * all file-based workflows working * fixes resulting from tests * remove reminder warnings * remove lock_height, add kernel_features + arguments (#399) * grin-wallet master now building against grin master (#402) (#403) Co-authored-by: Antioch Peverell <apeverell@protonmail.com> * Enhanced offset creation (#407) * initial tests reworking offset creation * invoice flow fixing + tests * further test fixes * change offset name in v4 slate, base64 serialize * logic optimisation * changes based on review feedback Co-authored-by: Antioch Peverell <apeverell@protonmail.com>
This commit is contained in:
parent
19da1e4aec
commit
9450d2a3b8
49 changed files with 5124 additions and 3800 deletions
3046
Cargo.lock
generated
3046
Cargo.lock
generated
File diff suppressed because it is too large
Load diff
|
@ -268,57 +268,6 @@ where
|
|||
)
|
||||
}
|
||||
|
||||
/// Verifies all messages in the slate match their public keys.
|
||||
///
|
||||
/// The option messages themselves are part of the `participant_data` field within the slate.
|
||||
/// Messages are signed with the same key used to sign for the paricipant's inputs, and can thus be
|
||||
/// verified with the public key found in the `public_blind_excess` field. This function is a
|
||||
/// simple helper to returns whether all signatures in the participant data match their public
|
||||
/// keys.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `slate` - The transaction [`Slate`](../grin_wallet_libwallet/slate/struct.Slate.html).
|
||||
///
|
||||
/// # Returns
|
||||
/// * `Ok(())` if successful and the signatures validate
|
||||
/// * or [`libwallet::Error`](../grin_wallet_libwallet/struct.Error.html) if an error is encountered.
|
||||
///
|
||||
/// # Example
|
||||
/// Set up as in [`new`](struct.Foreign.html#method.new) method above.
|
||||
/// ```
|
||||
/// # grin_wallet_api::doctest_helper_setup_doc_env_foreign!(wallet, wallet_config);
|
||||
///
|
||||
/// let mut api_foreign = Foreign::new(wallet.clone(), None, None);
|
||||
///
|
||||
/// # let slate = Slate::blank(2);
|
||||
/// // Receive a slate via some means
|
||||
///
|
||||
/// let res = api_foreign.verify_slate_messages(&slate);
|
||||
///
|
||||
/// if let Err(e) = res {
|
||||
/// // Messages don't validate, likely return an error
|
||||
/// // ...
|
||||
/// } else {
|
||||
/// // Slate messages are fine
|
||||
/// }
|
||||
///
|
||||
///
|
||||
/// ```
|
||||
|
||||
pub fn verify_slate_messages(&self, slate: &Slate) -> Result<(), Error> {
|
||||
if let Some(m) = self.middleware.as_ref() {
|
||||
let mut w_lock = self.wallet_inst.lock();
|
||||
let w = w_lock.lc_provider()?.wallet_inst()?;
|
||||
m(
|
||||
ForeignCheckMiddlewareFn::VerifySlateMessages,
|
||||
w.w2n_client().get_version_info(),
|
||||
Some(slate),
|
||||
)?;
|
||||
}
|
||||
foreign::verify_slate_messages(slate)
|
||||
}
|
||||
|
||||
/// Recieve a tranaction created by another party, returning the modified
|
||||
/// [`Slate`](../grin_wallet_libwallet/slate/struct.Slate.html) object, modified with
|
||||
/// the recipient's output for the transaction amount, and public signature data. This slate can
|
||||
|
@ -340,12 +289,6 @@ where
|
|||
/// excess value).
|
||||
/// * `dest_acct_name` - The name of the account into which the slate should be received. If
|
||||
/// `None`, the default account is used.
|
||||
/// * `message` - An optional participant message to include alongside the recipient's public
|
||||
/// ParticipantData within the slate. This message will include a signature created with the
|
||||
/// recipient's private excess value, and will be publically verifiable. Note this message is for
|
||||
/// the convenience of the participants during the exchange; it is not included in the final
|
||||
/// transaction sent to the chain. The message will be truncated to 256 characters.
|
||||
/// Validation of this message is optional.
|
||||
///
|
||||
/// # Returns
|
||||
/// * a result containing:
|
||||
|
@ -363,11 +306,11 @@ where
|
|||
/// # grin_wallet_api::doctest_helper_setup_doc_env_foreign!(wallet, wallet_config);
|
||||
///
|
||||
/// let mut api_foreign = Foreign::new(wallet.clone(), None, None);
|
||||
/// # let slate = Slate::blank(2);
|
||||
/// # let slate = Slate::blank(2, false);
|
||||
///
|
||||
/// // . . .
|
||||
/// // Obtain a sent slate somehow
|
||||
/// let result = api_foreign.receive_tx(&slate, None, None);
|
||||
/// let result = api_foreign.receive_tx(&slate, None);
|
||||
///
|
||||
/// if let Ok(slate) = result {
|
||||
/// // Send back to recipient somehow
|
||||
|
@ -375,12 +318,7 @@ where
|
|||
/// }
|
||||
/// ```
|
||||
|
||||
pub fn receive_tx(
|
||||
&self,
|
||||
slate: &Slate,
|
||||
dest_acct_name: Option<&str>,
|
||||
message: Option<String>,
|
||||
) -> Result<Slate, Error> {
|
||||
pub fn receive_tx(&self, slate: &Slate, dest_acct_name: Option<&str>) -> Result<Slate, Error> {
|
||||
let mut w_lock = self.wallet_inst.lock();
|
||||
let w = w_lock.lc_provider()?.wallet_inst()?;
|
||||
if let Some(m) = self.middleware.as_ref() {
|
||||
|
@ -395,7 +333,6 @@ where
|
|||
(&self.keychain_mask).as_ref(),
|
||||
slate,
|
||||
dest_acct_name,
|
||||
message,
|
||||
self.doctest_mode,
|
||||
)
|
||||
}
|
||||
|
@ -441,7 +378,7 @@ where
|
|||
/// // If result okay, send to payer, who will apply the transaction via their
|
||||
/// // owner API, then send back the slate
|
||||
/// // ...
|
||||
/// # let slate = Slate::blank(2);
|
||||
/// # let slate = Slate::blank(2, true);
|
||||
///
|
||||
/// let slate = api_foreign.finalize_invoice_tx(&slate);
|
||||
/// // if okay, then post via the owner API
|
||||
|
|
|
@ -116,88 +116,6 @@ pub trait ForeignRpc {
|
|||
|
||||
fn build_coinbase(&self, block_fees: &BlockFees) -> Result<VersionedCoinbase, ErrorKind>;
|
||||
|
||||
/**
|
||||
Networked version of [Foreign::verify_slate_messages](struct.Foreign.html#method.verify_slate_messages).
|
||||
|
||||
# Json rpc example
|
||||
|
||||
```
|
||||
# grin_wallet_api::doctest_helper_json_rpc_foreign_assert_response!(
|
||||
# r#"
|
||||
{
|
||||
"jsonrpc": "2.0",
|
||||
"method": "verify_slate_messages",
|
||||
"id": 1,
|
||||
"params": [ {
|
||||
"amount": "6000000000",
|
||||
"fee": "8000000",
|
||||
"height": "4",
|
||||
"id": "0436430c-2b02-624c-2032-570501212b00",
|
||||
"lock_height": "4",
|
||||
"ttl_cutoff_height": null,
|
||||
"payment_proof": null,
|
||||
"num_participants": 2,
|
||||
"participant_data": [
|
||||
{
|
||||
"id": "0",
|
||||
"message": "my message",
|
||||
"message_sig": "8f07ddd5e9f5179cff19486034181ed76505baaad53e5d994064127b56c5841b1d4c1358be398f801eb90d933774b5218fa7e769b11c4c640402253353656f75",
|
||||
"part_sig": null,
|
||||
"public_blind_excess": "034b4df2f0558b73ea72a1ca5c4ab20217c66bbe0829056fca7abe76888e9349ee",
|
||||
"public_nonce": "031b84c5567b126440995d3ed5aaba0565d71e1834604819ff9c17f5e9d5dd078f"
|
||||
}
|
||||
],
|
||||
"tx": {
|
||||
"body": {
|
||||
"inputs": [
|
||||
{
|
||||
"commit": "08e1da9e6dc4d6e808a718b2f110a991dd775d65ce5ae408a4e1f002a4961aa9e7",
|
||||
"features": "Coinbase"
|
||||
}
|
||||
],
|
||||
"kernels": [
|
||||
{
|
||||
"excess": "000000000000000000000000000000000000000000000000000000000000000000",
|
||||
"excess_sig": "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
|
||||
"features": "HeightLocked",
|
||||
"fee": "8000000",
|
||||
"lock_height": "4"
|
||||
}
|
||||
],
|
||||
"outputs": [
|
||||
{
|
||||
"commit": "094be57c91787fc2033d5d97fae099f1a6ddb37ea48370f1a138f09524c767fdd3",
|
||||
"features": "Plain",
|
||||
"proof": "2a42e9e902b70ce44e1fccb14de87ee0a97100bddf12c6bead1b9c5f4eb60300f29c13094fa12ffeee238fb4532b18f6b61cf51b23c1c7e1ad2e41560dc27edc0a2b9e647a0b3e4e806fced5b65e61d0f1f5197d3e2285c632d359e27b6b9206b2caffea4f67e0c7a2812e7a22c134b98cf89bd43d9f28b8bec25cce037a0ac5b1ae8f667e54e1250813a5263004486b4465ad4e641ab2b535736ea26535a11013564f08f483b7dab1c2bcc3ee38eadf2f7850eff7e3459a4bbabf9f0cf6c50d0c0a4120565cd4a2ce3e354c11721cd695760a24c70e0d5a0dfc3c5dcd51dfad6de2c237a682f36dc0b271f21bb3655e5333016aaa42c2efa1446e5f3c0a79ec417c4d30f77556951cb0f05dbfafb82d9f95951a9ea241fda2a6388f73ace036b98acce079f0e4feebccc96290a86dcc89118a901210b245f2d114cf94396e4dbb461e82aa26a0581389707957968c7cdc466213bb1cd417db207ef40c05842ab67a01a9b96eb1430ebc26e795bb491258d326d5174ad549401059e41782121e506744af8af9d8e493644a87d613600888541cbbe538c625883f3eb4aa3102c5cfcc25de8e97af8927619ce6a731b3b8462d51d993066b935b0648d2344ad72e4fd70f347fbd81041042e5ea31cc7b2e3156a920b80ecba487b950ca32ca95fae85b759c936246ecf441a9fdd95e8fee932d6782cdec686064018c857efc47fb4b2a122600d5fdd79af2486f44df7e629184e1c573bc0a9b3feb40b190ef2861a1ab45e2ac2201b9cd42e495deea247269820ed32389a2810ad6c0f9a296d2a2d9c54089fed50b7f5ecfcd33ab9954360e1d7f5598c32128cfcf2a1d8bf14616818da8a5343bfa88f0eedf392e9d4ab1ace1b60324129cd4852c2e27813a9cf71a6ae6229a4fcecc1a756b3e664c5f50af333082616815a3bec8fc0b75b8e4e767d719"
|
||||
}
|
||||
]
|
||||
},
|
||||
"offset": "d202964900000000d302964900000000d402964900000000d502964900000000"
|
||||
},
|
||||
"version_info": {
|
||||
"orig_version": 2,
|
||||
"version": 2,
|
||||
"block_header_version": 2
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
# "#
|
||||
# ,
|
||||
# r#"
|
||||
{
|
||||
"jsonrpc": "2.0",
|
||||
"id": 1,
|
||||
"result": {
|
||||
"Ok": null
|
||||
}
|
||||
}
|
||||
# "#
|
||||
# ,false, 1 ,false, false);
|
||||
```
|
||||
*/
|
||||
fn verify_slate_messages(&self, slate: VersionedSlate) -> Result<(), ErrorKind>;
|
||||
|
||||
/**
|
||||
Networked version of [Foreign::receive_tx](struct.Foreign.html#method.receive_tx).
|
||||
|
||||
|
@ -212,63 +130,21 @@ pub trait ForeignRpc {
|
|||
"id": 1,
|
||||
"params": [
|
||||
{
|
||||
"version_info": {
|
||||
"version": 2,
|
||||
"orig_version": 2,
|
||||
"block_header_version": 2
|
||||
"ver": "4:2",
|
||||
"id": "0436430c-2b02-624c-2032-570501212b00",
|
||||
"sta": "S1",
|
||||
"amt": "60000000000",
|
||||
"fee": "7000000",
|
||||
"off": "0gKWSQAAAADTApZJAAAAANQClkkAAAAA1QKWSQAAAAA=",
|
||||
"sigs": [
|
||||
{
|
||||
"xs": "AwFS0tcuLbp8YIatSaIZ2f8N/g/Zk9yuoi4FjCEAM86T",
|
||||
"nonce": "AxuExVZ7EmRAmV0+1aq6BWXXHhg0YEgZ/5wX9enV3QeP"
|
||||
}
|
||||
]
|
||||
},
|
||||
"num_participants": 2,
|
||||
"id": "0436430c-2b02-624c-2032-570501212b00",
|
||||
"tx": {
|
||||
"offset": "d202964900000000d302964900000000d402964900000000d502964900000000",
|
||||
"body": {
|
||||
"inputs": [
|
||||
{
|
||||
"features": "Coinbase",
|
||||
"commit": "087df32304c5d4ae8b2af0bc31e700019d722910ef87dd4eec3197b80b207e3045"
|
||||
},
|
||||
{
|
||||
"features": "Coinbase",
|
||||
"commit": "08e1da9e6dc4d6e808a718b2f110a991dd775d65ce5ae408a4e1f002a4961aa9e7"
|
||||
}
|
||||
],
|
||||
"outputs": [
|
||||
{
|
||||
"features": "Plain",
|
||||
"commit": "0812276cc788e6870612296d926cba9f0e7b9810670710b5a6e6f1ba006d395774",
|
||||
"proof": "dcff6175390c602bfa92c2ffd1a9b2d84dcc9ea941f6f317bdd0f875244ef23e696fd17c71df79760ce5ce1a96aab1d15dd057358dc835e972febeb86d50ccec0dad7cfe0246d742eb753cf7b88c045d15bc7123f8cf7155647ccf663fca92a83c9a65d0ed756ea7ebffd2cac90c380a102ed9caaa355d175ed0bf58d3ac2f5e909d6c447dfc6b605e04925c2b17c33ebd1908c965a5541ea5d2ed45a0958e6402f89d7a56df1992e036d836e74017e73ccad5cb3a82b8e139e309792a31b15f3ffd72ed033253428c156c2b9799458a25c1da65b719780a22de7fe7f437ae2fccd22cf7ea357ab5aa66a5ef7d71fb0dc64aa0b5761f68278062bb39bb296c787e4cabc5e2a2933a416ce1c9a9696160386449c437e9120f7bb26e5b0e74d1f2e7d5bcd7aafb2a92b87d1548f1f911fb06af7bd6cc13cee29f7c9cb79021aed18186272af0e9d189ec107c81a8a3aeb4782b0d950e4881aa51b776bb6844b25bce97035b48a9bdb2aea3608687bcdd479d4fa998b5a839ff88558e4a29dff0ed13b55900abb5d439b70793d902ae9ad34587b18c919f6b875c91d14deeb1c373f5e76570d59a6549758f655f1128a54f162dfe8868e1587028e26ad91e528c5ae7ee9335fa58fb59022b5de29d80f0764a9917390d46db899acc6a5b416e25ecc9dccb7153646addcc81cadb5f0078febc7e05d7735aba494f39ef05697bbcc9b47b2ccc79595d75fc13c80678b5e237edce58d731f34c05b1ddcaa649acf2d865bbbc3ceda10508bcdd29d0496744644bf1c3516f6687dfeef5649c7dff90627d642739a59d91a8d1d0c4dc55d74a949e1074427664b467992c9e0f7d3af9d6ea79513e8946ddc0d356bac49878e64e6a95b0a30214214faf2ce317fa622ff3266b32a816e10a18e6d789a5da1f23e67b4f970a68a7bcd9e18825ee274b0483896a40"
|
||||
}
|
||||
],
|
||||
"kernels": [
|
||||
{
|
||||
"features": "Plain",
|
||||
"fee": "7000000",
|
||||
"lock_height": "0",
|
||||
"excess": "000000000000000000000000000000000000000000000000000000000000000000",
|
||||
"excess_sig": "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"amount": "60000000000",
|
||||
"fee": "7000000",
|
||||
"height": "5",
|
||||
"lock_height": "0",
|
||||
"ttl_cutoff_height": null,
|
||||
"payment_proof": null,
|
||||
"participant_data": [
|
||||
{
|
||||
"id": "0",
|
||||
"public_blind_excess": "033ac2158fa0077f087de60c19d8e431753baa5b63b6e1477f05a2a6e7190d4592",
|
||||
"public_nonce": "031b84c5567b126440995d3ed5aaba0565d71e1834604819ff9c17f5e9d5dd078f",
|
||||
"part_sig": null,
|
||||
"message": null,
|
||||
"message_sig": null
|
||||
}
|
||||
]
|
||||
},
|
||||
null,
|
||||
"Thanks, Yeastplume"
|
||||
null,
|
||||
null
|
||||
]
|
||||
}
|
||||
# "#
|
||||
|
@ -279,73 +155,23 @@ pub trait ForeignRpc {
|
|||
"jsonrpc": "2.0",
|
||||
"result": {
|
||||
"Ok": {
|
||||
"amount": "60000000000",
|
||||
"fee": "7000000",
|
||||
"height": "5",
|
||||
"id": "0436430c-2b02-624c-2032-570501212b00",
|
||||
"lock_height": "0",
|
||||
"ttl_cutoff_height": null,
|
||||
"payment_proof": null,
|
||||
"num_participants": 2,
|
||||
"participant_data": [
|
||||
{
|
||||
"id": "0",
|
||||
"message": null,
|
||||
"message_sig": null,
|
||||
"part_sig": null,
|
||||
"public_blind_excess": "033ac2158fa0077f087de60c19d8e431753baa5b63b6e1477f05a2a6e7190d4592",
|
||||
"public_nonce": "031b84c5567b126440995d3ed5aaba0565d71e1834604819ff9c17f5e9d5dd078f"
|
||||
},
|
||||
{
|
||||
"id": "1",
|
||||
"message": "Thanks, Yeastplume",
|
||||
"message_sig": "8f07ddd5e9f5179cff19486034181ed76505baaad53e5d994064127b56c5841b30a1f1b21eade1b4bd211e1f137fbdbca1b78dc43da21b1695f6a0edf2437ff9",
|
||||
"part_sig": "8f07ddd5e9f5179cff19486034181ed76505baaad53e5d994064127b56c5841b2b35bd28dfd2269e0670e0cf9270bd6df2d03fbd64523ee4ae622396055b96fc",
|
||||
"public_blind_excess": "038fe0443243dab173c068ef5fa891b242d2b5eb890ea09475e6e381170442ee16",
|
||||
"public_nonce": "031b84c5567b126440995d3ed5aaba0565d71e1834604819ff9c17f5e9d5dd078f"
|
||||
}
|
||||
"coms": [
|
||||
{
|
||||
"c": "CE7pfe+ow3Ek1MabqnU+JTJTX6qB956l4EidslKX1b64",
|
||||
"p": "AH333d0e/KdXsgcHQMxgRig5DrWeFR+W/y6qU2H1Q1/Rqm6j/ryX/P4bMkjQQMgt42GAOSl2ui0RR8L7AhyHrQRPH5djk02dP0QxQXdi7tA8U84Xrtt4JFZcH0j8zsnEq8DSi9MrAs6b7kC/amDPfJwgPMJOS3efkB4SyYdXNpjPfwTjqs4m5xJiE4YFQkgArfMpXQn39F3d8YVceF6Y1F6uPNER0YVS5zOJVFjfFecaE4ONeJpMs2n03biqnFA7CA/YihRyRd8FItQTbTahg72UHmz5Tf/HhDixIZTU33EU0eJ6ei8BSSCjISI+y+uyuWQqIvjtTnSIMSXz51ey8RiFP/qxto8VwaLQIeWD/z/R6ihyCoEyWzzCMnup+y/ZsmRK2388ey4xmyU2o09n5vCTRvJNpryuGyQfhZBJNHbf41sYPlTxBeshm2AeDlOWVAlwHcH9lWLEKtl3UF6nvyZPAXcFaaSjWKcPsLLGWWn6w7I5VPDKCtrOBwMkPx2rYmUJqGVuepgXCcOsHVFpS6+lWq1FwQGTfL8+RdZwjAe+cUGXaaEKT2Tyt9U6VOrHPNvTJ5+RxfiZGksXYhw2GVqTkTZPoiHoqN7iHrw6brnNKUCjZ25+883UYxm9wR90h4Xkn/Qb7CwyQyVdg8aJW8DIk+anctdECmgyEkaxd3CdO9gtDcL1vKQMh46Fm2+CMZo4bgt/y8gBCiUXiwhBg4m6fGp3+ZrH9K5caGq2V0/NARb4VzvM2j7f3/NsnJLOL7i/sM4v5caySYxusW/C1A3p3cuhmafpPWSKvznWskjhlt5xJ+a4EuMIBJfyqCr6aaRxq1EedT5bF6HDnGcooGWJivZnRgjZKmJeluLwJY/i6waifQWG2InWH5f6qj+s9Yza"
|
||||
}
|
||||
],
|
||||
"tx": {
|
||||
"body": {
|
||||
"inputs": [
|
||||
"id": "0436430c-2b02-624c-2032-570501212b00",
|
||||
"off": "pAUskgAAAAGmBSySAAAAAu1WT6tQt1/F6jLOBS/Jvr8=",
|
||||
"sigs": [
|
||||
{
|
||||
"commit": "087df32304c5d4ae8b2af0bc31e700019d722910ef87dd4eec3197b80b207e3045",
|
||||
"features": "Coinbase"
|
||||
},
|
||||
{
|
||||
"commit": "08e1da9e6dc4d6e808a718b2f110a991dd775d65ce5ae408a4e1f002a4961aa9e7",
|
||||
"features": "Coinbase"
|
||||
"nonce": "AxuExVZ7EmRAmV0+1aq6BWXXHhg0YEgZ/5wX9enV3QeP",
|
||||
"part": "jwfd1en1F5z/GUhgNBge12UFuqrVPl2ZQGQSe1bFhBsFiJtL7+KpZhceAUs9zF+T6CiXy18Hr2cIYcJ9XmH+ag==",
|
||||
"xs": "A6ZTwAjgK+rW7OC8F/8UHcRBH5NW+xZBLlx7x6fcv3PU"
|
||||
}
|
||||
],
|
||||
"kernels": [
|
||||
{
|
||||
"excess": "000000000000000000000000000000000000000000000000000000000000000000",
|
||||
"excess_sig": "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
|
||||
"features": "Plain",
|
||||
"fee": "7000000",
|
||||
"lock_height": "0"
|
||||
}
|
||||
],
|
||||
"outputs": [
|
||||
{
|
||||
"commit": "084ee97defa8c37124d4c69baa753e2532535faa81f79ea5e0489db25297d5beb8",
|
||||
"features": "Plain",
|
||||
"proof": "007df7dddd1efca757b2070740cc604628390eb59e151f96ff2eaa5361f5435fd1aa6ea3febc97fcfe1b3248d040c82de36180392976ba2d1147c2fb021c87ad044f1f9763934d9d3f4431417762eed03c53ce17aedb7824565c1f48fccec9c4abc0d28bd32b02ce9bee40bf6a60cf7c9c203cc24e4b779f901e12c987573698cf7f04e3aace26e71262138605424800adf3295d09f7f45dddf1855c785e98d45eae3cd111d18552e733895458df15e71a13838d789a4cb369f4ddb8aa9c503b080fd88a147245df0522d4136d36a183bd941e6cf94dffc78438b12194d4df7114d1e27a7a2f014920a321223ecbebb2b9642a22f8ed4e74883125f3e757b2f118853ffab1b68f15c1a2d021e583ff3fd1ea28720a81325b3cc2327ba9fb2fd9b2644adb7f3c7b2e319b2536a34f67e6f09346f24da6bcae1b241f8590493476dfe35b183e54f105eb219b601e0e53965409701dc1fd9562c42ad977505ea7bf264f01770569a4a358a70fb0b2c65969fac3b23954f0ca0adace0703243f1dab626509a8656e7a981709c3ac1d51694bafa55aad45c101937cbf3e45d6708c07be71419769a10a4f64f2b7d53a54eac73cdbd3279f91c5f8991a4b17621c36195a9391364fa221e8a8dee21ebc3a6eb9cd2940a3676e7ef3cdd46319bdc11f748785e49ff41bec2c3243255d83c6895bc0c893e6a772d7440a68321246b177709d3bd82d0dc2f5bca40c878e859b6f82319a386e0b7fcbc8010a25178b08418389ba7c6a77f99ac7f4ae5c686ab6574fcd0116f8573bccda3edfdff36c9c92ce2fb8bfb0ce2fe5c6b2498c6eb16fc2d40de9ddcba199a7e93d648abf39d6b248e196de7127e6b812e3080497f2a82afa69a471ab511e753e5b17a1c39c6728a065898af6674608d92a625e96e2f0258fe2eb06a27d0586d889d61f97faaa3facf58cda"
|
||||
},
|
||||
{
|
||||
"commit": "0812276cc788e6870612296d926cba9f0e7b9810670710b5a6e6f1ba006d395774",
|
||||
"features": "Plain",
|
||||
"proof": "dcff6175390c602bfa92c2ffd1a9b2d84dcc9ea941f6f317bdd0f875244ef23e696fd17c71df79760ce5ce1a96aab1d15dd057358dc835e972febeb86d50ccec0dad7cfe0246d742eb753cf7b88c045d15bc7123f8cf7155647ccf663fca92a83c9a65d0ed756ea7ebffd2cac90c380a102ed9caaa355d175ed0bf58d3ac2f5e909d6c447dfc6b605e04925c2b17c33ebd1908c965a5541ea5d2ed45a0958e6402f89d7a56df1992e036d836e74017e73ccad5cb3a82b8e139e309792a31b15f3ffd72ed033253428c156c2b9799458a25c1da65b719780a22de7fe7f437ae2fccd22cf7ea357ab5aa66a5ef7d71fb0dc64aa0b5761f68278062bb39bb296c787e4cabc5e2a2933a416ce1c9a9696160386449c437e9120f7bb26e5b0e74d1f2e7d5bcd7aafb2a92b87d1548f1f911fb06af7bd6cc13cee29f7c9cb79021aed18186272af0e9d189ec107c81a8a3aeb4782b0d950e4881aa51b776bb6844b25bce97035b48a9bdb2aea3608687bcdd479d4fa998b5a839ff88558e4a29dff0ed13b55900abb5d439b70793d902ae9ad34587b18c919f6b875c91d14deeb1c373f5e76570d59a6549758f655f1128a54f162dfe8868e1587028e26ad91e528c5ae7ee9335fa58fb59022b5de29d80f0764a9917390d46db899acc6a5b416e25ecc9dccb7153646addcc81cadb5f0078febc7e05d7735aba494f39ef05697bbcc9b47b2ccc79595d75fc13c80678b5e237edce58d731f34c05b1ddcaa649acf2d865bbbc3ceda10508bcdd29d0496744644bf1c3516f6687dfeef5649c7dff90627d642739a59d91a8d1d0c4dc55d74a949e1074427664b467992c9e0f7d3af9d6ea79513e8946ddc0d356bac49878e64e6a95b0a30214214faf2ce317fa622ff3266b32a816e10a18e6d789a5da1f23e67b4f970a68a7bcd9e18825ee274b0483896a40"
|
||||
}
|
||||
]
|
||||
},
|
||||
"offset": "d202964900000000d302964900000000d402964900000000d502964900000000"
|
||||
},
|
||||
"version_info": {
|
||||
"orig_version": 2,
|
||||
"version": 2,
|
||||
"block_header_version": 2
|
||||
}
|
||||
],
|
||||
"sta": "S2",
|
||||
"ver": "4:2"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -357,6 +183,7 @@ pub trait ForeignRpc {
|
|||
&self,
|
||||
slate: VersionedSlate,
|
||||
dest_acct_name: Option<String>,
|
||||
//TODO: Remove post-HF3
|
||||
message: Option<String>,
|
||||
) -> Result<VersionedSlate, ErrorKind>;
|
||||
|
||||
|
@ -374,71 +201,30 @@ pub trait ForeignRpc {
|
|||
"method": "finalize_invoice_tx",
|
||||
"id": 1,
|
||||
"params": [{
|
||||
"version_info": {
|
||||
"version": 2,
|
||||
"orig_version": 2,
|
||||
"block_header_version": 2
|
||||
},
|
||||
"num_participants": 2,
|
||||
"ver": "4:2",
|
||||
"id": "0436430c-2b02-624c-2032-570501212b00",
|
||||
"tx": {
|
||||
"offset": "d202964900000000d302964900000000d402964900000000d502964900000000",
|
||||
"body": {
|
||||
"inputs": [
|
||||
{
|
||||
"features": "Coinbase",
|
||||
"commit": "087df32304c5d4ae8b2af0bc31e700019d722910ef87dd4eec3197b80b207e3045"
|
||||
},
|
||||
{
|
||||
"features": "Coinbase",
|
||||
"commit": "08e1da9e6dc4d6e808a718b2f110a991dd775d65ce5ae408a4e1f002a4961aa9e7"
|
||||
}
|
||||
],
|
||||
"outputs": [
|
||||
{
|
||||
"features": "Plain",
|
||||
"commit": "099b48cfb1f80a2347dc89818449e68e76a3c6817a532a8e9ef2b4a5ccf4363850",
|
||||
"proof": "7ebcd2ed9bf5fb29854033ba3d0e720613bdf7dfacc586d2f6084c1cde0a2b72e955d4ce625916701dc7c347132f40d0f102a34e801d745ee54b49b765d08aae0bb801c60403e57cafade3b4b174e795b633ab9e402b5b1b6e1243fd10bbcf9368a75cb6a6c375c7bdf02da9e03b7f210df45d942e6fba2729cd512a372e6ed91a1b5c9c22831febea843e3f85adcf198f39ac9f7b73b70c60bfb474aa69878ea8d1d32fef30166b59caacaec3fd024de29a90f1587e08d2c36b3d5c560cabf658e212e0a40a4129b3e5c35557058def5551f4eb395759597ba808b3c34eac3bfb9716e4480d7931c5789c538463ec75be0eb807c894047fda6cbcd22682d3c6d3823cb330f090a2099e3510a3706b57d46c95224394d7f1c0a20d99cc314b8f1d9d02668e2e435f62e1194de0be6a1f50f72ed777ed51c8819f527a94918d1aa8df6461e98ed4c2b18210de50fbcf8c3df210bfe326d41f1dc0ad748cb0320ae28401c85ab4f7dcb99d88a052e95dc85b76d22b36cabd60e06ab84bb7e4ddfdab9c9730c8a986583237ed1ecbb323ee8e79b8cadca4b438b7c09531670b471dda6a2eb3e747916c88ce7d9d8e1b7f61660eeb9e5a13c60e4dfe89d1177d81d6f6570fda85158e646a15f1e8b9e977494dc19a339aab2e0e478670d80092d6ba37646e60714ef64eb4a3d37fe15f8f38b59114af34b235489eed3f69b7781c5fe496eb43ffe245c14bd740f745844a38cf0d904347aaa2b64f51add18822dac009d8b63fa3e4c9b1fa72187f9a4acba1ab315daa1b04c9a41f3be846ac420b37990e6c947a16cc9d5c0671b292bf77d7d8b8974d2ad3afae95ba7772c37432840f53a007f31e0195f3abdf100c4477723cc6c6d5da14894a73dfac342833731036487488fdade7b9d556c06f26173b6b67598d3769447ce2828d71dd45ac5af436c6b0"
|
||||
},
|
||||
{
|
||||
"features": "Plain",
|
||||
"commit": "0812276cc788e6870612296d926cba9f0e7b9810670710b5a6e6f1ba006d395774",
|
||||
"proof": "dcff6175390c602bfa92c2ffd1a9b2d84dcc9ea941f6f317bdd0f875244ef23e696fd17c71df79760ce5ce1a96aab1d15dd057358dc835e972febeb86d50ccec0dad7cfe0246d742eb753cf7b88c045d15bc7123f8cf7155647ccf663fca92a83c9a65d0ed756ea7ebffd2cac90c380a102ed9caaa355d175ed0bf58d3ac2f5e909d6c447dfc6b605e04925c2b17c33ebd1908c965a5541ea5d2ed45a0958e6402f89d7a56df1992e036d836e74017e73ccad5cb3a82b8e139e309792a31b15f3ffd72ed033253428c156c2b9799458a25c1da65b719780a22de7fe7f437ae2fccd22cf7ea357ab5aa66a5ef7d71fb0dc64aa0b5761f68278062bb39bb296c787e4cabc5e2a2933a416ce1c9a9696160386449c437e9120f7bb26e5b0e74d1f2e7d5bcd7aafb2a92b87d1548f1f911fb06af7bd6cc13cee29f7c9cb79021aed18186272af0e9d189ec107c81a8a3aeb4782b0d950e4881aa51b776bb6844b25bce97035b48a9bdb2aea3608687bcdd479d4fa998b5a839ff88558e4a29dff0ed13b55900abb5d439b70793d902ae9ad34587b18c919f6b875c91d14deeb1c373f5e76570d59a6549758f655f1128a54f162dfe8868e1587028e26ad91e528c5ae7ee9335fa58fb59022b5de29d80f0764a9917390d46db899acc6a5b416e25ecc9dccb7153646addcc81cadb5f0078febc7e05d7735aba494f39ef05697bbcc9b47b2ccc79595d75fc13c80678b5e237edce58d731f34c05b1ddcaa649acf2d865bbbc3ceda10508bcdd29d0496744644bf1c3516f6687dfeef5649c7dff90627d642739a59d91a8d1d0c4dc55d74a949e1074427664b467992c9e0f7d3af9d6ea79513e8946ddc0d356bac49878e64e6a95b0a30214214faf2ce317fa622ff3266b32a816e10a18e6d789a5da1f23e67b4f970a68a7bcd9e18825ee274b0483896a40"
|
||||
}
|
||||
],
|
||||
"kernels": [
|
||||
{
|
||||
"features": "Plain",
|
||||
"fee": "7000000",
|
||||
"lock_height": "0",
|
||||
"excess": "000000000000000000000000000000000000000000000000000000000000000000",
|
||||
"excess_sig": "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"amount": "60000000000",
|
||||
"sta": "I2",
|
||||
"fee": "7000000",
|
||||
"height": "5",
|
||||
"lock_height": "0",
|
||||
"ttl_cutoff_height": null,
|
||||
"payment_proof": null,
|
||||
"participant_data": [
|
||||
"off": "dQ2/T9Q7f0z9aNJpilIvP/bmoArZiVsz8exGSTuDe0k=",
|
||||
"sigs": [
|
||||
{
|
||||
"id": "1",
|
||||
"public_blind_excess": "033bbe2a419ea2e9d6810a8d66552e709d1783ca50759a44dbaf63fc79c0164c4c",
|
||||
"public_nonce": "031b84c5567b126440995d3ed5aaba0565d71e1834604819ff9c17f5e9d5dd078f",
|
||||
"part_sig": null,
|
||||
"message": null,
|
||||
"message_sig": null
|
||||
"xs": "AwFS0tcuLbp8YIatSaIZ2f8N/g/Zk9yuoi4FjCEAM86T",
|
||||
"nonce": "AxuExVZ7EmRAmV0+1aq6BWXXHhg0YEgZ/5wX9enV3QeP",
|
||||
"part": "jwfd1en1F5z/GUhgNBge12UFuqrVPl2ZQGQSe1bFhBva2TTaoX235HfE7tkK/tQNEReJbfjE9YYbYwmpSYeAdA=="
|
||||
}
|
||||
],
|
||||
"coms": [
|
||||
{
|
||||
"f": 1,
|
||||
"c": "CH3zIwTF1K6LKvC8MecAAZ1yKRDvh91O7DGXuAsgfjBF"
|
||||
},
|
||||
{
|
||||
"id": "0",
|
||||
"public_blind_excess": "029f12f9f8c5489a18904de7cd46dc3384b79369d4cbc17cd74b299da8c2cf7445",
|
||||
"public_nonce": "031b84c5567b126440995d3ed5aaba0565d71e1834604819ff9c17f5e9d5dd078f",
|
||||
"part_sig": "8f07ddd5e9f5179cff19486034181ed76505baaad53e5d994064127b56c5841b1840d69ed5f33bc9b424422903d5d1d3e9b914143bcbe3b7ed32d8f15fcccce3",
|
||||
"message": null,
|
||||
"message_sig": null
|
||||
"f": 1,
|
||||
"c": "COHanm3E1ugIpxiy8RCpkd13XWXOWuQIpOHwAqSWGqnn"
|
||||
},
|
||||
{
|
||||
"c": "CBInbMeI5ocGEiltkmy6nw57mBBnBxC1pubxugBtOVd0",
|
||||
"p": "KEufkZlBHGu/cifq4VzJ+n7TBTSvPs/4Ww0BbaMpyuHx7fefAUJkNMuQr8wvCh+yluXFGpG15XofAjD9pPjFlQ55hvo3m5nWS2A5qGzH414EC6GSt4EEOVmFEmjKmHSpGIBeqVjIT3/ujTq0Ji8DL1o/hA683Sc7Kb6BARTm6GqVnY5MCAVy4+8knt1q1oUD7DvESGVIUg6id1pBrqZ6rJmUX86eendp1x+JOtfw0BCGkva2hSMSyub5hXBjBVvaWdzlIZJ8cAQLgCakG2UXyuChyUfKJEmEpcCt98ZIOwk5NGxI9hysN9UB9GocWHi2fO4NByP07q3J9dce1enzO0KUtY0+vu/qoT8gNXWZvlSc4Y5uLrHVDhI1zMQOyRhMaKYjdBpyOM5pqjodJRVrO36zj91vvlRzl5/u4zF98nnGDUiiiYJqpMdtvOJNUmiQ1Obi+D6A9nShJI/B3AN9mCAJASrhEz9eFYrmza2xjI1T5KiuVZXHWHgsZ6oMIPFG1SCFz0WjV5TOxFcCgw+JUqaXRHGPvm/g09pm40jdNHOgrO1wgPv1SUw+fhQZFvOxNbMyd/mY/Nms+8qHCYFIZumDil3NpMKUIs8VcpPm/CzMLSVCNReEO9jiHGHO1yMSwLSIFMMSAhsNMVmNI4mwsym6oRaZIqTDQXPdX1QFRb5QZqDykfGocOGq/5TBnwqFUlSIKheYS67aCOjq1T0VY+nuS8NnQnic7086sVgFTX3f4qKze1qKML6E/335p9dYqbdnUaNiIFdyGj7FuHNYLQyRzR/rvBU2YnOYVOoVyZAwI8GTxrV2i1VIQQj4l83EfROpCIsyVY5X8S9YB2aGScmaF7UZBRJzQOi0nEJ3Xxs8qwct+tM9M8weS6WWTXKO0KkF"
|
||||
}
|
||||
]
|
||||
}]
|
||||
|
@ -451,73 +237,41 @@ pub trait ForeignRpc {
|
|||
"jsonrpc": "2.0",
|
||||
"result": {
|
||||
"Ok": {
|
||||
"amount": "60000000000",
|
||||
"fee": "7000000",
|
||||
"height": "5",
|
||||
"id": "0436430c-2b02-624c-2032-570501212b00",
|
||||
"lock_height": "0",
|
||||
"ttl_cutoff_height": null,
|
||||
"payment_proof": null,
|
||||
"num_participants": 2,
|
||||
"participant_data": [
|
||||
"coms": [
|
||||
{
|
||||
"id": "1",
|
||||
"message": null,
|
||||
"message_sig": null,
|
||||
"part_sig": "8f07ddd5e9f5179cff19486034181ed76505baaad53e5d994064127b56c5841bc9ea21b259d61e4de177d9ef8ab475dfab0ec7299009a7fea61010f963f2e6c0",
|
||||
"public_blind_excess": "033bbe2a419ea2e9d6810a8d66552e709d1783ca50759a44dbaf63fc79c0164c4c",
|
||||
"public_nonce": "031b84c5567b126440995d3ed5aaba0565d71e1834604819ff9c17f5e9d5dd078f"
|
||||
"c": "CH3zIwTF1K6LKvC8MecAAZ1yKRDvh91O7DGXuAsgfjBF",
|
||||
"f": 1
|
||||
},
|
||||
{
|
||||
"id": "0",
|
||||
"message": null,
|
||||
"message_sig": null,
|
||||
"part_sig": "8f07ddd5e9f5179cff19486034181ed76505baaad53e5d994064127b56c5841b1840d69ed5f33bc9b424422903d5d1d3e9b914143bcbe3b7ed32d8f15fcccce3",
|
||||
"public_blind_excess": "029f12f9f8c5489a18904de7cd46dc3384b79369d4cbc17cd74b299da8c2cf7445",
|
||||
"public_nonce": "031b84c5567b126440995d3ed5aaba0565d71e1834604819ff9c17f5e9d5dd078f"
|
||||
"c": "COHanm3E1ugIpxiy8RCpkd13XWXOWuQIpOHwAqSWGqnn",
|
||||
"f": 1
|
||||
},
|
||||
{
|
||||
"c": "CZtIz7H4CiNH3ImBhEnmjnajxoF6UyqOnvK0pcz0NjhQ",
|
||||
"p": "KXAc6uJiysd7ebhoyIOikuYebegZK4aO3NEwCwlz2ROWsVas5r1nNAKjA94Q3dil5rfxe6ZVeldKZyvQTMJzqwTtjiyoC6xIM0XA7IQ/UhgUzhMB7JrcOJVqErTZSKzOcSlaT1K83rihyfLWstpdcxJipenAJ275BN+e+NSAAUIM1Z91ovGuXHocfGufFA52E+Uu+eJJ8p+TQLfvuAaZ5GAWQyRhb5j9TN49tSSXyRnpUiL//qy35l3sp+NoqAznE8Gd59pTaXJiKO4zb1vUlFOMEsy//rG5v9X8iQbRxkJFtRbxA/qW2cVpdYN2UsHg+lgD18zxFH2Pkn422nF/eteUcdvhkvX1D4enn8P+Aw26VptjS5LSzzB5k8zlRWM68mOJfNfm6/Tcr7F20HNYvcONA+RaSd+pyMZRfNaNFn/79sO03g4t0hkJy61MRnuE5XAL5HOjmsWcZp18FVxLyrm4Am7qNDHHec0nfkki0rl0Lh9meMvoaew7W370Ey3bbN0Gzyfb6yi+crlJ+ol2EOSOOg14n9Lup1q8l7PcfgDlyLPSTkDG8kESrbcjUriaK+8FmTRTOOnnYgKjxG76Y3CVKyrKQarbrg6jJTGsr82rbdBm12nr9Qz088ClnS1fp5YAoge5QXxiP3atBejMz81AOPlEi8QPEnynwNNy5GB04zT+SfWpVuwAVvTaYB5q+A6xpsSVEFSGnmZbKW2MFPNEyi3F/dXfSjZSU2NloWFa2bQiFlx3v4/mWoNcjgxB4HABTrZu+MUlIE6ZCzo9ZjweQiIbSWiVw3ovDBvwXpEjVAnD/j2JqaedbHhgmrGKRjMRkR9x+je7c7FfzTgUPRQE/SzoEATcf/ic8RFdzAw1zhwb+ZQVhvuVl3DyYYzLcRin"
|
||||
},
|
||||
{
|
||||
"c": "CBInbMeI5ocGEiltkmy6nw57mBBnBxC1pubxugBtOVd0",
|
||||
"p": "KEufkZlBHGu/cifq4VzJ+n7TBTSvPs/4Ww0BbaMpyuHx7fefAUJkNMuQr8wvCh+yluXFGpG15XofAjD9pPjFlQ55hvo3m5nWS2A5qGzH414EC6GSt4EEOVmFEmjKmHSpGIBeqVjIT3/ujTq0Ji8DL1o/hA683Sc7Kb6BARTm6GqVnY5MCAVy4+8knt1q1oUD7DvESGVIUg6id1pBrqZ6rJmUX86eendp1x+JOtfw0BCGkva2hSMSyub5hXBjBVvaWdzlIZJ8cAQLgCakG2UXyuChyUfKJEmEpcCt98ZIOwk5NGxI9hysN9UB9GocWHi2fO4NByP07q3J9dce1enzO0KUtY0+vu/qoT8gNXWZvlSc4Y5uLrHVDhI1zMQOyRhMaKYjdBpyOM5pqjodJRVrO36zj91vvlRzl5/u4zF98nnGDUiiiYJqpMdtvOJNUmiQ1Obi+D6A9nShJI/B3AN9mCAJASrhEz9eFYrmza2xjI1T5KiuVZXHWHgsZ6oMIPFG1SCFz0WjV5TOxFcCgw+JUqaXRHGPvm/g09pm40jdNHOgrO1wgPv1SUw+fhQZFvOxNbMyd/mY/Nms+8qHCYFIZumDil3NpMKUIs8VcpPm/CzMLSVCNReEO9jiHGHO1yMSwLSIFMMSAhsNMVmNI4mwsym6oRaZIqTDQXPdX1QFRb5QZqDykfGocOGq/5TBnwqFUlSIKheYS67aCOjq1T0VY+nuS8NnQnic7086sVgFTX3f4qKze1qKML6E/335p9dYqbdnUaNiIFdyGj7FuHNYLQyRzR/rvBU2YnOYVOoVyZAwI8GTxrV2i1VIQQj4l83EfROpCIsyVY5X8S9YB2aGScmaF7UZBRJzQOi0nEJ3Xxs8qwct+tM9M8weS6WWTXKO0KkF"
|
||||
}
|
||||
],
|
||||
"tx": {
|
||||
"body": {
|
||||
"inputs": [
|
||||
{
|
||||
"commit": "087df32304c5d4ae8b2af0bc31e700019d722910ef87dd4eec3197b80b207e3045",
|
||||
"features": "Coinbase"
|
||||
},
|
||||
{
|
||||
"commit": "08e1da9e6dc4d6e808a718b2f110a991dd775d65ce5ae408a4e1f002a4961aa9e7",
|
||||
"features": "Coinbase"
|
||||
}
|
||||
],
|
||||
"kernels": [
|
||||
{
|
||||
"excess": "09bac6083b05a32a9d9b37710c70dd0a1ef9329fde0848558976b6f1b81d80ceed",
|
||||
"excess_sig": "66074d25a751c4743342c90ad8ead9454daa00d9b9aed29bca321036d16c4b4da0e9c180a26b88565afcd269a7ac98f896c8db3dcbd48ab69443e8eac3beb3a4",
|
||||
"features": "Plain",
|
||||
"fee": "7000000",
|
||||
"lock_height": "0"
|
||||
}
|
||||
],
|
||||
"outputs": [
|
||||
{
|
||||
"commit": "099b48cfb1f80a2347dc89818449e68e76a3c6817a532a8e9ef2b4a5ccf4363850",
|
||||
"features": "Plain",
|
||||
"proof": "7ebcd2ed9bf5fb29854033ba3d0e720613bdf7dfacc586d2f6084c1cde0a2b72e955d4ce625916701dc7c347132f40d0f102a34e801d745ee54b49b765d08aae0bb801c60403e57cafade3b4b174e795b633ab9e402b5b1b6e1243fd10bbcf9368a75cb6a6c375c7bdf02da9e03b7f210df45d942e6fba2729cd512a372e6ed91a1b5c9c22831febea843e3f85adcf198f39ac9f7b73b70c60bfb474aa69878ea8d1d32fef30166b59caacaec3fd024de29a90f1587e08d2c36b3d5c560cabf658e212e0a40a4129b3e5c35557058def5551f4eb395759597ba808b3c34eac3bfb9716e4480d7931c5789c538463ec75be0eb807c894047fda6cbcd22682d3c6d3823cb330f090a2099e3510a3706b57d46c95224394d7f1c0a20d99cc314b8f1d9d02668e2e435f62e1194de0be6a1f50f72ed777ed51c8819f527a94918d1aa8df6461e98ed4c2b18210de50fbcf8c3df210bfe326d41f1dc0ad748cb0320ae28401c85ab4f7dcb99d88a052e95dc85b76d22b36cabd60e06ab84bb7e4ddfdab9c9730c8a986583237ed1ecbb323ee8e79b8cadca4b438b7c09531670b471dda6a2eb3e747916c88ce7d9d8e1b7f61660eeb9e5a13c60e4dfe89d1177d81d6f6570fda85158e646a15f1e8b9e977494dc19a339aab2e0e478670d80092d6ba37646e60714ef64eb4a3d37fe15f8f38b59114af34b235489eed3f69b7781c5fe496eb43ffe245c14bd740f745844a38cf0d904347aaa2b64f51add18822dac009d8b63fa3e4c9b1fa72187f9a4acba1ab315daa1b04c9a41f3be846ac420b37990e6c947a16cc9d5c0671b292bf77d7d8b8974d2ad3afae95ba7772c37432840f53a007f31e0195f3abdf100c4477723cc6c6d5da14894a73dfac342833731036487488fdade7b9d556c06f26173b6b67598d3769447ce2828d71dd45ac5af436c6b0"
|
||||
},
|
||||
{
|
||||
"commit": "0812276cc788e6870612296d926cba9f0e7b9810670710b5a6e6f1ba006d395774",
|
||||
"features": "Plain",
|
||||
"proof": "dcff6175390c602bfa92c2ffd1a9b2d84dcc9ea941f6f317bdd0f875244ef23e696fd17c71df79760ce5ce1a96aab1d15dd057358dc835e972febeb86d50ccec0dad7cfe0246d742eb753cf7b88c045d15bc7123f8cf7155647ccf663fca92a83c9a65d0ed756ea7ebffd2cac90c380a102ed9caaa355d175ed0bf58d3ac2f5e909d6c447dfc6b605e04925c2b17c33ebd1908c965a5541ea5d2ed45a0958e6402f89d7a56df1992e036d836e74017e73ccad5cb3a82b8e139e309792a31b15f3ffd72ed033253428c156c2b9799458a25c1da65b719780a22de7fe7f437ae2fccd22cf7ea357ab5aa66a5ef7d71fb0dc64aa0b5761f68278062bb39bb296c787e4cabc5e2a2933a416ce1c9a9696160386449c437e9120f7bb26e5b0e74d1f2e7d5bcd7aafb2a92b87d1548f1f911fb06af7bd6cc13cee29f7c9cb79021aed18186272af0e9d189ec107c81a8a3aeb4782b0d950e4881aa51b776bb6844b25bce97035b48a9bdb2aea3608687bcdd479d4fa998b5a839ff88558e4a29dff0ed13b55900abb5d439b70793d902ae9ad34587b18c919f6b875c91d14deeb1c373f5e76570d59a6549758f655f1128a54f162dfe8868e1587028e26ad91e528c5ae7ee9335fa58fb59022b5de29d80f0764a9917390d46db899acc6a5b416e25ecc9dccb7153646addcc81cadb5f0078febc7e05d7735aba494f39ef05697bbcc9b47b2ccc79595d75fc13c80678b5e237edce58d731f34c05b1ddcaa649acf2d865bbbc3ceda10508bcdd29d0496744644bf1c3516f6687dfeef5649c7dff90627d642739a59d91a8d1d0c4dc55d74a949e1074427664b467992c9e0f7d3af9d6ea79513e8946ddc0d356bac49878e64e6a95b0a30214214faf2ce317fa622ff3266b32a816e10a18e6d789a5da1f23e67b4f970a68a7bcd9e18825ee274b0483896a40"
|
||||
}
|
||||
]
|
||||
"fee": "7000000",
|
||||
"id": "0436430c-2b02-624c-2032-570501212b00",
|
||||
"off": "dQ2/T9Q7f0z9aNJpilIvP/bmoArZiVsz8exGSTuDe0k=",
|
||||
"sigs": [
|
||||
{
|
||||
"nonce": "AxuExVZ7EmRAmV0+1aq6BWXXHhg0YEgZ/5wX9enV3QeP",
|
||||
"part": "jwfd1en1F5z/GUhgNBge12UFuqrVPl2ZQGQSe1bFhBva2TTaoX235HfE7tkK/tQNEReJbfjE9YYbYwmpSYeAdA==",
|
||||
"xs": "AwFS0tcuLbp8YIatSaIZ2f8N/g/Zk9yuoi4FjCEAM86T"
|
||||
},
|
||||
"offset": "d202964900000000d302964900000000d402964900000000d502964900000000"
|
||||
},
|
||||
"version_info": {
|
||||
"orig_version": 2,
|
||||
"version": 2,
|
||||
"block_header_version": 2
|
||||
}
|
||||
{
|
||||
"nonce": "AxuExVZ7EmRAmV0+1aq6BWXXHhg0YEgZ/5wX9enV3QeP",
|
||||
"part": "jwfd1en1F5z/GUhgNBge12UFuqrVPl2ZQGQSe1bFhBs1/f5VJx8q5z119Yxw0e+2mzOEx7xQfVfpnlbed+IIdA==",
|
||||
"xs": "Azu+KkGeounWgQqNZlUucJ0Xg8pQdZpE269j/HnAFkxM"
|
||||
}
|
||||
],
|
||||
"sta": "I3",
|
||||
"ver": "4:2"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -543,15 +297,12 @@ where
|
|||
Ok(VersionedCoinbase::into_version(cb, SlateVersion::V3))
|
||||
}
|
||||
|
||||
fn verify_slate_messages(&self, slate: VersionedSlate) -> Result<(), ErrorKind> {
|
||||
Foreign::verify_slate_messages(self, &Slate::from(slate)).map_err(|e| e.kind())
|
||||
}
|
||||
|
||||
fn receive_tx(
|
||||
&self,
|
||||
in_slate: VersionedSlate,
|
||||
dest_acct_name: Option<String>,
|
||||
message: Option<String>,
|
||||
//TODO: Remove post HF3
|
||||
_message: Option<String>,
|
||||
) -> Result<VersionedSlate, ErrorKind> {
|
||||
let version = in_slate.version();
|
||||
let slate_from = Slate::from(in_slate);
|
||||
|
@ -559,7 +310,6 @@ where
|
|||
self,
|
||||
&slate_from,
|
||||
dest_acct_name.as_ref().map(String::as_str),
|
||||
message,
|
||||
)
|
||||
.map_err(|e| e.kind())?;
|
||||
Ok(VersionedSlate::into_version(out_slate, version).map_err(|e| e.kind())?)
|
||||
|
|
111
api/src/owner.rs
111
api/src/owner.rs
|
@ -625,7 +625,6 @@ where
|
|||
/// max_outputs: 500,
|
||||
/// num_change_outputs: 1,
|
||||
/// selection_strategy_is_use_all: false,
|
||||
/// message: Some("Have some Grins. Love, Yeastplume".to_owned()),
|
||||
/// ..Default::default()
|
||||
/// };
|
||||
/// let result = api_owner.init_send_tx(
|
||||
|
@ -637,7 +636,7 @@ where
|
|||
/// // Send slate somehow
|
||||
/// // ...
|
||||
/// // Lock our outputs if we're happy the slate was (or is being) sent
|
||||
/// api_owner.tx_lock_outputs(None, &slate, 0);
|
||||
/// api_owner.tx_lock_outputs(None, &slate);
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
|
@ -670,14 +669,14 @@ where
|
|||
let comm_adapter = create_sender(&sa.method, &sa.dest, tor_config_lock.clone())
|
||||
.map_err(|e| ErrorKind::GenericError(format!("{}", e)))?;
|
||||
slate = comm_adapter.send_tx(&slate)?;
|
||||
self.tx_lock_outputs(keychain_mask, &slate, 0)?;
|
||||
self.tx_lock_outputs(keychain_mask, &slate)?;
|
||||
let slate = match sa.finalize {
|
||||
true => self.finalize_tx(keychain_mask, &slate)?,
|
||||
false => slate,
|
||||
};
|
||||
|
||||
if sa.post_tx {
|
||||
self.post_tx(keychain_mask, slate.tx_or_err()?, sa.fluff)?;
|
||||
self.post_tx(keychain_mask, &slate, sa.fluff)?;
|
||||
}
|
||||
Ok(slate)
|
||||
}
|
||||
|
@ -766,7 +765,7 @@ where
|
|||
///
|
||||
/// // . . .
|
||||
/// // The slate has been recieved from the invoicer, somehow
|
||||
/// # let slate = Slate::blank(2);
|
||||
/// # let slate = Slate::blank(2, true);
|
||||
/// let args = InitTxArgs {
|
||||
/// src_acct_name: None,
|
||||
/// amount: slate.amount,
|
||||
|
@ -835,7 +834,6 @@ where
|
|||
/// max_outputs: 500,
|
||||
/// num_change_outputs: 1,
|
||||
/// selection_strategy_is_use_all: false,
|
||||
/// message: Some("Remember to lock this when we're happy this is sent".to_owned()),
|
||||
/// ..Default::default()
|
||||
/// };
|
||||
/// let result = api_owner.init_send_tx(
|
||||
|
@ -847,7 +845,7 @@ where
|
|||
/// // Send slate somehow
|
||||
/// // ...
|
||||
/// // Lock our outputs if we're happy the slate was (or is being) sent
|
||||
/// api_owner.tx_lock_outputs(None, &slate, 0);
|
||||
/// api_owner.tx_lock_outputs(None, &slate);
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
|
@ -855,11 +853,10 @@ where
|
|||
&self,
|
||||
keychain_mask: Option<&SecretKey>,
|
||||
slate: &Slate,
|
||||
participant_id: usize,
|
||||
) -> Result<(), Error> {
|
||||
let mut w_lock = self.wallet_inst.lock();
|
||||
let w = w_lock.lc_provider()?.wallet_inst()?;
|
||||
owner::tx_lock_outputs(&mut **w, keychain_mask, slate, participant_id)
|
||||
owner::tx_lock_outputs(&mut **w, keychain_mask, slate)
|
||||
}
|
||||
|
||||
/// Finalizes a transaction, after all parties
|
||||
|
@ -899,7 +896,6 @@ where
|
|||
/// max_outputs: 500,
|
||||
/// num_change_outputs: 1,
|
||||
/// selection_strategy_is_use_all: false,
|
||||
/// message: Some("Finalize this tx now".to_owned()),
|
||||
/// ..Default::default()
|
||||
/// };
|
||||
/// let result = api_owner.init_send_tx(
|
||||
|
@ -911,7 +907,7 @@ where
|
|||
/// // Send slate somehow
|
||||
/// // ...
|
||||
/// // Lock our outputs if we're happy the slate was (or is being) sent
|
||||
/// let res = api_owner.tx_lock_outputs(None, &slate, 0);
|
||||
/// let res = api_owner.tx_lock_outputs(None, &slate);
|
||||
/// //
|
||||
/// // Retrieve slate back from recipient
|
||||
/// //
|
||||
|
@ -959,7 +955,6 @@ where
|
|||
/// max_outputs: 500,
|
||||
/// num_change_outputs: 1,
|
||||
/// selection_strategy_is_use_all: false,
|
||||
/// message: Some("Post this tx".to_owned()),
|
||||
/// ..Default::default()
|
||||
/// };
|
||||
/// let result = api_owner.init_send_tx(
|
||||
|
@ -971,19 +966,19 @@ where
|
|||
/// // Send slate somehow
|
||||
/// // ...
|
||||
/// // Lock our outputs if we're happy the slate was (or is being) sent
|
||||
/// let res = api_owner.tx_lock_outputs(None, &slate, 0);
|
||||
/// let res = api_owner.tx_lock_outputs(None, &slate);
|
||||
/// //
|
||||
/// // Retrieve slate back from recipient
|
||||
/// //
|
||||
/// let res = api_owner.finalize_tx(None, &slate);
|
||||
/// let res = api_owner.post_tx(None, slate.tx_or_err().unwrap(), true);
|
||||
/// let res = api_owner.post_tx(None, &slate, true);
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
pub fn post_tx(
|
||||
&self,
|
||||
keychain_mask: Option<&SecretKey>,
|
||||
tx: &Transaction,
|
||||
slate: &Slate,
|
||||
fluff: bool,
|
||||
) -> Result<(), Error> {
|
||||
let client = {
|
||||
|
@ -993,7 +988,7 @@ where
|
|||
let _ = w.keychain(keychain_mask)?;
|
||||
w.w2n_client().clone()
|
||||
};
|
||||
owner::post_tx(&client, tx, fluff)
|
||||
owner::post_tx(&client, slate.tx_or_err()?, fluff)
|
||||
}
|
||||
|
||||
/// Cancels a transaction. This entails:
|
||||
|
@ -1031,7 +1026,6 @@ where
|
|||
/// max_outputs: 500,
|
||||
/// num_change_outputs: 1,
|
||||
/// selection_strategy_is_use_all: false,
|
||||
/// message: Some("Cancel this tx".to_owned()),
|
||||
/// ..Default::default()
|
||||
/// };
|
||||
/// let result = api_owner.init_send_tx(
|
||||
|
@ -1043,7 +1037,7 @@ where
|
|||
/// // Send slate somehow
|
||||
/// // ...
|
||||
/// // Lock our outputs if we're happy the slate was (or is being) sent
|
||||
/// let res = api_owner.tx_lock_outputs(None, &slate, 0);
|
||||
/// let res = api_owner.tx_lock_outputs(None, &slate);
|
||||
/// //
|
||||
/// // We didn't get the slate back, or something else went wrong
|
||||
/// //
|
||||
|
@ -1098,86 +1092,21 @@ where
|
|||
/// let result = api_owner.retrieve_txs(None, update_from_node, tx_id, tx_slate_id);
|
||||
///
|
||||
/// if let Ok((was_updated, tx_log_entries)) = result {
|
||||
/// let stored_tx = api_owner.get_stored_tx(None, &tx_log_entries[0]).unwrap();
|
||||
/// let stored_tx = api_owner.get_stored_tx(None, tx_log_entries[0].tx_slate_id.unwrap()).unwrap();
|
||||
/// //...
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
// TODO: Should be accepting an id, not an entire entry struct
|
||||
pub fn get_stored_tx(
|
||||
&self,
|
||||
keychain_mask: Option<&SecretKey>,
|
||||
tx_log_entry: &TxLogEntry,
|
||||
tx_id: Uuid,
|
||||
) -> Result<Option<Transaction>, Error> {
|
||||
let mut w_lock = self.wallet_inst.lock();
|
||||
let w = w_lock.lc_provider()?.wallet_inst()?;
|
||||
// Test keychain mask, to keep API consistent
|
||||
let _ = w.keychain(keychain_mask)?;
|
||||
owner::get_stored_tx(&**w, tx_log_entry)
|
||||
}
|
||||
|
||||
/// Verifies all messages in the slate match their public keys.
|
||||
///
|
||||
/// The optional messages themselves are part of the `participant_data` field within the slate.
|
||||
/// Messages are signed with the same key used to sign for the paricipant's inputs, and can thus be
|
||||
/// verified with the public key found in the `public_blind_excess` field. This function is a
|
||||
/// simple helper to returns whether all signatures in the participant data match their public
|
||||
/// keys.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `keychain_mask` - Wallet secret mask to XOR against the stored wallet seed before using, if
|
||||
/// being used.
|
||||
/// * `slate` - The transaction [`Slate`](../grin_wallet_libwallet/slate/struct.Slate.html).
|
||||
///
|
||||
/// # Returns
|
||||
/// * `Ok(())` if successful and the signatures validate
|
||||
/// * or [`libwallet::Error`](../grin_wallet_libwallet/struct.Error.html) if an error is encountered.
|
||||
///
|
||||
/// # Example
|
||||
/// Set up as in [`new`](struct.Owner.html#method.new) method above.
|
||||
/// ```
|
||||
/// # grin_wallet_api::doctest_helper_setup_doc_env!(wallet, wallet_config);
|
||||
///
|
||||
/// let mut api_owner = Owner::new(wallet.clone(), None);
|
||||
/// let args = InitTxArgs {
|
||||
/// src_acct_name: None,
|
||||
/// amount: 2_000_000_000,
|
||||
/// minimum_confirmations: 10,
|
||||
/// max_outputs: 500,
|
||||
/// num_change_outputs: 1,
|
||||
/// selection_strategy_is_use_all: false,
|
||||
/// message: Some("Just verify messages".to_owned()),
|
||||
/// ..Default::default()
|
||||
/// };
|
||||
/// let result = api_owner.init_send_tx(
|
||||
/// None,
|
||||
/// args,
|
||||
/// );
|
||||
///
|
||||
/// if let Ok(slate) = result {
|
||||
/// // Send slate somehow
|
||||
/// // ...
|
||||
/// // Lock our outputs if we're happy the slate was (or is being) sent
|
||||
/// let res = api_owner.tx_lock_outputs(None, &slate, 0);
|
||||
/// //
|
||||
/// // Retrieve slate back from recipient
|
||||
/// //
|
||||
/// let res = api_owner.verify_slate_messages(None, &slate);
|
||||
/// }
|
||||
/// ```
|
||||
pub fn verify_slate_messages(
|
||||
&self,
|
||||
keychain_mask: Option<&SecretKey>,
|
||||
slate: &Slate,
|
||||
) -> Result<(), Error> {
|
||||
{
|
||||
let mut w_lock = self.wallet_inst.lock();
|
||||
let w = w_lock.lc_provider()?.wallet_inst()?;
|
||||
// Test keychain mask, to keep API consistent
|
||||
let _ = w.keychain(keychain_mask)?;
|
||||
}
|
||||
owner::verify_slate_messages(slate)
|
||||
owner::get_stored_tx(&**w, &tx_id)
|
||||
}
|
||||
|
||||
/// Scans the entire UTXO set from the node, identify which outputs belong to the given wallet
|
||||
|
@ -2149,6 +2078,16 @@ where
|
|||
) -> Result<(bool, bool), Error> {
|
||||
owner::verify_payment_proof(self.wallet_inst.clone(), keychain_mask, proof)
|
||||
}
|
||||
|
||||
/// Return my participant data
|
||||
// TODO: This will be removed once state is added to slate
|
||||
pub fn context_is_invoice(
|
||||
&self,
|
||||
keychain_mask: Option<&SecretKey>,
|
||||
slate: &Slate,
|
||||
) -> Result<bool, Error> {
|
||||
owner::context_is_invoice(self.wallet_inst.clone(), keychain_mask, slate)
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
|
|
|
@ -19,7 +19,6 @@ use crate::config::{TorConfig, WalletConfig};
|
|||
use crate::core::core::Transaction;
|
||||
use crate::core::global;
|
||||
use crate::keychain::{Identifier, Keychain};
|
||||
use crate::libwallet::slate_versions::v4::TransactionV4;
|
||||
use crate::libwallet::{
|
||||
AcctPathMapping, ErrorKind, InitTxArgs, IssueInvoiceTxArgs, NodeClient, NodeHeightResult,
|
||||
OutputCommitMapping, PaymentProof, Slate, SlateVersion, StatusMessage, TxLogEntry,
|
||||
|
@ -262,7 +261,6 @@ pub trait OwnerRpc {
|
|||
"id": 0,
|
||||
"kernel_excess": "0838e19c490038b10f051c9c190a9b1f96d59bbd242f5d3143f50630deb74342ed",
|
||||
"kernel_lookup_min_height": 1,
|
||||
"messages": null,
|
||||
"num_inputs": 0,
|
||||
"num_outputs": 1,
|
||||
"parent_key_id": "0200000000000000000000000000000000",
|
||||
|
@ -283,7 +281,6 @@ pub trait OwnerRpc {
|
|||
"id": 1,
|
||||
"kernel_excess": "08cd9d890c0b6a004f700aa5939a1ce0488fe2a11fa33cf096b50732ceab0be1df",
|
||||
"kernel_lookup_min_height": 2,
|
||||
"messages": null,
|
||||
"num_inputs": 0,
|
||||
"num_outputs": 1,
|
||||
"parent_key_id": "0200000000000000000000000000000000",
|
||||
|
@ -380,7 +377,6 @@ pub trait OwnerRpc {
|
|||
"max_outputs": 500,
|
||||
"num_change_outputs": 1,
|
||||
"selection_strategy_is_use_all": true,
|
||||
"message": "my message",
|
||||
"target_slate_version": null,
|
||||
"payment_proof_recipient_address": "pa7wkkdgs5bkteha7lykl7ff2wztgdrxxo442xdcq2lnaphe5aidd4id",
|
||||
"ttl_blocks": null,
|
||||
|
@ -393,67 +389,29 @@ pub trait OwnerRpc {
|
|||
# ,
|
||||
# r#"
|
||||
{
|
||||
"id": 1,
|
||||
"jsonrpc": "2.0",
|
||||
"result": {
|
||||
"Ok": {
|
||||
"amount": "6000000000",
|
||||
"fee": "8000000",
|
||||
"height": "4",
|
||||
"id": "0436430c-2b02-624c-2032-570501212b00",
|
||||
"lock_height": "0",
|
||||
"ttl_cutoff_height": null,
|
||||
"num_participants": 2,
|
||||
"payment_proof": {
|
||||
"receiver_address": "783f6528669742a990e0faf0a5fca5d5b3330e37bbb9cd5c628696d03ce4e810",
|
||||
"receiver_signature": null,
|
||||
"sender_address": "32cdd63928854f8b2628b1dce4626ddcdf35d56cb7cfdf7d64cca5822b78d4d3"
|
||||
},
|
||||
"participant_data": [
|
||||
{
|
||||
"id": "0",
|
||||
"message": "my message",
|
||||
"message_sig": "8f07ddd5e9f5179cff19486034181ed76505baaad53e5d994064127b56c5841b1d4c1358be398f801eb90d933774b5218fa7e769b11c4c640402253353656f75",
|
||||
"part_sig": null,
|
||||
"public_blind_excess": "034b4df2f0558b73ea72a1ca5c4ab20217c66bbe0829056fca7abe76888e9349ee",
|
||||
"public_nonce": "031b84c5567b126440995d3ed5aaba0565d71e1834604819ff9c17f5e9d5dd078f"
|
||||
"id": 1,
|
||||
"jsonrpc": "2.0",
|
||||
"result": {
|
||||
"Ok": {
|
||||
"amt": "6000000000",
|
||||
"fee": "8000000",
|
||||
"id": "0436430c-2b02-624c-2032-570501212b00",
|
||||
"off": "0gKWSQAAAADTApZJAAAAANQClkkAAAAA1QKWSQAAAAA=",
|
||||
"proof": {
|
||||
"raddr": "eD9lKGaXQqmQ4Prwpfyl1bMzDje7uc1cYoaW0Dzk6BA=",
|
||||
"saddr": "Ms3WOSiFT4smKLHc5GJt3N811Wy3z999ZMylgit41NM="
|
||||
},
|
||||
"sigs": [
|
||||
{
|
||||
"nonce": "AxuExVZ7EmRAmV0+1aq6BWXXHhg0YEgZ/5wX9enV3QeP",
|
||||
"xs": "Ajh4zoRXJ/Ok7HbKPz20s4otBdY2uMNjIQi4V/7WPJbe"
|
||||
}
|
||||
],
|
||||
"sta": "S1",
|
||||
"ver": "4:2"
|
||||
}
|
||||
}
|
||||
],
|
||||
"tx": {
|
||||
"body": {
|
||||
"inputs": [
|
||||
{
|
||||
"commit": "08e1da9e6dc4d6e808a718b2f110a991dd775d65ce5ae408a4e1f002a4961aa9e7",
|
||||
"features": "Coinbase"
|
||||
}
|
||||
],
|
||||
"kernels": [
|
||||
{
|
||||
"excess": "000000000000000000000000000000000000000000000000000000000000000000",
|
||||
"excess_sig": "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
|
||||
"features": "Plain",
|
||||
"fee": "8000000",
|
||||
"lock_height": "0"
|
||||
}
|
||||
],
|
||||
"outputs": [
|
||||
{
|
||||
"commit": "094be57c91787fc2033d5d97fae099f1a6ddb37ea48370f1a138f09524c767fdd3",
|
||||
"features": "Plain",
|
||||
"proof": "1319630b26d02363861ebdb15514086dc8b0772b4bb63ef9b828704e0ac348efada6747dd7a29848138d630c7b403e573c9cde04be5d25f2d344db4b010d6b890dd6c54cc0911c0cadc7a8a225b2ec3f2dcac88189a17aa62257e969eef9de9172009d8e864e413f1953998b28531e580d3ea495a512d320e8d4ff50e7495a6c283c6e544d16364d34272805893526f1e3b6fd176ef4adc5671b165cf28efcfb8d25c0dfcd018a2c5e65beeb9201f3983194e5a521c0844d05c700654dfaed1b9b39dae08cc9afab9cb891256bc0237ad2ce78da8b44586580f52dd346dcafde5e471917f16e4c4b51e966e1946f13e31771503c85bb0f1b41d1c7fcc953e70af55400638a35e7f5610f9f4c5b881a35060a693deaf46e1839c54a8f7d2c626b05acd3450b72ae8f2e0f8721bcbbd8562141d3fef851c6ac3c8069fa6389389bc4fcba5e4fb49709a3b63a59ba96a82827dfbd6f16de849ef95f3114593d207aff6e030152929fa220b0c3b54ca419cfcbffb7a0106dd3154e858878c7d8f38cadcc376c502bdc50292b494484936d0846fc3fac10910962bca4ddcca5c80b458fd7df15e9a6c2f39b516425a2190a97c9d0e2e2f105ee29905f36e3a648a135ebf387d0bb2a6b61d95b215319d6dc9ee8b4b2798810fb6e01c007041b288c2b39e805c9af86c88dd4a380b6a346b4a0e67bba6aaac5acc70088ac3297486b90cfe371d9464552747a2f7680f42d5629fb09bed835382d842234712c0574c5b4f256c226e77602429983e4def71541cff80ccf4cd3b761685c91463c8e1c7bf828699c688509282b85e752424df3da670b3cfacdea2f66cfb804fecdf8b7eb056e8917fdae78d83c011964e3d5a0748873f817d0abf4b04c20452733eac35c318b96e100a5ace0f54085bd24f968b8fc5b276e0d7b134f01db50b3d2771cdcf1423d44"
|
||||
}
|
||||
]
|
||||
},
|
||||
"offset": "d202964900000000d302964900000000d402964900000000d502964900000000"
|
||||
},
|
||||
"version_info": {
|
||||
"orig_version": 4,
|
||||
"version": 4,
|
||||
"block_header_version": 2
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
# "#
|
||||
# , 4, false, false, false, false);
|
||||
```
|
||||
|
@ -474,7 +432,6 @@ pub trait OwnerRpc {
|
|||
"token": "d202964900000000d302964900000000d402964900000000d502964900000000",
|
||||
"args": {
|
||||
"amount": "6000000000",
|
||||
"message": "Please give me your grins",
|
||||
"dest_acct_name": null,
|
||||
"target_slate_version": null
|
||||
}
|
||||
|
@ -489,51 +446,17 @@ pub trait OwnerRpc {
|
|||
"jsonrpc": "2.0",
|
||||
"result": {
|
||||
"Ok": {
|
||||
"amount": "6000000000",
|
||||
"fee": "0",
|
||||
"height": "4",
|
||||
"amt": "6000000000",
|
||||
"id": "0436430c-2b02-624c-2032-570501212b00",
|
||||
"lock_height": "0",
|
||||
"ttl_cutoff_height": null,
|
||||
"num_participants": 2,
|
||||
"payment_proof": null,
|
||||
"participant_data": [
|
||||
"off": "0gKWSQAAAADTApZJAAAAANQClkkAAAAA1QKWSQAAAAA=",
|
||||
"sigs": [
|
||||
{
|
||||
"id": "1",
|
||||
"message": "Please give me your grins",
|
||||
"message_sig": "8f07ddd5e9f5179cff19486034181ed76505baaad53e5d994064127b56c5841bd9bccdcf5d3a402bccc77d36346d3a899259a884f643e90266984289b39a59d2",
|
||||
"part_sig": null,
|
||||
"public_blind_excess": "028e95921cc0d5be5922362265d352c9bdabe51a9e1502a3f0d4a10387f1893f40",
|
||||
"public_nonce": "031b84c5567b126440995d3ed5aaba0565d71e1834604819ff9c17f5e9d5dd078f"
|
||||
"nonce": "AxuExVZ7EmRAmV0+1aq6BWXXHhg0YEgZ/5wX9enV3QeP",
|
||||
"xs": "Ao6VkhzA1b5ZIjYiZdNSyb2r5RqeFQKj8NShA4fxiT9A"
|
||||
}
|
||||
],
|
||||
"tx": {
|
||||
"body": {
|
||||
"inputs": [],
|
||||
"kernels": [
|
||||
{
|
||||
"excess": "000000000000000000000000000000000000000000000000000000000000000000",
|
||||
"excess_sig": "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
|
||||
"features": "Plain",
|
||||
"fee": "0",
|
||||
"lock_height": "0"
|
||||
}
|
||||
],
|
||||
"outputs": [
|
||||
{
|
||||
"commit": "09cf47204446c326e361a1a92f34b174deff732daaedb80d7339fbe3db5ca2f6ba",
|
||||
"features": "Plain",
|
||||
"proof": "b368448efb3bfae95e7f18381883d64cbc9f01390e9c677d5d9f3523df43754dd174811768b7ffcafbfb284ae7413bdf56640ecb06918a5c38a5dae6cb33baff099c7cca6b052e07f915faecedee50a11ceaf41a7809bd33b51e22306ddf42620f7118133a418802f6e98222a8f3683cf3d5a5314155d0bf5f2e8be68e81ebe049ece23b0135d7b878c1ecebbf03de69fb8cbaf5f9611a430ae3083f71e0a74db8899b0083123a9e1924db8d340fdcc0bba4816afc613a0c6622fa89a84f31982cd4298a3b4c4de9d5f67800f48c6b37b4b49fb527290ec92f1551f4570abe42ac6ac42b05e3579b33533b784061ccbd2507af419079c3ea846f1af1aa2bfb04837166c60eab8207fed9000d3c2f5166e655e9220051223b90fb5751becc8a18cf10fb43cbc1cdeb8d0f11f5d0eb9dffdd4480abd69a49737f526b41b78f3c00bd7ef10f6ad3d8704f9ac6e79196c57b315a37265ca561fa333733e9c9275a2a4dc703b509b3ff11e6d983dd43a06566c82832ae0da9c8e9759038c6c86b30a05dd5cacc42c10fad496dee8cf63127233ae0bd27c766aed7448ebd7afbaa35c5491795fca7441b5373c4912e99ffbded6c7082d67f0b688f5af662be375f76699a69fcccb9c1c1841056fb4b6ec3f1c4dc40f032675fc2c87bab58e3375dac567533c4d0e3f1521e561029e231f3675368bde5817d177bd9c20b8cd7eb3b94260b0794f207bb33b9b8157518dbac8d725352b27ffa0e2aaa95d04592a87a6ee68deebaf1c51183704bea8ddd4298616fa353bc411936eafa1b31cc667a41a13a2d1a91db48464ea26c39ee1f68e67cbdd652165b040b43df2c80beda6af53dfbe0aa3aeb06c1887f9be83ed19b4b7094ba35700dad3ea4090594e662ae2a1c276b969751ab6d5d49a2c727d7ee2c80ffdc3d1ba040a20269b9bfc45930f467dbb43f64"
|
||||
}
|
||||
]
|
||||
},
|
||||
"offset": "d202964900000000d302964900000000d402964900000000d502964900000000"
|
||||
},
|
||||
"version_info": {
|
||||
"orig_version": 4,
|
||||
"version": 4,
|
||||
"block_header_version": 2
|
||||
}
|
||||
"sta": "I1",
|
||||
"ver": "4:2"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -560,51 +483,17 @@ pub trait OwnerRpc {
|
|||
"params": {
|
||||
"token": "d202964900000000d302964900000000d402964900000000d502964900000000",
|
||||
"slate": {
|
||||
"amount": "6000000000",
|
||||
"fee": "0",
|
||||
"height": "4",
|
||||
"amt": "6000000000",
|
||||
"id": "0436430c-2b02-624c-2032-570501212b00",
|
||||
"lock_height": "0",
|
||||
"ttl_cutoff_height": null,
|
||||
"num_participants": 2,
|
||||
"payment_proof": null,
|
||||
"participant_data": [
|
||||
"off": "QNjHljmHml/Ot7ogVQDjr3a0M8mBzgN/SLj6NuVh8IM=",
|
||||
"sigs": [
|
||||
{
|
||||
"id": "1",
|
||||
"message": "Please give me your grins",
|
||||
"message_sig": "1b84c5567b126440995d3ed5aaba0565d71e1834604819ff9c17f5e9d5dd078fd2599ab38942986602e943f684a85992893a6d34367dc7cc2b403a5dcfcdbcd9",
|
||||
"part_sig": null,
|
||||
"public_blind_excess": "028e95921cc0d5be5922362265d352c9bdabe51a9e1502a3f0d4a10387f1893f40",
|
||||
"public_nonce": "031b84c5567b126440995d3ed5aaba0565d71e1834604819ff9c17f5e9d5dd078f"
|
||||
"nonce": "AxuExVZ7EmRAmV0+1aq6BWXXHhg0YEgZ/5wX9enV3QeP",
|
||||
"xs": "Ao6VkhzA1b5ZIjYiZdNSyb2r5RqeFQKj8NShA4fxiT9A"
|
||||
}
|
||||
],
|
||||
"tx": {
|
||||
"body": {
|
||||
"inputs": [],
|
||||
"kernels": [
|
||||
{
|
||||
"excess": "000000000000000000000000000000000000000000000000000000000000000000",
|
||||
"excess_sig": "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
|
||||
"features": "Plain",
|
||||
"fee": "0",
|
||||
"lock_height": "0"
|
||||
}
|
||||
],
|
||||
"outputs": [
|
||||
{
|
||||
"commit": "09cf47204446c326e361a1a92f34b174deff732daaedb80d7339fbe3db5ca2f6ba",
|
||||
"features": "Plain",
|
||||
"proof": "8f511614315626b5f39224482351d766f5a8ef136262befc050d839be8479b0a13470cd88f4436346d213d83847a4055c6e0ac63681556470349a1aab47034a3015eb64d8163955998e2dd4165dd24386b1e279974b05deb5d46ba2bc321f7000c0784f8f10690605ffe717119d045e02b141ed12d8fc6d20930483a8af889ef533495eb442fcff36d98ebc104f13fc645c28431b3296e4a11f7c991ff97f9abbc2f8886762d7f29fdacb31d52c6850e6ccf5386117d89e8ea4ca3071c56c218dd5d3bcd65f6c06ed9f51f848507ca1d594f41796d1cf99f68a5c3f0c5dd9873602284cff31269b102fcc6c68607565faaf0adb04ed4ff3ea5d41f3b5235ac6cb90e4046c808c9c48c27172c891b20085c56a99913ef47fd8b3dc4920cef50534b9319a7cefe0df10a0206a634ac837e11da92df83ff58b1a14de81313400988aa48b946fcbe1b81f0e79e13f7c6c639b1c10983b424bda08d0ce593a20f1f47e0aa01473e7144f116b76d9ebc60599053d8f1542d60747793d99064e51fce8f8866390325d48d6e8e3bbdbc1822c864303451525c6cb4c6902f105a70134186fb32110d8192fc2528a9483fc8a4001f4bdeab1dd7b3d1ccb9ae2e746a78013ef74043f0b2436f0ca49627af1768b7c791c669bd331fd18c16ef88ad0a29861db70f2f76f3e74fde5accb91b73573e31333333223693d6fbc786e740c085e4fc6e7bde0a3f54e9703f816c54f012d3b1f41ec4d253d9337af61e7f1f1383bd929421ac346e3d2771dfee0b60503b33938e7c83eb37af3b6bf66041a3519a2b4cb557b34e3b9afcf95524f9a011425a34d32e7b6e9f255291094930acae26e8f7a1e4e6bc405d0f88e919f354f3ba85356a34f1aba5f7da1fad88e2692f4129cc1fb80a2122b2d996c6ccf7f08d8248e511d92af9ce49039de728848a2dc74101f4e94a"
|
||||
}
|
||||
]
|
||||
},
|
||||
"offset": "d202964900000000d302964900000000d402964900000000d502964900000000"
|
||||
},
|
||||
"version_info": {
|
||||
"orig_version": 4,
|
||||
"version": 4,
|
||||
"block_header_version": 2
|
||||
}
|
||||
"sta": "I1",
|
||||
"ver": "4:2"
|
||||
},
|
||||
"args": {
|
||||
"src_acct_name": null,
|
||||
|
@ -613,7 +502,6 @@ pub trait OwnerRpc {
|
|||
"max_outputs": 500,
|
||||
"num_change_outputs": 1,
|
||||
"selection_strategy_is_use_all": true,
|
||||
"message": "Ok, here are your grins",
|
||||
"target_slate_version": null,
|
||||
"payment_proof_recipient_address": null,
|
||||
"ttl_blocks": null,
|
||||
|
@ -630,69 +518,28 @@ pub trait OwnerRpc {
|
|||
"jsonrpc": "2.0",
|
||||
"result": {
|
||||
"Ok": {
|
||||
"amount": "6000000000",
|
||||
"fee": "8000000",
|
||||
"height": "4",
|
||||
"id": "0436430c-2b02-624c-2032-570501212b00",
|
||||
"lock_height": "0",
|
||||
"ttl_cutoff_height": null,
|
||||
"num_participants": 2,
|
||||
"payment_proof": null,
|
||||
"participant_data": [
|
||||
"coms": [
|
||||
{
|
||||
"id": "1",
|
||||
"message": "Please give me your grins",
|
||||
"message_sig": "1b84c5567b126440995d3ed5aaba0565d71e1834604819ff9c17f5e9d5dd078fd2599ab38942986602e943f684a85992893a6d34367dc7cc2b403a5dcfcdbcd9",
|
||||
"part_sig": null,
|
||||
"public_blind_excess": "028e95921cc0d5be5922362265d352c9bdabe51a9e1502a3f0d4a10387f1893f40",
|
||||
"public_nonce": "031b84c5567b126440995d3ed5aaba0565d71e1834604819ff9c17f5e9d5dd078f"
|
||||
"c": "COHanm3E1ugIpxiy8RCpkd13XWXOWuQIpOHwAqSWGqnn",
|
||||
"f": 1
|
||||
},
|
||||
{
|
||||
"id": "0",
|
||||
"message": "Ok, here are your grins",
|
||||
"message_sig": "8f07ddd5e9f5179cff19486034181ed76505baaad53e5d994064127b56c5841be91ae0f6b50fabc39eefa28118cccdd8fbf5b5afe96972630450f47b72433646",
|
||||
"part_sig": "8f07ddd5e9f5179cff19486034181ed76505baaad53e5d994064127b56c5841b619d40fb6a6fb60449ef9727aeb782e7a5b50fdbfd2d735b49ccc55b477cd319",
|
||||
"public_blind_excess": "0309e22f2adaa9b81f51414b775b86acd096e17794eb8159bfcfef27caa4bf5c90",
|
||||
"public_nonce": "031b84c5567b126440995d3ed5aaba0565d71e1834604819ff9c17f5e9d5dd078f"
|
||||
"c": "CUvlfJF4f8IDPV2X+uCZ8abds36kg3DxoTjwlSTHZ/3T",
|
||||
"p": "ExljCybQI2OGHr2xVRQIbciwdytLtj75uChwTgrDSO+tpnR916KYSBONYwx7QD5XPJzeBL5dJfLTRNtLAQ1riQ3WxUzAkRwMrceooiWy7D8tysiBiaF6piJX6Wnu+d6RcgCdjoZOQT8ZU5mLKFMeWA0+pJWlEtMg6NT/UOdJWmwoPG5UTRY2TTQnKAWJNSbx47b9F270rcVnGxZc8o78+40lwN/NAYosXmW+65IB85gxlOWlIcCETQXHAGVN+u0bmzna4IzJr6ucuJEla8AjetLOeNqLRFhlgPUt00bcr95eRxkX8W5MS1HpZuGUbxPjF3FQPIW7DxtB0cf8yVPnCvVUAGOKNef1YQ+fTFuIGjUGCmk96vRuGDnFSo99LGJrBazTRQtyro8uD4chvLvYViFB0/74UcasPIBp+mOJOJvE/LpeT7SXCaO2Olm6lqgoJ9+9bxbehJ75XzEUWT0gev9uAwFSkp+iILDDtUykGc/L/7egEG3TFU6FiHjH2POMrcw3bFAr3FApK0lEhJNtCEb8P6wQkQlivKTdzKXIC0WP198V6abC85tRZCWiGQqXydDi4vEF7imQXzbjpkihNevzh9C7Kmth2VshUxnW3J7otLJ5iBD7bgHABwQbKIwrOegFya+GyI3Uo4C2o0a0oOZ7umqqxazHAIisMpdIa5DP43HZRkVSdHovdoD0LVYp+wm+2DU4LYQiNHEsBXTFtPJWwibndgJCmYPk3vcVQc/4DM9M07dhaFyRRjyOHHv4KGmcaIUJKCuF51JCTfPaZws8+s3qL2bPuAT+zfi36wVuiRf9rnjYPAEZZOPVoHSIc/gX0Kv0sEwgRScz6sNcMYuW4QClrOD1QIW9JPlouPxbJ24NexNPAdtQs9J3HNzxQj1E"
|
||||
}
|
||||
],
|
||||
"tx": {
|
||||
"body": {
|
||||
"inputs": [
|
||||
{
|
||||
"commit": "08e1da9e6dc4d6e808a718b2f110a991dd775d65ce5ae408a4e1f002a4961aa9e7",
|
||||
"features": "Coinbase"
|
||||
}
|
||||
],
|
||||
"kernels": [
|
||||
{
|
||||
"excess": "000000000000000000000000000000000000000000000000000000000000000000",
|
||||
"excess_sig": "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
|
||||
"features": "Plain",
|
||||
"fee": "8000000",
|
||||
"lock_height": "0"
|
||||
}
|
||||
],
|
||||
"outputs": [
|
||||
{
|
||||
"commit": "09cf47204446c326e361a1a92f34b174deff732daaedb80d7339fbe3db5ca2f6ba",
|
||||
"features": "Plain",
|
||||
"proof": "8f511614315626b5f39224482351d766f5a8ef136262befc050d839be8479b0a13470cd88f4436346d213d83847a4055c6e0ac63681556470349a1aab47034a3015eb64d8163955998e2dd4165dd24386b1e279974b05deb5d46ba2bc321f7000c0784f8f10690605ffe717119d045e02b141ed12d8fc6d20930483a8af889ef533495eb442fcff36d98ebc104f13fc645c28431b3296e4a11f7c991ff97f9abbc2f8886762d7f29fdacb31d52c6850e6ccf5386117d89e8ea4ca3071c56c218dd5d3bcd65f6c06ed9f51f848507ca1d594f41796d1cf99f68a5c3f0c5dd9873602284cff31269b102fcc6c68607565faaf0adb04ed4ff3ea5d41f3b5235ac6cb90e4046c808c9c48c27172c891b20085c56a99913ef47fd8b3dc4920cef50534b9319a7cefe0df10a0206a634ac837e11da92df83ff58b1a14de81313400988aa48b946fcbe1b81f0e79e13f7c6c639b1c10983b424bda08d0ce593a20f1f47e0aa01473e7144f116b76d9ebc60599053d8f1542d60747793d99064e51fce8f8866390325d48d6e8e3bbdbc1822c864303451525c6cb4c6902f105a70134186fb32110d8192fc2528a9483fc8a4001f4bdeab1dd7b3d1ccb9ae2e746a78013ef74043f0b2436f0ca49627af1768b7c791c669bd331fd18c16ef88ad0a29861db70f2f76f3e74fde5accb91b73573e31333333223693d6fbc786e740c085e4fc6e7bde0a3f54e9703f816c54f012d3b1f41ec4d253d9337af61e7f1f1383bd929421ac346e3d2771dfee0b60503b33938e7c83eb37af3b6bf66041a3519a2b4cb557b34e3b9afcf95524f9a011425a34d32e7b6e9f255291094930acae26e8f7a1e4e6bc405d0f88e919f354f3ba85356a34f1aba5f7da1fad88e2692f4129cc1fb80a2122b2d996c6ccf7f08d8248e511d92af9ce49039de728848a2dc74101f4e94a"
|
||||
},
|
||||
{
|
||||
"commit": "094be57c91787fc2033d5d97fae099f1a6ddb37ea48370f1a138f09524c767fdd3",
|
||||
"features": "Plain",
|
||||
"proof": "1319630b26d02363861ebdb15514086dc8b0772b4bb63ef9b828704e0ac348efada6747dd7a29848138d630c7b403e573c9cde04be5d25f2d344db4b010d6b890dd6c54cc0911c0cadc7a8a225b2ec3f2dcac88189a17aa62257e969eef9de9172009d8e864e413f1953998b28531e580d3ea495a512d320e8d4ff50e7495a6c283c6e544d16364d34272805893526f1e3b6fd176ef4adc5671b165cf28efcfb8d25c0dfcd018a2c5e65beeb9201f3983194e5a521c0844d05c700654dfaed1b9b39dae08cc9afab9cb891256bc0237ad2ce78da8b44586580f52dd346dcafde5e471917f16e4c4b51e966e1946f13e31771503c85bb0f1b41d1c7fcc953e70af55400638a35e7f5610f9f4c5b881a35060a693deaf46e1839c54a8f7d2c626b05acd3450b72ae8f2e0f8721bcbbd8562141d3fef851c6ac3c8069fa6389389bc4fcba5e4fb49709a3b63a59ba96a82827dfbd6f16de849ef95f3114593d207aff6e030152929fa220b0c3b54ca419cfcbffb7a0106dd3154e858878c7d8f38cadcc376c502bdc50292b494484936d0846fc3fac10910962bca4ddcca5c80b458fd7df15e9a6c2f39b516425a2190a97c9d0e2e2f105ee29905f36e3a648a135ebf387d0bb2a6b61d95b215319d6dc9ee8b4b2798810fb6e01c007041b288c2b39e805c9af86c88dd4a380b6a346b4a0e67bba6aaac5acc70088ac3297486b90cfe371d9464552747a2f7680f42d5629fb09bed835382d842234712c0574c5b4f256c226e77602429983e4def71541cff80ccf4cd3b761685c91463c8e1c7bf828699c688509282b85e752424df3da670b3cfacdea2f66cfb804fecdf8b7eb056e8917fdae78d83c011964e3d5a0748873f817d0abf4b04c20452733eac35c318b96e100a5ace0f54085bd24f968b8fc5b276e0d7b134f01db50b3d2771cdcf1423d44"
|
||||
}
|
||||
]
|
||||
},
|
||||
"offset": "d202964900000000d302964900000000d402964900000000d502964900000000"
|
||||
},
|
||||
"version_info": {
|
||||
"orig_version": 4,
|
||||
"version": 4,
|
||||
"block_header_version": 2
|
||||
}
|
||||
"fee": "8000000",
|
||||
"id": "0436430c-2b02-624c-2032-570501212b00",
|
||||
"off": "CkRYNMTRYXJonCcMMoSQZz8qlEXOyLnKGeS9dhyGDbk=",
|
||||
"sigs": [
|
||||
{
|
||||
"nonce": "AxuExVZ7EmRAmV0+1aq6BWXXHhg0YEgZ/5wX9enV3QeP",
|
||||
"part": "jwfd1en1F5z/GUhgNBge12UFuqrVPl2ZQGQSe1bFhBvxgE9v6OVfVVa72Af++81ysw2Qz3CPjFZEesxjIoJ05g==",
|
||||
"xs": "Ajh4zoRXJ/Ok7HbKPz20s4otBdY2uMNjIQi4V/7WPJbe"
|
||||
}
|
||||
],
|
||||
"sta": "I2",
|
||||
"ver": "4:2"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -721,58 +568,18 @@ pub trait OwnerRpc {
|
|||
"params": {
|
||||
"token": "d202964900000000d302964900000000d402964900000000d502964900000000",
|
||||
"slate": {
|
||||
"amount": "6000000000",
|
||||
"fee": "8000000",
|
||||
"height": "4",
|
||||
"ver": "4:2",
|
||||
"id": "0436430c-2b02-624c-2032-570501212b00",
|
||||
"lock_height": "4",
|
||||
"ttl_cutoff_height": null,
|
||||
"num_participants": 2,
|
||||
"payment_proof": null,
|
||||
"participant_data": [
|
||||
{
|
||||
"id": "0",
|
||||
"message": "my message",
|
||||
"message_sig": "8f07ddd5e9f5179cff19486034181ed76505baaad53e5d994064127b56c5841b1d4c1358be398f801eb90d933774b5218fa7e769b11c4c640402253353656f75",
|
||||
"part_sig": null,
|
||||
"public_blind_excess": "034b4df2f0558b73ea72a1ca5c4ab20217c66bbe0829056fca7abe76888e9349ee",
|
||||
"public_nonce": "031b84c5567b126440995d3ed5aaba0565d71e1834604819ff9c17f5e9d5dd078f"
|
||||
}
|
||||
],
|
||||
"tx": {
|
||||
"body": {
|
||||
"inputs": [
|
||||
{
|
||||
"commit": "08e1da9e6dc4d6e808a718b2f110a991dd775d65ce5ae408a4e1f002a4961aa9e7",
|
||||
"features": "Coinbase"
|
||||
}
|
||||
],
|
||||
"kernels": [
|
||||
{
|
||||
"excess": "000000000000000000000000000000000000000000000000000000000000000000",
|
||||
"excess_sig": "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
|
||||
"features": "HeightLocked",
|
||||
"fee": "8000000",
|
||||
"lock_height": "4"
|
||||
}
|
||||
],
|
||||
"outputs": [
|
||||
{
|
||||
"commit": "094be57c91787fc2033d5d97fae099f1a6ddb37ea48370f1a138f09524c767fdd3",
|
||||
"features": "Plain",
|
||||
"proof": "2a42e9e902b70ce44e1fccb14de87ee0a97100bddf12c6bead1b9c5f4eb60300f29c13094fa12ffeee238fb4532b18f6b61cf51b23c1c7e1ad2e41560dc27edc0a2b9e647a0b3e4e806fced5b65e61d0f1f5197d3e2285c632d359e27b6b9206b2caffea4f67e0c7a2812e7a22c134b98cf89bd43d9f28b8bec25cce037a0ac5b1ae8f667e54e1250813a5263004486b4465ad4e641ab2b535736ea26535a11013564f08f483b7dab1c2bcc3ee38eadf2f7850eff7e3459a4bbabf9f0cf6c50d0c0a4120565cd4a2ce3e354c11721cd695760a24c70e0d5a0dfc3c5dcd51dfad6de2c237a682f36dc0b271f21bb3655e5333016aaa42c2efa1446e5f3c0a79ec417c4d30f77556951cb0f05dbfafb82d9f95951a9ea241fda2a6388f73ace036b98acce079f0e4feebccc96290a86dcc89118a901210b245f2d114cf94396e4dbb461e82aa26a0581389707957968c7cdc466213bb1cd417db207ef40c05842ab67a01a9b96eb1430ebc26e795bb491258d326d5174ad549401059e41782121e506744af8af9d8e493644a87d613600888541cbbe538c625883f3eb4aa3102c5cfcc25de8e97af8927619ce6a731b3b8462d51d993066b935b0648d2344ad72e4fd70f347fbd81041042e5ea31cc7b2e3156a920b80ecba487b950ca32ca95fae85b759c936246ecf441a9fdd95e8fee932d6782cdec686064018c857efc47fb4b2a122600d5fdd79af2486f44df7e629184e1c573bc0a9b3feb40b190ef2861a1ab45e2ac2201b9cd42e495deea247269820ed32389a2810ad6c0f9a296d2a2d9c54089fed50b7f5ecfcd33ab9954360e1d7f5598c32128cfcf2a1d8bf14616818da8a5343bfa88f0eedf392e9d4ab1ace1b60324129cd4852c2e27813a9cf71a6ae6229a4fcecc1a756b3e664c5f50af333082616815a3bec8fc0b75b8e4e767d719"
|
||||
}
|
||||
]
|
||||
},
|
||||
"offset": "d202964900000000d302964900000000d402964900000000d502964900000000"
|
||||
},
|
||||
"version_info": {
|
||||
"orig_version": 4,
|
||||
"version": 4,
|
||||
"block_header_version": 2
|
||||
}
|
||||
},
|
||||
"participant_id": 0
|
||||
"sta": "S1",
|
||||
"amt": "60000000000",
|
||||
"fee": "7000000",
|
||||
"sigs": [
|
||||
{
|
||||
"xs": "AzrCFY+gB38IfeYMGdjkMXU7qltjtuFHfwWipucZDUWS",
|
||||
"nonce": "AxuExVZ7EmRAmV0+1aq6BWXXHhg0YEgZ/5wX9enV3QeP"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
# "#
|
||||
|
@ -790,12 +597,7 @@ pub trait OwnerRpc {
|
|||
|
||||
```
|
||||
*/
|
||||
fn tx_lock_outputs(
|
||||
&self,
|
||||
token: Token,
|
||||
slate: VersionedSlate,
|
||||
participant_id: usize,
|
||||
) -> Result<(), ErrorKind>;
|
||||
fn tx_lock_outputs(&self, token: Token, slate: VersionedSlate) -> Result<(), ErrorKind>;
|
||||
|
||||
/**
|
||||
Networked version of [Owner::finalize_tx](struct.Owner.html#method.finalize_tx).
|
||||
|
@ -809,72 +611,23 @@ pub trait OwnerRpc {
|
|||
"id": 1,
|
||||
"params": {
|
||||
"token": "d202964900000000d302964900000000d402964900000000d502964900000000",
|
||||
"slate": {
|
||||
"version_info": {
|
||||
"version": 3,
|
||||
"orig_version": 3,
|
||||
"block_header_version": 2
|
||||
},
|
||||
"num_participants": 2,
|
||||
"slate":
|
||||
{
|
||||
"ver": "4:2",
|
||||
"id": "0436430c-2b02-624c-2032-570501212b00",
|
||||
"payment_proof": null,
|
||||
"tx": {
|
||||
"offset": "d202964900000000d302964900000000d402964900000000d502964900000000",
|
||||
"body": {
|
||||
"inputs": [
|
||||
{
|
||||
"features": "Coinbase",
|
||||
"commit": "087df32304c5d4ae8b2af0bc31e700019d722910ef87dd4eec3197b80b207e3045"
|
||||
},
|
||||
{
|
||||
"features": "Coinbase",
|
||||
"commit": "08e1da9e6dc4d6e808a718b2f110a991dd775d65ce5ae408a4e1f002a4961aa9e7"
|
||||
}
|
||||
],
|
||||
"outputs": [
|
||||
{
|
||||
"features": "Plain",
|
||||
"commit": "099b48cfb1f80a2347dc89818449e68e76a3c6817a532a8e9ef2b4a5ccf4363850",
|
||||
"proof": "7ebcd2ed9bf5fb29854033ba3d0e720613bdf7dfacc586d2f6084c1cde0a2b72e955d4ce625916701dc7c347132f40d0f102a34e801d745ee54b49b765d08aae0bb801c60403e57cafade3b4b174e795b633ab9e402b5b1b6e1243fd10bbcf9368a75cb6a6c375c7bdf02da9e03b7f210df45d942e6fba2729cd512a372e6ed91a1b5c9c22831febea843e3f85adcf198f39ac9f7b73b70c60bfb474aa69878ea8d1d32fef30166b59caacaec3fd024de29a90f1587e08d2c36b3d5c560cabf658e212e0a40a4129b3e5c35557058def5551f4eb395759597ba808b3c34eac3bfb9716e4480d7931c5789c538463ec75be0eb807c894047fda6cbcd22682d3c6d3823cb330f090a2099e3510a3706b57d46c95224394d7f1c0a20d99cc314b8f1d9d02668e2e435f62e1194de0be6a1f50f72ed777ed51c8819f527a94918d1aa8df6461e98ed4c2b18210de50fbcf8c3df210bfe326d41f1dc0ad748cb0320ae28401c85ab4f7dcb99d88a052e95dc85b76d22b36cabd60e06ab84bb7e4ddfdab9c9730c8a986583237ed1ecbb323ee8e79b8cadca4b438b7c09531670b471dda6a2eb3e747916c88ce7d9d8e1b7f61660eeb9e5a13c60e4dfe89d1177d81d6f6570fda85158e646a15f1e8b9e977494dc19a339aab2e0e478670d80092d6ba37646e60714ef64eb4a3d37fe15f8f38b59114af34b235489eed3f69b7781c5fe496eb43ffe245c14bd740f745844a38cf0d904347aaa2b64f51add18822dac009d8b63fa3e4c9b1fa72187f9a4acba1ab315daa1b04c9a41f3be846ac420b37990e6c947a16cc9d5c0671b292bf77d7d8b8974d2ad3afae95ba7772c37432840f53a007f31e0195f3abdf100c4477723cc6c6d5da14894a73dfac342833731036487488fdade7b9d556c06f26173b6b67598d3769447ce2828d71dd45ac5af436c6b0"
|
||||
},
|
||||
{
|
||||
"features": "Plain",
|
||||
"commit": "0812276cc788e6870612296d926cba9f0e7b9810670710b5a6e6f1ba006d395774",
|
||||
"proof": "dcff6175390c602bfa92c2ffd1a9b2d84dcc9ea941f6f317bdd0f875244ef23e696fd17c71df79760ce5ce1a96aab1d15dd057358dc835e972febeb86d50ccec0dad7cfe0246d742eb753cf7b88c045d15bc7123f8cf7155647ccf663fca92a83c9a65d0ed756ea7ebffd2cac90c380a102ed9caaa355d175ed0bf58d3ac2f5e909d6c447dfc6b605e04925c2b17c33ebd1908c965a5541ea5d2ed45a0958e6402f89d7a56df1992e036d836e74017e73ccad5cb3a82b8e139e309792a31b15f3ffd72ed033253428c156c2b9799458a25c1da65b719780a22de7fe7f437ae2fccd22cf7ea357ab5aa66a5ef7d71fb0dc64aa0b5761f68278062bb39bb296c787e4cabc5e2a2933a416ce1c9a9696160386449c437e9120f7bb26e5b0e74d1f2e7d5bcd7aafb2a92b87d1548f1f911fb06af7bd6cc13cee29f7c9cb79021aed18186272af0e9d189ec107c81a8a3aeb4782b0d950e4881aa51b776bb6844b25bce97035b48a9bdb2aea3608687bcdd479d4fa998b5a839ff88558e4a29dff0ed13b55900abb5d439b70793d902ae9ad34587b18c919f6b875c91d14deeb1c373f5e76570d59a6549758f655f1128a54f162dfe8868e1587028e26ad91e528c5ae7ee9335fa58fb59022b5de29d80f0764a9917390d46db899acc6a5b416e25ecc9dccb7153646addcc81cadb5f0078febc7e05d7735aba494f39ef05697bbcc9b47b2ccc79595d75fc13c80678b5e237edce58d731f34c05b1ddcaa649acf2d865bbbc3ceda10508bcdd29d0496744644bf1c3516f6687dfeef5649c7dff90627d642739a59d91a8d1d0c4dc55d74a949e1074427664b467992c9e0f7d3af9d6ea79513e8946ddc0d356bac49878e64e6a95b0a30214214faf2ce317fa622ff3266b32a816e10a18e6d789a5da1f23e67b4f970a68a7bcd9e18825ee274b0483896a40"
|
||||
}
|
||||
],
|
||||
"kernels": [
|
||||
{
|
||||
"features": "Plain",
|
||||
"fee": "7000000",
|
||||
"lock_height": "0",
|
||||
"excess": "000000000000000000000000000000000000000000000000000000000000000000",
|
||||
"excess_sig": "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"
|
||||
}
|
||||
]
|
||||
"sta": "S2",
|
||||
"off": "pAUskgAAAAGmBSySAAAAAu1WT6tQt1/F6jLOBS/Jvr8=",
|
||||
"sigs": [
|
||||
{
|
||||
"xs": "Azu+KkGeounWgQqNZlUucJ0Xg8pQdZpE269j/HnAFkxM",
|
||||
"nonce": "AxuExVZ7EmRAmV0+1aq6BWXXHhg0YEgZ/5wX9enV3QeP",
|
||||
"part": "jwfd1en1F5z/GUhgNBge12UFuqrVPl2ZQGQSe1bFhBs1/f5VJx8q5z119Yxw0e+2mzOEx7xQfVfpnlbed+IIdA=="
|
||||
}
|
||||
},
|
||||
"amount": "60000000000",
|
||||
"fee": "7000000",
|
||||
"height": "5",
|
||||
"lock_height": "0",
|
||||
"ttl_cutoff_height": null,
|
||||
"participant_data": [
|
||||
{
|
||||
"id": "0",
|
||||
"public_blind_excess": "033ac2158fa0077f087de60c19d8e431753baa5b63b6e1477f05a2a6e7190d4592",
|
||||
"public_nonce": "031b84c5567b126440995d3ed5aaba0565d71e1834604819ff9c17f5e9d5dd078f",
|
||||
"part_sig": null,
|
||||
"message": null,
|
||||
"message_sig": null
|
||||
},
|
||||
{
|
||||
"id": "1",
|
||||
"public_blind_excess": "024f9bc78c984c78d6e916d3a00746aa30fa1172124c8dbc0cbddcb7b486719bc7",
|
||||
"public_nonce": "031b84c5567b126440995d3ed5aaba0565d71e1834604819ff9c17f5e9d5dd078f",
|
||||
"part_sig": "8f07ddd5e9f5179cff19486034181ed76505baaad53e5d994064127b56c5841ba9c6dd6185c2b819799700fa1a69201f96cc6dfb9ca205a0ef7c35fb81d57dac",
|
||||
"message": null,
|
||||
"message_sig": null
|
||||
],
|
||||
"coms": [
|
||||
{
|
||||
"c": "CZtIz7H4CiNH3ImBhEnmjnajxoF6UyqOnvK0pcz0NjhQ",
|
||||
"p": "KXAc6uJiysd7ebhoyIOikuYebegZK4aO3NEwCwlz2ROWsVas5r1nNAKjA94Q3dil5rfxe6ZVeldKZyvQTMJzqwTtjiyoC6xIM0XA7IQ/UhgUzhMB7JrcOJVqErTZSKzOcSlaT1K83rihyfLWstpdcxJipenAJ275BN+e+NSAAUIM1Z91ovGuXHocfGufFA52E+Uu+eJJ8p+TQLfvuAaZ5GAWQyRhb5j9TN49tSSXyRnpUiL//qy35l3sp+NoqAznE8Gd59pTaXJiKO4zb1vUlFOMEsy//rG5v9X8iQbRxkJFtRbxA/qW2cVpdYN2UsHg+lgD18zxFH2Pkn422nF/eteUcdvhkvX1D4enn8P+Aw26VptjS5LSzzB5k8zlRWM68mOJfNfm6/Tcr7F20HNYvcONA+RaSd+pyMZRfNaNFn/79sO03g4t0hkJy61MRnuE5XAL5HOjmsWcZp18FVxLyrm4Am7qNDHHec0nfkki0rl0Lh9meMvoaew7W370Ey3bbN0Gzyfb6yi+crlJ+ol2EOSOOg14n9Lup1q8l7PcfgDlyLPSTkDG8kESrbcjUriaK+8FmTRTOOnnYgKjxG76Y3CVKyrKQarbrg6jJTGsr82rbdBm12nr9Qz088ClnS1fp5YAoge5QXxiP3atBejMz81AOPlEi8QPEnynwNNy5GB04zT+SfWpVuwAVvTaYB5q+A6xpsSVEFSGnmZbKW2MFPNEyi3F/dXfSjZSU2NloWFa2bQiFlx3v4/mWoNcjgxB4HABTrZu+MUlIE6ZCzo9ZjweQiIbSWiVw3ovDBvwXpEjVAnD/j2JqaedbHhgmrGKRjMRkR9x+je7c7FfzTgUPRQE/SzoEATcf/ic8RFdzAw1zhwb+ZQVhvuVl3DyYYzLcRin"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
@ -887,74 +640,42 @@ pub trait OwnerRpc {
|
|||
"jsonrpc": "2.0",
|
||||
"id": 1,
|
||||
"result": {
|
||||
"Ok": {
|
||||
"amount": "60000000000",
|
||||
"fee": "7000000",
|
||||
"height": "5",
|
||||
"id": "0436430c-2b02-624c-2032-570501212b00",
|
||||
"lock_height": "0",
|
||||
"ttl_cutoff_height": null,
|
||||
"num_participants": 2,
|
||||
"payment_proof": null,
|
||||
"participant_data": [
|
||||
"Ok": {
|
||||
"coms": [
|
||||
{
|
||||
"id": "0",
|
||||
"message": null,
|
||||
"message_sig": null,
|
||||
"part_sig": "8f07ddd5e9f5179cff19486034181ed76505baaad53e5d994064127b56c5841b38641aefa907a2fc1c051b1f73202794fffb6d422e328516a5c6b2ef41e935f8",
|
||||
"public_blind_excess": "033ac2158fa0077f087de60c19d8e431753baa5b63b6e1477f05a2a6e7190d4592",
|
||||
"public_nonce": "031b84c5567b126440995d3ed5aaba0565d71e1834604819ff9c17f5e9d5dd078f"
|
||||
"c": "CH3zIwTF1K6LKvC8MecAAZ1yKRDvh91O7DGXuAsgfjBF",
|
||||
"f": 1
|
||||
},
|
||||
{
|
||||
"id": "1",
|
||||
"message": null,
|
||||
"message_sig": null,
|
||||
"part_sig": "8f07ddd5e9f5179cff19486034181ed76505baaad53e5d994064127b56c5841ba9c6dd6185c2b819799700fa1a69201f96cc6dfb9ca205a0ef7c35fb81d57dac",
|
||||
"public_blind_excess": "024f9bc78c984c78d6e916d3a00746aa30fa1172124c8dbc0cbddcb7b486719bc7",
|
||||
"public_nonce": "031b84c5567b126440995d3ed5aaba0565d71e1834604819ff9c17f5e9d5dd078f"
|
||||
"c": "COHanm3E1ugIpxiy8RCpkd13XWXOWuQIpOHwAqSWGqnn",
|
||||
"f": 1
|
||||
},
|
||||
{
|
||||
"c": "CZtIz7H4CiNH3ImBhEnmjnajxoF6UyqOnvK0pcz0NjhQ",
|
||||
"p": "KXAc6uJiysd7ebhoyIOikuYebegZK4aO3NEwCwlz2ROWsVas5r1nNAKjA94Q3dil5rfxe6ZVeldKZyvQTMJzqwTtjiyoC6xIM0XA7IQ/UhgUzhMB7JrcOJVqErTZSKzOcSlaT1K83rihyfLWstpdcxJipenAJ275BN+e+NSAAUIM1Z91ovGuXHocfGufFA52E+Uu+eJJ8p+TQLfvuAaZ5GAWQyRhb5j9TN49tSSXyRnpUiL//qy35l3sp+NoqAznE8Gd59pTaXJiKO4zb1vUlFOMEsy//rG5v9X8iQbRxkJFtRbxA/qW2cVpdYN2UsHg+lgD18zxFH2Pkn422nF/eteUcdvhkvX1D4enn8P+Aw26VptjS5LSzzB5k8zlRWM68mOJfNfm6/Tcr7F20HNYvcONA+RaSd+pyMZRfNaNFn/79sO03g4t0hkJy61MRnuE5XAL5HOjmsWcZp18FVxLyrm4Am7qNDHHec0nfkki0rl0Lh9meMvoaew7W370Ey3bbN0Gzyfb6yi+crlJ+ol2EOSOOg14n9Lup1q8l7PcfgDlyLPSTkDG8kESrbcjUriaK+8FmTRTOOnnYgKjxG76Y3CVKyrKQarbrg6jJTGsr82rbdBm12nr9Qz088ClnS1fp5YAoge5QXxiP3atBejMz81AOPlEi8QPEnynwNNy5GB04zT+SfWpVuwAVvTaYB5q+A6xpsSVEFSGnmZbKW2MFPNEyi3F/dXfSjZSU2NloWFa2bQiFlx3v4/mWoNcjgxB4HABTrZu+MUlIE6ZCzo9ZjweQiIbSWiVw3ovDBvwXpEjVAnD/j2JqaedbHhgmrGKRjMRkR9x+je7c7FfzTgUPRQE/SzoEATcf/ic8RFdzAw1zhwb+ZQVhvuVl3DyYYzLcRin"
|
||||
},
|
||||
{
|
||||
"c": "CBInbMeI5ocGEiltkmy6nw57mBBnBxC1pubxugBtOVd0",
|
||||
"p": "KEufkZlBHGu/cifq4VzJ+n7TBTSvPs/4Ww0BbaMpyuHx7fefAUJkNMuQr8wvCh+yluXFGpG15XofAjD9pPjFlQ55hvo3m5nWS2A5qGzH414EC6GSt4EEOVmFEmjKmHSpGIBeqVjIT3/ujTq0Ji8DL1o/hA683Sc7Kb6BARTm6GqVnY5MCAVy4+8knt1q1oUD7DvESGVIUg6id1pBrqZ6rJmUX86eendp1x+JOtfw0BCGkva2hSMSyub5hXBjBVvaWdzlIZJ8cAQLgCakG2UXyuChyUfKJEmEpcCt98ZIOwk5NGxI9hysN9UB9GocWHi2fO4NByP07q3J9dce1enzO0KUtY0+vu/qoT8gNXWZvlSc4Y5uLrHVDhI1zMQOyRhMaKYjdBpyOM5pqjodJRVrO36zj91vvlRzl5/u4zF98nnGDUiiiYJqpMdtvOJNUmiQ1Obi+D6A9nShJI/B3AN9mCAJASrhEz9eFYrmza2xjI1T5KiuVZXHWHgsZ6oMIPFG1SCFz0WjV5TOxFcCgw+JUqaXRHGPvm/g09pm40jdNHOgrO1wgPv1SUw+fhQZFvOxNbMyd/mY/Nms+8qHCYFIZumDil3NpMKUIs8VcpPm/CzMLSVCNReEO9jiHGHO1yMSwLSIFMMSAhsNMVmNI4mwsym6oRaZIqTDQXPdX1QFRb5QZqDykfGocOGq/5TBnwqFUlSIKheYS67aCOjq1T0VY+nuS8NnQnic7086sVgFTX3f4qKze1qKML6E/335p9dYqbdnUaNiIFdyGj7FuHNYLQyRzR/rvBU2YnOYVOoVyZAwI8GTxrV2i1VIQQj4l83EfROpCIsyVY5X8S9YB2aGScmaF7UZBRJzQOi0nEJ3Xxs8qwct+tM9M8weS6WWTXKO0KkF"
|
||||
}
|
||||
],
|
||||
"tx": {
|
||||
"body": {
|
||||
"inputs": [
|
||||
{
|
||||
"commit": "087df32304c5d4ae8b2af0bc31e700019d722910ef87dd4eec3197b80b207e3045",
|
||||
"features": "Coinbase"
|
||||
},
|
||||
{
|
||||
"commit": "08e1da9e6dc4d6e808a718b2f110a991dd775d65ce5ae408a4e1f002a4961aa9e7",
|
||||
"features": "Coinbase"
|
||||
}
|
||||
],
|
||||
"kernels": [
|
||||
{
|
||||
"excess": "09bac6083b05a32a9d9b37710c70dd0a1ef9329fde0848558976b6f1b81d80ceed",
|
||||
"excess_sig": "66074d25a751c4743342c90ad8ead9454daa00d9b9aed29bca321036d16c4b4da0e9c180a26b88565afcd269a7ac98f896c8db3dcbd48ab69443e8eac3beb3a4",
|
||||
"features": "Plain",
|
||||
"fee": "7000000",
|
||||
"lock_height": "0"
|
||||
}
|
||||
],
|
||||
"outputs": [
|
||||
{
|
||||
"commit": "099b48cfb1f80a2347dc89818449e68e76a3c6817a532a8e9ef2b4a5ccf4363850",
|
||||
"features": "Plain",
|
||||
"proof": "7ebcd2ed9bf5fb29854033ba3d0e720613bdf7dfacc586d2f6084c1cde0a2b72e955d4ce625916701dc7c347132f40d0f102a34e801d745ee54b49b765d08aae0bb801c60403e57cafade3b4b174e795b633ab9e402b5b1b6e1243fd10bbcf9368a75cb6a6c375c7bdf02da9e03b7f210df45d942e6fba2729cd512a372e6ed91a1b5c9c22831febea843e3f85adcf198f39ac9f7b73b70c60bfb474aa69878ea8d1d32fef30166b59caacaec3fd024de29a90f1587e08d2c36b3d5c560cabf658e212e0a40a4129b3e5c35557058def5551f4eb395759597ba808b3c34eac3bfb9716e4480d7931c5789c538463ec75be0eb807c894047fda6cbcd22682d3c6d3823cb330f090a2099e3510a3706b57d46c95224394d7f1c0a20d99cc314b8f1d9d02668e2e435f62e1194de0be6a1f50f72ed777ed51c8819f527a94918d1aa8df6461e98ed4c2b18210de50fbcf8c3df210bfe326d41f1dc0ad748cb0320ae28401c85ab4f7dcb99d88a052e95dc85b76d22b36cabd60e06ab84bb7e4ddfdab9c9730c8a986583237ed1ecbb323ee8e79b8cadca4b438b7c09531670b471dda6a2eb3e747916c88ce7d9d8e1b7f61660eeb9e5a13c60e4dfe89d1177d81d6f6570fda85158e646a15f1e8b9e977494dc19a339aab2e0e478670d80092d6ba37646e60714ef64eb4a3d37fe15f8f38b59114af34b235489eed3f69b7781c5fe496eb43ffe245c14bd740f745844a38cf0d904347aaa2b64f51add18822dac009d8b63fa3e4c9b1fa72187f9a4acba1ab315daa1b04c9a41f3be846ac420b37990e6c947a16cc9d5c0671b292bf77d7d8b8974d2ad3afae95ba7772c37432840f53a007f31e0195f3abdf100c4477723cc6c6d5da14894a73dfac342833731036487488fdade7b9d556c06f26173b6b67598d3769447ce2828d71dd45ac5af436c6b0"
|
||||
},
|
||||
{
|
||||
"commit": "0812276cc788e6870612296d926cba9f0e7b9810670710b5a6e6f1ba006d395774",
|
||||
"features": "Plain",
|
||||
"proof": "dcff6175390c602bfa92c2ffd1a9b2d84dcc9ea941f6f317bdd0f875244ef23e696fd17c71df79760ce5ce1a96aab1d15dd057358dc835e972febeb86d50ccec0dad7cfe0246d742eb753cf7b88c045d15bc7123f8cf7155647ccf663fca92a83c9a65d0ed756ea7ebffd2cac90c380a102ed9caaa355d175ed0bf58d3ac2f5e909d6c447dfc6b605e04925c2b17c33ebd1908c965a5541ea5d2ed45a0958e6402f89d7a56df1992e036d836e74017e73ccad5cb3a82b8e139e309792a31b15f3ffd72ed033253428c156c2b9799458a25c1da65b719780a22de7fe7f437ae2fccd22cf7ea357ab5aa66a5ef7d71fb0dc64aa0b5761f68278062bb39bb296c787e4cabc5e2a2933a416ce1c9a9696160386449c437e9120f7bb26e5b0e74d1f2e7d5bcd7aafb2a92b87d1548f1f911fb06af7bd6cc13cee29f7c9cb79021aed18186272af0e9d189ec107c81a8a3aeb4782b0d950e4881aa51b776bb6844b25bce97035b48a9bdb2aea3608687bcdd479d4fa998b5a839ff88558e4a29dff0ed13b55900abb5d439b70793d902ae9ad34587b18c919f6b875c91d14deeb1c373f5e76570d59a6549758f655f1128a54f162dfe8868e1587028e26ad91e528c5ae7ee9335fa58fb59022b5de29d80f0764a9917390d46db899acc6a5b416e25ecc9dccb7153646addcc81cadb5f0078febc7e05d7735aba494f39ef05697bbcc9b47b2ccc79595d75fc13c80678b5e237edce58d731f34c05b1ddcaa649acf2d865bbbc3ceda10508bcdd29d0496744644bf1c3516f6687dfeef5649c7dff90627d642739a59d91a8d1d0c4dc55d74a949e1074427664b467992c9e0f7d3af9d6ea79513e8946ddc0d356bac49878e64e6a95b0a30214214faf2ce317fa622ff3266b32a816e10a18e6d789a5da1f23e67b4f970a68a7bcd9e18825ee274b0483896a40"
|
||||
}
|
||||
]
|
||||
"fee": "7000000",
|
||||
"id": "0436430c-2b02-624c-2032-570501212b00",
|
||||
"off": "dQ2/T9Q7f0z9aNJpilIvP/bmoArZiVsz8exGSTuDe0k=",
|
||||
"sigs": [
|
||||
{
|
||||
"nonce": "AxuExVZ7EmRAmV0+1aq6BWXXHhg0YEgZ/5wX9enV3QeP",
|
||||
"part": "jwfd1en1F5z/GUhgNBge12UFuqrVPl2ZQGQSe1bFhBs1/f5VJx8q5z119Yxw0e+2mzOEx7xQfVfpnlbed+IIdA==",
|
||||
"xs": "Azu+KkGeounWgQqNZlUucJ0Xg8pQdZpE269j/HnAFkxM"
|
||||
},
|
||||
"offset": "d202964900000000d302964900000000d402964900000000d502964900000000"
|
||||
},
|
||||
"version_info": {
|
||||
"orig_version": 3,
|
||||
"version": 3,
|
||||
"block_header_version": 2
|
||||
}
|
||||
{
|
||||
"nonce": "AxuExVZ7EmRAmV0+1aq6BWXXHhg0YEgZ/5wX9enV3QeP",
|
||||
"part": "jwfd1en1F5z/GUhgNBge12UFuqrVPl2ZQGQSe1bFhBva2TTaoX235HfE7tkK/tQNEReJbfjE9YYbYwmpSYeAdA==",
|
||||
"xs": "AwFS0tcuLbp8YIatSaIZ2f8N/g/Zk9yuoi4FjCEAM86T"
|
||||
}
|
||||
],
|
||||
"sta": "S3",
|
||||
"ver": "4:2"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -977,42 +698,43 @@ pub trait OwnerRpc {
|
|||
"method": "post_tx",
|
||||
"params": {
|
||||
"token": "d202964900000000d302964900000000d402964900000000d502964900000000",
|
||||
"tx": {
|
||||
"offset": "d202964900000000d302964900000000d402964900000000d502964900000000",
|
||||
"body": {
|
||||
"inputs": [
|
||||
"slate": {
|
||||
"ver": "4:2",
|
||||
"id": "0436430c-2b02-624c-2032-570501212b00",
|
||||
"sta": "S3",
|
||||
"fee": "7000000",
|
||||
"off": "dQ2/T9Q7f0z9aNJpilIvP/bmoArZiVsz8exGSTuDe0k=",
|
||||
"sigs": [
|
||||
{
|
||||
"xs": "Azu+KkGeounWgQqNZlUucJ0Xg8pQdZpE269j/HnAFkxM",
|
||||
"nonce": "AxuExVZ7EmRAmV0+1aq6BWXXHhg0YEgZ/5wX9enV3QeP",
|
||||
"part": "jwfd1en1F5z/GUhgNBge12UFuqrVPl2ZQGQSe1bFhBs1/f5VJx8q5z119Yxw0e+2mzOEx7xQfVfpnlbed+IIdA=="
|
||||
},
|
||||
{
|
||||
"xs": "AwFS0tcuLbp8YIatSaIZ2f8N/g/Zk9yuoi4FjCEAM86T",
|
||||
"nonce": "AxuExVZ7EmRAmV0+1aq6BWXXHhg0YEgZ/5wX9enV3QeP",
|
||||
"part": "jwfd1en1F5z/GUhgNBge12UFuqrVPl2ZQGQSe1bFhBva2TTaoX235HfE7tkK/tQNEReJbfjE9YYbYwmpSYeAdA=="
|
||||
}
|
||||
],
|
||||
"coms": [
|
||||
{
|
||||
"features": "Coinbase",
|
||||
"commit": "087df32304c5d4ae8b2af0bc31e700019d722910ef87dd4eec3197b80b207e3045"
|
||||
"f": 1,
|
||||
"c": "CH3zIwTF1K6LKvC8MecAAZ1yKRDvh91O7DGXuAsgfjBF"
|
||||
},
|
||||
{
|
||||
"features": "Coinbase",
|
||||
"commit": "08e1da9e6dc4d6e808a718b2f110a991dd775d65ce5ae408a4e1f002a4961aa9e7"
|
||||
}
|
||||
],
|
||||
"outputs": [
|
||||
{
|
||||
"features": "Plain",
|
||||
"commit": "099b48cfb1f80a2347dc89818449e68e76a3c6817a532a8e9ef2b4a5ccf4363850",
|
||||
"proof": "7ebcd2ed9bf5fb29854033ba3d0e720613bdf7dfacc586d2f6084c1cde0a2b72e955d4ce625916701dc7c347132f40d0f102a34e801d745ee54b49b765d08aae0bb801c60403e57cafade3b4b174e795b633ab9e402b5b1b6e1243fd10bbcf9368a75cb6a6c375c7bdf02da9e03b7f210df45d942e6fba2729cd512a372e6ed91a1b5c9c22831febea843e3f85adcf198f39ac9f7b73b70c60bfb474aa69878ea8d1d32fef30166b59caacaec3fd024de29a90f1587e08d2c36b3d5c560cabf658e212e0a40a4129b3e5c35557058def5551f4eb395759597ba808b3c34eac3bfb9716e4480d7931c5789c538463ec75be0eb807c894047fda6cbcd22682d3c6d3823cb330f090a2099e3510a3706b57d46c95224394d7f1c0a20d99cc314b8f1d9d02668e2e435f62e1194de0be6a1f50f72ed777ed51c8819f527a94918d1aa8df6461e98ed4c2b18210de50fbcf8c3df210bfe326d41f1dc0ad748cb0320ae28401c85ab4f7dcb99d88a052e95dc85b76d22b36cabd60e06ab84bb7e4ddfdab9c9730c8a986583237ed1ecbb323ee8e79b8cadca4b438b7c09531670b471dda6a2eb3e747916c88ce7d9d8e1b7f61660eeb9e5a13c60e4dfe89d1177d81d6f6570fda85158e646a15f1e8b9e977494dc19a339aab2e0e478670d80092d6ba37646e60714ef64eb4a3d37fe15f8f38b59114af34b235489eed3f69b7781c5fe496eb43ffe245c14bd740f745844a38cf0d904347aaa2b64f51add18822dac009d8b63fa3e4c9b1fa72187f9a4acba1ab315daa1b04c9a41f3be846ac420b37990e6c947a16cc9d5c0671b292bf77d7d8b8974d2ad3afae95ba7772c37432840f53a007f31e0195f3abdf100c4477723cc6c6d5da14894a73dfac342833731036487488fdade7b9d556c06f26173b6b67598d3769447ce2828d71dd45ac5af436c6b0"
|
||||
"f": 1,
|
||||
"c": "COHanm3E1ugIpxiy8RCpkd13XWXOWuQIpOHwAqSWGqnn"
|
||||
},
|
||||
{
|
||||
"features": "Plain",
|
||||
"commit": "0812276cc788e6870612296d926cba9f0e7b9810670710b5a6e6f1ba006d395774",
|
||||
"proof": "dcff6175390c602bfa92c2ffd1a9b2d84dcc9ea941f6f317bdd0f875244ef23e696fd17c71df79760ce5ce1a96aab1d15dd057358dc835e972febeb86d50ccec0dad7cfe0246d742eb753cf7b88c045d15bc7123f8cf7155647ccf663fca92a83c9a65d0ed756ea7ebffd2cac90c380a102ed9caaa355d175ed0bf58d3ac2f5e909d6c447dfc6b605e04925c2b17c33ebd1908c965a5541ea5d2ed45a0958e6402f89d7a56df1992e036d836e74017e73ccad5cb3a82b8e139e309792a31b15f3ffd72ed033253428c156c2b9799458a25c1da65b719780a22de7fe7f437ae2fccd22cf7ea357ab5aa66a5ef7d71fb0dc64aa0b5761f68278062bb39bb296c787e4cabc5e2a2933a416ce1c9a9696160386449c437e9120f7bb26e5b0e74d1f2e7d5bcd7aafb2a92b87d1548f1f911fb06af7bd6cc13cee29f7c9cb79021aed18186272af0e9d189ec107c81a8a3aeb4782b0d950e4881aa51b776bb6844b25bce97035b48a9bdb2aea3608687bcdd479d4fa998b5a839ff88558e4a29dff0ed13b55900abb5d439b70793d902ae9ad34587b18c919f6b875c91d14deeb1c373f5e76570d59a6549758f655f1128a54f162dfe8868e1587028e26ad91e528c5ae7ee9335fa58fb59022b5de29d80f0764a9917390d46db899acc6a5b416e25ecc9dccb7153646addcc81cadb5f0078febc7e05d7735aba494f39ef05697bbcc9b47b2ccc79595d75fc13c80678b5e237edce58d731f34c05b1ddcaa649acf2d865bbbc3ceda10508bcdd29d0496744644bf1c3516f6687dfeef5649c7dff90627d642739a59d91a8d1d0c4dc55d74a949e1074427664b467992c9e0f7d3af9d6ea79513e8946ddc0d356bac49878e64e6a95b0a30214214faf2ce317fa622ff3266b32a816e10a18e6d789a5da1f23e67b4f970a68a7bcd9e18825ee274b0483896a40"
|
||||
}
|
||||
],
|
||||
"kernels": [
|
||||
"c": "CZtIz7H4CiNH3ImBhEnmjnajxoF6UyqOnvK0pcz0NjhQ",
|
||||
"p": "KXAc6uJiysd7ebhoyIOikuYebegZK4aO3NEwCwlz2ROWsVas5r1nNAKjA94Q3dil5rfxe6ZVeldKZyvQTMJzqwTtjiyoC6xIM0XA7IQ/UhgUzhMB7JrcOJVqErTZSKzOcSlaT1K83rihyfLWstpdcxJipenAJ275BN+e+NSAAUIM1Z91ovGuXHocfGufFA52E+Uu+eJJ8p+TQLfvuAaZ5GAWQyRhb5j9TN49tSSXyRnpUiL//qy35l3sp+NoqAznE8Gd59pTaXJiKO4zb1vUlFOMEsy//rG5v9X8iQbRxkJFtRbxA/qW2cVpdYN2UsHg+lgD18zxFH2Pkn422nF/eteUcdvhkvX1D4enn8P+Aw26VptjS5LSzzB5k8zlRWM68mOJfNfm6/Tcr7F20HNYvcONA+RaSd+pyMZRfNaNFn/79sO03g4t0hkJy61MRnuE5XAL5HOjmsWcZp18FVxLyrm4Am7qNDHHec0nfkki0rl0Lh9meMvoaew7W370Ey3bbN0Gzyfb6yi+crlJ+ol2EOSOOg14n9Lup1q8l7PcfgDlyLPSTkDG8kESrbcjUriaK+8FmTRTOOnnYgKjxG76Y3CVKyrKQarbrg6jJTGsr82rbdBm12nr9Qz088ClnS1fp5YAoge5QXxiP3atBejMz81AOPlEi8QPEnynwNNy5GB04zT+SfWpVuwAVvTaYB5q+A6xpsSVEFSGnmZbKW2MFPNEyi3F/dXfSjZSU2NloWFa2bQiFlx3v4/mWoNcjgxB4HABTrZu+MUlIE6ZCzo9ZjweQiIbSWiVw3ovDBvwXpEjVAnD/j2JqaedbHhgmrGKRjMRkR9x+je7c7FfzTgUPRQE/SzoEATcf/ic8RFdzAw1zhwb+ZQVhvuVl3DyYYzLcRin"
|
||||
},
|
||||
{
|
||||
"features": "Plain",
|
||||
"fee": "7000000",
|
||||
"lock_height": "0",
|
||||
"excess": "09bac6083b05a32a9d9b37710c70dd0a1ef9329fde0848558976b6f1b81d80ceed",
|
||||
"excess_sig": "66074d25a751c4743342c90ad8ead9454daa00d9b9aed29bca321036d16c4b4da0e9c180a26b88565afcd269a7ac98f896c8db3dcbd48ab69443e8eac3beb3a4"
|
||||
"c": "CBInbMeI5ocGEiltkmy6nw57mBBnBxC1pubxugBtOVd0",
|
||||
"p": "KEufkZlBHGu/cifq4VzJ+n7TBTSvPs/4Ww0BbaMpyuHx7fefAUJkNMuQr8wvCh+yluXFGpG15XofAjD9pPjFlQ55hvo3m5nWS2A5qGzH414EC6GSt4EEOVmFEmjKmHSpGIBeqVjIT3/ujTq0Ji8DL1o/hA683Sc7Kb6BARTm6GqVnY5MCAVy4+8knt1q1oUD7DvESGVIUg6id1pBrqZ6rJmUX86eendp1x+JOtfw0BCGkva2hSMSyub5hXBjBVvaWdzlIZJ8cAQLgCakG2UXyuChyUfKJEmEpcCt98ZIOwk5NGxI9hysN9UB9GocWHi2fO4NByP07q3J9dce1enzO0KUtY0+vu/qoT8gNXWZvlSc4Y5uLrHVDhI1zMQOyRhMaKYjdBpyOM5pqjodJRVrO36zj91vvlRzl5/u4zF98nnGDUiiiYJqpMdtvOJNUmiQ1Obi+D6A9nShJI/B3AN9mCAJASrhEz9eFYrmza2xjI1T5KiuVZXHWHgsZ6oMIPFG1SCFz0WjV5TOxFcCgw+JUqaXRHGPvm/g09pm40jdNHOgrO1wgPv1SUw+fhQZFvOxNbMyd/mY/Nms+8qHCYFIZumDil3NpMKUIs8VcpPm/CzMLSVCNReEO9jiHGHO1yMSwLSIFMMSAhsNMVmNI4mwsym6oRaZIqTDQXPdX1QFRb5QZqDykfGocOGq/5TBnwqFUlSIKheYS67aCOjq1T0VY+nuS8NnQnic7086sVgFTX3f4qKze1qKML6E/335p9dYqbdnUaNiIFdyGj7FuHNYLQyRzR/rvBU2YnOYVOoVyZAwI8GTxrV2i1VIQQj4l83EfROpCIsyVY5X8S9YB2aGScmaF7UZBRJzQOi0nEJ3Xxs8qwct+tM9M8weS6WWTXKO0KkF"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
},
|
||||
"fluff": false
|
||||
}
|
||||
}
|
||||
|
@ -1031,7 +753,7 @@ pub trait OwnerRpc {
|
|||
```
|
||||
*/
|
||||
|
||||
fn post_tx(&self, token: Token, tx: TransactionV4, fluff: bool) -> Result<(), ErrorKind>;
|
||||
fn post_tx(&self, token: Token, slate: VersionedSlate, fluff: bool) -> Result<(), ErrorKind>;
|
||||
|
||||
/**
|
||||
Networked version of [Owner::cancel_tx](struct.Owner.html#method.cancel_tx).
|
||||
|
@ -1083,39 +805,7 @@ pub trait OwnerRpc {
|
|||
"id": 1,
|
||||
"params": {
|
||||
"token": "d202964900000000d302964900000000d402964900000000d502964900000000",
|
||||
"tx": {
|
||||
"amount_credited": "59993000000",
|
||||
"amount_debited": "120000000000",
|
||||
"confirmation_ts": "2019-01-15T16:01:26Z",
|
||||
"confirmed": false,
|
||||
"creation_ts": "2019-01-15T16:01:26Z",
|
||||
"fee": "7000000",
|
||||
"id": 5,
|
||||
"messages": {
|
||||
"messages": [
|
||||
{
|
||||
"id": "0",
|
||||
"message": null,
|
||||
"message_sig": null,
|
||||
"public_key": "033ac2158fa0077f087de60c19d8e431753baa5b63b6e1477f05a2a6e7190d4592"
|
||||
},
|
||||
{
|
||||
"id": "1",
|
||||
"message": null,
|
||||
"message_sig": null,
|
||||
"public_key": "024f9bc78c984c78d6e916d3a00746aa30fa1172124c8dbc0cbddcb7b486719bc7"
|
||||
}
|
||||
]
|
||||
},
|
||||
"num_inputs": 2,
|
||||
"num_outputs": 1,
|
||||
"parent_key_id": "0200000000000000000000000000000000",
|
||||
"stored_tx": "0436430c-2b02-624c-2032-570501212b00.grintx",
|
||||
"tx_slate_id": "0436430c-2b02-624c-2032-570501212b00",
|
||||
"tx_type": "TxSent",
|
||||
"kernel_excess": null,
|
||||
"kernel_lookup_min_height": null
|
||||
}
|
||||
"id": "0436430c-2b02-624c-2032-570501212b00"
|
||||
}
|
||||
}
|
||||
# "#
|
||||
|
@ -1127,23 +817,16 @@ pub trait OwnerRpc {
|
|||
"result": {
|
||||
"Ok": {
|
||||
"body": {
|
||||
"inputs": [
|
||||
{
|
||||
"commit": "087df32304c5d4ae8b2af0bc31e700019d722910ef87dd4eec3197b80b207e3045",
|
||||
"features": "Coinbase"
|
||||
},
|
||||
{
|
||||
"commit": "08e1da9e6dc4d6e808a718b2f110a991dd775d65ce5ae408a4e1f002a4961aa9e7",
|
||||
"features": "Coinbase"
|
||||
}
|
||||
],
|
||||
"inputs": [],
|
||||
"kernels": [
|
||||
{
|
||||
"excess": "000000000000000000000000000000000000000000000000000000000000000000",
|
||||
"excess_sig": "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
|
||||
"features": "Plain",
|
||||
"fee": "7000000",
|
||||
"lock_height": "0"
|
||||
"features": {
|
||||
"Plain": {
|
||||
"fee": 7000000
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"outputs": [
|
||||
|
@ -1151,15 +834,10 @@ pub trait OwnerRpc {
|
|||
"commit": "099b48cfb1f80a2347dc89818449e68e76a3c6817a532a8e9ef2b4a5ccf4363850",
|
||||
"features": "Plain",
|
||||
"proof": "29701ceae262cac77b79b868c883a292e61e6de8192b868edcd1300b0973d91396b156ace6bd673402a303de10ddd8a5e6b7f17ba6557a574a672bd04cc273ab04ed8e2ca80bac483345c0ec843f521814ce1301ec9adc38956a12b4d948acce71295a4f52bcdeb8a1c9f2d6b2da5d731262a5e9c0276ef904df9ef8d48001420cd59f75a2f1ae5c7a1c7c6b9f140e7613e52ef9e249f29f9340b7efb80699e460164324616f98fd4cde3db52497c919e95222fffeacb7e65deca7e368a80ce713c19de7da5369726228ee336f5bd494538c12ccbffeb1b9bfd5fc8906d1c64245b516f103fa96d9c56975837652c1e0fa5803d7ccf1147d8f927e36da717f7ad79471dbe192f5f50f87a79fc3fe030dba569b634b92d2cf307993cce545633af263897cd7e6ebf4dcafb176d07358bdc38d03e45a49dfa9c8c6517cd68d167ffbf6c3b4de0e2dd21909cbad4c467b84e5700be473a39ac59c669d7c155c4bcab9b8026eea3431c779cd277e4922d2b9742e1f6678cbe869ec3b5b7ef4132ddb6cdd06cf27dbeb28be72b949fa897610e48e3a0d789fd2eea75abc97b3dc7e00e5c8b3d24e40c6f24112adb72352b89a2bef0599345338e9e76202a3c46efa6370952b2aca41aadbae0ea32531acafcdab6dd066d769ebf50cf4f3c0a59d2d5fa79600a207b9417c623f76ad05e8cccfcd4038f9448bc40f127ca7c0d372e46074e334fe49f5a956ec0056f4da601e6af80eb1a6c4951054869e665b296d8c14f344ca2dc5fdd5df4a3652536365a1615ad9b422165c77bf8fe65a835c8e0c41e070014eb66ef8c525204e990b3a3d663c1e42221b496895c37a2f0c1bf05e91235409c3fe3d89a9a79d6c78609ab18a463311911f71fa37bb73b15fcd38143d1404fd2ce81004dc7ff89cf1115dcc0c35ce1c1bf9941586fb959770f2618ccb7118a7"
|
||||
},
|
||||
{
|
||||
"commit": "0812276cc788e6870612296d926cba9f0e7b9810670710b5a6e6f1ba006d395774",
|
||||
"features": "Plain",
|
||||
"proof": "284b9f9199411c6bbf7227eae15cc9fa7ed30534af3ecff85b0d016da329cae1f1edf79f01426434cb90afcc2f0a1fb296e5c51a91b5e57a1f0230fda4f8c5950e7986fa379b99d64b6039a86cc7e35e040ba192b781043959851268ca9874a918805ea958c84f7fee8d3ab4262f032f5a3f840ebcdd273b29be810114e6e86a959d8e4c080572e3ef249edd6ad68503ec3bc4486548520ea2775a41aea67aac99945fce9e7a7769d71f893ad7f0d0108692f6b6852312cae6f9857063055bda59dce521927c70040b8026a41b6517cae0a1c947ca244984a5c0adf7c6483b0939346c48f61cac37d501f46a1c5878b67cee0d0723f4eeadc9f5d71ed5e9f33b4294b58d3ebeefeaa13f20357599be549ce18e6e2eb1d50e1235ccc40ec9184c68a623741a7238ce69aa3a1d25156b3b7eb38fdd6fbe5473979feee3317df279c60d48a289826aa4c76dbce24d526890d4e6e2f83e80f674a1248fc1dc037d982009012ae1133f5e158ae6cdadb18c8d53e4a8ae5595c758782c67aa0c20f146d52085cf45a35794cec45702830f8952a69744718fbe6fe0d3da66e348dd3473a0aced7080fbf5494c3e7e141916f3b135b33277f998fcd9acfbca8709814866e9838a5dcda4c29422cf157293e6fc2ccc2d25423517843bd8e21c61ced72312c0b48814c312021b0d31598d2389b0b329baa1169922a4c34173dd5f540545be5066a0f291f1a870e1aaff94c19f0a855254882a17984baeda08e8ead53d1563e9ee4bc36742789cef4f3ab158054d7ddfe2a2b37b5a8a30be84ff7df9a7d758a9b76751a3622057721a3ec5b873582d0c91cd1febbc153662739854ea15c9903023c193c6b5768b55484108f897cdc47d13a9088b32558e57f12f5807668649c99a17b51905127340e8b49c42775f1b3cab072dfad33d33cc1e4ba5964d728ed0a905"
|
||||
}
|
||||
]
|
||||
},
|
||||
"offset": "d202964900000000d302964900000000d402964900000000d502964900000000"
|
||||
"offset": "0000000000000000000000000000000000000000000000000000000000000000"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1167,93 +845,7 @@ pub trait OwnerRpc {
|
|||
# , 5, true, true, false, false);
|
||||
```
|
||||
*/
|
||||
fn get_stored_tx(
|
||||
&self,
|
||||
token: Token,
|
||||
tx: &TxLogEntry,
|
||||
) -> Result<Option<TransactionV4>, ErrorKind>;
|
||||
|
||||
/**
|
||||
Networked version of [Owner::verify_slate_messages](struct.Owner.html#method.verify_slate_messages).
|
||||
|
||||
```
|
||||
# grin_wallet_api::doctest_helper_json_rpc_owner_assert_response!(
|
||||
# r#"
|
||||
{
|
||||
"jsonrpc": "2.0",
|
||||
"method": "verify_slate_messages",
|
||||
"id": 1,
|
||||
"params": {
|
||||
"token": "d202964900000000d302964900000000d402964900000000d502964900000000",
|
||||
"slate": {
|
||||
"amount": "6000000000",
|
||||
"fee": "8000000",
|
||||
"height": "4",
|
||||
"id": "0436430c-2b02-624c-2032-570501212b00",
|
||||
"lock_height": "4",
|
||||
"ttl_cutoff_height": null,
|
||||
"num_participants": 2,
|
||||
"participant_data": [
|
||||
{
|
||||
"id": "0",
|
||||
"message": "my message",
|
||||
"message_sig": "8f07ddd5e9f5179cff19486034181ed76505baaad53e5d994064127b56c5841b1d4c1358be398f801eb90d933774b5218fa7e769b11c4c640402253353656f75",
|
||||
"part_sig": null,
|
||||
"public_blind_excess": "034b4df2f0558b73ea72a1ca5c4ab20217c66bbe0829056fca7abe76888e9349ee",
|
||||
"public_nonce": "031b84c5567b126440995d3ed5aaba0565d71e1834604819ff9c17f5e9d5dd078f"
|
||||
}
|
||||
],
|
||||
"tx": {
|
||||
"body": {
|
||||
"inputs": [
|
||||
{
|
||||
"commit": "08e1da9e6dc4d6e808a718b2f110a991dd775d65ce5ae408a4e1f002a4961aa9e7",
|
||||
"features": "Coinbase"
|
||||
}
|
||||
],
|
||||
"kernels": [
|
||||
{
|
||||
"excess": "000000000000000000000000000000000000000000000000000000000000000000",
|
||||
"excess_sig": "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
|
||||
"features": "HeightLocked",
|
||||
"fee": "8000000",
|
||||
"lock_height": "4"
|
||||
}
|
||||
],
|
||||
"outputs": [
|
||||
{
|
||||
"commit": "094be57c91787fc2033d5d97fae099f1a6ddb37ea48370f1a138f09524c767fdd3",
|
||||
"features": "Plain",
|
||||
"proof": "2a42e9e902b70ce44e1fccb14de87ee0a97100bddf12c6bead1b9c5f4eb60300f29c13094fa12ffeee238fb4532b18f6b61cf51b23c1c7e1ad2e41560dc27edc0a2b9e647a0b3e4e806fced5b65e61d0f1f5197d3e2285c632d359e27b6b9206b2caffea4f67e0c7a2812e7a22c134b98cf89bd43d9f28b8bec25cce037a0ac5b1ae8f667e54e1250813a5263004486b4465ad4e641ab2b535736ea26535a11013564f08f483b7dab1c2bcc3ee38eadf2f7850eff7e3459a4bbabf9f0cf6c50d0c0a4120565cd4a2ce3e354c11721cd695760a24c70e0d5a0dfc3c5dcd51dfad6de2c237a682f36dc0b271f21bb3655e5333016aaa42c2efa1446e5f3c0a79ec417c4d30f77556951cb0f05dbfafb82d9f95951a9ea241fda2a6388f73ace036b98acce079f0e4feebccc96290a86dcc89118a901210b245f2d114cf94396e4dbb461e82aa26a0581389707957968c7cdc466213bb1cd417db207ef40c05842ab67a01a9b96eb1430ebc26e795bb491258d326d5174ad549401059e41782121e506744af8af9d8e493644a87d613600888541cbbe538c625883f3eb4aa3102c5cfcc25de8e97af8927619ce6a731b3b8462d51d993066b935b0648d2344ad72e4fd70f347fbd81041042e5ea31cc7b2e3156a920b80ecba487b950ca32ca95fae85b759c936246ecf441a9fdd95e8fee932d6782cdec686064018c857efc47fb4b2a122600d5fdd79af2486f44df7e629184e1c573bc0a9b3feb40b190ef2861a1ab45e2ac2201b9cd42e495deea247269820ed32389a2810ad6c0f9a296d2a2d9c54089fed50b7f5ecfcd33ab9954360e1d7f5598c32128cfcf2a1d8bf14616818da8a5343bfa88f0eedf392e9d4ab1ace1b60324129cd4852c2e27813a9cf71a6ae6229a4fcecc1a756b3e664c5f50af333082616815a3bec8fc0b75b8e4e767d719"
|
||||
}
|
||||
]
|
||||
},
|
||||
"offset": "d202964900000000d302964900000000d402964900000000d502964900000000",
|
||||
"payment_proof": null
|
||||
},
|
||||
"version_info": {
|
||||
"orig_version": 3,
|
||||
"version": 3,
|
||||
"block_header_version": 2
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
# "#
|
||||
# ,
|
||||
# r#"
|
||||
{
|
||||
"jsonrpc": "2.0",
|
||||
"id": 1,
|
||||
"result": {
|
||||
"Ok": null
|
||||
}
|
||||
}
|
||||
# "#
|
||||
# , 0 ,false, false, false, false);
|
||||
```
|
||||
*/
|
||||
fn verify_slate_messages(&self, token: Token, slate: VersionedSlate) -> Result<(), ErrorKind>;
|
||||
fn get_stored_tx(&self, token: Token, id: Uuid) -> Result<Option<Transaction>, ErrorKind>;
|
||||
|
||||
/**
|
||||
Networked version of [Owner::scan](struct.Owner.html#method.scan).
|
||||
|
@ -1886,11 +1478,11 @@ pub trait OwnerRpc {
|
|||
"result": {
|
||||
"Ok": {
|
||||
"amount": "60000000000",
|
||||
"excess": "09bac6083b05a32a9d9b37710c70dd0a1ef9329fde0848558976b6f1b81d80ceed",
|
||||
"excess": "091f151170bfac881479bfb56c7012c52cd4ce4198ad661586374dd499925922fb",
|
||||
"recipient_address": "pa7wkkdgs5bkteha7lykl7ff2wztgdrxxo442xdcq2lnaphe5aidd4id",
|
||||
"recipient_sig": "42b6f2bbcee432185993867d1a338e260454ead536bf728f4dcc8f535508715e92a0695486ba3c9945d8ecb2cf7f703a955780253b12d0048f02d318c8f08702",
|
||||
"recipient_sig": "b9b1885a3f33297df32e1aa4db23220bd305da8ed92ff6873faf3ab2c116fea25e9d0e34bd4f567f022b88a37400821ffbcaec71c9a8c3a327c4626611886d0d",
|
||||
"sender_address": "glg5mojiqvhywjriwhooiytn3tptlvlmw7h567lezssyek3y2tjzznad",
|
||||
"sender_sig": "5e3f5596852e83f6db7152fc51c41b4ed8742eb8045fa85a6965c52d09fcb46ba67d4f86660c9f3dc55ab84faea79d11c3831aa77934f7e90695e63d523f8604"
|
||||
"sender_sig": "611b92331e395c3d29871ac35b1fce78ec595e28ccbe8cc55452da40775e8e46d35a2e84eaffd986935da3275e34d46a8d777d02dabcf4339704c2a621da9700"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1919,11 +1511,11 @@ pub trait OwnerRpc {
|
|||
"token": "d202964900000000d302964900000000d402964900000000d502964900000000",
|
||||
"proof": {
|
||||
"amount": "60000000000",
|
||||
"excess": "09bac6083b05a32a9d9b37710c70dd0a1ef9329fde0848558976b6f1b81d80ceed",
|
||||
"excess": "091f151170bfac881479bfb56c7012c52cd4ce4198ad661586374dd499925922fb",
|
||||
"recipient_address": "pa7wkkdgs5bkteha7lykl7ff2wztgdrxxo442xdcq2lnaphe5aidd4id",
|
||||
"recipient_sig": "42b6f2bbcee432185993867d1a338e260454ead536bf728f4dcc8f535508715e92a0695486ba3c9945d8ecb2cf7f703a955780253b12d0048f02d318c8f08702",
|
||||
"recipient_sig": "b9b1885a3f33297df32e1aa4db23220bd305da8ed92ff6873faf3ab2c116fea25e9d0e34bd4f567f022b88a37400821ffbcaec71c9a8c3a327c4626611886d0d",
|
||||
"sender_address": "glg5mojiqvhywjriwhooiytn3tptlvlmw7h567lezssyek3y2tjzznad",
|
||||
"sender_sig": "5e3f5596852e83f6db7152fc51c41b4ed8742eb8045fa85a6965c52d09fcb46ba67d4f86660c9f3dc55ab84faea79d11c3831aa77934f7e90695e63d523f8604"
|
||||
"sender_sig": "611b92331e395c3d29871ac35b1fce78ec595e28ccbe8cc55452da40775e8e46d35a2e84eaffd986935da3275e34d46a8d777d02dabcf4339704c2a621da9700"
|
||||
}
|
||||
},
|
||||
"id": 1
|
||||
|
@ -2105,17 +1697,11 @@ where
|
|||
Ok(VersionedSlate::into_version(out_slate, version).map_err(|e| e.kind())?)
|
||||
}
|
||||
|
||||
fn tx_lock_outputs(
|
||||
&self,
|
||||
token: Token,
|
||||
in_slate: VersionedSlate,
|
||||
participant_id: usize,
|
||||
) -> Result<(), ErrorKind> {
|
||||
fn tx_lock_outputs(&self, token: Token, in_slate: VersionedSlate) -> Result<(), ErrorKind> {
|
||||
Owner::tx_lock_outputs(
|
||||
self,
|
||||
(&token.keychain_mask).as_ref(),
|
||||
&Slate::from(in_slate),
|
||||
participant_id,
|
||||
)
|
||||
.map_err(|e| e.kind())
|
||||
}
|
||||
|
@ -2130,31 +1716,20 @@ where
|
|||
.map_err(|e| e.kind())
|
||||
}
|
||||
|
||||
fn get_stored_tx(
|
||||
&self,
|
||||
token: Token,
|
||||
tx: &TxLogEntry,
|
||||
) -> Result<Option<TransactionV4>, ErrorKind> {
|
||||
Owner::get_stored_tx(self, (&token.keychain_mask).as_ref(), tx)
|
||||
.map(|x| x.map(TransactionV4::from))
|
||||
.map_err(|e| e.kind())
|
||||
fn get_stored_tx(&self, token: Token, uuid: Uuid) -> Result<Option<Transaction>, ErrorKind> {
|
||||
Owner::get_stored_tx(self, (&token.keychain_mask).as_ref(), uuid).map_err(|e| e.kind())
|
||||
}
|
||||
|
||||
fn post_tx(&self, token: Token, tx: TransactionV4, fluff: bool) -> Result<(), ErrorKind> {
|
||||
fn post_tx(&self, token: Token, slate: VersionedSlate, fluff: bool) -> Result<(), ErrorKind> {
|
||||
Owner::post_tx(
|
||||
self,
|
||||
(&token.keychain_mask).as_ref(),
|
||||
&Transaction::from(tx),
|
||||
&Slate::from(slate),
|
||||
fluff,
|
||||
)
|
||||
.map_err(|e| e.kind())
|
||||
}
|
||||
|
||||
fn verify_slate_messages(&self, token: Token, slate: VersionedSlate) -> Result<(), ErrorKind> {
|
||||
Owner::verify_slate_messages(self, (&token.keychain_mask).as_ref(), &Slate::from(slate))
|
||||
.map_err(|e| e.kind())
|
||||
}
|
||||
|
||||
fn scan(
|
||||
&self,
|
||||
token: Token,
|
||||
|
@ -2408,7 +1983,7 @@ pub fn run_doctest_owner(
|
|||
mask1.clone(),
|
||||
);
|
||||
|
||||
let mut slate_outer = Slate::blank(2);
|
||||
let mut slate_outer = Slate::blank(2, false);
|
||||
|
||||
let rec_phrase_2 = util::ZeroingString::from(
|
||||
"hour kingdom ripple lunch razor inquiry coyote clay stamp mean \
|
||||
|
@ -2501,25 +2076,19 @@ pub fn run_doctest_owner(
|
|||
{
|
||||
let mut w_lock = wallet2.lock();
|
||||
let w2 = w_lock.lc_provider().unwrap().wallet_inst().unwrap();
|
||||
slate = api_impl::foreign::receive_tx(
|
||||
&mut **w2,
|
||||
(&mask2).as_ref(),
|
||||
&slate,
|
||||
None,
|
||||
None,
|
||||
true,
|
||||
)
|
||||
.unwrap();
|
||||
slate = api_impl::foreign::receive_tx(&mut **w2, (&mask2).as_ref(), &slate, None, true)
|
||||
.unwrap();
|
||||
w2.close().unwrap();
|
||||
}
|
||||
// Spit out slate for input to finalize_tx
|
||||
if lock_tx {
|
||||
api_impl::owner::tx_lock_outputs(&mut **w, (&mask2).as_ref(), &slate, 0).unwrap();
|
||||
println!("LOCKING TX");
|
||||
api_impl::owner::tx_lock_outputs(&mut **w, (&mask1).as_ref(), &slate).unwrap();
|
||||
}
|
||||
println!("RECEIPIENT SLATE");
|
||||
println!("{}", serde_json::to_string_pretty(&slate).unwrap());
|
||||
if finalize_tx {
|
||||
slate = api_impl::owner::finalize_tx(&mut **w, (&mask2).as_ref(), &slate).unwrap();
|
||||
slate = api_impl::owner::finalize_tx(&mut **w, (&mask1).as_ref(), &slate).unwrap();
|
||||
error!("FINALIZED TX SLATE");
|
||||
println!("{}", serde_json::to_string_pretty(&slate).unwrap());
|
||||
}
|
||||
|
|
|
@ -20,10 +20,11 @@ use crate::config::{TorConfig, WalletConfig, WALLET_CONFIG_FILE_NAME};
|
|||
use crate::core::{core, global};
|
||||
use crate::error::{Error, ErrorKind};
|
||||
use crate::impls::{create_sender, KeybaseAllChannels, SlateGetter as _, SlateReceiver as _};
|
||||
use crate::impls::{PathToSlate, SlatePutter};
|
||||
use crate::impls::{HttpSlateSender, PathToSlate, SlatePutter};
|
||||
use crate::keychain;
|
||||
use crate::libwallet::{
|
||||
self, InitTxArgs, IssueInvoiceTxArgs, NodeClient, PaymentProof, WalletLCProvider,
|
||||
self, InitTxArgs, IssueInvoiceTxArgs, NodeClient, PaymentProof, Slate, SlateVersion,
|
||||
WalletLCProvider,
|
||||
};
|
||||
use crate::util::secp::key::SecretKey;
|
||||
use crate::util::{Mutex, ZeroingString};
|
||||
|
@ -266,7 +267,6 @@ where
|
|||
/// Arguments for the send command
|
||||
pub struct SendArgs {
|
||||
pub amount: u64,
|
||||
pub message: Option<String>,
|
||||
pub minimum_confirmations: u64,
|
||||
pub selection_strategy: String,
|
||||
pub estimate_selection_strategies: bool,
|
||||
|
@ -278,6 +278,8 @@ pub struct SendArgs {
|
|||
pub target_slate_version: Option<u16>,
|
||||
pub payment_proof_address: Option<OnionV3Address>,
|
||||
pub ttl_blocks: Option<u64>,
|
||||
//TODO: Remove HF3
|
||||
pub output_v4_slate: bool,
|
||||
}
|
||||
|
||||
pub fn send<L, C, K>(
|
||||
|
@ -293,6 +295,60 @@ where
|
|||
K: keychain::Keychain + 'static,
|
||||
{
|
||||
let wallet_inst = owner_api.wallet_inst.clone();
|
||||
// Check other version, and if it only supports 3 set the target slate
|
||||
// version to 3 to avoid removing the transaction object
|
||||
// TODO: This block is temporary, for the period between the release of v4.0.0 and HF3,
|
||||
// after which this should be removable
|
||||
let mut args = args;
|
||||
{
|
||||
let invalid = || {
|
||||
ErrorKind::GenericError(format!(
|
||||
"Invalid wallet comm type and destination. method: {}, dest: {}",
|
||||
args.method, args.dest
|
||||
))
|
||||
};
|
||||
let trailing = match args.dest.ends_with('/') {
|
||||
true => "",
|
||||
false => "/",
|
||||
};
|
||||
let url_str = format!("{}{}v2/foreign", args.dest, trailing);
|
||||
match args.method.as_ref() {
|
||||
"http" => {
|
||||
let v_sender = HttpSlateSender::new(&args.dest).map_err(|_| invalid())?;
|
||||
let other_version = v_sender.check_other_version(&url_str)?;
|
||||
if other_version == SlateVersion::V3 {
|
||||
args.target_slate_version = Some(3);
|
||||
}
|
||||
}
|
||||
"tor" => {
|
||||
let v_sender = HttpSlateSender::with_socks_proxy(
|
||||
&args.dest,
|
||||
&tor_config.as_ref().unwrap().socks_proxy_addr,
|
||||
&tor_config.as_ref().unwrap().send_config_dir,
|
||||
)
|
||||
.map_err(|_| invalid())?;
|
||||
let other_version = v_sender.check_other_version(&url_str)?;
|
||||
if other_version == SlateVersion::V3 {
|
||||
args.target_slate_version = Some(3);
|
||||
}
|
||||
}
|
||||
"file" => {
|
||||
// For files spit out a V3 Slate if we're before HF3,
|
||||
// Or V4 slate otherwise
|
||||
let cur_height = {
|
||||
libwallet::wallet_lock!(wallet_inst, w);
|
||||
w.w2n_client().get_chain_tip()?.0
|
||||
};
|
||||
// TODO: Floonet HF4
|
||||
if cur_height < 786240 && !args.output_v4_slate {
|
||||
args.target_slate_version = Some(3);
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
// end block to delete post HF3
|
||||
|
||||
controller::owner_single_use(None, keychain_mask, Some(owner_api), |api, m| {
|
||||
if args.estimate_selection_strategies {
|
||||
let strategies = vec!["smallest", "all"]
|
||||
|
@ -321,7 +377,6 @@ where
|
|||
max_outputs: args.max_outputs as u32,
|
||||
num_change_outputs: args.change_outputs as u32,
|
||||
selection_strategy_is_use_all: args.selection_strategy == "all",
|
||||
message: args.message.clone(),
|
||||
target_slate_version: args.target_slate_version,
|
||||
payment_proof_recipient_address: args.payment_proof_address.clone(),
|
||||
ttl_blocks: args.ttl_blocks,
|
||||
|
@ -347,34 +402,35 @@ where
|
|||
|
||||
match args.method.as_str() {
|
||||
"file" => {
|
||||
PathToSlate((&args.dest).into()).put_tx(&slate)?;
|
||||
api.tx_lock_outputs(m, &slate, 0)?;
|
||||
PathToSlate((&args.dest).into()).put_tx(&slate, false)?;
|
||||
api.tx_lock_outputs(m, &slate)?;
|
||||
return Ok(());
|
||||
}
|
||||
"binfile" => {
|
||||
PathToSlate((&args.dest).into()).put_tx(&slate, true)?;
|
||||
api.tx_lock_outputs(m, &slate)?;
|
||||
return Ok(());
|
||||
}
|
||||
"self" => {
|
||||
api.tx_lock_outputs(m, &slate, 0)?;
|
||||
api.tx_lock_outputs(m, &slate)?;
|
||||
let km = match keychain_mask.as_ref() {
|
||||
None => None,
|
||||
Some(&m) => Some(m.to_owned()),
|
||||
};
|
||||
controller::foreign_single_use(wallet_inst, km, |api| {
|
||||
slate = api.receive_tx(&slate, Some(&args.dest), None)?;
|
||||
slate = api.receive_tx(&slate, Some(&args.dest))?;
|
||||
Ok(())
|
||||
})?;
|
||||
}
|
||||
method => {
|
||||
let sender = create_sender(method, &args.dest, tor_config)?;
|
||||
slate = sender.send_tx(&slate)?;
|
||||
api.tx_lock_outputs(m, &slate, 0)?;
|
||||
api.tx_lock_outputs(m, &slate)?;
|
||||
}
|
||||
}
|
||||
|
||||
api.verify_slate_messages(m, &slate).map_err(|e| {
|
||||
error!("Error validating participant messages: {}", e);
|
||||
e
|
||||
})?;
|
||||
slate = api.finalize_tx(m, &slate)?;
|
||||
let result = api.post_tx(m, slate.tx_or_err()?, args.fluff);
|
||||
let result = api.post_tx(m, &slate, args.fluff);
|
||||
match result {
|
||||
Ok(_) => {
|
||||
info!("Tx sent ok",);
|
||||
|
@ -394,7 +450,6 @@ where
|
|||
/// Receive command argument
|
||||
pub struct ReceiveArgs {
|
||||
pub input: String,
|
||||
pub message: Option<String>,
|
||||
}
|
||||
|
||||
pub fn receive<L, C, K>(
|
||||
|
@ -408,20 +463,16 @@ where
|
|||
C: NodeClient + 'static,
|
||||
K: keychain::Keychain + 'static,
|
||||
{
|
||||
let mut slate = PathToSlate((&args.input).into()).get_tx()?;
|
||||
let (mut slate, was_bin) = PathToSlate((&args.input).into()).get_tx()?;
|
||||
let km = match keychain_mask.as_ref() {
|
||||
None => None,
|
||||
Some(&m) => Some(m.to_owned()),
|
||||
};
|
||||
controller::foreign_single_use(owner_api.wallet_inst.clone(), km, |api| {
|
||||
if let Err(e) = api.verify_slate_messages(&slate) {
|
||||
error!("Error validating participant messages: {}", e);
|
||||
return Err(e);
|
||||
}
|
||||
slate = api.receive_tx(&slate, Some(&g_args.account), args.message.clone())?;
|
||||
slate = api.receive_tx(&slate, Some(&g_args.account))?;
|
||||
Ok(())
|
||||
})?;
|
||||
PathToSlate(format!("{}.response", args.input).into()).put_tx(&slate)?;
|
||||
PathToSlate(format!("{}.response", args.input).into()).put_tx(&slate, was_bin)?;
|
||||
info!(
|
||||
"Response file {}.response generated, and can be sent back to the transaction originator.",
|
||||
args.input
|
||||
|
@ -447,25 +498,18 @@ where
|
|||
C: NodeClient + 'static,
|
||||
K: keychain::Keychain + 'static,
|
||||
{
|
||||
let mut slate = PathToSlate((&args.input).into()).get_tx()?;
|
||||
let (mut slate, was_bin) = PathToSlate((&args.input).into()).get_tx()?;
|
||||
|
||||
// Rather than duplicating the entire command, we'll just
|
||||
// try to determine what kind of finalization this is
|
||||
// based on the slate contents
|
||||
// for now, we can tell this is an invoice transaction
|
||||
// if the receipient (participant 1) hasn't completed sigs
|
||||
let part_data = slate.participant_with_id(1);
|
||||
let is_invoice = {
|
||||
match part_data {
|
||||
None => {
|
||||
error!("Expected slate participant data missing");
|
||||
return Err(ErrorKind::ArgumentError(
|
||||
"Expected Slate participant data missing".into(),
|
||||
))?;
|
||||
}
|
||||
Some(p) => !p.is_complete(),
|
||||
}
|
||||
};
|
||||
let mut is_invoice = false;
|
||||
controller::owner_single_use(None, keychain_mask, Some(owner_api), |api, m| {
|
||||
is_invoice = api.context_is_invoice(m, &slate)?;
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
if is_invoice {
|
||||
let km = match keychain_mask.as_ref() {
|
||||
|
@ -473,19 +517,11 @@ where
|
|||
Some(&m) => Some(m.to_owned()),
|
||||
};
|
||||
controller::foreign_single_use(owner_api.wallet_inst.clone(), km, |api| {
|
||||
if let Err(e) = api.verify_slate_messages(&slate) {
|
||||
error!("Error validating participant messages: {}", e);
|
||||
return Err(e);
|
||||
}
|
||||
slate = api.finalize_invoice_tx(&slate)?;
|
||||
Ok(())
|
||||
})?;
|
||||
} else {
|
||||
controller::owner_single_use(None, keychain_mask, Some(owner_api), |api, m| {
|
||||
if let Err(e) = api.verify_slate_messages(m, &slate) {
|
||||
error!("Error validating participant messages: {}", e);
|
||||
return Err(e);
|
||||
}
|
||||
slate = api.finalize_tx(m, &slate)?;
|
||||
Ok(())
|
||||
})?;
|
||||
|
@ -493,7 +529,7 @@ where
|
|||
|
||||
if !args.nopost {
|
||||
controller::owner_single_use(None, keychain_mask, Some(owner_api), |api, m| {
|
||||
let result = api.post_tx(m, slate.tx_or_err()?, args.fluff);
|
||||
let result = api.post_tx(m, &slate, args.fluff);
|
||||
match result {
|
||||
Ok(_) => {
|
||||
info!(
|
||||
|
@ -510,7 +546,7 @@ where
|
|||
}
|
||||
|
||||
if args.dest.is_some() {
|
||||
PathToSlate((&args.dest.unwrap()).into()).put_tx(&slate)?;
|
||||
PathToSlate((&args.dest.unwrap()).into()).put_tx(&slate, was_bin)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
@ -522,6 +558,11 @@ pub struct IssueInvoiceArgs {
|
|||
pub dest: String,
|
||||
/// issue invoice tx args
|
||||
pub issue_args: IssueInvoiceTxArgs,
|
||||
/// whether to output as bin
|
||||
pub bin: bool,
|
||||
// TODO: Remove HF3
|
||||
/// whether to output a V4 slate
|
||||
pub output_v4_slate: bool,
|
||||
}
|
||||
|
||||
pub fn issue_invoice_tx<L, C, K>(
|
||||
|
@ -534,9 +575,23 @@ where
|
|||
C: NodeClient + 'static,
|
||||
K: keychain::Keychain + 'static,
|
||||
{
|
||||
//TODO: Remove block HF3
|
||||
let args = {
|
||||
let mut a = args;
|
||||
let wallet_inst = owner_api.wallet_inst.clone();
|
||||
let cur_height = {
|
||||
libwallet::wallet_lock!(wallet_inst, w);
|
||||
w.w2n_client().get_chain_tip()?.0
|
||||
};
|
||||
// TODO: Floonet HF4
|
||||
if cur_height < 786240 && !a.output_v4_slate && !a.bin {
|
||||
a.issue_args.target_slate_version = Some(3);
|
||||
}
|
||||
a
|
||||
};
|
||||
controller::owner_single_use(None, keychain_mask, Some(owner_api), |api, m| {
|
||||
let slate = api.issue_invoice_tx(m, args.issue_args)?;
|
||||
PathToSlate((&args.dest).into()).put_tx(&slate)?;
|
||||
PathToSlate((&args.dest).into()).put_tx(&slate, args.bin)?;
|
||||
Ok(())
|
||||
})?;
|
||||
Ok(())
|
||||
|
@ -544,7 +599,6 @@ where
|
|||
|
||||
/// Arguments for the process_invoice command
|
||||
pub struct ProcessInvoiceArgs {
|
||||
pub message: Option<String>,
|
||||
pub minimum_confirmations: u64,
|
||||
pub selection_strategy: String,
|
||||
pub method: String,
|
||||
|
@ -568,7 +622,7 @@ where
|
|||
C: NodeClient + 'static,
|
||||
K: keychain::Keychain + 'static,
|
||||
{
|
||||
let slate = PathToSlate((&args.input).into()).get_tx()?;
|
||||
let (slate, _) = PathToSlate((&args.input).into()).get_tx()?;
|
||||
let wallet_inst = owner_api.wallet_inst.clone();
|
||||
controller::owner_single_use(None, keychain_mask, Some(owner_api), |api, m| {
|
||||
if args.estimate_selection_strategies {
|
||||
|
@ -598,15 +652,10 @@ where
|
|||
max_outputs: args.max_outputs as u32,
|
||||
num_change_outputs: 1u32,
|
||||
selection_strategy_is_use_all: args.selection_strategy == "all",
|
||||
message: args.message.clone(),
|
||||
ttl_blocks: args.ttl_blocks,
|
||||
send_args: None,
|
||||
..Default::default()
|
||||
};
|
||||
if let Err(e) = api.verify_slate_messages(m, &slate) {
|
||||
error!("Error validating participant messages: {}", e);
|
||||
return Err(e);
|
||||
}
|
||||
let result = api.process_invoice_tx(m, &slate, init_args);
|
||||
let mut slate = match result {
|
||||
Ok(s) => {
|
||||
|
@ -627,11 +676,16 @@ where
|
|||
match args.method.as_str() {
|
||||
"file" => {
|
||||
let slate_putter = PathToSlate((&args.dest).into());
|
||||
slate_putter.put_tx(&slate)?;
|
||||
api.tx_lock_outputs(m, &slate, 0)?;
|
||||
slate_putter.put_tx(&slate, false)?;
|
||||
api.tx_lock_outputs(m, &slate)?;
|
||||
}
|
||||
"filebin" => {
|
||||
let slate_putter = PathToSlate((&args.dest).into());
|
||||
slate_putter.put_tx(&slate, true)?;
|
||||
api.tx_lock_outputs(m, &slate)?;
|
||||
}
|
||||
"self" => {
|
||||
api.tx_lock_outputs(m, &slate, 0)?;
|
||||
api.tx_lock_outputs(m, &slate)?;
|
||||
let km = match keychain_mask.as_ref() {
|
||||
None => None,
|
||||
Some(&m) => Some(m.to_owned()),
|
||||
|
@ -644,7 +698,7 @@ where
|
|||
method => {
|
||||
let sender = create_sender(method, &args.dest, tor_config)?;
|
||||
slate = sender.send_tx(&slate)?;
|
||||
api.tx_lock_outputs(m, &slate, 0)?;
|
||||
api.tx_lock_outputs(m, &slate)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -769,7 +823,6 @@ where
|
|||
)?;
|
||||
// should only be one here, but just in case
|
||||
for tx in txs {
|
||||
display::tx_messages(&tx, dark_scheme)?;
|
||||
display::payment_proof(&tx)?;
|
||||
}
|
||||
}
|
||||
|
@ -795,10 +848,10 @@ where
|
|||
C: NodeClient + 'static,
|
||||
K: keychain::Keychain + 'static,
|
||||
{
|
||||
let slate = PathToSlate((&args.input).into()).get_tx()?;
|
||||
let slate = PathToSlate((&args.input).into()).get_tx()?.0;
|
||||
|
||||
controller::owner_single_use(None, keychain_mask, Some(owner_api), |api, m| {
|
||||
api.post_tx(m, slate.tx_or_err()?, args.fluff)?;
|
||||
api.post_tx(m, &slate, args.fluff)?;
|
||||
info!("Posted transaction");
|
||||
return Ok(());
|
||||
})?;
|
||||
|
@ -824,7 +877,7 @@ where
|
|||
{
|
||||
controller::owner_single_use(None, keychain_mask, Some(owner_api), |api, m| {
|
||||
let (_, txs) = api.retrieve_txs(m, true, Some(args.id), None)?;
|
||||
let stored_tx = api.get_stored_tx(m, &txs[0])?;
|
||||
let stored_tx = api.get_stored_tx(m, txs[0].tx_slate_id.unwrap())?;
|
||||
if stored_tx.is_none() {
|
||||
error!(
|
||||
"Transaction with id {} does not have transaction data. Not reposting.",
|
||||
|
@ -841,7 +894,9 @@ where
|
|||
);
|
||||
return Ok(());
|
||||
}
|
||||
api.post_tx(m, &stored_tx.unwrap(), args.fluff)?;
|
||||
let mut slate = Slate::blank(2, false);
|
||||
slate.tx = Some(stored_tx.unwrap());
|
||||
api.post_tx(m, &slate, args.fluff)?;
|
||||
info!("Reposted transaction at {}", args.id);
|
||||
return Ok(());
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright 2019 The Grin Developers
|
||||
// Copyright 2020 The Grin Developers
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
|
@ -17,7 +17,7 @@ use crate::core::global;
|
|||
use crate::libwallet::{
|
||||
AcctPathMapping, Error, OutputCommitMapping, OutputStatus, TxLogEntry, WalletInfo,
|
||||
};
|
||||
use crate::util::{static_secp_instance, ToHex};
|
||||
use crate::util::ToHex;
|
||||
use grin_wallet_util::OnionV3Address;
|
||||
use prettytable;
|
||||
use std::io::prelude::Write;
|
||||
|
@ -448,81 +448,6 @@ pub fn accounts(acct_mappings: Vec<AcctPathMapping>) {
|
|||
println!();
|
||||
}
|
||||
|
||||
/// Display transaction log messages
|
||||
pub fn tx_messages(tx: &TxLogEntry, dark_background_color_scheme: bool) -> Result<(), Error> {
|
||||
let title = format!("Transaction Messages - Transaction '{}'", tx.id,);
|
||||
println!();
|
||||
if term::stdout().is_none() {
|
||||
println!("Could not open terminal");
|
||||
return Ok(());
|
||||
}
|
||||
let mut t = term::stdout().unwrap();
|
||||
t.fg(term::color::MAGENTA).unwrap();
|
||||
writeln!(t, "{}", title).unwrap();
|
||||
t.reset().unwrap();
|
||||
|
||||
let msgs = match tx.messages.clone() {
|
||||
None => {
|
||||
writeln!(t, "None").unwrap();
|
||||
t.reset().unwrap();
|
||||
return Ok(());
|
||||
}
|
||||
Some(m) => m.clone(),
|
||||
};
|
||||
|
||||
if msgs.messages.is_empty() {
|
||||
writeln!(t, "None").unwrap();
|
||||
t.reset().unwrap();
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let mut table = table!();
|
||||
|
||||
table.set_titles(row![
|
||||
bMG->"Participant Id",
|
||||
bMG->"Message",
|
||||
bMG->"Public Key",
|
||||
bMG->"Signature",
|
||||
]);
|
||||
|
||||
let secp = static_secp_instance();
|
||||
let secp_lock = secp.lock();
|
||||
|
||||
for m in msgs.messages {
|
||||
let id = format!("{}", m.id);
|
||||
let public_key = format!("{}", m.public_key.serialize_vec(&secp_lock, true).to_hex());
|
||||
let message = match m.message {
|
||||
Some(m) => format!("{}", m),
|
||||
None => "None".to_owned(),
|
||||
};
|
||||
let message_sig = match m.message_sig {
|
||||
Some(s) => format!("{}", s.serialize_der(&secp_lock).to_hex()),
|
||||
None => "None".to_owned(),
|
||||
};
|
||||
if dark_background_color_scheme {
|
||||
table.add_row(row![
|
||||
bFC->id,
|
||||
bFC->message,
|
||||
bFC->public_key,
|
||||
bFB->message_sig,
|
||||
]);
|
||||
} else {
|
||||
table.add_row(row![
|
||||
bFD->id,
|
||||
bFb->message,
|
||||
bFD->public_key,
|
||||
bFB->message_sig,
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
table.set_format(*prettytable::format::consts::FORMAT_NO_COLSEP);
|
||||
table.printstd();
|
||||
println!();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Display individual Payment Proof
|
||||
pub fn payment_proof(tx: &TxLogEntry) -> Result<(), Error> {
|
||||
let title = format!("Payment Proof - Transaction '{}'", tx.id,);
|
||||
|
|
|
@ -200,9 +200,9 @@ fn accounts_test_impl(test_dir: &'static str) -> Result<(), libwallet::Error> {
|
|||
};
|
||||
let mut slate = api.init_send_tx(m, args)?;
|
||||
slate = client1.send_tx_slate_direct("wallet2", &slate)?;
|
||||
api.tx_lock_outputs(m, &slate, 0)?;
|
||||
api.tx_lock_outputs(m, &slate)?;
|
||||
slate = api.finalize_tx(m, &slate)?;
|
||||
api.post_tx(m, slate.tx_or_err()?, false)?;
|
||||
api.post_tx(m, &slate, false)?;
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
|
|
|
@ -190,8 +190,8 @@ fn scan_impl(test_dir: &'static str) -> Result<(), libwallet::Error> {
|
|||
let slate = api.init_send_tx(m, args)?;
|
||||
// output tx file
|
||||
let send_file = format!("{}/part_tx_1.tx", test_dir);
|
||||
PathToSlate(send_file.into()).put_tx(&slate)?;
|
||||
api.tx_lock_outputs(m, &slate, 0)?;
|
||||
PathToSlate(send_file.into()).put_tx(&slate, false)?;
|
||||
api.tx_lock_outputs(m, &slate)?;
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
|
|
|
@ -19,6 +19,7 @@ extern crate grin_wallet_impls as impls;
|
|||
|
||||
use grin_wallet_libwallet as libwallet;
|
||||
use grin_wallet_util::grin_core as core;
|
||||
use grin_wallet_util::OnionV3Address;
|
||||
|
||||
use impls::test_framework::{self, LocalWalletClient};
|
||||
use impls::{PathToSlate, SlateGetter as _, SlatePutter as _};
|
||||
|
@ -26,16 +27,14 @@ use std::sync::atomic::Ordering;
|
|||
use std::thread;
|
||||
use std::time::Duration;
|
||||
|
||||
use grin_wallet_libwallet::InitTxArgs;
|
||||
|
||||
use serde_json;
|
||||
use grin_wallet_libwallet::{InitTxArgs, IssueInvoiceTxArgs, Slate};
|
||||
|
||||
#[macro_use]
|
||||
mod common;
|
||||
use common::{clean_output_dir, create_wallet_proxy, setup};
|
||||
|
||||
/// self send impl
|
||||
fn file_exchange_test_impl(test_dir: &'static str) -> Result<(), libwallet::Error> {
|
||||
fn file_exchange_test_impl(test_dir: &'static str, use_bin: bool) -> Result<(), libwallet::Error> {
|
||||
// Create a new proxy to simulate server and wallet responses
|
||||
let mut wallet_proxy = create_wallet_proxy(test_dir);
|
||||
let chain = wallet_proxy.chain.clone();
|
||||
|
@ -99,11 +98,18 @@ fn file_exchange_test_impl(test_dir: &'static str) -> Result<(), libwallet::Erro
|
|||
let _ =
|
||||
test_framework::award_blocks_to_wallet(&chain, wallet1.clone(), mask1, bh as usize, false);
|
||||
|
||||
let send_file = format!("{}/part_tx_1.tx", test_dir);
|
||||
let receive_file = format!("{}/part_tx_2.tx", test_dir);
|
||||
|
||||
// test optional message
|
||||
let message = "sender test message, sender test message";
|
||||
let (send_file, receive_file, final_file) = match use_bin {
|
||||
false => (
|
||||
format!("{}/standard_S1.tx", test_dir),
|
||||
format!("{}/standard_S2.tx", test_dir),
|
||||
format!("{}/standard_S3.tx", test_dir),
|
||||
),
|
||||
true => (
|
||||
format!("{}/standard_S1.txbin", test_dir),
|
||||
format!("{}/standard_S2.txbin", test_dir),
|
||||
format!("{}/standard_S3.txbin", test_dir),
|
||||
),
|
||||
};
|
||||
|
||||
// Should have 5 in account1 (5 spendable), 5 in account (2 spendable)
|
||||
wallet::controller::owner_single_use(Some(wallet1.clone()), mask1, None, |api, m| {
|
||||
|
@ -119,13 +125,12 @@ fn file_exchange_test_impl(test_dir: &'static str) -> Result<(), libwallet::Erro
|
|||
max_outputs: 500,
|
||||
num_change_outputs: 1,
|
||||
selection_strategy_is_use_all: true,
|
||||
message: Some(message.to_owned()),
|
||||
..Default::default()
|
||||
};
|
||||
let slate = api.init_send_tx(m, args)?;
|
||||
// output tx file
|
||||
PathToSlate((&send_file).into()).put_tx(&slate)?;
|
||||
api.tx_lock_outputs(m, &slate, 0)?;
|
||||
PathToSlate((&send_file).into()).put_tx(&slate, use_bin)?;
|
||||
api.tx_lock_outputs(m, &slate)?;
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
|
@ -135,32 +140,22 @@ fn file_exchange_test_impl(test_dir: &'static str) -> Result<(), libwallet::Erro
|
|||
w.set_parent_key_id_by_name("account1")?;
|
||||
}
|
||||
|
||||
let mut slate = PathToSlate((&send_file).into()).get_tx()?;
|
||||
let mut naughty_slate = slate.clone();
|
||||
naughty_slate.participant_data[0].message = Some("I changed the message".to_owned());
|
||||
|
||||
// verify messages on slate match
|
||||
wallet::controller::owner_single_use(Some(wallet1.clone()), mask1, None, |api, m| {
|
||||
api.verify_slate_messages(m, &slate)?;
|
||||
assert!(api.verify_slate_messages(m, &naughty_slate).is_err());
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
let sender2_message = "And this is sender 2's message".to_owned();
|
||||
let mut slate = PathToSlate((&send_file).into()).get_tx()?.0;
|
||||
|
||||
// wallet 2 receives file, completes, sends file back
|
||||
wallet::controller::foreign_single_use(wallet2.clone(), mask2_i.clone(), |api| {
|
||||
slate = api.receive_tx(&slate, None, Some(sender2_message.clone()))?;
|
||||
PathToSlate((&receive_file).into()).put_tx(&slate)?;
|
||||
slate = api.receive_tx(&slate, None)?;
|
||||
PathToSlate((&receive_file).into()).put_tx(&slate, use_bin)?;
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
// wallet 1 finalises and posts
|
||||
wallet::controller::owner_single_use(Some(wallet1.clone()), mask1, None, |api, m| {
|
||||
let mut slate = PathToSlate(receive_file.into()).get_tx()?;
|
||||
api.verify_slate_messages(m, &slate)?;
|
||||
let mut slate = PathToSlate(receive_file.into()).get_tx()?.0;
|
||||
slate = api.finalize_tx(m, &slate)?;
|
||||
api.post_tx(m, slate.tx_or_err()?, false)?;
|
||||
// Output final file for reference
|
||||
PathToSlate((&final_file).into()).put_tx(&slate, use_bin)?;
|
||||
api.post_tx(m, &slate, false)?;
|
||||
bh += 1;
|
||||
Ok(())
|
||||
})?;
|
||||
|
@ -186,33 +181,117 @@ fn file_exchange_test_impl(test_dir: &'static str) -> Result<(), libwallet::Erro
|
|||
Ok(())
|
||||
})?;
|
||||
|
||||
// Check messages, all participants should have both
|
||||
wallet::controller::owner_single_use(Some(wallet1.clone()), mask1, None, |api, m| {
|
||||
let (_, tx) = api.retrieve_txs(m, true, None, Some(slate.id))?;
|
||||
assert_eq!(
|
||||
tx[0].clone().messages.unwrap().messages[0].message,
|
||||
Some(message.to_owned())
|
||||
);
|
||||
assert_eq!(
|
||||
tx[0].clone().messages.unwrap().messages[1].message,
|
||||
Some(sender2_message.to_owned())
|
||||
);
|
||||
// Now other types of exchange, for reference
|
||||
// Invoice transaction
|
||||
let (send_file, receive_file, final_file) = match use_bin {
|
||||
false => (
|
||||
format!("{}/invoice_I1.tx", test_dir),
|
||||
format!("{}/invoice_I2.tx", test_dir),
|
||||
format!("{}/invoice_I3.tx", test_dir),
|
||||
),
|
||||
true => (
|
||||
format!("{}/invoice_I1.txbin", test_dir),
|
||||
format!("{}/invoice_I2.txbin", test_dir),
|
||||
format!("{}/invoice_I3.txbin", test_dir),
|
||||
),
|
||||
};
|
||||
|
||||
let msg_json = serde_json::to_string_pretty(&tx[0].clone().messages.unwrap()).unwrap();
|
||||
println!("{}", msg_json);
|
||||
let mut slate = Slate::blank(2, true);
|
||||
|
||||
wallet::controller::owner_single_use(Some(wallet2.clone()), mask2, None, |api, m| {
|
||||
let args = IssueInvoiceTxArgs {
|
||||
amount: 1000000000,
|
||||
..Default::default()
|
||||
};
|
||||
slate = api.issue_invoice_tx(m, args)?;
|
||||
PathToSlate((&send_file).into()).put_tx(&slate, use_bin)?;
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
wallet::controller::owner_single_use(Some(wallet1.clone()), mask1, None, |api, m| {
|
||||
let args = InitTxArgs {
|
||||
src_acct_name: None,
|
||||
amount: slate.amount,
|
||||
minimum_confirmations: 2,
|
||||
max_outputs: 500,
|
||||
num_change_outputs: 1,
|
||||
selection_strategy_is_use_all: true,
|
||||
..Default::default()
|
||||
};
|
||||
slate = PathToSlate((&send_file).into()).get_tx()?.0;
|
||||
slate = api.process_invoice_tx(m, &slate, args)?;
|
||||
api.tx_lock_outputs(m, &slate)?;
|
||||
PathToSlate((&receive_file).into()).put_tx(&slate, use_bin)?;
|
||||
Ok(())
|
||||
})?;
|
||||
wallet::controller::foreign_single_use(wallet2.clone(), mask2_i.clone(), |api| {
|
||||
// Wallet 2 receives the invoice transaction
|
||||
slate = PathToSlate((&receive_file).into()).get_tx()?.0;
|
||||
slate = api.finalize_invoice_tx(&slate)?;
|
||||
PathToSlate((&final_file).into()).put_tx(&slate, use_bin)?;
|
||||
Ok(())
|
||||
})?;
|
||||
wallet::controller::owner_single_use(Some(wallet1.clone()), mask1, None, |api, m| {
|
||||
api.post_tx(m, &slate, false)?;
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
// Standard, with payment proof
|
||||
let _ = test_framework::award_blocks_to_wallet(&chain, wallet1.clone(), mask1, 3, false);
|
||||
let (send_file, receive_file, final_file) = match use_bin {
|
||||
false => (
|
||||
format!("{}/standard_pp_S1.tx", test_dir),
|
||||
format!("{}/standard_pp_S2.tx", test_dir),
|
||||
format!("{}/standard_pp_S3.tx", test_dir),
|
||||
),
|
||||
true => (
|
||||
format!("{}/standard_pp_S1.txbin", test_dir),
|
||||
format!("{}/standard_pp_S2.txbin", test_dir),
|
||||
format!("{}/standard_pp_S3.txbin", test_dir),
|
||||
),
|
||||
};
|
||||
let mut slate = Slate::blank(2, true);
|
||||
let mut address = None;
|
||||
wallet::controller::owner_single_use(Some(wallet2.clone()), mask2, None, |api, m| {
|
||||
let (_, tx) = api.retrieve_txs(m, true, None, Some(slate.id))?;
|
||||
assert_eq!(
|
||||
tx[0].clone().messages.unwrap().messages[0].message,
|
||||
Some(message.to_owned())
|
||||
);
|
||||
assert_eq!(
|
||||
tx[0].clone().messages.unwrap().messages[1].message,
|
||||
Some(sender2_message)
|
||||
);
|
||||
address = Some(api.get_public_proof_address(m, 0)?);
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
let address = OnionV3Address::from_bytes(address.as_ref().unwrap().to_bytes());
|
||||
|
||||
wallet::controller::owner_single_use(Some(wallet1.clone()), mask1, None, |api, m| {
|
||||
// send to send
|
||||
let args = InitTxArgs {
|
||||
src_acct_name: Some("mining".to_owned()),
|
||||
amount: reward,
|
||||
minimum_confirmations: 2,
|
||||
max_outputs: 500,
|
||||
num_change_outputs: 1,
|
||||
selection_strategy_is_use_all: true,
|
||||
payment_proof_recipient_address: Some(address.clone()),
|
||||
..Default::default()
|
||||
};
|
||||
let slate = api.init_send_tx(m, args)?;
|
||||
PathToSlate((&send_file).into()).put_tx(&slate, use_bin)?;
|
||||
api.tx_lock_outputs(m, &slate)?;
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
wallet::controller::foreign_single_use(wallet2.clone(), mask2_i.clone(), |api| {
|
||||
slate = PathToSlate((&send_file).into()).get_tx()?.0;
|
||||
slate = api.receive_tx(&slate, None)?;
|
||||
PathToSlate((&receive_file).into()).put_tx(&slate, use_bin)?;
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
// wallet 1 finalises and posts
|
||||
wallet::controller::owner_single_use(Some(wallet1.clone()), mask1, None, |api, m| {
|
||||
slate = PathToSlate(receive_file.into()).get_tx()?.0;
|
||||
slate = api.finalize_tx(m, &slate)?;
|
||||
// Output final file for reference
|
||||
PathToSlate((&final_file).into()).put_tx(&slate, use_bin)?;
|
||||
api.post_tx(m, &slate, false)?;
|
||||
bh += 1;
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
|
@ -223,10 +302,21 @@ fn file_exchange_test_impl(test_dir: &'static str) -> Result<(), libwallet::Erro
|
|||
}
|
||||
|
||||
#[test]
|
||||
fn wallet_file_exchange() {
|
||||
let test_dir = "test_output/file_exchange";
|
||||
fn wallet_file_exchange_json() {
|
||||
let test_dir = "test_output/file_exchange_json";
|
||||
setup(test_dir);
|
||||
if let Err(e) = file_exchange_test_impl(test_dir) {
|
||||
// Json output
|
||||
if let Err(e) = file_exchange_test_impl(test_dir, false) {
|
||||
panic!("Libwallet Error: {} - {}", e, e.backtrace().unwrap());
|
||||
}
|
||||
clean_output_dir(test_dir);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn wallet_file_exchange_bin() {
|
||||
let test_dir = "test_output/file_exchange_bin";
|
||||
setup(test_dir);
|
||||
if let Err(e) = file_exchange_test_impl(test_dir, true) {
|
||||
panic!("Libwallet Error: {} - {}", e, e.backtrace().unwrap());
|
||||
}
|
||||
clean_output_dir(test_dir);
|
||||
|
|
|
@ -21,7 +21,7 @@ use grin_wallet_libwallet as libwallet;
|
|||
use grin_wallet_util::grin_core as core;
|
||||
|
||||
use impls::test_framework::{self, LocalWalletClient};
|
||||
use libwallet::{InitTxArgs, IssueInvoiceTxArgs, Slate};
|
||||
use libwallet::{InitTxArgs, IssueInvoiceTxArgs, Slate, SlateState};
|
||||
use std::sync::atomic::Ordering;
|
||||
use std::thread;
|
||||
use std::time::Duration;
|
||||
|
@ -95,7 +95,7 @@ fn invoice_tx_impl(test_dir: &'static str) -> Result<(), libwallet::Error> {
|
|||
Ok(())
|
||||
})?;
|
||||
|
||||
let mut slate = Slate::blank(2);
|
||||
let mut slate = Slate::blank(2, true);
|
||||
|
||||
wallet::controller::owner_single_use(Some(wallet2.clone()), mask2, None, |api, m| {
|
||||
// Wallet 2 inititates an invoice transaction, requesting payment
|
||||
|
@ -106,6 +106,7 @@ fn invoice_tx_impl(test_dir: &'static str) -> Result<(), libwallet::Error> {
|
|||
slate = api.issue_invoice_tx(m, args)?;
|
||||
Ok(())
|
||||
})?;
|
||||
assert_eq!(slate.state, SlateState::Invoice1);
|
||||
|
||||
wallet::controller::owner_single_use(Some(wallet1.clone()), mask1, None, |api, m| {
|
||||
// Wallet 1 receives the invoice transaction
|
||||
|
@ -119,9 +120,10 @@ fn invoice_tx_impl(test_dir: &'static str) -> Result<(), libwallet::Error> {
|
|||
..Default::default()
|
||||
};
|
||||
slate = api.process_invoice_tx(m, &slate, args)?;
|
||||
api.tx_lock_outputs(m, &slate, 0)?;
|
||||
api.tx_lock_outputs(m, &slate)?;
|
||||
Ok(())
|
||||
})?;
|
||||
assert_eq!(slate.state, SlateState::Invoice2);
|
||||
|
||||
// wallet 2 finalizes and posts
|
||||
wallet::controller::foreign_single_use(wallet2.clone(), mask2_i.clone(), |api| {
|
||||
|
@ -129,10 +131,11 @@ fn invoice_tx_impl(test_dir: &'static str) -> Result<(), libwallet::Error> {
|
|||
slate = api.finalize_invoice_tx(&slate)?;
|
||||
Ok(())
|
||||
})?;
|
||||
assert_eq!(slate.state, SlateState::Invoice3);
|
||||
|
||||
// wallet 1 posts so wallet 2 doesn't get the mined amount
|
||||
wallet::controller::owner_single_use(Some(wallet1.clone()), mask1, None, |api, m| {
|
||||
api.post_tx(m, slate.tx_or_err()?, false)?;
|
||||
api.post_tx(m, &slate, false)?;
|
||||
Ok(())
|
||||
})?;
|
||||
bh += 1;
|
||||
|
@ -151,7 +154,6 @@ fn invoice_tx_impl(test_dir: &'static str) -> Result<(), libwallet::Error> {
|
|||
wallet2_info.last_confirmed_height, bh
|
||||
);
|
||||
assert!(refreshed);
|
||||
assert_eq!(wallet2_info.amount_currently_spendable, slate.amount);
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
|
@ -187,11 +189,14 @@ fn invoice_tx_impl(test_dir: &'static str) -> Result<(), libwallet::Error> {
|
|||
selection_strategy_is_use_all: true,
|
||||
..Default::default()
|
||||
};
|
||||
println!("Self invoice slate init: {}", slate);
|
||||
slate = api.process_invoice_tx(m, &slate, args)?;
|
||||
api.tx_lock_outputs(m, &slate, 0)?;
|
||||
api.tx_lock_outputs(m, &slate)?;
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
println!("Self invoice slate after process: {}", slate);
|
||||
|
||||
// wallet 1 finalizes and posts
|
||||
wallet::controller::foreign_single_use(wallet1.clone(), mask1_i.clone(), |api| {
|
||||
// Wallet 2 receives the invoice transaction
|
||||
|
|
|
@ -76,7 +76,8 @@ fn no_change_test_impl(test_dir: &'static str) -> Result<(), libwallet::Error> {
|
|||
let fee = core::libtx::tx_fee(1, 1, 1, None);
|
||||
|
||||
// send a single block's worth of transactions with minimal strategy
|
||||
let mut slate = Slate::blank(2);
|
||||
let mut slate = Slate::blank(2, false);
|
||||
let mut stored_excess = None;
|
||||
wallet::controller::owner_single_use(Some(wallet1.clone()), mask1, None, |api, m| {
|
||||
let args = InitTxArgs {
|
||||
src_acct_name: None,
|
||||
|
@ -89,19 +90,42 @@ fn no_change_test_impl(test_dir: &'static str) -> Result<(), libwallet::Error> {
|
|||
};
|
||||
slate = api.init_send_tx(m, args)?;
|
||||
slate = client1.send_tx_slate_direct("wallet2", &slate)?;
|
||||
api.tx_lock_outputs(m, &slate, 0)?;
|
||||
api.tx_lock_outputs(m, &slate)?;
|
||||
slate = api.finalize_tx(m, &slate)?;
|
||||
api.post_tx(m, slate.tx_or_err()?, false)?;
|
||||
println!("Posted Slate: {:?}", slate);
|
||||
println!("Posted TX: {}", slate);
|
||||
stored_excess = Some(slate.tx.as_ref().unwrap().body.kernels[0].excess);
|
||||
api.post_tx(m, &slate, false)?;
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
// ensure stored excess is correct in both wallets
|
||||
// Wallet 1 calculated the excess with the full slate // Wallet 2 only had the excess provided by
|
||||
// wallet 1
|
||||
|
||||
// Refresh and check transaction log for wallet 1
|
||||
wallet::controller::owner_single_use(Some(wallet1.clone()), mask2, None, |api, m| {
|
||||
wallet::controller::owner_single_use(Some(wallet1.clone()), mask1, None, |api, m| {
|
||||
let (refreshed, txs) = api.retrieve_txs(m, true, None, Some(slate.id))?;
|
||||
assert!(refreshed);
|
||||
let tx = txs[0].clone();
|
||||
println!("SIMPLE SEND - SENDING WALLET");
|
||||
println!("{:?}", tx);
|
||||
println!();
|
||||
assert!(tx.confirmed);
|
||||
assert_eq!(stored_excess, tx.kernel_excess);
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
// Refresh and check transaction log for wallet 2
|
||||
wallet::controller::owner_single_use(Some(wallet2.clone()), mask2, None, |api, m| {
|
||||
let (refreshed, txs) = api.retrieve_txs(m, true, None, Some(slate.id))?;
|
||||
assert!(refreshed);
|
||||
let tx = txs[0].clone();
|
||||
println!("SIMPLE SEND - RECEIVING WALLET");
|
||||
println!("{:?}", tx);
|
||||
println!();
|
||||
assert!(tx.confirmed);
|
||||
assert_eq!(stored_excess, tx.kernel_excess);
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
|
@ -128,7 +152,7 @@ fn no_change_test_impl(test_dir: &'static str) -> Result<(), libwallet::Error> {
|
|||
..Default::default()
|
||||
};
|
||||
slate = api.process_invoice_tx(m, &slate, args)?;
|
||||
api.tx_lock_outputs(m, &slate, 0)?;
|
||||
api.tx_lock_outputs(m, &slate)?;
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
|
@ -139,16 +163,34 @@ fn no_change_test_impl(test_dir: &'static str) -> Result<(), libwallet::Error> {
|
|||
Ok(())
|
||||
})?;
|
||||
wallet::controller::owner_single_use(Some(wallet2.clone()), mask1, None, |api, m| {
|
||||
api.post_tx(m, slate.tx_or_err()?, false)?;
|
||||
println!("Invoice Posted TX: {}", slate);
|
||||
stored_excess = Some(slate.tx.as_ref().unwrap().body.kernels[0].excess);
|
||||
api.post_tx(m, &slate, false)?;
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
// check wallet 2's version
|
||||
wallet::controller::owner_single_use(Some(wallet2.clone()), mask2, None, |api, m| {
|
||||
let (refreshed, txs) = api.retrieve_txs(m, true, None, Some(slate.id))?;
|
||||
assert!(refreshed);
|
||||
for tx in txs {
|
||||
stored_excess = tx.kernel_excess;
|
||||
println!("Wallet 2: {:?}", tx);
|
||||
println!();
|
||||
assert!(tx.confirmed);
|
||||
assert_eq!(stored_excess, tx.kernel_excess);
|
||||
}
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
// Refresh and check transaction log for wallet 1
|
||||
wallet::controller::owner_single_use(Some(wallet1.clone()), mask2, None, |api, m| {
|
||||
wallet::controller::owner_single_use(Some(wallet1.clone()), mask1, None, |api, m| {
|
||||
let (refreshed, txs) = api.retrieve_txs(m, true, None, Some(slate.id))?;
|
||||
assert!(refreshed);
|
||||
for tx in txs {
|
||||
println!("{:?}", tx);
|
||||
println!("Wallet 1: {:?}", tx);
|
||||
println!();
|
||||
assert_eq!(stored_excess, tx.kernel_excess);
|
||||
assert!(tx.confirmed);
|
||||
}
|
||||
Ok(())
|
||||
|
|
|
@ -87,7 +87,7 @@ fn payment_proofs_test_impl(test_dir: &'static str) -> Result<(), libwallet::Err
|
|||
let address = OnionV3Address::from_bytes(address.as_ref().unwrap().to_bytes());
|
||||
println!("Public address is: {:?}", address);
|
||||
let amount = 60_000_000_000;
|
||||
let mut slate = Slate::blank(1);
|
||||
let mut slate = Slate::blank(1, false);
|
||||
wallet::controller::owner_single_use(Some(wallet1.clone()), mask1, None, |sender_api, m| {
|
||||
// note this will increment the block count as part of the transaction "Posting"
|
||||
let args = InitTxArgs {
|
||||
|
@ -111,12 +111,12 @@ fn payment_proofs_test_impl(test_dir: &'static str) -> Result<(), libwallet::Err
|
|||
slate_i.payment_proof.as_ref().unwrap().sender_address
|
||||
);
|
||||
|
||||
// Check we are creating a tx with the expected lock_height of 0.
|
||||
// Check we are creating a tx with kernel features 0
|
||||
// We will check this produces a Plain kernel later.
|
||||
assert_eq!(0, slate.lock_height);
|
||||
assert_eq!(0, slate.kernel_features);
|
||||
|
||||
slate = client1.send_tx_slate_direct("wallet2", &slate_i)?;
|
||||
sender_api.tx_lock_outputs(m, &slate, 0)?;
|
||||
sender_api.tx_lock_outputs(m, &slate)?;
|
||||
|
||||
// Ensure what's stored in TX log for payment proof is correct
|
||||
let (_, txs) = sender_api.retrieve_txs(m, true, None, Some(slate.id))?;
|
||||
|
@ -135,7 +135,7 @@ fn payment_proofs_test_impl(test_dir: &'static str) -> Result<(), libwallet::Err
|
|||
assert!(pp.is_err());
|
||||
|
||||
slate = sender_api.finalize_tx(m, &slate)?;
|
||||
sender_api.post_tx(m, slate.tx_or_err()?, true)?;
|
||||
sender_api.post_tx(m, &slate, true)?;
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
|
@ -145,7 +145,7 @@ fn payment_proofs_test_impl(test_dir: &'static str) -> Result<(), libwallet::Err
|
|||
// Check payment proof here
|
||||
let mut pp = sender_api.retrieve_payment_proof(m, true, None, Some(slate.id))?;
|
||||
|
||||
println!("{:?}", pp);
|
||||
println!("Payment proof: {:?}", pp);
|
||||
|
||||
// verify, should be good
|
||||
let res = sender_api.verify_payment_proof(m, &pp)?;
|
||||
|
|
|
@ -99,7 +99,7 @@ fn file_repost_test_impl(test_dir: &'static str) -> Result<(), libwallet::Error>
|
|||
let send_file = format!("{}/part_tx_1.tx", test_dir);
|
||||
let receive_file = format!("{}/part_tx_2.tx", test_dir);
|
||||
|
||||
let mut slate = Slate::blank(2);
|
||||
let mut slate = Slate::blank(2, false);
|
||||
|
||||
// Should have 5 in account1 (5 spendable), 5 in account (2 spendable)
|
||||
wallet::controller::owner_single_use(Some(wallet1.clone()), mask1, None, |api, m| {
|
||||
|
@ -118,8 +118,8 @@ fn file_repost_test_impl(test_dir: &'static str) -> Result<(), libwallet::Error>
|
|||
..Default::default()
|
||||
};
|
||||
let slate = api.init_send_tx(m, args)?;
|
||||
PathToSlate((&send_file).into()).put_tx(&slate)?;
|
||||
api.tx_lock_outputs(m, &slate, 0)?;
|
||||
PathToSlate((&send_file).into()).put_tx(&slate, false)?;
|
||||
api.tx_lock_outputs(m, &slate)?;
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
|
@ -133,9 +133,9 @@ fn file_repost_test_impl(test_dir: &'static str) -> Result<(), libwallet::Error>
|
|||
}
|
||||
|
||||
wallet::controller::foreign_single_use(wallet1.clone(), mask1_i.clone(), |api| {
|
||||
slate = PathToSlate((&send_file).into()).get_tx()?;
|
||||
slate = api.receive_tx(&slate, None, None)?;
|
||||
PathToSlate((&receive_file).into()).put_tx(&slate)?;
|
||||
slate = PathToSlate((&send_file).into()).get_tx()?.0;
|
||||
slate = api.receive_tx(&slate, None)?;
|
||||
PathToSlate((&receive_file).into()).put_tx(&slate, false)?;
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
|
@ -147,7 +147,7 @@ fn file_repost_test_impl(test_dir: &'static str) -> Result<(), libwallet::Error>
|
|||
|
||||
// wallet 1 finalize
|
||||
wallet::controller::owner_single_use(Some(wallet1.clone()), mask1, None, |api, m| {
|
||||
slate = PathToSlate((&receive_file).into()).get_tx()?;
|
||||
slate = PathToSlate((&receive_file).into()).get_tx()?.0;
|
||||
slate = api.finalize_tx(m, &slate)?;
|
||||
Ok(())
|
||||
})?;
|
||||
|
@ -155,8 +155,10 @@ fn file_repost_test_impl(test_dir: &'static str) -> Result<(), libwallet::Error>
|
|||
// Now repost from cached
|
||||
wallet::controller::owner_single_use(Some(wallet1.clone()), mask1, None, |api, m| {
|
||||
let (_, txs) = api.retrieve_txs(m, true, None, Some(slate.id))?;
|
||||
let stored_tx = api.get_stored_tx(m, &txs[0])?;
|
||||
api.post_tx(m, &stored_tx.unwrap(), false)?;
|
||||
println!("TXS[0]: {:?}", txs[0]);
|
||||
let stored_tx = api.get_stored_tx(m, txs[0].tx_slate_id.unwrap())?;
|
||||
println!("Stored tx: {:?}", stored_tx);
|
||||
api.post_tx(m, &slate, false)?;
|
||||
bh += 1;
|
||||
Ok(())
|
||||
})?;
|
||||
|
@ -196,7 +198,7 @@ fn file_repost_test_impl(test_dir: &'static str) -> Result<(), libwallet::Error>
|
|||
w.set_parent_key_id_by_name("account1")?;
|
||||
}
|
||||
|
||||
let mut slate = Slate::blank(2);
|
||||
let mut slate = Slate::blank(2, false);
|
||||
let amount = 60_000_000_000;
|
||||
|
||||
wallet::controller::owner_single_use(Some(wallet1.clone()), mask1, None, |sender_api, m| {
|
||||
|
@ -212,7 +214,7 @@ fn file_repost_test_impl(test_dir: &'static str) -> Result<(), libwallet::Error>
|
|||
};
|
||||
let slate_i = sender_api.init_send_tx(m, args)?;
|
||||
slate = client1.send_tx_slate_direct("wallet2", &slate_i)?;
|
||||
sender_api.tx_lock_outputs(m, &slate, 0)?;
|
||||
sender_api.tx_lock_outputs(m, &slate)?;
|
||||
slate = sender_api.finalize_tx(m, &slate)?;
|
||||
Ok(())
|
||||
})?;
|
||||
|
@ -223,8 +225,9 @@ fn file_repost_test_impl(test_dir: &'static str) -> Result<(), libwallet::Error>
|
|||
// Now repost from cached
|
||||
wallet::controller::owner_single_use(Some(wallet1.clone()), mask1, None, |api, m| {
|
||||
let (_, txs) = api.retrieve_txs(m, true, None, Some(slate.id))?;
|
||||
let stored_tx = api.get_stored_tx(m, &txs[0])?;
|
||||
api.post_tx(m, &stored_tx.unwrap(), false)?;
|
||||
let stored_tx = api.get_stored_tx(m, txs[0].tx_slate_id.unwrap())?;
|
||||
slate.tx = stored_tx;
|
||||
api.post_tx(m, &slate, false)?;
|
||||
bh += 1;
|
||||
Ok(())
|
||||
})?;
|
||||
|
|
|
@ -169,8 +169,8 @@ fn revert(
|
|||
let slate = api.init_send_tx(m, args)?;
|
||||
// output tx file
|
||||
let send_file = format!("{}/part_tx_1.tx", test_dir);
|
||||
PathToSlate(send_file.into()).put_tx(&slate)?;
|
||||
api.tx_lock_outputs(m, &slate, 0)?;
|
||||
PathToSlate(send_file.into()).put_tx(&slate, false)?;
|
||||
api.tx_lock_outputs(m, &slate)?;
|
||||
let slate = client1.send_tx_slate_direct("wallet2", &slate)?;
|
||||
let slate = api.finalize_tx(m, &slate)?;
|
||||
tx = Some(slate.tx);
|
||||
|
|
|
@ -94,14 +94,14 @@ fn self_send_test_impl(test_dir: &'static str) -> Result<(), libwallet::Error> {
|
|||
..Default::default()
|
||||
};
|
||||
let mut slate = api.init_send_tx(m, args)?;
|
||||
api.tx_lock_outputs(m, &slate, 0)?;
|
||||
api.tx_lock_outputs(m, &slate)?;
|
||||
// Send directly to self
|
||||
wallet::controller::foreign_single_use(wallet1.clone(), mask1_i.clone(), |api| {
|
||||
slate = api.receive_tx(&slate, Some("listener"), None)?;
|
||||
slate = api.receive_tx(&slate, Some("listener"))?;
|
||||
Ok(())
|
||||
})?;
|
||||
slate = api.finalize_tx(m, &slate)?;
|
||||
api.post_tx(m, slate.tx_or_err()?, false)?; // mines a block
|
||||
api.post_tx(m, &slate, false)?; // mines a block
|
||||
bh += 1;
|
||||
Ok(())
|
||||
})?;
|
||||
|
|
|
@ -22,7 +22,7 @@ use grin_wallet_util::grin_core as core;
|
|||
|
||||
use self::core::core::transaction;
|
||||
use self::core::global;
|
||||
use self::libwallet::{InitTxArgs, OutputStatus, Slate};
|
||||
use self::libwallet::{InitTxArgs, OutputStatus, Slate, SlateState};
|
||||
use impls::test_framework::{self, LocalWalletClient};
|
||||
use std::sync::atomic::Ordering;
|
||||
use std::thread;
|
||||
|
@ -97,7 +97,7 @@ fn basic_transaction_api(test_dir: &'static str) -> Result<(), libwallet::Error>
|
|||
// assert wallet contents
|
||||
// and a single use api for a send command
|
||||
let amount = 60_000_000_000;
|
||||
let mut slate = Slate::blank(1);
|
||||
let mut slate = Slate::blank(1, false);
|
||||
wallet::controller::owner_single_use(Some(wallet1.clone()), mask1, None, |sender_api, m| {
|
||||
// note this will increment the block count as part of the transaction "Posting"
|
||||
let args = InitTxArgs {
|
||||
|
@ -111,13 +111,17 @@ fn basic_transaction_api(test_dir: &'static str) -> Result<(), libwallet::Error>
|
|||
};
|
||||
let slate_i = sender_api.init_send_tx(m, args)?;
|
||||
|
||||
assert_eq!(slate_i.state, SlateState::Standard1);
|
||||
|
||||
// Check we are creating a tx with the expected lock_height of 0.
|
||||
// We will check this produces a Plain kernel later.
|
||||
assert_eq!(0, slate.lock_height);
|
||||
assert_eq!(0, slate.kernel_features);
|
||||
|
||||
slate = client1.send_tx_slate_direct("wallet2", &slate_i)?;
|
||||
sender_api.tx_lock_outputs(m, &slate, 0)?;
|
||||
assert_eq!(slate.state, SlateState::Standard2);
|
||||
sender_api.tx_lock_outputs(m, &slate)?;
|
||||
slate = sender_api.finalize_tx(m, &slate)?;
|
||||
assert_eq!(slate.state, SlateState::Standard3);
|
||||
|
||||
// Check we have a single kernel and that it is a Plain kernel (no lock_height).
|
||||
assert_eq!(slate.tx_or_err()?.kernels().len(), 1);
|
||||
|
@ -175,7 +179,7 @@ fn basic_transaction_api(test_dir: &'static str) -> Result<(), libwallet::Error>
|
|||
|
||||
// post transaction
|
||||
wallet::controller::owner_single_use(Some(wallet1.clone()), mask1, None, |api, m| {
|
||||
api.post_tx(m, slate.tx_or_err()?, false)?;
|
||||
api.post_tx(m, &slate, false)?;
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
|
@ -299,7 +303,7 @@ fn basic_transaction_api(test_dir: &'static str) -> Result<(), libwallet::Error>
|
|||
};
|
||||
let slate_i = sender_api.init_send_tx(m, args)?;
|
||||
slate = client1.send_tx_slate_direct("wallet2", &slate_i)?;
|
||||
sender_api.tx_lock_outputs(m, &slate, 0)?;
|
||||
sender_api.tx_lock_outputs(m, &slate)?;
|
||||
slate = sender_api.finalize_tx(m, &slate)?;
|
||||
Ok(())
|
||||
})?;
|
||||
|
@ -313,8 +317,9 @@ fn basic_transaction_api(test_dir: &'static str) -> Result<(), libwallet::Error>
|
|||
.iter()
|
||||
.find(|t| t.tx_slate_id == Some(slate.id))
|
||||
.unwrap();
|
||||
let stored_tx = sender_api.get_stored_tx(m, &tx)?;
|
||||
sender_api.post_tx(m, &stored_tx.unwrap(), false)?;
|
||||
let stored_tx = sender_api.get_stored_tx(m, tx.tx_slate_id.unwrap())?;
|
||||
slate.tx = stored_tx;
|
||||
sender_api.post_tx(m, &slate, false)?;
|
||||
let (_, wallet1_info) = sender_api.retrieve_summary_info(m, true, 1)?;
|
||||
// should be mined now
|
||||
assert_eq!(
|
||||
|
@ -395,7 +400,7 @@ fn tx_rollback(test_dir: &'static str) -> Result<(), libwallet::Error> {
|
|||
let _ = test_framework::award_blocks_to_wallet(&chain, wallet1.clone(), mask1, 5, false);
|
||||
|
||||
let amount = 30_000_000_000;
|
||||
let mut slate = Slate::blank(1);
|
||||
let mut slate = Slate::blank(1, false);
|
||||
wallet::controller::owner_single_use(Some(wallet1.clone()), mask1, None, |sender_api, m| {
|
||||
// note this will increment the block count as part of the transaction "Posting"
|
||||
let args = InitTxArgs {
|
||||
|
@ -410,7 +415,7 @@ fn tx_rollback(test_dir: &'static str) -> Result<(), libwallet::Error> {
|
|||
|
||||
let slate_i = sender_api.init_send_tx(m, args)?;
|
||||
slate = client1.send_tx_slate_direct("wallet2", &slate_i)?;
|
||||
sender_api.tx_lock_outputs(m, &slate, 0)?;
|
||||
sender_api.tx_lock_outputs(m, &slate)?;
|
||||
slate = sender_api.finalize_tx(m, &slate)?;
|
||||
Ok(())
|
||||
})?;
|
||||
|
|
|
@ -77,7 +77,7 @@ fn ttl_cutoff_test_impl(test_dir: &'static str) -> Result<(), libwallet::Error>
|
|||
test_framework::award_blocks_to_wallet(&chain, wallet1.clone(), mask1, bh as usize, false);
|
||||
|
||||
let amount = 60_000_000_000;
|
||||
let mut slate = Slate::blank(1);
|
||||
let mut slate = Slate::blank(1, false);
|
||||
wallet::controller::owner_single_use(Some(wallet1.clone()), mask1, None, |sender_api, m| {
|
||||
// note this will increment the block count as part of the transaction "Posting"
|
||||
let args = InitTxArgs {
|
||||
|
@ -93,7 +93,7 @@ fn ttl_cutoff_test_impl(test_dir: &'static str) -> Result<(), libwallet::Error>
|
|||
let slate_i = sender_api.init_send_tx(m, args)?;
|
||||
|
||||
slate = client1.send_tx_slate_direct("wallet2", &slate_i)?;
|
||||
sender_api.tx_lock_outputs(m, &slate, 0)?;
|
||||
sender_api.tx_lock_outputs(m, &slate)?;
|
||||
|
||||
let (_, txs) = sender_api.retrieve_txs(m, true, None, Some(slate.id))?;
|
||||
let tx = txs[0].clone();
|
||||
|
@ -127,7 +127,7 @@ fn ttl_cutoff_test_impl(test_dir: &'static str) -> Result<(), libwallet::Error>
|
|||
})?;
|
||||
|
||||
// try again, except try and send off the transaction for completion beyond the expiry
|
||||
let mut slate = Slate::blank(1);
|
||||
let mut slate = Slate::blank(1, false);
|
||||
wallet::controller::owner_single_use(Some(wallet1.clone()), mask1, None, |sender_api, m| {
|
||||
// note this will increment the block count as part of the transaction "Posting"
|
||||
let args = InitTxArgs {
|
||||
|
@ -141,7 +141,7 @@ fn ttl_cutoff_test_impl(test_dir: &'static str) -> Result<(), libwallet::Error>
|
|||
..Default::default()
|
||||
};
|
||||
let slate_i = sender_api.init_send_tx(m, args)?;
|
||||
sender_api.tx_lock_outputs(m, &slate_i, 0)?;
|
||||
sender_api.tx_lock_outputs(m, &slate_i)?;
|
||||
slate = slate_i;
|
||||
|
||||
let (_, txs) = sender_api.retrieve_txs(m, true, None, Some(slate.id))?;
|
||||
|
|
|
@ -16,45 +16,114 @@
|
|||
use std::fs::File;
|
||||
use std::io::{Read, Write};
|
||||
|
||||
use crate::libwallet::{Error, ErrorKind, Slate, SlateVersion, VersionedSlate};
|
||||
use crate::client_utils::byte_ser;
|
||||
use crate::libwallet::slate_versions::v3::SlateV3;
|
||||
use crate::libwallet::slate_versions::v4::SlateV4;
|
||||
use crate::libwallet::{
|
||||
Error, ErrorKind, Slate, SlateState, SlateVersion, VersionedBinSlate, VersionedSlate,
|
||||
};
|
||||
use crate::{SlateGetter, SlatePutter};
|
||||
use std::convert::TryFrom;
|
||||
use std::path::PathBuf;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct PathToSlate(pub PathBuf);
|
||||
|
||||
impl SlatePutter for PathToSlate {
|
||||
fn put_tx(&self, slate: &Slate) -> Result<(), Error> {
|
||||
fn put_tx(&self, slate: &Slate, as_bin: bool) -> Result<(), Error> {
|
||||
// For testing (output raw slate data for reference)
|
||||
/*{
|
||||
let mut raw_path = self.0.clone();
|
||||
raw_path.set_extension("raw");
|
||||
let mut raw_slate = File::create(&raw_path)?;
|
||||
raw_slate.write_all(&format!("{:?}", slate).as_bytes())?;
|
||||
raw_slate.sync_all()?;
|
||||
}*/
|
||||
let mut pub_tx = File::create(&self.0)?;
|
||||
let _r: crate::adapters::Reminder;
|
||||
// TODO:
|
||||
let out_slate = {
|
||||
// TODO: This will need to be filled with any incompatibilities in the V4 Slate
|
||||
if false {
|
||||
warn!("Transaction contains features that require grin-wallet 4.0.0 or later");
|
||||
warn!("Please ensure the other party is running grin-wallet v4.0.0 or later before sending");
|
||||
VersionedSlate::into_version(slate.clone(), SlateVersion::V4)?
|
||||
// TODO: Remove post HF3
|
||||
if slate.version_info.version == 2 || slate.version_info.version == 3 {
|
||||
// if the slate we read in in V3 or 2 (holdover from 3.0.0), output a slate V3,
|
||||
// which can be read by v3.x wallets
|
||||
let v4_slate = SlateV4::from(slate.clone());
|
||||
let mut v3_slate = SlateV3::try_from(&v4_slate)?;
|
||||
// Fill in V3 participant IDs according to state
|
||||
if slate.state == SlateState::Invoice1 {
|
||||
for mut e in v3_slate.participant_data.iter_mut() {
|
||||
if Some(e.public_blind_excess.clone()) == slate.participant_id {
|
||||
e.id = 1;
|
||||
} else {
|
||||
e.id = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
if slate.state == SlateState::Invoice2 {
|
||||
for mut e in v3_slate.participant_data.iter_mut() {
|
||||
if Some(e.public_blind_excess.clone()) == slate.participant_id {
|
||||
e.id = 0;
|
||||
} else {
|
||||
e.id = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
if slate.state == SlateState::Standard1 {
|
||||
for mut e in v3_slate.participant_data.iter_mut() {
|
||||
if Some(e.public_blind_excess.clone()) == slate.participant_id {
|
||||
e.id = 0;
|
||||
} else {
|
||||
e.id = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
if slate.state == SlateState::Standard2 {
|
||||
for mut e in v3_slate.participant_data.iter_mut() {
|
||||
if Some(e.public_blind_excess.clone()) == slate.participant_id {
|
||||
e.id = 1;
|
||||
} else {
|
||||
e.id = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
v3_slate.version_info.version = 3;
|
||||
VersionedSlate::V3(v3_slate)
|
||||
} else {
|
||||
let mut s = slate.clone();
|
||||
s.version_info.version = 3;
|
||||
s.version_info.orig_version = 3;
|
||||
VersionedSlate::into_version(s, SlateVersion::V3)?
|
||||
VersionedSlate::into_version(slate.clone(), SlateVersion::V4)?
|
||||
}
|
||||
};
|
||||
pub_tx.write_all(
|
||||
serde_json::to_string(&out_slate)
|
||||
.map_err(|_| ErrorKind::SlateSer)?
|
||||
.as_bytes(),
|
||||
)?;
|
||||
if as_bin {
|
||||
let bin_slate =
|
||||
VersionedBinSlate::try_from(out_slate).map_err(|_| ErrorKind::SlateSer)?;
|
||||
pub_tx.write_all(&byte_ser::to_bytes(&bin_slate).map_err(|_| ErrorKind::SlateSer)?)?;
|
||||
} else {
|
||||
pub_tx.write_all(
|
||||
serde_json::to_string_pretty(&out_slate)
|
||||
.map_err(|_| ErrorKind::SlateSer)?
|
||||
.as_bytes(),
|
||||
)?;
|
||||
}
|
||||
pub_tx.sync_all()?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl SlateGetter for PathToSlate {
|
||||
fn get_tx(&self) -> Result<Slate, Error> {
|
||||
fn get_tx(&self) -> Result<(Slate, bool), Error> {
|
||||
// try as bin first, then as json
|
||||
let mut pub_tx_f = File::open(&self.0)?;
|
||||
let mut content = String::new();
|
||||
pub_tx_f.read_to_string(&mut content)?;
|
||||
Ok(Slate::deserialize_upgrade(&content)?)
|
||||
let mut data = Vec::new();
|
||||
pub_tx_f.read_to_end(&mut data)?;
|
||||
let bin_res = byte_ser::from_bytes::<VersionedBinSlate>(&data);
|
||||
if let Err(e) = bin_res {
|
||||
debug!("Not a valid binary slate: {} - Will try JSON", e);
|
||||
} else {
|
||||
if let Ok(s) = bin_res {
|
||||
return Ok((Slate::upgrade(s.into())?, true));
|
||||
}
|
||||
}
|
||||
|
||||
// Otherwise try json
|
||||
let content = String::from_utf8(data).map_err(|_| ErrorKind::SlateSer)?;
|
||||
Ok((Slate::deserialize_upgrade(&content)?, false))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -65,7 +65,7 @@ impl HttpSlateSender {
|
|||
}
|
||||
|
||||
/// Check version of the listening wallet
|
||||
fn check_other_version(&self, url: &str) -> Result<SlateVersion, Error> {
|
||||
pub fn check_other_version(&self, url: &str) -> Result<SlateVersion, Error> {
|
||||
let req = json!({
|
||||
"jsonrpc": "2.0",
|
||||
"method": "check_version",
|
||||
|
@ -143,10 +143,6 @@ impl HttpSlateSender {
|
|||
}
|
||||
}
|
||||
|
||||
#[deprecated(
|
||||
since = "3.0.0",
|
||||
note = "Remember to handle SlateV4 incompatibilities here"
|
||||
)]
|
||||
impl SlateSender for HttpSlateSender {
|
||||
fn send_tx(&self, slate: &Slate) -> Result<Slate, Error> {
|
||||
let trailing = match self.base_url.ends_with('/') {
|
||||
|
@ -184,13 +180,14 @@ impl SlateSender for HttpSlateSender {
|
|||
SlateVersion::V4 => VersionedSlate::into_version(slate.clone(), SlateVersion::V4)?,
|
||||
SlateVersion::V3 => {
|
||||
let mut slate = slate.clone();
|
||||
let _r: crate::adapters::Reminder;
|
||||
//TODO: Fill out with Slate V4 incompatibilities
|
||||
// * Will need to set particpant id to 1 manually if this is invoice
|
||||
// * Set slate height manually
|
||||
// * Reconcile unknown slate states from V3
|
||||
if false {
|
||||
return Err(ErrorKind::ClientCallback("feature x requested, but other wallet does not support feature x. Please urge other user to upgrade, or re-send tx without feature x".into()).into());
|
||||
}
|
||||
slate.version_info.version = 3;
|
||||
slate.version_info.orig_version = 3;
|
||||
VersionedSlate::into_version(slate, SlateVersion::V3)?
|
||||
}
|
||||
};
|
||||
|
@ -208,7 +205,10 @@ impl SlateSender for HttpSlateSender {
|
|||
trace!("Sending receive_tx request: {}", req);
|
||||
|
||||
let res: String = self.post(&url_str, None, req).map_err(|e| {
|
||||
let report = format!("Posting transaction slate (is recipient listening?): {}", e);
|
||||
let report = format!(
|
||||
"Sending transaction slate to other wallet (is recipient listening?): {}",
|
||||
e
|
||||
);
|
||||
error!("{}", report);
|
||||
ErrorKind::ClientCallback(report)
|
||||
})?;
|
||||
|
@ -227,7 +227,10 @@ impl SlateSender for HttpSlateSender {
|
|||
let slate_value = res["result"]["Ok"].clone();
|
||||
trace!("slate_value: {}", slate_value);
|
||||
let slate = Slate::deserialize_upgrade(&serde_json::to_string(&slate_value).unwrap())
|
||||
.map_err(|_| ErrorKind::SlateDeser)?;
|
||||
.map_err(|e| {
|
||||
error!("Error deserializing response slate: {}", e);
|
||||
ErrorKind::SlateDeser
|
||||
})?;
|
||||
|
||||
Ok(slate)
|
||||
}
|
||||
|
|
|
@ -395,17 +395,12 @@ impl SlateReceiver for KeybaseAllChannels {
|
|||
slate.amount as f64 / 1_000_000_000.0,
|
||||
tx_uuid,
|
||||
);
|
||||
if let Err(e) = slate.verify_messages() {
|
||||
error!("Error validating participant messages: {}", e);
|
||||
return Err(e);
|
||||
}
|
||||
let res = {
|
||||
foreign::receive_tx(
|
||||
&mut **wallet_inst,
|
||||
Some(mask.as_ref().unwrap()),
|
||||
&slate,
|
||||
None,
|
||||
None,
|
||||
false,
|
||||
)
|
||||
};
|
||||
|
|
|
@ -25,13 +25,6 @@ use crate::libwallet::{Error, ErrorKind, Slate};
|
|||
use crate::tor::config::complete_tor_address;
|
||||
use crate::util::ZeroingString;
|
||||
|
||||
/// Little SlateV4 reminder warning helper
|
||||
#[deprecated(
|
||||
since = "3.0.0",
|
||||
note = "Remember to handle SlateV4 incompatibilities here"
|
||||
)]
|
||||
pub struct Reminder;
|
||||
|
||||
/// Sends transactions to a corresponding SlateReceiver
|
||||
pub trait SlateSender {
|
||||
/// Send a transaction slate to another listening wallet and return result
|
||||
|
@ -55,13 +48,14 @@ pub trait SlateReceiver {
|
|||
/// Posts slates to be read later by a corresponding getter
|
||||
pub trait SlatePutter {
|
||||
/// Send a transaction asynchronously
|
||||
fn put_tx(&self, slate: &Slate) -> Result<(), Error>;
|
||||
fn put_tx(&self, slate: &Slate, as_bin: bool) -> Result<(), Error>;
|
||||
}
|
||||
|
||||
/// Checks for a transaction from a corresponding SlatePutter, returns the transaction if it exists
|
||||
pub trait SlateGetter {
|
||||
/// Receive a transaction async. (Actually just read it from wherever and return the slate)
|
||||
fn get_tx(&self) -> Result<Slate, Error>;
|
||||
/// Receive a transaction async. (Actually just read it from wherever and return the slate).
|
||||
/// Returns (Slate, whether it was in binary form)
|
||||
fn get_tx(&self) -> Result<(Slate, bool), Error>;
|
||||
}
|
||||
|
||||
/// select a SlateSender based on method and dest fields from, e.g., SendArgs
|
||||
|
|
|
@ -320,13 +320,8 @@ where
|
|||
&mut self,
|
||||
keychain_mask: Option<&SecretKey>,
|
||||
slate_id: &[u8],
|
||||
participant_id: usize,
|
||||
) -> Result<Context, Error> {
|
||||
let ctx_key = to_key_u64(
|
||||
PRIVATE_TX_CONTEXT_PREFIX,
|
||||
&mut slate_id.to_vec(),
|
||||
participant_id as u64,
|
||||
);
|
||||
let ctx_key = to_key_u64(PRIVATE_TX_CONTEXT_PREFIX, &mut slate_id.to_vec(), 0);
|
||||
let (blind_xor_key, nonce_xor_key) =
|
||||
private_ctx_xor_keys(&self.keychain(keychain_mask)?, slate_id)?;
|
||||
|
||||
|
@ -369,11 +364,8 @@ where
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn get_stored_tx(&self, entry: &TxLogEntry) -> Result<Option<Transaction>, Error> {
|
||||
let filename = match entry.stored_tx.clone() {
|
||||
Some(f) => f,
|
||||
None => return Ok(None),
|
||||
};
|
||||
fn get_stored_tx(&self, uuid: &str) -> Result<Option<Transaction>, Error> {
|
||||
let filename = format!("{}.grintx", uuid);
|
||||
let path = path::Path::new(&self.data_file_dir)
|
||||
.join(TX_SAVE_DIR)
|
||||
.join(filename);
|
||||
|
@ -682,17 +674,8 @@ where
|
|||
self.save(out.clone())
|
||||
}
|
||||
|
||||
fn save_private_context(
|
||||
&mut self,
|
||||
slate_id: &[u8],
|
||||
participant_id: usize,
|
||||
ctx: &Context,
|
||||
) -> Result<(), Error> {
|
||||
let ctx_key = to_key_u64(
|
||||
PRIVATE_TX_CONTEXT_PREFIX,
|
||||
&mut slate_id.to_vec(),
|
||||
participant_id as u64,
|
||||
);
|
||||
fn save_private_context(&mut self, slate_id: &[u8], ctx: &Context) -> Result<(), Error> {
|
||||
let ctx_key = to_key_u64(PRIVATE_TX_CONTEXT_PREFIX, &mut slate_id.to_vec(), 0);
|
||||
let (blind_xor_key, nonce_xor_key) = private_ctx_xor_keys(self.keychain(), slate_id)?;
|
||||
|
||||
let mut s_ctx = ctx.clone();
|
||||
|
@ -709,16 +692,8 @@ where
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn delete_private_context(
|
||||
&mut self,
|
||||
slate_id: &[u8],
|
||||
participant_id: usize,
|
||||
) -> Result<(), Error> {
|
||||
let ctx_key = to_key_u64(
|
||||
PRIVATE_TX_CONTEXT_PREFIX,
|
||||
&mut slate_id.to_vec(),
|
||||
participant_id as u64,
|
||||
);
|
||||
fn delete_private_context(&mut self, slate_id: &[u8]) -> Result<(), Error> {
|
||||
let ctx_key = to_key_u64(PRIVATE_TX_CONTEXT_PREFIX, &mut slate_id.to_vec(), 0);
|
||||
self.db
|
||||
.borrow()
|
||||
.as_ref()
|
||||
|
|
383
impls/src/client_utils/byte_ser.rs
Normal file
383
impls/src/client_utils/byte_ser.rs
Normal file
|
@ -0,0 +1,383 @@
|
|||
// Copyright 2019 The Grin Developers
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// Simple serde byte array serializer, assumes target already
|
||||
// knows how to serialize itself into binary (because that all
|
||||
// this serializer can do)
|
||||
use serde::de::Visitor;
|
||||
use serde::{de, ser, Deserialize, Serialize};
|
||||
use std;
|
||||
use std::fmt::{self, Display};
|
||||
|
||||
pub type Result<T> = std::result::Result<T, Error>;
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub enum Error {
|
||||
Message(String),
|
||||
}
|
||||
|
||||
impl ser::Error for Error {
|
||||
fn custom<T: Display>(msg: T) -> Self {
|
||||
Error::Message(msg.to_string())
|
||||
}
|
||||
}
|
||||
|
||||
impl de::Error for Error {
|
||||
fn custom<T: Display>(msg: T) -> Self {
|
||||
Error::Message(msg.to_string())
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for Error {
|
||||
fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
|
||||
match self {
|
||||
Error::Message(msg) => formatter.write_str(msg),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::error::Error for Error {}
|
||||
|
||||
pub struct ByteSerializer {
|
||||
output: Vec<u8>,
|
||||
}
|
||||
|
||||
pub fn to_bytes<T>(value: &T) -> Result<Vec<u8>>
|
||||
where
|
||||
T: Serialize,
|
||||
{
|
||||
let mut serializer = ByteSerializer { output: vec![] };
|
||||
value.serialize(&mut serializer)?;
|
||||
Ok(serializer.output)
|
||||
}
|
||||
|
||||
impl<'a> ser::Serializer for &'a mut ByteSerializer {
|
||||
type Ok = ();
|
||||
type Error = Error;
|
||||
type SerializeSeq = Self;
|
||||
type SerializeTuple = Self;
|
||||
type SerializeTupleStruct = Self;
|
||||
type SerializeTupleVariant = Self;
|
||||
type SerializeMap = Self;
|
||||
type SerializeStruct = Self;
|
||||
type SerializeStructVariant = Self;
|
||||
|
||||
fn serialize_bool(self, _: bool) -> Result<()> {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn serialize_i8(self, _: i8) -> Result<()> {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn serialize_i16(self, _: i16) -> Result<()> {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn serialize_i32(self, _: i32) -> Result<()> {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn serialize_i64(self, _: i64) -> Result<()> {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn serialize_u8(self, _: u8) -> Result<()> {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn serialize_u16(self, _: u16) -> Result<()> {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn serialize_u32(self, _: u32) -> Result<()> {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn serialize_u64(self, _: u64) -> Result<()> {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn serialize_f32(self, _: f32) -> Result<()> {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn serialize_f64(self, _: f64) -> Result<()> {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn serialize_char(self, _: char) -> Result<()> {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn serialize_str(self, _: &str) -> Result<()> {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn serialize_bytes(self, v: &[u8]) -> Result<()> {
|
||||
for byte in v {
|
||||
self.output.push(*byte)
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn serialize_none(self) -> Result<()> {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn serialize_some<T>(self, _value: &T) -> Result<()>
|
||||
where
|
||||
T: ?Sized + Serialize,
|
||||
{
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn serialize_unit(self) -> Result<()> {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn serialize_unit_struct(self, _name: &'static str) -> Result<()> {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn serialize_unit_variant(
|
||||
self,
|
||||
_name: &'static str,
|
||||
_variant_index: u32,
|
||||
_variant: &'static str,
|
||||
) -> Result<()> {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn serialize_newtype_struct<T>(self, _name: &'static str, _value: &T) -> Result<()>
|
||||
where
|
||||
T: ?Sized + Serialize,
|
||||
{
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn serialize_newtype_variant<T>(
|
||||
self,
|
||||
_name: &'static str,
|
||||
_variant_index: u32,
|
||||
_variant: &'static str,
|
||||
_value: &T,
|
||||
) -> Result<()>
|
||||
where
|
||||
T: ?Sized + Serialize,
|
||||
{
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn serialize_seq(self, _len: Option<usize>) -> Result<Self::SerializeSeq> {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn serialize_tuple(self, _len: usize) -> Result<Self::SerializeTuple> {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn serialize_tuple_struct(
|
||||
self,
|
||||
_name: &'static str,
|
||||
_len: usize,
|
||||
) -> Result<Self::SerializeTupleStruct> {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn serialize_tuple_variant(
|
||||
self,
|
||||
_name: &'static str,
|
||||
_variant_index: u32,
|
||||
_variant: &'static str,
|
||||
_len: usize,
|
||||
) -> Result<Self::SerializeTupleVariant> {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn serialize_map(self, _len: Option<usize>) -> Result<Self::SerializeMap> {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn serialize_struct(self, _name: &'static str, _len: usize) -> Result<Self::SerializeStruct> {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn serialize_struct_variant(
|
||||
self,
|
||||
_name: &'static str,
|
||||
_variant_index: u32,
|
||||
_variant: &'static str,
|
||||
_len: usize,
|
||||
) -> Result<Self::SerializeStructVariant> {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> ser::SerializeSeq for &'a mut ByteSerializer {
|
||||
type Ok = ();
|
||||
type Error = Error;
|
||||
|
||||
fn serialize_element<T>(&mut self, _value: &T) -> Result<()>
|
||||
where
|
||||
T: ?Sized + Serialize,
|
||||
{
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn end(self) -> Result<()> {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> ser::SerializeTuple for &'a mut ByteSerializer {
|
||||
type Ok = ();
|
||||
type Error = Error;
|
||||
|
||||
fn serialize_element<T>(&mut self, _value: &T) -> Result<()>
|
||||
where
|
||||
T: ?Sized + Serialize,
|
||||
{
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn end(self) -> Result<()> {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> ser::SerializeTupleStruct for &'a mut ByteSerializer {
|
||||
type Ok = ();
|
||||
type Error = Error;
|
||||
|
||||
fn serialize_field<T>(&mut self, _value: &T) -> Result<()>
|
||||
where
|
||||
T: ?Sized + Serialize,
|
||||
{
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn end(self) -> Result<()> {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> ser::SerializeTupleVariant for &'a mut ByteSerializer {
|
||||
type Ok = ();
|
||||
type Error = Error;
|
||||
|
||||
fn serialize_field<T>(&mut self, _value: &T) -> Result<()>
|
||||
where
|
||||
T: ?Sized + Serialize,
|
||||
{
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn end(self) -> Result<()> {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> ser::SerializeMap for &'a mut ByteSerializer {
|
||||
type Ok = ();
|
||||
type Error = Error;
|
||||
|
||||
fn serialize_key<T>(&mut self, _key: &T) -> Result<()>
|
||||
where
|
||||
T: ?Sized + Serialize,
|
||||
{
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn serialize_value<T>(&mut self, _value: &T) -> Result<()>
|
||||
where
|
||||
T: ?Sized + Serialize,
|
||||
{
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn end(self) -> Result<()> {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> ser::SerializeStruct for &'a mut ByteSerializer {
|
||||
type Ok = ();
|
||||
type Error = Error;
|
||||
|
||||
fn serialize_field<T>(&mut self, _key: &'static str, _value: &T) -> Result<()>
|
||||
where
|
||||
T: ?Sized + Serialize,
|
||||
{
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn end(self) -> Result<()> {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> ser::SerializeStructVariant for &'a mut ByteSerializer {
|
||||
type Ok = ();
|
||||
type Error = Error;
|
||||
|
||||
fn serialize_field<T>(&mut self, _key: &'static str, _value: &T) -> Result<()>
|
||||
where
|
||||
T: ?Sized + Serialize,
|
||||
{
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn end(self) -> Result<()> {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
|
||||
// Simple Deserializer
|
||||
|
||||
pub struct ByteDeserializer<'de> {
|
||||
input: &'de [u8],
|
||||
}
|
||||
|
||||
impl<'de> ByteDeserializer<'de> {
|
||||
pub fn from_bytes(input: &'de [u8]) -> Self {
|
||||
ByteDeserializer { input }
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_bytes<'a, T>(b: &'a [u8]) -> Result<T>
|
||||
where
|
||||
T: Deserialize<'a>,
|
||||
{
|
||||
let mut deserializer = ByteDeserializer::from_bytes(b);
|
||||
let t = T::deserialize(&mut deserializer)?;
|
||||
Ok(t)
|
||||
}
|
||||
|
||||
impl<'de, 'a> de::Deserializer<'de> for &'a mut ByteDeserializer<'de> {
|
||||
type Error = Error;
|
||||
|
||||
fn deserialize_any<V>(self, visitor: V) -> Result<V::Value>
|
||||
where
|
||||
V: Visitor<'de>,
|
||||
{
|
||||
visitor.visit_bytes(self.input)
|
||||
}
|
||||
|
||||
serde::forward_to_deserialize_any! {
|
||||
bool i8 i16 i32 i64 i128 u8 u16 u32 u64 u128 f32 f64 char str string
|
||||
bytes byte_buf option unit unit_struct newtype_struct seq tuple
|
||||
tuple_struct map struct enum identifier ignored_any
|
||||
}
|
||||
}
|
|
@ -143,7 +143,7 @@ impl fmt::Display for Error {
|
|||
write!(f, "duplicate RPC batch response ID: {}", v)
|
||||
}
|
||||
Error::_WrongBatchResponseId(ref v) => write!(f, "wrong RPC batch response ID: {}", v),
|
||||
_ => f.write_str(std::error::Error::description(self)),
|
||||
ref e => f.write_str(&format!("{}", e)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
pub mod byte_ser;
|
||||
mod client;
|
||||
pub mod json_rpc;
|
||||
|
||||
|
|
|
@ -254,7 +254,7 @@ where
|
|||
};
|
||||
let slate_i = owner::init_send_tx(&mut **w, keychain_mask, args, test_mode)?;
|
||||
let slate = client.send_tx_slate_direct(dest, &slate_i)?;
|
||||
owner::tx_lock_outputs(&mut **w, keychain_mask, &slate, 0)?;
|
||||
owner::tx_lock_outputs(&mut **w, keychain_mask, &slate)?;
|
||||
owner::finalize_tx(&mut **w, keychain_mask, &slate)?
|
||||
};
|
||||
let client = {
|
||||
|
|
|
@ -219,14 +219,8 @@ where
|
|||
let w = w_lock.lc_provider()?.wallet_inst()?;
|
||||
let mask = wallet.2.clone();
|
||||
// receive tx
|
||||
match foreign::receive_tx(
|
||||
&mut **w,
|
||||
(&mask).as_ref(),
|
||||
&Slate::from(slate),
|
||||
None,
|
||||
None,
|
||||
false,
|
||||
) {
|
||||
match foreign::receive_tx(&mut **w, (&mask).as_ref(), &Slate::from(slate), None, false)
|
||||
{
|
||||
Err(e) => {
|
||||
return Ok(WalletProxyMessage {
|
||||
sender_id: m.dest,
|
||||
|
|
|
@ -26,6 +26,7 @@ strum = "0.15"
|
|||
strum_macros = "0.15"
|
||||
ed25519-dalek = "1.0.0-pre.1"
|
||||
byteorder = "1"
|
||||
base64 = "0.9"
|
||||
|
||||
grin_wallet_util = { path = "../util", version = "4.0.0-alpha.1" }
|
||||
grin_wallet_config = { path = "../config", version = "4.0.0-alpha.1" }
|
||||
|
|
|
@ -16,17 +16,17 @@
|
|||
use strum::IntoEnumIterator;
|
||||
|
||||
use crate::api_impl::owner::check_ttl;
|
||||
use crate::grin_core::core::transaction::Transaction;
|
||||
use crate::grin_keychain::Keychain;
|
||||
use crate::grin_util::secp::key::SecretKey;
|
||||
use crate::internal::{tx, updater};
|
||||
use crate::internal::{selection, tx, updater};
|
||||
use crate::slate_versions::SlateVersion;
|
||||
use crate::{
|
||||
address, BlockFees, CbData, Error, ErrorKind, NodeClient, Slate, TxLogEntryType, VersionInfo,
|
||||
WalletBackend,
|
||||
address, BlockFees, CbData, Error, ErrorKind, NodeClient, Slate, SlateState, TxLogEntryType,
|
||||
VersionInfo, WalletBackend,
|
||||
};
|
||||
|
||||
const FOREIGN_API_VERSION: u16 = 2;
|
||||
const USER_MESSAGE_MAX_LEN: usize = 256;
|
||||
|
||||
/// Return the version info
|
||||
pub fn check_version() -> VersionInfo {
|
||||
|
@ -51,18 +51,12 @@ where
|
|||
updater::build_coinbase(&mut *w, keychain_mask, block_fees, test_mode)
|
||||
}
|
||||
|
||||
/// verify slate messages
|
||||
pub fn verify_slate_messages(slate: &Slate) -> Result<(), Error> {
|
||||
slate.verify_messages()
|
||||
}
|
||||
|
||||
/// Receive a tx as recipient
|
||||
pub fn receive_tx<'a, T: ?Sized, C, K>(
|
||||
w: &mut T,
|
||||
keychain_mask: Option<&SecretKey>,
|
||||
slate: &Slate,
|
||||
dest_acct_name: Option<&str>,
|
||||
message: Option<String>,
|
||||
use_test_rng: bool,
|
||||
) -> Result<Slate, Error>
|
||||
where
|
||||
|
@ -96,28 +90,25 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
let message = match message {
|
||||
Some(mut m) => {
|
||||
m.truncate(USER_MESSAGE_MAX_LEN);
|
||||
Some(m)
|
||||
}
|
||||
None => None,
|
||||
};
|
||||
// if this is compact mode, we need to create the transaction now
|
||||
if ret_slate.is_compact() {
|
||||
ret_slate.tx = Some(Transaction::empty());
|
||||
}
|
||||
|
||||
tx::add_output_to_slate(
|
||||
let height = w.last_confirmed_height()?;
|
||||
let keychain = w.keychain(keychain_mask)?;
|
||||
|
||||
let context = tx::add_output_to_slate(
|
||||
&mut *w,
|
||||
keychain_mask,
|
||||
&mut ret_slate,
|
||||
height,
|
||||
&parent_key_id,
|
||||
1,
|
||||
message,
|
||||
false,
|
||||
use_test_rng,
|
||||
)?;
|
||||
tx::update_message(&mut *w, keychain_mask, &ret_slate)?;
|
||||
|
||||
let keychain = w.keychain(keychain_mask)?;
|
||||
let excess = ret_slate.calc_excess(&keychain)?;
|
||||
let excess = ret_slate.calc_excess(keychain.secp())?;
|
||||
|
||||
if let Some(ref mut p) = ret_slate.payment_proof {
|
||||
let sig = tx::create_payment_proof_signature(
|
||||
|
@ -129,7 +120,15 @@ where
|
|||
|
||||
p.receiver_signature = Some(sig);
|
||||
}
|
||||
// Can remove amount and fee now
|
||||
// as well as sender's sig data
|
||||
if ret_slate.is_compact() {
|
||||
ret_slate.amount = 0;
|
||||
ret_slate.fee = 0;
|
||||
ret_slate.remove_other_sigdata(&keychain, &context.sec_nonce, &context.sec_key)?;
|
||||
}
|
||||
|
||||
ret_slate.state = SlateState::Standard2;
|
||||
Ok(ret_slate)
|
||||
}
|
||||
|
||||
|
@ -146,14 +145,23 @@ where
|
|||
{
|
||||
let mut sl = slate.clone();
|
||||
check_ttl(w, &sl)?;
|
||||
let context = w.get_private_context(keychain_mask, sl.id.as_bytes(), 1)?;
|
||||
tx::complete_tx(&mut *w, keychain_mask, &mut sl, 1, &context)?;
|
||||
let context = w.get_private_context(keychain_mask, sl.id.as_bytes())?;
|
||||
if sl.is_compact() {
|
||||
let mut temp_ctx = context.clone();
|
||||
temp_ctx.sec_key = context.initial_sec_key.clone();
|
||||
temp_ctx.sec_nonce = context.initial_sec_nonce.clone();
|
||||
selection::repopulate_tx(&mut *w, keychain_mask, &mut sl, &temp_ctx, false)?;
|
||||
}
|
||||
tx::complete_tx(&mut *w, keychain_mask, &mut sl, &context)?;
|
||||
tx::update_stored_tx(&mut *w, keychain_mask, &context, &mut sl, true)?;
|
||||
tx::update_message(&mut *w, keychain_mask, &sl)?;
|
||||
{
|
||||
let mut batch = w.batch(keychain_mask)?;
|
||||
batch.delete_private_context(sl.id.as_bytes(), 1)?;
|
||||
batch.delete_private_context(sl.id.as_bytes())?;
|
||||
batch.commit()?;
|
||||
}
|
||||
sl.state = SlateState::Invoice3;
|
||||
if sl.is_compact() {
|
||||
sl.amount = 0;
|
||||
}
|
||||
Ok(sl)
|
||||
}
|
||||
|
|
|
@ -25,7 +25,7 @@ use crate::util::OnionV3Address;
|
|||
use crate::api_impl::owner_updater::StatusMessage;
|
||||
use crate::grin_keychain::{Identifier, Keychain};
|
||||
use crate::internal::{keys, scan, selection, tx, updater};
|
||||
use crate::slate::{PaymentInfo, Slate};
|
||||
use crate::slate::{PaymentInfo, Slate, SlateState};
|
||||
use crate::types::{AcctPathMapping, NodeClient, TxLogEntry, WalletBackend, WalletInfo};
|
||||
use crate::{
|
||||
address, wallet_lock, InitTxArgs, IssueInvoiceTxArgs, NodeHeightResult, OutputCommitMapping,
|
||||
|
@ -38,8 +38,6 @@ use ed25519_dalek::SecretKey as DalekSecretKey;
|
|||
use std::sync::mpsc::Sender;
|
||||
use std::sync::Arc;
|
||||
|
||||
const USER_MESSAGE_MAX_LEN: usize = 256;
|
||||
|
||||
/// List of accounts
|
||||
pub fn accounts<'a, T: ?Sized, C, K>(w: &mut T) -> Result<Vec<AcctPathMapping>, Error>
|
||||
where
|
||||
|
@ -318,15 +316,18 @@ where
|
|||
None => w.parent_key_id(),
|
||||
};
|
||||
|
||||
let message = match args.message {
|
||||
Some(mut m) => {
|
||||
m.truncate(USER_MESSAGE_MAX_LEN);
|
||||
Some(m)
|
||||
}
|
||||
None => None,
|
||||
};
|
||||
let mut slate = tx::new_tx_slate(
|
||||
&mut *w,
|
||||
args.amount,
|
||||
false,
|
||||
2,
|
||||
use_test_rng,
|
||||
args.ttl_blocks,
|
||||
)?;
|
||||
|
||||
let mut slate = tx::new_tx_slate(&mut *w, args.amount, 2, use_test_rng, args.ttl_blocks)?;
|
||||
if let Some(v) = args.target_slate_version {
|
||||
slate.version_info.version = v;
|
||||
};
|
||||
|
||||
// if we just want to estimate, don't save a context, just send the results
|
||||
// back
|
||||
|
@ -346,17 +347,17 @@ where
|
|||
return Ok(slate);
|
||||
}
|
||||
|
||||
let height = w.w2n_client().get_chain_tip()?.0;
|
||||
let mut context = tx::add_inputs_to_slate(
|
||||
&mut *w,
|
||||
keychain_mask,
|
||||
&mut slate,
|
||||
height,
|
||||
args.minimum_confirmations,
|
||||
args.max_outputs as usize,
|
||||
args.num_change_outputs as usize,
|
||||
args.selection_strategy_is_use_all,
|
||||
&parent_key_id,
|
||||
0,
|
||||
message,
|
||||
true,
|
||||
use_test_rng,
|
||||
)?;
|
||||
|
@ -385,11 +386,12 @@ where
|
|||
// recieve the transaction back
|
||||
{
|
||||
let mut batch = w.batch(keychain_mask)?;
|
||||
batch.save_private_context(slate.id.as_bytes(), 0, &context)?;
|
||||
batch.save_private_context(slate.id.as_bytes(), &context)?;
|
||||
batch.commit()?;
|
||||
}
|
||||
if let Some(v) = args.target_slate_version {
|
||||
slate.version_info.orig_version = v;
|
||||
|
||||
if slate.is_compact() {
|
||||
slate.compact()?;
|
||||
}
|
||||
|
||||
Ok(slate)
|
||||
|
@ -418,36 +420,32 @@ where
|
|||
None => w.parent_key_id(),
|
||||
};
|
||||
|
||||
let message = match args.message {
|
||||
Some(mut m) => {
|
||||
m.truncate(USER_MESSAGE_MAX_LEN);
|
||||
Some(m)
|
||||
}
|
||||
None => None,
|
||||
};
|
||||
|
||||
let mut slate = tx::new_tx_slate(&mut *w, args.amount, 2, use_test_rng, None)?;
|
||||
let mut slate = tx::new_tx_slate(&mut *w, args.amount, true, 2, use_test_rng, None)?;
|
||||
let height = w.w2n_client().get_chain_tip()?.0;
|
||||
let context = tx::add_output_to_slate(
|
||||
&mut *w,
|
||||
keychain_mask,
|
||||
&mut slate,
|
||||
height,
|
||||
&parent_key_id,
|
||||
1,
|
||||
message,
|
||||
true,
|
||||
use_test_rng,
|
||||
)?;
|
||||
|
||||
if let Some(v) = args.target_slate_version {
|
||||
slate.version_info.version = v;
|
||||
};
|
||||
|
||||
// Save the aggsig context in our DB for when we
|
||||
// recieve the transaction back
|
||||
{
|
||||
let mut batch = w.batch(keychain_mask)?;
|
||||
batch.save_private_context(slate.id.as_bytes(), 1, &context)?;
|
||||
batch.save_private_context(slate.id.as_bytes(), &context)?;
|
||||
batch.commit()?;
|
||||
}
|
||||
|
||||
if let Some(v) = args.target_slate_version {
|
||||
slate.version_info.orig_version = v;
|
||||
if slate.is_compact() {
|
||||
slate.compact()?;
|
||||
}
|
||||
|
||||
Ok(slate)
|
||||
|
@ -493,49 +491,77 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
let message = match args.message {
|
||||
Some(mut m) => {
|
||||
m.truncate(USER_MESSAGE_MAX_LEN);
|
||||
Some(m)
|
||||
}
|
||||
None => None,
|
||||
};
|
||||
|
||||
// update slate current height
|
||||
ret_slate.height = w.w2n_client().get_chain_tip()?.0;
|
||||
let height = w.w2n_client().get_chain_tip()?.0;
|
||||
|
||||
// update ttl if desired
|
||||
if let Some(b) = args.ttl_blocks {
|
||||
ret_slate.ttl_cutoff_height = Some(ret_slate.height + b);
|
||||
ret_slate.ttl_cutoff_height = height + b;
|
||||
}
|
||||
|
||||
let context = tx::add_inputs_to_slate(
|
||||
// if this is compact mode, we need to create the transaction now
|
||||
if ret_slate.is_compact() {
|
||||
ret_slate.tx = Some(Transaction::empty());
|
||||
}
|
||||
|
||||
// if self sending, make sure to store 'initiator' keys
|
||||
let context_res = w.get_private_context(keychain_mask, slate.id.as_bytes());
|
||||
|
||||
let mut context = tx::add_inputs_to_slate(
|
||||
&mut *w,
|
||||
keychain_mask,
|
||||
&mut ret_slate,
|
||||
height,
|
||||
args.minimum_confirmations,
|
||||
args.max_outputs as usize,
|
||||
args.num_change_outputs as usize,
|
||||
args.selection_strategy_is_use_all,
|
||||
&parent_key_id,
|
||||
0,
|
||||
message,
|
||||
false,
|
||||
use_test_rng,
|
||||
)?;
|
||||
|
||||
let keychain = w.keychain(keychain_mask)?;
|
||||
// needs to be stored as we're removing sig data for return trip. this needs to be present
|
||||
// when locking transaction context and updating tx log with excess later
|
||||
context.calculated_excess = Some(ret_slate.calc_excess(keychain.secp())?);
|
||||
|
||||
// if self-sending, merge contexts
|
||||
if let Ok(c) = context_res {
|
||||
context.initial_sec_key = c.initial_sec_key;
|
||||
context.initial_sec_nonce = c.initial_sec_nonce;
|
||||
context.is_invoice = c.is_invoice;
|
||||
context.fee = c.fee;
|
||||
context.amount = c.amount;
|
||||
for o in c.output_ids.iter() {
|
||||
context.output_ids.push(o.clone());
|
||||
}
|
||||
for i in c.input_ids.iter() {
|
||||
context.input_ids.push(i.clone());
|
||||
}
|
||||
}
|
||||
|
||||
// adjust offset with inputs, repopulate inputs (initiator needs them for now)
|
||||
// TODO: Revisit post-HF3
|
||||
if ret_slate.is_compact() {
|
||||
tx::sub_inputs_from_offset(&mut *w, keychain_mask, &context, &mut ret_slate)?;
|
||||
selection::repopulate_tx(&mut *w, keychain_mask, &mut ret_slate, &context, false)?;
|
||||
}
|
||||
|
||||
// Save the aggsig context in our DB for when we
|
||||
// recieve the transaction back
|
||||
{
|
||||
let mut batch = w.batch(keychain_mask)?;
|
||||
batch.save_private_context(slate.id.as_bytes(), 0, &context)?;
|
||||
batch.save_private_context(slate.id.as_bytes(), &context)?;
|
||||
batch.commit()?;
|
||||
}
|
||||
|
||||
if let Some(v) = args.target_slate_version {
|
||||
ret_slate.version_info.orig_version = v;
|
||||
// Can remove amount as well as other sig data now
|
||||
if ret_slate.is_compact() {
|
||||
ret_slate.amount = 0;
|
||||
ret_slate.remove_other_sigdata(&keychain, &context.sec_nonce, &context.sec_key)?;
|
||||
}
|
||||
|
||||
ret_slate.state = SlateState::Invoice2;
|
||||
Ok(ret_slate)
|
||||
}
|
||||
|
||||
|
@ -544,15 +570,32 @@ pub fn tx_lock_outputs<'a, T: ?Sized, C, K>(
|
|||
w: &mut T,
|
||||
keychain_mask: Option<&SecretKey>,
|
||||
slate: &Slate,
|
||||
participant_id: usize,
|
||||
) -> Result<(), Error>
|
||||
where
|
||||
T: WalletBackend<'a, C, K>,
|
||||
C: NodeClient + 'a,
|
||||
K: Keychain + 'a,
|
||||
{
|
||||
let context = w.get_private_context(keychain_mask, slate.id.as_bytes(), participant_id)?;
|
||||
selection::lock_tx_context(&mut *w, keychain_mask, slate, &context)
|
||||
let context = w.get_private_context(keychain_mask, slate.id.as_bytes())?;
|
||||
let mut sl = slate.clone();
|
||||
let mut excess_override = None;
|
||||
if sl.is_compact() && sl.tx == None {
|
||||
// attempt to repopulate if we're the initiator
|
||||
sl.tx = Some(Transaction::empty());
|
||||
selection::repopulate_tx(&mut *w, keychain_mask, &mut sl, &context, true)?;
|
||||
} else if sl.participant_data.len() == 1 {
|
||||
// purely for invoice workflow, payer needs the excess back temporarily for storage
|
||||
excess_override = context.calculated_excess;
|
||||
}
|
||||
let height = w.w2n_client().get_chain_tip()?.0;
|
||||
selection::lock_tx_context(
|
||||
&mut *w,
|
||||
keychain_mask,
|
||||
&sl,
|
||||
height,
|
||||
&context,
|
||||
excess_override,
|
||||
)
|
||||
}
|
||||
|
||||
/// Finalize slate
|
||||
|
@ -568,17 +611,32 @@ where
|
|||
{
|
||||
let mut sl = slate.clone();
|
||||
check_ttl(w, &sl)?;
|
||||
let context = w.get_private_context(keychain_mask, sl.id.as_bytes(), 0)?;
|
||||
let context = w.get_private_context(keychain_mask, sl.id.as_bytes())?;
|
||||
let parent_key_id = w.parent_key_id();
|
||||
tx::complete_tx(&mut *w, keychain_mask, &mut sl, 0, &context)?;
|
||||
|
||||
// since we're now actually inserting our inputs, pick an offset and adjust
|
||||
// our contribution to the excess by offset amount
|
||||
// TODO: Post HF3, this should allow for inputs to be picked at this stage
|
||||
// as opposed to locking them prior to this stage, as the excess to this point
|
||||
// will just be the change output
|
||||
|
||||
if sl.is_compact() {
|
||||
tx::sub_inputs_from_offset(&mut *w, keychain_mask, &context, &mut sl)?;
|
||||
selection::repopulate_tx(&mut *w, keychain_mask, &mut sl, &context, true)?;
|
||||
}
|
||||
|
||||
tx::complete_tx(&mut *w, keychain_mask, &mut sl, &context)?;
|
||||
tx::verify_slate_payment_proof(&mut *w, keychain_mask, &parent_key_id, &context, &sl)?;
|
||||
tx::update_stored_tx(&mut *w, keychain_mask, &context, &sl, false)?;
|
||||
tx::update_message(&mut *w, keychain_mask, &sl)?;
|
||||
{
|
||||
let mut batch = w.batch(keychain_mask)?;
|
||||
batch.delete_private_context(sl.id.as_bytes(), 0)?;
|
||||
batch.delete_private_context(sl.id.as_bytes())?;
|
||||
batch.commit()?;
|
||||
}
|
||||
sl.state = SlateState::Standard3;
|
||||
if sl.is_compact() {
|
||||
sl.amount = 0;
|
||||
}
|
||||
Ok(sl)
|
||||
}
|
||||
|
||||
|
@ -612,16 +670,13 @@ where
|
|||
}
|
||||
|
||||
/// get stored tx
|
||||
pub fn get_stored_tx<'a, T: ?Sized, C, K>(
|
||||
w: &T,
|
||||
entry: &TxLogEntry,
|
||||
) -> Result<Option<Transaction>, Error>
|
||||
pub fn get_stored_tx<'a, T: ?Sized, C, K>(w: &T, id: &Uuid) -> Result<Option<Transaction>, Error>
|
||||
where
|
||||
T: WalletBackend<'a, C, K>,
|
||||
C: NodeClient + 'a,
|
||||
K: Keychain + 'a,
|
||||
{
|
||||
w.get_stored_tx(entry)
|
||||
w.get_stored_tx(&format!("{}", id))
|
||||
}
|
||||
|
||||
/// Posts a transaction to the chain
|
||||
|
@ -644,11 +699,6 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
/// verify slate messages
|
||||
pub fn verify_slate_messages(slate: &Slate) -> Result<(), Error> {
|
||||
slate.verify_messages()
|
||||
}
|
||||
|
||||
/// check repair
|
||||
/// Accepts a wallet inst instead of a raw wallet so it can
|
||||
/// lock as little as possible
|
||||
|
@ -727,6 +777,26 @@ where
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// return whether slate was an invoice tx
|
||||
pub fn context_is_invoice<'a, L, C, K>(
|
||||
wallet_inst: Arc<Mutex<Box<dyn WalletInst<'a, L, C, K>>>>,
|
||||
keychain_mask: Option<&SecretKey>,
|
||||
slate: &Slate,
|
||||
) -> Result<bool, Error>
|
||||
where
|
||||
L: WalletLCProvider<'a, C, K>,
|
||||
C: NodeClient + 'a,
|
||||
K: Keychain + 'a,
|
||||
{
|
||||
let context = {
|
||||
wallet_lock!(wallet_inst, w);
|
||||
let context = w.get_private_context(keychain_mask, slate.id.as_bytes())?;
|
||||
context
|
||||
};
|
||||
Ok(context.is_invoice)
|
||||
}
|
||||
|
||||
/// Experimental, wrap the entire definition of how a wallet's state is updated
|
||||
pub fn update_wallet_state<'a, L, C, K>(
|
||||
wallet_inst: Arc<Mutex<Box<dyn WalletInst<'a, L, C, K>>>>,
|
||||
|
@ -873,8 +943,8 @@ where
|
|||
{
|
||||
// Refuse if TTL is expired
|
||||
let last_confirmed_height = w.last_confirmed_height()?;
|
||||
if let Some(e) = slate.ttl_cutoff_height {
|
||||
if last_confirmed_height >= e {
|
||||
if slate.ttl_cutoff_height != 0 {
|
||||
if last_confirmed_height >= slate.ttl_cutoff_height {
|
||||
return Err(ErrorKind::TransactionExpired.into());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -24,31 +24,6 @@ use grin_wallet_util::OnionV3Address;
|
|||
|
||||
use ed25519_dalek::Signature as DalekSignature;
|
||||
|
||||
/// Send TX API Args
|
||||
// TODO: This is here to ensure the legacy V1 API remains intact
|
||||
// remove this when v1 api is removed
|
||||
#[derive(Clone, Serialize, Deserialize)]
|
||||
pub struct SendTXArgs {
|
||||
/// amount to send
|
||||
pub amount: u64,
|
||||
/// minimum confirmations
|
||||
pub minimum_confirmations: u64,
|
||||
/// payment method
|
||||
pub method: String,
|
||||
/// destination url
|
||||
pub dest: String,
|
||||
/// Max number of outputs
|
||||
pub max_outputs: usize,
|
||||
/// Number of change outputs to generate
|
||||
pub num_change_outputs: usize,
|
||||
/// whether to use all outputs (combine)
|
||||
pub selection_strategy_is_use_all: bool,
|
||||
/// Optional message, that will be signed
|
||||
pub message: Option<String>,
|
||||
/// Optional slate version to target when sending
|
||||
pub target_slate_version: Option<u16>,
|
||||
}
|
||||
|
||||
/// V2 Init / Send TX API Args
|
||||
#[derive(Clone, Serialize, Deserialize)]
|
||||
pub struct InitTxArgs {
|
||||
|
@ -80,12 +55,6 @@ pub struct InitTxArgs {
|
|||
/// as many outputs as are needed to meet the amount, (and no more) starting with the smallest
|
||||
/// value outputs.
|
||||
pub selection_strategy_is_use_all: bool,
|
||||
/// An optional participant message to include alongside the sender's public
|
||||
/// ParticipantData within the slate. This message will include a signature created with the
|
||||
/// sender's private excess value, and will be publically verifiable. Note this message is for
|
||||
/// the convenience of the participants during the exchange; it is not included in the final
|
||||
/// transaction sent to the chain. The message will be truncated to 256 characters.
|
||||
pub message: Option<String>,
|
||||
/// Optionally set the output target slate version (acceptable
|
||||
/// down to the minimum slate version compatible with the current. If `None` the slate
|
||||
/// is generated with the latest version.
|
||||
|
@ -133,7 +102,6 @@ impl Default for InitTxArgs {
|
|||
max_outputs: 500,
|
||||
num_change_outputs: 1,
|
||||
selection_strategy_is_use_all: true,
|
||||
message: None,
|
||||
target_slate_version: None,
|
||||
ttl_blocks: None,
|
||||
estimate_only: Some(false),
|
||||
|
@ -153,8 +121,6 @@ pub struct IssueInvoiceTxArgs {
|
|||
/// The invoice amount in nanogrins. (`1 G = 1_000_000_000nG`)
|
||||
#[serde(with = "secp_ser::string_or_u64")]
|
||||
pub amount: u64,
|
||||
/// Optional message, that will be signed
|
||||
pub message: Option<String>,
|
||||
/// Optionally set the output target slate version (acceptable
|
||||
/// down to the minimum slate version compatible with the current. If `None` the slate
|
||||
/// is generated with the latest version.
|
||||
|
@ -166,7 +132,6 @@ impl Default for IssueInvoiceTxArgs {
|
|||
IssueInvoiceTxArgs {
|
||||
dest_acct_name: None,
|
||||
amount: 0,
|
||||
message: None,
|
||||
target_slate_version: None,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -186,6 +186,10 @@ pub enum ErrorKind {
|
|||
#[fail(display = "Committed Error")]
|
||||
Committed(committed::Error),
|
||||
|
||||
/// Error from summing commitments
|
||||
#[fail(display = "Committed Error: {}", _0)]
|
||||
Commit(String),
|
||||
|
||||
/// Can't parse slate version
|
||||
#[fail(display = "Can't parse slate version")]
|
||||
SlateVersionParse,
|
||||
|
@ -258,6 +262,14 @@ pub enum ErrorKind {
|
|||
#[fail(display = "Transaction Expired")]
|
||||
TransactionExpired,
|
||||
|
||||
/// Kernel features args don't exist
|
||||
#[fail(display = "Kernel Features Arg {} missing", _0)]
|
||||
KernelFeaturesMissing(String),
|
||||
|
||||
/// Unknown Kernel Feature
|
||||
#[fail(display = "Unknown Kernel Feature: {}", _0)]
|
||||
UnknownKernelFeatures(u8),
|
||||
|
||||
/// Other
|
||||
#[fail(display = "Generic error: {}", _0)]
|
||||
GenericError(String),
|
||||
|
|
|
@ -24,6 +24,7 @@ use crate::grin_core::libtx::{
|
|||
};
|
||||
use crate::grin_keychain::{Identifier, Keychain};
|
||||
use crate::grin_util::secp::key::SecretKey;
|
||||
use crate::grin_util::secp::pedersen;
|
||||
use crate::internal::keys;
|
||||
use crate::slate::Slate;
|
||||
use crate::types::*;
|
||||
|
@ -40,11 +41,13 @@ pub fn build_send_tx<'a, T: ?Sized, C, K>(
|
|||
keychain: &K,
|
||||
keychain_mask: Option<&SecretKey>,
|
||||
slate: &mut Slate,
|
||||
current_height: u64,
|
||||
minimum_confirmations: u64,
|
||||
max_outputs: usize,
|
||||
change_outputs: usize,
|
||||
selection_strategy_is_use_all: bool,
|
||||
parent_key_id: Identifier,
|
||||
is_invoice: bool,
|
||||
use_test_nonce: bool,
|
||||
) -> Result<Context, Error>
|
||||
where
|
||||
|
@ -52,16 +55,21 @@ where
|
|||
C: NodeClient + 'a,
|
||||
K: Keychain + 'a,
|
||||
{
|
||||
//TODO: Revise HF3. If we're sending V4 slates, only include
|
||||
// change outputs in excess sum
|
||||
let include_inputs_in_sum = !slate.is_compact();
|
||||
|
||||
let (elems, inputs, change_amounts_derivations, fee) = select_send_tx(
|
||||
wallet,
|
||||
keychain_mask,
|
||||
slate.amount,
|
||||
slate.height,
|
||||
current_height,
|
||||
minimum_confirmations,
|
||||
max_outputs,
|
||||
change_outputs,
|
||||
selection_strategy_is_use_all,
|
||||
&parent_key_id,
|
||||
include_inputs_in_sum,
|
||||
)?;
|
||||
|
||||
// Update the fee on the slate so we account for this when building the tx.
|
||||
|
@ -75,10 +83,11 @@ where
|
|||
blinding.secret_key(&keychain.secp()).unwrap(),
|
||||
&parent_key_id,
|
||||
use_test_nonce,
|
||||
0,
|
||||
is_invoice,
|
||||
);
|
||||
|
||||
context.fee = fee;
|
||||
context.amount = slate.amount;
|
||||
|
||||
// Store our private identifiers for each input
|
||||
for input in inputs {
|
||||
|
@ -105,7 +114,9 @@ pub fn lock_tx_context<'a, T: ?Sized, C, K>(
|
|||
wallet: &mut T,
|
||||
keychain_mask: Option<&SecretKey>,
|
||||
slate: &Slate,
|
||||
current_height: u64,
|
||||
context: &Context,
|
||||
excess_override: Option<pedersen::Commitment>,
|
||||
) -> Result<(), Error>
|
||||
where
|
||||
T: WalletBackend<'a, C, K>,
|
||||
|
@ -132,9 +143,8 @@ where
|
|||
|
||||
let tx_entry = {
|
||||
let lock_inputs = context.get_inputs();
|
||||
let messages = Some(slate.participant_messages());
|
||||
let slate_id = slate.id;
|
||||
let height = slate.height;
|
||||
let height = current_height;
|
||||
let parent_key_id = context.parent_key_id.clone();
|
||||
let mut batch = wallet.batch(keychain_mask)?;
|
||||
let log_id = batch.next_tx_log_id(&parent_key_id)?;
|
||||
|
@ -142,13 +152,19 @@ where
|
|||
t.tx_slate_id = Some(slate_id);
|
||||
let filename = format!("{}.grintx", slate_id);
|
||||
t.stored_tx = Some(filename);
|
||||
t.fee = Some(slate.fee);
|
||||
t.ttl_cutoff_height = slate.ttl_cutoff_height;
|
||||
t.fee = Some(context.fee);
|
||||
t.ttl_cutoff_height = match slate.ttl_cutoff_height {
|
||||
0 => None,
|
||||
n => Some(n),
|
||||
};
|
||||
|
||||
if let Ok(e) = slate.calc_excess(&keychain) {
|
||||
if let Ok(e) = slate.calc_excess(keychain.secp()) {
|
||||
t.kernel_excess = Some(e)
|
||||
}
|
||||
t.kernel_lookup_min_height = Some(slate.height);
|
||||
if let Some(e) = excess_override {
|
||||
t.kernel_excess = Some(e)
|
||||
}
|
||||
t.kernel_lookup_min_height = Some(current_height);
|
||||
|
||||
let mut amount_debited = 0;
|
||||
t.num_inputs = lock_inputs.len();
|
||||
|
@ -160,7 +176,6 @@ where
|
|||
}
|
||||
|
||||
t.amount_debited = amount_debited;
|
||||
t.messages = messages;
|
||||
|
||||
// store extra payment proof info, if required
|
||||
if let Some(ref p) = slate.payment_proof {
|
||||
|
@ -225,9 +240,11 @@ pub fn build_recipient_output<'a, T: ?Sized, C, K>(
|
|||
wallet: &mut T,
|
||||
keychain_mask: Option<&SecretKey>,
|
||||
slate: &mut Slate,
|
||||
current_height: u64,
|
||||
parent_key_id: Identifier,
|
||||
is_invoice: bool,
|
||||
use_test_rng: bool,
|
||||
) -> Result<(Identifier, Context), Error>
|
||||
) -> Result<(Identifier, Context, TxLogEntry), Error>
|
||||
where
|
||||
T: WalletBackend<'a, C, K>,
|
||||
C: NodeClient + 'a,
|
||||
|
@ -238,7 +255,7 @@ where
|
|||
let keychain = wallet.keychain(keychain_mask)?;
|
||||
let key_id_inner = key_id.clone();
|
||||
let amount = slate.amount;
|
||||
let height = slate.height;
|
||||
let height = current_height;
|
||||
|
||||
let slate_id = slate.id;
|
||||
let blinding = slate.add_transaction_elements(
|
||||
|
@ -255,11 +272,12 @@ where
|
|||
.unwrap(),
|
||||
&parent_key_id,
|
||||
use_test_rng,
|
||||
1,
|
||||
is_invoice,
|
||||
);
|
||||
|
||||
context.add_output(&key_id, &None, amount);
|
||||
let messages = Some(slate.participant_messages());
|
||||
context.amount = amount;
|
||||
context.fee = slate.fee;
|
||||
let commit = wallet.calc_commit_for_cache(keychain_mask, amount, &key_id_inner)?;
|
||||
let mut batch = wallet.batch(keychain_mask)?;
|
||||
let log_id = batch.next_tx_log_id(&parent_key_id)?;
|
||||
|
@ -267,13 +285,15 @@ where
|
|||
t.tx_slate_id = Some(slate_id);
|
||||
t.amount_credited = amount;
|
||||
t.num_outputs = 1;
|
||||
t.messages = messages;
|
||||
t.ttl_cutoff_height = slate.ttl_cutoff_height;
|
||||
t.ttl_cutoff_height = match slate.ttl_cutoff_height {
|
||||
0 => None,
|
||||
n => Some(n),
|
||||
};
|
||||
// when invoicing, this will be invalid
|
||||
if let Ok(e) = slate.calc_excess(&keychain) {
|
||||
if let Ok(e) = slate.calc_excess(keychain.secp()) {
|
||||
t.kernel_excess = Some(e)
|
||||
}
|
||||
t.kernel_lookup_min_height = Some(slate.height);
|
||||
t.kernel_lookup_min_height = Some(current_height);
|
||||
batch.save(OutputData {
|
||||
root_key_id: parent_key_id.clone(),
|
||||
key_id: key_id_inner.clone(),
|
||||
|
@ -287,10 +307,10 @@ where
|
|||
is_coinbase: false,
|
||||
tx_log_entry: Some(log_id),
|
||||
})?;
|
||||
batch.save_tx_log_entry(t, &parent_key_id)?;
|
||||
batch.save_tx_log_entry(t.clone(), &parent_key_id)?;
|
||||
batch.commit()?;
|
||||
|
||||
Ok((key_id, context))
|
||||
Ok((key_id, context, t))
|
||||
}
|
||||
|
||||
/// Builds a transaction to send to someone from the HD seed associated with the
|
||||
|
@ -306,6 +326,7 @@ pub fn select_send_tx<'a, T: ?Sized, C, K, B>(
|
|||
change_outputs: usize,
|
||||
selection_strategy_is_use_all: bool,
|
||||
parent_key_id: &Identifier,
|
||||
include_inputs_in_sum: bool,
|
||||
) -> Result<
|
||||
(
|
||||
Vec<Box<build::Append<K, B>>>,
|
||||
|
@ -333,8 +354,15 @@ where
|
|||
)?;
|
||||
|
||||
// build transaction skeleton with inputs and change
|
||||
let (parts, change_amounts_derivations) =
|
||||
inputs_and_change(&coins, wallet, keychain_mask, amount, fee, change_outputs)?;
|
||||
let (parts, change_amounts_derivations) = inputs_and_change(
|
||||
&coins,
|
||||
wallet,
|
||||
keychain_mask,
|
||||
amount,
|
||||
fee,
|
||||
change_outputs,
|
||||
include_inputs_in_sum,
|
||||
)?;
|
||||
|
||||
Ok((parts, coins, change_amounts_derivations, fee))
|
||||
}
|
||||
|
@ -455,6 +483,7 @@ pub fn inputs_and_change<'a, T: ?Sized, C, K, B>(
|
|||
amount: u64,
|
||||
fee: u64,
|
||||
num_change_outputs: usize,
|
||||
include_inputs_in_sum: bool,
|
||||
) -> Result<
|
||||
(
|
||||
Vec<Box<build::Append<K, B>>>,
|
||||
|
@ -479,11 +508,13 @@ where
|
|||
let change = total - amount - fee;
|
||||
|
||||
// build inputs using the appropriate derived key_ids
|
||||
for coin in coins {
|
||||
if coin.is_coinbase {
|
||||
parts.push(build::coinbase_input(coin.value, coin.key_id.clone()));
|
||||
} else {
|
||||
parts.push(build::input(coin.value, coin.key_id.clone()));
|
||||
if include_inputs_in_sum {
|
||||
for coin in coins {
|
||||
if coin.is_coinbase {
|
||||
parts.push(build::coinbase_input(coin.value, coin.key_id.clone()));
|
||||
} else {
|
||||
parts.push(build::input(coin.value, coin.key_id.clone()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -616,3 +647,53 @@ fn select_from(amount: u64, select_all: bool, outputs: Vec<OutputData>) -> Optio
|
|||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// Repopulates output in the slate's tranacstion
|
||||
/// with outputs from the stored context
|
||||
/// change outputs and tx log entry
|
||||
/// Remove the explicitly stored excess
|
||||
pub fn repopulate_tx<'a, T: ?Sized, C, K>(
|
||||
wallet: &mut T,
|
||||
keychain_mask: Option<&SecretKey>,
|
||||
slate: &mut Slate,
|
||||
context: &Context,
|
||||
update_fee: bool,
|
||||
) -> Result<(), Error>
|
||||
where
|
||||
T: WalletBackend<'a, C, K>,
|
||||
C: NodeClient + 'a,
|
||||
K: Keychain + 'a,
|
||||
{
|
||||
// restore the original amount, fee
|
||||
slate.amount = context.amount;
|
||||
if update_fee {
|
||||
slate.fee = context.fee;
|
||||
}
|
||||
|
||||
let keychain = wallet.keychain(keychain_mask)?;
|
||||
|
||||
// restore my signature data
|
||||
slate.add_participant_info(&keychain, &context.sec_key, &context.sec_nonce, None)?;
|
||||
|
||||
let mut parts = vec![];
|
||||
for (id, _, value) in &context.get_inputs() {
|
||||
let input = wallet.iter().find(|out| out.key_id == *id);
|
||||
if let Some(i) = input {
|
||||
if i.is_coinbase {
|
||||
parts.push(build::coinbase_input(*value, i.key_id.clone()));
|
||||
} else {
|
||||
parts.push(build::input(*value, i.key_id.clone()));
|
||||
}
|
||||
}
|
||||
}
|
||||
for (id, _, value) in &context.get_outputs() {
|
||||
let output = wallet.iter().find(|out| out.key_id == *id);
|
||||
if let Some(i) = output {
|
||||
parts.push(build::output(*value, i.key_id.clone()));
|
||||
}
|
||||
}
|
||||
let _ = slate.add_transaction_elements(&keychain, &ProofBuilder::new(&keychain), parts)?;
|
||||
// restore the original offset
|
||||
slate.tx_or_err_mut()?.offset = slate.offset.clone();
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
@ -20,7 +20,7 @@ use uuid::Uuid;
|
|||
|
||||
use crate::grin_core::consensus::valid_header_version;
|
||||
use crate::grin_core::core::HeaderVersion;
|
||||
use crate::grin_keychain::{Identifier, Keychain};
|
||||
use crate::grin_keychain::{BlindSum, BlindingFactor, Identifier, Keychain, SwitchCommitmentType};
|
||||
use crate::grin_util::secp::key::SecretKey;
|
||||
use crate::grin_util::secp::pedersen;
|
||||
use crate::grin_util::Mutex;
|
||||
|
@ -44,7 +44,8 @@ lazy_static! {
|
|||
pub fn new_tx_slate<'a, T: ?Sized, C, K>(
|
||||
wallet: &mut T,
|
||||
amount: u64,
|
||||
num_participants: usize,
|
||||
is_invoice: bool,
|
||||
num_participants: u8,
|
||||
use_test_rng: bool,
|
||||
ttl_blocks: Option<u64>,
|
||||
) -> Result<Slate, Error>
|
||||
|
@ -54,9 +55,9 @@ where
|
|||
K: Keychain + 'a,
|
||||
{
|
||||
let current_height = wallet.w2n_client().get_chain_tip()?.0;
|
||||
let mut slate = Slate::blank(num_participants);
|
||||
let mut slate = Slate::blank(num_participants, is_invoice);
|
||||
if let Some(b) = ttl_blocks {
|
||||
slate.ttl_cutoff_height = Some(current_height + b);
|
||||
slate.ttl_cutoff_height = current_height + b;
|
||||
}
|
||||
if use_test_rng {
|
||||
{
|
||||
|
@ -67,7 +68,6 @@ where
|
|||
*SLATE_COUNTER.lock() += 1;
|
||||
}
|
||||
slate.amount = amount;
|
||||
slate.height = current_height;
|
||||
|
||||
if valid_header_version(current_height, HeaderVersion(1)) {
|
||||
slate.version_info.block_header_version = 1;
|
||||
|
@ -81,9 +81,9 @@ where
|
|||
slate.version_info.block_header_version = 3;
|
||||
}
|
||||
|
||||
// Set the lock_height explicitly to 0 here.
|
||||
// Set the features explicitly to 0 here.
|
||||
// This will generate a Plain kernel (rather than a HeightLocked kernel).
|
||||
slate.lock_height = 0;
|
||||
slate.kernel_features = 0;
|
||||
|
||||
Ok(slate)
|
||||
}
|
||||
|
@ -140,14 +140,13 @@ pub fn add_inputs_to_slate<'a, T: ?Sized, C, K>(
|
|||
wallet: &mut T,
|
||||
keychain_mask: Option<&SecretKey>,
|
||||
slate: &mut Slate,
|
||||
current_height: u64,
|
||||
minimum_confirmations: u64,
|
||||
max_outputs: usize,
|
||||
num_change_outputs: usize,
|
||||
selection_strategy_is_use_all: bool,
|
||||
parent_key_id: &Identifier,
|
||||
participant_id: usize,
|
||||
message: Option<String>,
|
||||
is_initator: bool,
|
||||
is_initiator: bool,
|
||||
use_test_rng: bool,
|
||||
) -> Result<Context, Error>
|
||||
where
|
||||
|
@ -170,11 +169,13 @@ where
|
|||
&wallet.keychain(keychain_mask)?,
|
||||
keychain_mask,
|
||||
slate,
|
||||
current_height,
|
||||
minimum_confirmations,
|
||||
max_outputs,
|
||||
num_change_outputs,
|
||||
selection_strategy_is_use_all,
|
||||
parent_key_id.clone(),
|
||||
!is_initiator,
|
||||
use_test_rng,
|
||||
)?;
|
||||
|
||||
|
@ -185,18 +186,17 @@ where
|
|||
&wallet.keychain(keychain_mask)?,
|
||||
&mut context.sec_key,
|
||||
&context.sec_nonce,
|
||||
participant_id,
|
||||
message,
|
||||
use_test_rng,
|
||||
)?;
|
||||
|
||||
if !is_initator {
|
||||
context.initial_sec_key = context.sec_key.clone();
|
||||
|
||||
if !is_initiator {
|
||||
// perform partial sig
|
||||
slate.fill_round_2(
|
||||
&wallet.keychain(keychain_mask)?,
|
||||
&context.sec_key,
|
||||
&context.sec_nonce,
|
||||
participant_id,
|
||||
)?;
|
||||
}
|
||||
|
||||
|
@ -208,9 +208,8 @@ pub fn add_output_to_slate<'a, T: ?Sized, C, K>(
|
|||
wallet: &mut T,
|
||||
keychain_mask: Option<&SecretKey>,
|
||||
slate: &mut Slate,
|
||||
current_height: u64,
|
||||
parent_key_id: &Identifier,
|
||||
participant_id: usize,
|
||||
message: Option<String>,
|
||||
is_initiator: bool,
|
||||
use_test_rng: bool,
|
||||
) -> Result<Context, Error>
|
||||
|
@ -219,33 +218,36 @@ where
|
|||
C: NodeClient + 'a,
|
||||
K: Keychain + 'a,
|
||||
{
|
||||
let keychain = wallet.keychain(keychain_mask)?;
|
||||
// create an output using the amount in the slate
|
||||
let (_, mut context) = selection::build_recipient_output(
|
||||
let (_, mut context, mut tx) = selection::build_recipient_output(
|
||||
wallet,
|
||||
keychain_mask,
|
||||
slate,
|
||||
current_height,
|
||||
parent_key_id.clone(),
|
||||
is_initiator,
|
||||
use_test_rng,
|
||||
)?;
|
||||
|
||||
// fill public keys
|
||||
slate.fill_round_1(
|
||||
&wallet.keychain(keychain_mask)?,
|
||||
&keychain,
|
||||
&mut context.sec_key,
|
||||
&context.sec_nonce,
|
||||
1,
|
||||
message,
|
||||
use_test_rng,
|
||||
)?;
|
||||
|
||||
context.initial_sec_key = context.sec_key.clone();
|
||||
|
||||
if !is_initiator {
|
||||
// perform partial sig
|
||||
slate.fill_round_2(
|
||||
&wallet.keychain(keychain_mask)?,
|
||||
&context.sec_key,
|
||||
&context.sec_nonce,
|
||||
participant_id,
|
||||
)?;
|
||||
slate.fill_round_2(&keychain, &context.sec_key, &context.sec_nonce)?;
|
||||
// update excess in stored transaction
|
||||
let mut batch = wallet.batch(keychain_mask)?;
|
||||
tx.kernel_excess = Some(slate.calc_excess(keychain.secp())?);
|
||||
batch.save_tx_log_entry(tx.clone(), &parent_key_id)?;
|
||||
batch.commit()?;
|
||||
}
|
||||
|
||||
Ok(context)
|
||||
|
@ -256,7 +258,6 @@ pub fn complete_tx<'a, T: ?Sized, C, K>(
|
|||
wallet: &mut T,
|
||||
keychain_mask: Option<&SecretKey>,
|
||||
slate: &mut Slate,
|
||||
participant_id: usize,
|
||||
context: &Context,
|
||||
) -> Result<(), Error>
|
||||
where
|
||||
|
@ -264,14 +265,23 @@ where
|
|||
C: NodeClient + 'a,
|
||||
K: Keychain + 'a,
|
||||
{
|
||||
slate.fill_round_2(
|
||||
&wallet.keychain(keychain_mask)?,
|
||||
&context.sec_key,
|
||||
&context.sec_nonce,
|
||||
participant_id,
|
||||
)?;
|
||||
// when self sending invoice tx, use initiator nonce to finalize
|
||||
let (sec_key, sec_nonce) = {
|
||||
if context.initial_sec_key != context.sec_key
|
||||
&& context.initial_sec_nonce != context.sec_nonce
|
||||
{
|
||||
(
|
||||
context.initial_sec_key.clone(),
|
||||
context.initial_sec_nonce.clone(),
|
||||
)
|
||||
} else {
|
||||
(context.sec_key.clone(), context.sec_nonce.clone())
|
||||
}
|
||||
};
|
||||
slate.fill_round_2(&wallet.keychain(keychain_mask)?, &sec_key, &sec_nonce)?;
|
||||
|
||||
// Final transaction can be built by anyone at this stage
|
||||
trace!("Slate to finalize is: {}", slate);
|
||||
slate.finalize(&wallet.keychain(keychain_mask)?)?;
|
||||
Ok(())
|
||||
}
|
||||
|
@ -351,9 +361,11 @@ where
|
|||
Some(t) => t,
|
||||
None => return Err(ErrorKind::TransactionDoesntExist(slate.id.to_string()).into()),
|
||||
};
|
||||
wallet.store_tx(&format!("{}", tx.tx_slate_id.unwrap()), slate.tx_or_err()?)?;
|
||||
let parent_key = tx.parent_key_id.clone();
|
||||
tx.kernel_excess = Some(slate.tx_or_err()?.body.kernels[0].excess);
|
||||
{
|
||||
let keychain = wallet.keychain(keychain_mask)?;
|
||||
tx.kernel_excess = Some(slate.calc_excess(keychain.secp())?);
|
||||
}
|
||||
|
||||
if let Some(ref p) = slate.clone().payment_proof {
|
||||
let derivation_index = match context.payment_proof_derivation_index {
|
||||
|
@ -362,7 +374,7 @@ where
|
|||
};
|
||||
let keychain = wallet.keychain(keychain_mask)?;
|
||||
let parent_key_id = wallet.parent_key_id();
|
||||
let excess = slate.calc_excess(&keychain)?;
|
||||
let excess = slate.calc_excess(keychain.secp())?;
|
||||
let sender_key =
|
||||
address::address_from_derivation_path(&keychain, &parent_key_id, derivation_index)?;
|
||||
let sender_address = OnionV3Address::from_private(&sender_key.0)?;
|
||||
|
@ -377,34 +389,52 @@ where
|
|||
})
|
||||
}
|
||||
|
||||
wallet.store_tx(&format!("{}", tx.tx_slate_id.unwrap()), slate.tx_or_err()?)?;
|
||||
|
||||
let mut batch = wallet.batch(keychain_mask)?;
|
||||
batch.save_tx_log_entry(tx, &parent_key)?;
|
||||
batch.commit()?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Update the transaction participant messages
|
||||
pub fn update_message<'a, T: ?Sized, C, K>(
|
||||
/// Update the transaction's offset by subtracting the inputs
|
||||
/// stored in the context
|
||||
pub fn sub_inputs_from_offset<'a, T: ?Sized, C, K>(
|
||||
wallet: &mut T,
|
||||
keychain_mask: Option<&SecretKey>,
|
||||
slate: &Slate,
|
||||
context: &Context,
|
||||
slate: &mut Slate,
|
||||
) -> Result<(), Error>
|
||||
where
|
||||
T: WalletBackend<'a, C, K>,
|
||||
C: NodeClient + 'a,
|
||||
K: Keychain + 'a,
|
||||
{
|
||||
let tx_vec = updater::retrieve_txs(wallet, None, Some(slate.id), None, false)?;
|
||||
if tx_vec.is_empty() {
|
||||
return Err(ErrorKind::TransactionDoesntExist(slate.id.to_string()).into());
|
||||
}
|
||||
let mut batch = wallet.batch(keychain_mask)?;
|
||||
for mut tx in tx_vec.into_iter() {
|
||||
tx.messages = Some(slate.participant_messages());
|
||||
let parent_key = tx.parent_key_id.clone();
|
||||
batch.save_tx_log_entry(tx, &parent_key)?;
|
||||
}
|
||||
batch.commit()?;
|
||||
let k = wallet.keychain(keychain_mask)?;
|
||||
// Offset has been created and adjusted
|
||||
// Now subtract sum total of all my inputs from the offset
|
||||
let new_offset = k.blind_sum(
|
||||
&context
|
||||
.get_inputs()
|
||||
.iter()
|
||||
.map(
|
||||
|i| match k.derive_key(i.2, &i.0, SwitchCommitmentType::Regular) {
|
||||
Ok(k) => BlindingFactor::from_secret_key(k),
|
||||
Err(e) => {
|
||||
error!("Error deriving key for offset: {}", e);
|
||||
BlindingFactor::zero()
|
||||
}
|
||||
},
|
||||
)
|
||||
.fold(
|
||||
BlindSum::new().add_blinding_factor(slate.offset.clone()),
|
||||
|acc, x| acc.sub_blinding_factor(x.clone()),
|
||||
),
|
||||
)?;
|
||||
|
||||
slate.offset = new_offset.clone();
|
||||
slate.tx_or_err_mut()?.offset = new_offset;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
@ -531,7 +561,7 @@ where
|
|||
}
|
||||
let msg = payment_proof_message(
|
||||
slate.amount,
|
||||
&slate.calc_excess(&keychain)?,
|
||||
&slate.calc_excess(&keychain.secp())?,
|
||||
orig_sender_address.to_ed25519()?,
|
||||
)?;
|
||||
let sig = match p.receiver_signature {
|
||||
|
|
|
@ -55,15 +55,15 @@ pub mod slate_versions;
|
|||
mod types;
|
||||
|
||||
pub use crate::error::{Error, ErrorKind};
|
||||
pub use crate::slate::{ParticipantData, ParticipantMessageData, Slate};
|
||||
pub use crate::slate::{ParticipantData, Slate, SlateState};
|
||||
pub use crate::slate_versions::{
|
||||
SlateVersion, VersionedCoinbase, VersionedSlate, CURRENT_SLATE_VERSION,
|
||||
SlateVersion, VersionedBinSlate, VersionedCoinbase, VersionedSlate, CURRENT_SLATE_VERSION,
|
||||
GRIN_BLOCK_HEADER_VERSION,
|
||||
};
|
||||
pub use api_impl::owner_updater::StatusMessage;
|
||||
pub use api_impl::types::{
|
||||
BlockFees, InitTxArgs, InitTxSendArgs, IssueInvoiceTxArgs, NodeHeightResult,
|
||||
OutputCommitMapping, PaymentProof, SendTXArgs, VersionInfo,
|
||||
OutputCommitMapping, PaymentProof, VersionInfo,
|
||||
};
|
||||
pub use internal::scan::scan;
|
||||
pub use slate_versions::ser as dalek_ser;
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -20,8 +20,9 @@
|
|||
use crate::slate::Slate;
|
||||
use crate::slate_versions::v3::{CoinbaseV3, SlateV3};
|
||||
use crate::slate_versions::v4::{CoinbaseV4, SlateV4};
|
||||
use crate::slate_versions::v4_bin::SlateV4Bin;
|
||||
use crate::types::CbData;
|
||||
use crate::Error;
|
||||
use crate::{Error, ErrorKind};
|
||||
use std::convert::TryFrom;
|
||||
|
||||
pub mod ser;
|
||||
|
@ -30,6 +31,8 @@ pub mod ser;
|
|||
pub mod v3;
|
||||
#[allow(missing_docs)]
|
||||
pub mod v4;
|
||||
#[allow(missing_docs)]
|
||||
pub mod v4_bin;
|
||||
|
||||
/// The most recent version of the slate
|
||||
pub const CURRENT_SLATE_VERSION: u16 = 4;
|
||||
|
@ -91,6 +94,37 @@ impl From<VersionedSlate> for Slate {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Serialize)]
|
||||
#[serde(untagged)]
|
||||
/// Binary versions, can only be parsed 1:1 into the appropriate
|
||||
/// version, and VersionedSlate can up/downgrade from there
|
||||
pub enum VersionedBinSlate {
|
||||
/// Version 4, binary
|
||||
V4(SlateV4Bin),
|
||||
}
|
||||
|
||||
impl TryFrom<VersionedSlate> for VersionedBinSlate {
|
||||
type Error = Error;
|
||||
fn try_from(slate: VersionedSlate) -> Result<VersionedBinSlate, Error> {
|
||||
match slate {
|
||||
VersionedSlate::V4(s) => Ok(VersionedBinSlate::V4(SlateV4Bin(s))),
|
||||
VersionedSlate::V3(_) => {
|
||||
return Err(
|
||||
ErrorKind::Compatibility("V3 Slate does not support binary".to_owned()).into(),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<VersionedBinSlate> for VersionedSlate {
|
||||
fn from(slate: VersionedBinSlate) -> VersionedSlate {
|
||||
match slate {
|
||||
VersionedBinSlate::V4(s) => VersionedSlate::V4(s.0),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Serialize)]
|
||||
#[serde(untagged)]
|
||||
/// Versions are ordered newest to oldest so serde attempts to
|
||||
|
|
|
@ -13,6 +13,183 @@
|
|||
// limitations under the License.
|
||||
//! Sane serialization & deserialization of cryptographic structs into hex
|
||||
|
||||
use crate::grin_keychain::BlindingFactor;
|
||||
use crate::grin_util::secp::pedersen::{Commitment, RangeProof};
|
||||
use crate::grin_util::secp::PublicKey;
|
||||
use base64;
|
||||
use serde::{Deserialize, Deserializer, Serializer};
|
||||
|
||||
/// Seralizes a byte string into base64
|
||||
pub fn as_base64<T, S>(bytes: T, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
T: AsRef<[u8]>,
|
||||
S: Serializer,
|
||||
{
|
||||
serializer.serialize_str(&base64::encode(&bytes))
|
||||
}
|
||||
|
||||
/// Creates a BlindingFactor from a base64 string
|
||||
pub fn blindingfactor_from_base64<'de, D>(deserializer: D) -> Result<BlindingFactor, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
use serde::de::Error;
|
||||
|
||||
let val = String::deserialize(deserializer)
|
||||
.and_then(|string| base64::decode(&string).map_err(|err| Error::custom(err.to_string())))?;
|
||||
Ok(BlindingFactor::from_slice(&val))
|
||||
}
|
||||
|
||||
/// Creates a RangeProof from a base64 string
|
||||
pub fn rangeproof_from_base64<'de, D>(deserializer: D) -> Result<RangeProof, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
use serde::de::{Error, IntoDeserializer};
|
||||
|
||||
let val = String::deserialize(deserializer)
|
||||
.and_then(|string| base64::decode(&string).map_err(|err| Error::custom(err.to_string())))?;
|
||||
RangeProof::deserialize(val.into_deserializer())
|
||||
}
|
||||
|
||||
/// Creates a RangeProof from a hex string
|
||||
pub fn commitment_from_base64<'de, D>(deserializer: D) -> Result<Commitment, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
use serde::de::{Error, IntoDeserializer};
|
||||
|
||||
let val = String::deserialize(deserializer)
|
||||
.and_then(|string| base64::decode(&string).map_err(|err| Error::custom(err.to_string())))?;
|
||||
Commitment::deserialize(val.into_deserializer())
|
||||
}
|
||||
|
||||
/// Creates a PublicKey from a hex string
|
||||
pub fn pubkey_from_base64<'de, D>(deserializer: D) -> Result<PublicKey, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
use serde::de::{Error, IntoDeserializer};
|
||||
|
||||
let val = String::deserialize(deserializer)
|
||||
.and_then(|string| base64::decode(&string).map_err(|err| Error::custom(err.to_string())))?;
|
||||
PublicKey::deserialize(val.into_deserializer())
|
||||
}
|
||||
|
||||
/// Serializes an secp256k1 pubkey to base64
|
||||
pub mod pubkey_base64 {
|
||||
use crate::grin_util::secp::PublicKey;
|
||||
use crate::grin_util::static_secp_instance;
|
||||
use base64;
|
||||
use serde::{Deserialize, Deserializer, Serializer};
|
||||
|
||||
///
|
||||
pub fn serialize<S>(key: &PublicKey, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer,
|
||||
{
|
||||
let static_secp = static_secp_instance();
|
||||
let static_secp = static_secp.lock();
|
||||
serializer.serialize_str(&base64::encode(
|
||||
&key.serialize_vec(&static_secp, true).to_vec(),
|
||||
))
|
||||
}
|
||||
|
||||
///
|
||||
pub fn deserialize<'de, D>(deserializer: D) -> Result<PublicKey, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
use serde::de::Error;
|
||||
let static_secp = static_secp_instance();
|
||||
let static_secp = static_secp.lock();
|
||||
String::deserialize(deserializer)
|
||||
.and_then(|string| {
|
||||
base64::decode(&string).map_err(|err| Error::custom(err.to_string()))
|
||||
})
|
||||
.and_then(|bytes: Vec<u8>| {
|
||||
PublicKey::from_slice(&static_secp, &bytes)
|
||||
.map_err(|err| Error::custom(err.to_string()))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// Serializes an Option<secp::Signature> to and from hex
|
||||
pub mod option_sig_base64 {
|
||||
use crate::grin_util::{secp, static_secp_instance};
|
||||
use base64;
|
||||
use serde::de::Error;
|
||||
use serde::{Deserialize, Deserializer, Serializer};
|
||||
|
||||
///
|
||||
pub fn serialize<S>(sig: &Option<secp::Signature>, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer,
|
||||
{
|
||||
let static_secp = static_secp_instance();
|
||||
let static_secp = static_secp.lock();
|
||||
match sig {
|
||||
Some(sig) => serializer.serialize_str(&base64::encode(
|
||||
&sig.serialize_compact(&static_secp).to_vec(),
|
||||
)),
|
||||
None => serializer.serialize_none(),
|
||||
}
|
||||
}
|
||||
|
||||
///
|
||||
pub fn deserialize<'de, D>(deserializer: D) -> Result<Option<secp::Signature>, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
let static_secp = static_secp_instance();
|
||||
let static_secp = static_secp.lock();
|
||||
Option::<String>::deserialize(deserializer).and_then(|res| match res {
|
||||
Some(string) => base64::decode(&string)
|
||||
.map_err(|err| Error::custom(err.to_string()))
|
||||
.and_then(|bytes: Vec<u8>| {
|
||||
let mut b = [0u8; 64];
|
||||
b.copy_from_slice(&bytes[0..64]);
|
||||
secp::Signature::from_compact(&static_secp, &b)
|
||||
.map(Some)
|
||||
.map_err(|err| Error::custom(err.to_string()))
|
||||
}),
|
||||
None => Ok(None),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// Serializes an Option<secp::Signature> to and from hex
|
||||
pub mod option_rangeproof_base64 {
|
||||
use crate::grin_util::secp::pedersen::RangeProof;
|
||||
use base64;
|
||||
use serde::de::{Error, IntoDeserializer};
|
||||
use serde::{Deserialize, Deserializer, Serializer};
|
||||
|
||||
///
|
||||
pub fn serialize<S>(proof: &Option<RangeProof>, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer,
|
||||
{
|
||||
match proof {
|
||||
Some(p) => serializer.serialize_str(&base64::encode(&p)),
|
||||
None => serializer.serialize_none(),
|
||||
}
|
||||
}
|
||||
|
||||
///
|
||||
pub fn deserialize<'de, D>(deserializer: D) -> Result<Option<RangeProof>, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
Option::<String>::deserialize(deserializer).and_then(|res| match res {
|
||||
Some(string) => base64::decode(&string)
|
||||
.map_err(|err| Error::custom(err.to_string()))
|
||||
.and_then(|val| Ok(Some(RangeProof::deserialize(val.into_deserializer())?))),
|
||||
None => Ok(None),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// Serializes an OnionV3Address to and from hex
|
||||
pub mod option_ov3_serde {
|
||||
use serde::de::Error;
|
||||
|
@ -103,6 +280,36 @@ pub mod dalek_pubkey_serde {
|
|||
}
|
||||
}
|
||||
|
||||
/// Serializes an ed25519 PublicKey to and from base64
|
||||
pub mod dalek_pubkey_base64 {
|
||||
use base64;
|
||||
use ed25519_dalek::PublicKey as DalekPublicKey;
|
||||
use serde::{Deserialize, Deserializer, Serializer};
|
||||
|
||||
///
|
||||
pub fn serialize<S>(key: &DalekPublicKey, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer,
|
||||
{
|
||||
serializer.serialize_str(&base64::encode(&key.to_bytes()))
|
||||
}
|
||||
|
||||
///
|
||||
pub fn deserialize<'de, D>(deserializer: D) -> Result<DalekPublicKey, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
use serde::de::Error;
|
||||
String::deserialize(deserializer)
|
||||
.and_then(|string| {
|
||||
base64::decode(&string).map_err(|err| Error::custom(err.to_string()))
|
||||
})
|
||||
.and_then(|bytes: Vec<u8>| {
|
||||
DalekPublicKey::from_bytes(&bytes).map_err(|err| Error::custom(err.to_string()))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// Serializes an Option<ed25519_dalek::PublicKey> to and from hex
|
||||
pub mod option_dalek_pubkey_serde {
|
||||
use ed25519_dalek::PublicKey as DalekPublicKey;
|
||||
|
@ -212,6 +419,162 @@ pub mod option_dalek_sig_serde {
|
|||
}
|
||||
}
|
||||
|
||||
/// Serializes an Option<ed25519_dalek::PublicKey> to and from base64
|
||||
pub mod option_dalek_sig_base64 {
|
||||
use base64;
|
||||
use ed25519_dalek::Signature as DalekSignature;
|
||||
use serde::de::Error;
|
||||
use serde::{Deserialize, Deserializer, Serializer};
|
||||
|
||||
///
|
||||
pub fn serialize<S>(sig: &Option<DalekSignature>, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer,
|
||||
{
|
||||
match sig {
|
||||
Some(s) => serializer.serialize_str(&base64::encode(&s.to_bytes().to_vec())),
|
||||
None => serializer.serialize_none(),
|
||||
}
|
||||
}
|
||||
|
||||
///
|
||||
pub fn deserialize<'de, D>(deserializer: D) -> Result<Option<DalekSignature>, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
Option::<String>::deserialize(deserializer).and_then(|res| match res {
|
||||
Some(string) => base64::decode(&string)
|
||||
.map_err(|err| Error::custom(err.to_string()))
|
||||
.and_then(|bytes: Vec<u8>| {
|
||||
let mut b = [0u8; 64];
|
||||
b.copy_from_slice(&bytes[0..64]);
|
||||
DalekSignature::from_bytes(&b)
|
||||
.map(Some)
|
||||
.map_err(|err| Error::custom(err.to_string()))
|
||||
}),
|
||||
None => Ok(None),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// Serializes slates 'version_info' field
|
||||
pub mod version_info_v4 {
|
||||
use serde::de::Error;
|
||||
use serde::{Deserialize, Deserializer, Serializer};
|
||||
|
||||
use crate::slate_versions::v4::VersionCompatInfoV4;
|
||||
|
||||
///
|
||||
pub fn serialize<S>(v: &VersionCompatInfoV4, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer,
|
||||
{
|
||||
serializer.serialize_str(&format!("{}:{}", v.version, v.block_header_version))
|
||||
}
|
||||
|
||||
///
|
||||
pub fn deserialize<'de, D>(deserializer: D) -> Result<VersionCompatInfoV4, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
String::deserialize(deserializer).and_then(|s| {
|
||||
let mut retval = VersionCompatInfoV4 {
|
||||
version: 0,
|
||||
block_header_version: 0,
|
||||
};
|
||||
let v: Vec<&str> = s.split(':').collect();
|
||||
if v.len() != 2 {
|
||||
return Err(Error::custom("Cannot parse version"));
|
||||
}
|
||||
match u16::from_str_radix(v[0], 10) {
|
||||
Ok(u) => retval.version = u,
|
||||
Err(e) => return Err(Error::custom(format!("Cannot parse version: {}", e))),
|
||||
}
|
||||
match u16::from_str_radix(v[1], 10) {
|
||||
Ok(u) => retval.block_header_version = u,
|
||||
Err(e) => return Err(Error::custom(format!("Cannot parse version: {}", e))),
|
||||
}
|
||||
Ok(retval)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// Serializes slates 'state' field
|
||||
pub mod slate_state_v4 {
|
||||
use serde::de::Error;
|
||||
use serde::{Deserialize, Deserializer, Serializer};
|
||||
|
||||
use crate::slate_versions::v4::SlateStateV4;
|
||||
|
||||
///
|
||||
pub fn serialize<S>(st: &SlateStateV4, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer,
|
||||
{
|
||||
let label = match st {
|
||||
SlateStateV4::Unknown => "NA",
|
||||
SlateStateV4::Standard1 => "S1",
|
||||
SlateStateV4::Standard2 => "S2",
|
||||
SlateStateV4::Standard3 => "S3",
|
||||
SlateStateV4::Invoice1 => "I1",
|
||||
SlateStateV4::Invoice2 => "I2",
|
||||
SlateStateV4::Invoice3 => "I3",
|
||||
};
|
||||
serializer.serialize_str(label)
|
||||
}
|
||||
|
||||
///
|
||||
pub fn deserialize<'de, D>(deserializer: D) -> Result<SlateStateV4, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
String::deserialize(deserializer).and_then(|s| {
|
||||
let retval = match s.as_str() {
|
||||
"NA" => SlateStateV4::Unknown,
|
||||
"S1" => SlateStateV4::Standard1,
|
||||
"S2" => SlateStateV4::Standard2,
|
||||
"S3" => SlateStateV4::Standard3,
|
||||
"I1" => SlateStateV4::Invoice1,
|
||||
"I2" => SlateStateV4::Invoice2,
|
||||
"I3" => SlateStateV4::Invoice3,
|
||||
_ => return Err(Error::custom("Invalid Slate state")),
|
||||
};
|
||||
Ok(retval)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// Serializes an secp256k1 pubkey to base64
|
||||
pub mod uuid_base64 {
|
||||
use base64;
|
||||
use serde::{Deserialize, Deserializer, Serializer};
|
||||
use uuid::Uuid;
|
||||
|
||||
///
|
||||
pub fn serialize<S>(id: &Uuid, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer,
|
||||
{
|
||||
serializer.serialize_str(&base64::encode(&id.as_bytes()))
|
||||
}
|
||||
|
||||
///
|
||||
pub fn deserialize<'de, D>(deserializer: D) -> Result<Uuid, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
use serde::de::Error;
|
||||
String::deserialize(deserializer)
|
||||
.and_then(|string| {
|
||||
base64::decode(&string).map_err(|err| Error::custom(err.to_string()))
|
||||
})
|
||||
.and_then(|bytes: Vec<u8>| {
|
||||
let mut b = [0u8; 16];
|
||||
b.copy_from_slice(&bytes[0..16]);
|
||||
Ok(Uuid::from_bytes(b))
|
||||
})
|
||||
}
|
||||
}
|
||||
// Test serialization methods of components that are being used
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
|
|
|
@ -58,6 +58,7 @@ pub struct SlateV3 {
|
|||
/// TTL, the block height at which wallets
|
||||
/// should refuse to process the transaction and unlock all
|
||||
/// associated outputs
|
||||
#[serde(default = "default_ttl_none")]
|
||||
#[serde(with = "secp_ser::opt_string_or_u64")]
|
||||
pub ttl_cutoff_height: Option<u64>,
|
||||
/// Participant data, each participant in the transaction will
|
||||
|
@ -73,6 +74,10 @@ fn default_payment_none() -> Option<PaymentInfoV3> {
|
|||
None
|
||||
}
|
||||
|
||||
fn default_ttl_none() -> Option<u64> {
|
||||
None
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||
pub struct VersionCompatInfoV3 {
|
||||
/// The current version of the slate format
|
||||
|
|
|
@ -14,9 +14,53 @@
|
|||
|
||||
//! Contains V4 of the slate (grin-wallet 4.0.0)
|
||||
//! Changes from V3:
|
||||
//! * TBD
|
||||
//! /#### Top-Level Slate Struct
|
||||
|
||||
use crate::grin_core::core::transaction::OutputFeatures;
|
||||
//! * The `version_info` struct is removed, and is replaced with `ver`, which has the format "[version]:[block header version]"
|
||||
//! * `id` becomes a short-form base-64 encoding of the UUID binary
|
||||
//! * `sta` is added, with possible values S1|S2|S3|I1|I2|I3|NA
|
||||
//! * `num_participants` is renamed to `num_parts`
|
||||
//! * `num_parts` may be omitted from the slate. If omitted its value is assumed to be 2.
|
||||
//! * `amount` is renamed to `amt`
|
||||
//! * `amt` may be removed from the slate on the S2 phase of a transaction.
|
||||
//! * `fee` may be removed from the slate on the S2 phase of a transaction. It may also be ommited when intiating an I1 transaction, and added during the I2 phase.
|
||||
//! * `lock_height` is removed
|
||||
//! * `feat` is added to the slate denoting the Kernel feature set. May be omitted from the slate if kernel is plain (0)
|
||||
//! * `ttl_cutoff_height` is renamed to `ttl`
|
||||
//! * `ttl` may be omitted from the slate. If omitted its value is assumed to be 0 (no TTL).
|
||||
//! * The `participant_data` struct is renamed to `sigs`
|
||||
//! * `tx` is removed
|
||||
//! * The `coms` (commitments) array is added, from which the final transaction object can be reconstructed
|
||||
//! * The `payment_proof` struct is renamed to `proof`
|
||||
//! * The feat_args struct is added, which may be populated for non-Plain kernels
|
||||
//! * `proof` may be omitted from the slate if it is None (null),
|
||||
//! * `off` (offset) is added, and will be modified by every participant in the transaction with a random
|
||||
//! value - the value of their inputs' blinding factors
|
||||
//!
|
||||
//! #### Participant Data (`sigs`)
|
||||
//!
|
||||
//! * `public_blind_excess` is renamed to `xs`
|
||||
//! * `public_nonce` is renamed to `nonce`
|
||||
//! * `part_sig` is renamed to `part`
|
||||
//! * `part` may be omitted if it has not yet been filled out
|
||||
//! * `xs` becomes Base64 encoded instead of a hex string
|
||||
//! * `nonce` becomes Base64 encoded instead of a hex string
|
||||
//! * `part` becomes Base64 encoded instead of a hex string
|
||||
//! * `message` is removed
|
||||
//! * `message_sig` is removed
|
||||
//! * `id` is removed. Parties can identify themselves via the keys stored in their transaction context
|
||||
//!
|
||||
//! #### Payment Proof Data (`proof`)
|
||||
//!
|
||||
//! * The `sender_address` field is renamed to `saddr`
|
||||
//! * The `receiver_address` field is renamed to `raddr`
|
||||
//! * The `receiver_signature` field is renamed to `rsig`
|
||||
//! * `saddr` is Base64 encoded instead of a hex string
|
||||
//! * `raddr` is Base64 encoded instead of a hex string
|
||||
//! * `rsig` is Base64 encoded instead of a hex string
|
||||
//! * `rsig` may be omitted if it has not yet been filled out
|
||||
|
||||
use crate::grin_core::core::transaction::KernelFeatures;
|
||||
use crate::grin_core::libtx::secp_ser;
|
||||
use crate::grin_core::map_vec;
|
||||
use crate::grin_keychain::{BlindingFactor, Identifier};
|
||||
|
@ -25,7 +69,7 @@ use crate::grin_util::secp::key::PublicKey;
|
|||
use crate::grin_util::secp::pedersen::{Commitment, RangeProof};
|
||||
use crate::grin_util::secp::Signature;
|
||||
use crate::slate::CompatKernelFeatures;
|
||||
use crate::slate_versions::ser as dalek_ser;
|
||||
use crate::slate_versions::ser;
|
||||
use crate::{Error, ErrorKind};
|
||||
use ed25519_dalek::PublicKey as DalekPublicKey;
|
||||
use ed25519_dalek::Signature as DalekSignature;
|
||||
|
@ -39,88 +83,194 @@ use crate::slate_versions::v3::{
|
|||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||
pub struct SlateV4 {
|
||||
// Required Fields
|
||||
/// Versioning info
|
||||
pub version_info: VersionCompatInfoV4,
|
||||
/// The number of participants intended to take part in this transaction
|
||||
pub num_participants: usize,
|
||||
#[serde(with = "ser::version_info_v4")]
|
||||
pub ver: VersionCompatInfoV4,
|
||||
/// Unique transaction ID, selected by sender
|
||||
pub id: Uuid,
|
||||
/// The core transaction data:
|
||||
/// inputs, outputs, kernels, kernel offset
|
||||
/// Optional as of V4 to allow for a compact
|
||||
/// transaction initiation
|
||||
pub tx: Option<TransactionV4>,
|
||||
/// Slate state
|
||||
#[serde(with = "ser::slate_state_v4")]
|
||||
pub sta: SlateStateV4,
|
||||
/// Offset, modified by each participant inserting inputs
|
||||
/// as the transaction progresses
|
||||
#[serde(
|
||||
serialize_with = "ser::as_base64",
|
||||
deserialize_with = "ser::blindingfactor_from_base64"
|
||||
)]
|
||||
#[serde(default = "default_offset_zero")]
|
||||
#[serde(skip_serializing_if = "offset_is_zero")]
|
||||
pub off: BlindingFactor,
|
||||
// Optional fields depending on state
|
||||
/// The number of participants intended to take part in this transaction
|
||||
#[serde(default = "default_num_participants_2")]
|
||||
#[serde(skip_serializing_if = "num_parts_is_2")]
|
||||
pub num_parts: u8,
|
||||
/// base amount (excluding fee)
|
||||
#[serde(with = "secp_ser::string_or_u64")]
|
||||
pub amount: u64,
|
||||
#[serde(skip_serializing_if = "u64_is_blank")]
|
||||
#[serde(default = "default_u64_0")]
|
||||
pub amt: u64,
|
||||
/// fee amount
|
||||
#[serde(with = "secp_ser::string_or_u64")]
|
||||
#[serde(default = "default_u64")]
|
||||
#[serde(skip_serializing_if = "u64_is_blank")]
|
||||
pub fee: u64,
|
||||
/// Block height for the transaction
|
||||
#[serde(with = "secp_ser::string_or_u64")]
|
||||
pub height: u64,
|
||||
/// Lock height
|
||||
#[serde(with = "secp_ser::string_or_u64")]
|
||||
pub lock_height: u64,
|
||||
/// kernel features, if any
|
||||
#[serde(skip_serializing_if = "u8_is_blank")]
|
||||
#[serde(default = "default_u8_0")]
|
||||
pub feat: u8,
|
||||
/// TTL, the block height at which wallets
|
||||
/// should refuse to process the transaction and unlock all
|
||||
/// associated outputs
|
||||
#[serde(with = "secp_ser::opt_string_or_u64")]
|
||||
pub ttl_cutoff_height: Option<u64>,
|
||||
#[serde(with = "secp_ser::string_or_u64")]
|
||||
#[serde(skip_serializing_if = "u64_is_blank")]
|
||||
#[serde(default = "default_u64_0")]
|
||||
pub ttl: u64,
|
||||
// Structs always required
|
||||
/// Participant data, each participant in the transaction will
|
||||
/// insert their public data here. For now, 0 is sender and 1
|
||||
/// is receiver, though this will change for multi-party
|
||||
pub participant_data: Vec<ParticipantDataV4>,
|
||||
pub sigs: Vec<ParticipantDataV4>,
|
||||
// Situational, but required at some point in the tx
|
||||
/// Inputs/Output commits added to slate
|
||||
#[serde(default = "default_coms_none")]
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub coms: Option<Vec<CommitsV4>>,
|
||||
// Optional Structs
|
||||
/// Payment Proof
|
||||
#[serde(default = "default_payment_none")]
|
||||
pub payment_proof: Option<PaymentInfoV4>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub proof: Option<PaymentInfoV4>,
|
||||
/// Kernel features arguments
|
||||
#[serde(default = "default_kernel_features_none")]
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub feat_args: Option<KernelFeaturesArgsV4>,
|
||||
}
|
||||
|
||||
fn default_payment_none() -> Option<PaymentInfoV4> {
|
||||
None
|
||||
}
|
||||
|
||||
fn default_offset_zero() -> BlindingFactor {
|
||||
BlindingFactor::zero()
|
||||
}
|
||||
|
||||
fn offset_is_zero(o: &BlindingFactor) -> bool {
|
||||
*o == BlindingFactor::zero()
|
||||
}
|
||||
|
||||
fn default_coms_none() -> Option<Vec<CommitsV4>> {
|
||||
None
|
||||
}
|
||||
|
||||
fn default_u64_0() -> u64 {
|
||||
0
|
||||
}
|
||||
|
||||
fn num_parts_is_2(n: &u8) -> bool {
|
||||
*n == 2
|
||||
}
|
||||
|
||||
fn default_num_participants_2() -> u8 {
|
||||
2
|
||||
}
|
||||
|
||||
fn default_kernel_features_none() -> Option<KernelFeaturesArgsV4> {
|
||||
None
|
||||
}
|
||||
|
||||
/// Slate state definition
|
||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||
pub enum SlateStateV4 {
|
||||
/// Unknown, coming from earlier versions of the slate
|
||||
Unknown,
|
||||
/// Standard flow, freshly init
|
||||
Standard1,
|
||||
/// Standard flow, return journey
|
||||
Standard2,
|
||||
/// Standard flow, ready for transaction posting
|
||||
Standard3,
|
||||
/// Invoice flow, freshly init
|
||||
Invoice1,
|
||||
///Invoice flow, return journey
|
||||
Invoice2,
|
||||
/// Invoice flow, ready for tranasction posting
|
||||
Invoice3,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
|
||||
/// Kernel features arguments definition
|
||||
pub struct KernelFeaturesArgsV4 {
|
||||
/// Lock height, for HeightLocked
|
||||
pub lock_hgt: u64,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
|
||||
pub struct VersionCompatInfoV4 {
|
||||
/// The current version of the slate format
|
||||
pub version: u16,
|
||||
/// Original version this slate was converted from
|
||||
pub orig_version: u16,
|
||||
/// Version of grin block header this slate is compatible with
|
||||
pub block_header_version: u16,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
|
||||
pub struct ParticipantDataV4 {
|
||||
/// Id of participant in the transaction. (For now, 0=sender, 1=rec)
|
||||
#[serde(with = "secp_ser::string_or_u64")]
|
||||
pub id: u64,
|
||||
/// Public key corresponding to private blinding factor
|
||||
#[serde(with = "secp_ser::pubkey_serde")]
|
||||
pub public_blind_excess: PublicKey,
|
||||
#[serde(with = "ser::pubkey_base64")]
|
||||
pub xs: PublicKey,
|
||||
/// Public key corresponding to private nonce
|
||||
#[serde(with = "secp_ser::pubkey_serde")]
|
||||
pub public_nonce: PublicKey,
|
||||
#[serde(with = "ser::pubkey_base64")]
|
||||
pub nonce: PublicKey,
|
||||
/// Public partial signature
|
||||
#[serde(with = "secp_ser::option_sig_serde")]
|
||||
pub part_sig: Option<Signature>,
|
||||
/// A message for other participants
|
||||
pub message: Option<String>,
|
||||
/// Signature, created with private key corresponding to 'public_blind_excess'
|
||||
#[serde(with = "secp_ser::option_sig_serde")]
|
||||
pub message_sig: Option<Signature>,
|
||||
#[serde(default = "default_part_sig_none")]
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
#[serde(with = "ser::option_sig_base64")]
|
||||
pub part: Option<Signature>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||
pub struct PaymentInfoV4 {
|
||||
#[serde(with = "dalek_ser::dalek_pubkey_serde")]
|
||||
pub sender_address: DalekPublicKey,
|
||||
#[serde(with = "dalek_ser::dalek_pubkey_serde")]
|
||||
pub receiver_address: DalekPublicKey,
|
||||
#[serde(with = "dalek_ser::option_dalek_sig_serde")]
|
||||
pub receiver_signature: Option<DalekSignature>,
|
||||
fn default_part_sig_none() -> Option<Signature> {
|
||||
None
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, Eq, PartialEq)]
|
||||
pub struct PaymentInfoV4 {
|
||||
#[serde(with = "ser::dalek_pubkey_base64")]
|
||||
pub saddr: DalekPublicKey,
|
||||
#[serde(with = "ser::dalek_pubkey_base64")]
|
||||
pub raddr: DalekPublicKey,
|
||||
#[serde(default = "default_receiver_signature_none")]
|
||||
#[serde(with = "ser::option_dalek_sig_base64")]
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub rsig: Option<DalekSignature>,
|
||||
}
|
||||
|
||||
fn default_receiver_signature_none() -> Option<DalekSignature> {
|
||||
None
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, Serialize, Deserialize)]
|
||||
pub struct CommitsV4 {
|
||||
/// Options for an output's structure or use
|
||||
#[serde(default = "default_output_feature")]
|
||||
#[serde(skip_serializing_if = "output_feature_is_plain")]
|
||||
pub f: OutputFeaturesV4,
|
||||
/// The homomorphic commitment representing the output amount
|
||||
#[serde(
|
||||
serialize_with = "ser::as_base64",
|
||||
deserialize_with = "ser::commitment_from_base64"
|
||||
)]
|
||||
pub c: Commitment,
|
||||
/// A proof that the commitment is in the right range
|
||||
/// Only applies for transaction outputs
|
||||
#[serde(with = "ser::option_rangeproof_base64")]
|
||||
#[serde(default = "default_range_proof")]
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub p: Option<RangeProof>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Copy, Debug, Clone, PartialEq, Eq)]
|
||||
pub struct OutputFeaturesV4(pub u8);
|
||||
|
||||
/// A transaction
|
||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||
pub struct TransactionV4 {
|
||||
|
@ -130,26 +280,63 @@ pub struct TransactionV4 {
|
|||
serialize_with = "secp_ser::as_hex",
|
||||
deserialize_with = "secp_ser::blind_from_hex"
|
||||
)]
|
||||
#[serde(default = "default_blinding_factor")]
|
||||
#[serde(skip_serializing_if = "blinding_factor_is_zero")]
|
||||
pub offset: BlindingFactor,
|
||||
/// The transaction body - inputs/outputs/kernels
|
||||
pub body: TransactionBodyV4,
|
||||
}
|
||||
|
||||
fn default_blinding_factor() -> BlindingFactor {
|
||||
BlindingFactor::zero()
|
||||
}
|
||||
|
||||
fn blinding_factor_is_zero(bf: &BlindingFactor) -> bool {
|
||||
*bf == BlindingFactor::zero()
|
||||
}
|
||||
|
||||
/// TransactionBody is a common abstraction for transaction and block
|
||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||
pub struct TransactionBodyV4 {
|
||||
/// List of inputs spent by the transaction.
|
||||
pub inputs: Vec<InputV4>,
|
||||
#[serde(default = "default_inputs")]
|
||||
#[serde(skip_serializing_if = "inputs_are_empty")]
|
||||
pub ins: Vec<InputV4>,
|
||||
/// List of outputs the transaction produces.
|
||||
pub outputs: Vec<OutputV4>,
|
||||
#[serde(default = "default_outputs")]
|
||||
#[serde(skip_serializing_if = "outputs_are_empty")]
|
||||
pub outs: Vec<OutputV4>,
|
||||
/// List of kernels that make up this transaction (usually a single kernel).
|
||||
pub kernels: Vec<TxKernelV4>,
|
||||
pub kers: Vec<TxKernelV4>,
|
||||
}
|
||||
|
||||
fn inputs_are_empty(v: &Vec<InputV4>) -> bool {
|
||||
v.len() == 0
|
||||
}
|
||||
|
||||
fn default_inputs() -> Vec<InputV4> {
|
||||
vec![]
|
||||
}
|
||||
|
||||
fn outputs_are_empty(v: &Vec<OutputV4>) -> bool {
|
||||
v.len() == 0
|
||||
}
|
||||
|
||||
fn default_outputs() -> Vec<OutputV4> {
|
||||
vec![]
|
||||
}
|
||||
|
||||
fn default_range_proof() -> Option<RangeProof> {
|
||||
None
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||
pub struct InputV4 {
|
||||
/// The features of the output being spent.
|
||||
/// We will check maturity for coinbase output.
|
||||
pub features: OutputFeatures,
|
||||
#[serde(default = "default_output_feature")]
|
||||
#[serde(skip_serializing_if = "output_feature_is_plain")]
|
||||
pub features: OutputFeaturesV4,
|
||||
/// The commit referencing the output being spent.
|
||||
#[serde(
|
||||
serialize_with = "secp_ser::as_hex",
|
||||
|
@ -161,31 +348,47 @@ pub struct InputV4 {
|
|||
#[derive(Debug, Copy, Clone, Serialize, Deserialize)]
|
||||
pub struct OutputV4 {
|
||||
/// Options for an output's structure or use
|
||||
pub features: OutputFeatures,
|
||||
#[serde(default = "default_output_feature")]
|
||||
#[serde(skip_serializing_if = "output_feature_is_plain")]
|
||||
pub features: OutputFeaturesV4,
|
||||
/// The homomorphic commitment representing the output amount
|
||||
#[serde(
|
||||
serialize_with = "secp_ser::as_hex",
|
||||
deserialize_with = "secp_ser::commitment_from_hex"
|
||||
serialize_with = "ser::as_base64",
|
||||
deserialize_with = "ser::commitment_from_base64"
|
||||
)]
|
||||
pub commit: Commitment,
|
||||
pub com: Commitment,
|
||||
/// A proof that the commitment is in the right range
|
||||
#[serde(
|
||||
serialize_with = "secp_ser::as_hex",
|
||||
deserialize_with = "secp_ser::rangeproof_from_hex"
|
||||
serialize_with = "ser::as_base64",
|
||||
deserialize_with = "ser::rangeproof_from_base64"
|
||||
)]
|
||||
pub proof: RangeProof,
|
||||
pub prf: RangeProof,
|
||||
}
|
||||
|
||||
fn default_output_feature() -> OutputFeaturesV4 {
|
||||
OutputFeaturesV4(0)
|
||||
}
|
||||
|
||||
fn output_feature_is_plain(o: &OutputFeaturesV4) -> bool {
|
||||
o.0 == 0
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||
pub struct TxKernelV4 {
|
||||
/// Options for a kernel's structure or use
|
||||
#[serde(default = "default_kernel_feature")]
|
||||
#[serde(skip_serializing_if = "kernel_feature_is_plain")]
|
||||
pub features: CompatKernelFeatures,
|
||||
/// Fee originally included in the transaction this proof is for.
|
||||
#[serde(with = "secp_ser::string_or_u64")]
|
||||
#[serde(default = "default_u64")]
|
||||
#[serde(skip_serializing_if = "u64_is_blank")]
|
||||
pub fee: u64,
|
||||
/// This kernel is not valid earlier than lock_height blocks
|
||||
/// The max lock_height of all *inputs* to this transaction
|
||||
#[serde(with = "secp_ser::string_or_u64")]
|
||||
#[serde(default = "default_u64")]
|
||||
#[serde(skip_serializing_if = "u64_is_blank")]
|
||||
pub lock_height: u64,
|
||||
/// Remainder of the sum of all transaction commitments. If the transaction
|
||||
/// is well formed, amounts components should sum to zero and the excess
|
||||
|
@ -194,13 +397,69 @@ pub struct TxKernelV4 {
|
|||
serialize_with = "secp_ser::as_hex",
|
||||
deserialize_with = "secp_ser::commitment_from_hex"
|
||||
)]
|
||||
#[serde(default = "default_commitment")]
|
||||
#[serde(skip_serializing_if = "commitment_is_blank")]
|
||||
pub excess: Commitment,
|
||||
/// The signature proving the excess is a valid public key, which signs
|
||||
/// the transaction fee.
|
||||
#[serde(with = "secp_ser::sig_serde")]
|
||||
#[serde(default = "default_sig")]
|
||||
#[serde(skip_serializing_if = "sig_is_blank")]
|
||||
pub excess_sig: secp::Signature,
|
||||
}
|
||||
|
||||
fn default_kernel_feature() -> CompatKernelFeatures {
|
||||
CompatKernelFeatures::Plain
|
||||
}
|
||||
|
||||
fn kernel_feature_is_plain(k: &CompatKernelFeatures) -> bool {
|
||||
match k {
|
||||
CompatKernelFeatures::Plain => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
fn default_commitment() -> Commitment {
|
||||
Commitment::from_vec([0u8; 1].to_vec())
|
||||
}
|
||||
|
||||
fn commitment_is_blank(c: &Commitment) -> bool {
|
||||
for b in c.0.iter() {
|
||||
if *b != 0 {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
true
|
||||
}
|
||||
|
||||
fn default_sig() -> secp::Signature {
|
||||
Signature::from_raw_data(&[0; 64]).unwrap()
|
||||
}
|
||||
|
||||
fn sig_is_blank(s: &secp::Signature) -> bool {
|
||||
for b in s.to_raw_data().iter() {
|
||||
if *b != 0 {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
true
|
||||
}
|
||||
|
||||
fn default_u64() -> u64 {
|
||||
0
|
||||
}
|
||||
|
||||
fn u64_is_blank(u: &u64) -> bool {
|
||||
*u == 0
|
||||
}
|
||||
|
||||
fn default_u8_0() -> u8 {
|
||||
0
|
||||
}
|
||||
|
||||
fn u8_is_blank(u: &u8) -> bool {
|
||||
*u == 0
|
||||
}
|
||||
/// A mining node requests new coinbase via the foreign api every time a new candidate block is built.
|
||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||
pub struct CoinbaseV4 {
|
||||
|
@ -222,36 +481,66 @@ impl From<SlateV3> for SlateV4 {
|
|||
tx,
|
||||
amount,
|
||||
fee,
|
||||
height,
|
||||
height: _,
|
||||
lock_height,
|
||||
ttl_cutoff_height,
|
||||
participant_data,
|
||||
payment_proof,
|
||||
} = slate;
|
||||
} = slate.clone();
|
||||
let participant_data = map_vec!(participant_data, |data| ParticipantDataV4::from(data));
|
||||
let version_info = VersionCompatInfoV4::from(&version_info);
|
||||
let ver = VersionCompatInfoV4::from(&version_info);
|
||||
|
||||
let payment_proof = match payment_proof {
|
||||
Some(p) => Some(PaymentInfoV4::from(&p)),
|
||||
None => None,
|
||||
};
|
||||
let tx = TransactionV4::from(tx);
|
||||
let ttl_cutoff_height = match ttl_cutoff_height {
|
||||
None => 0,
|
||||
Some(n) => n,
|
||||
};
|
||||
let (feat, feat_args) = match lock_height {
|
||||
0 => (0, None),
|
||||
n => (1, Some(KernelFeaturesArgsV4 { lock_hgt: n })),
|
||||
};
|
||||
SlateV4 {
|
||||
version_info,
|
||||
num_participants,
|
||||
ver,
|
||||
num_parts: num_participants as u8,
|
||||
id,
|
||||
tx: Some(tx),
|
||||
amount,
|
||||
sta: SlateStateV4::Unknown,
|
||||
coms: (&slate).into(),
|
||||
amt: amount,
|
||||
fee,
|
||||
height,
|
||||
lock_height,
|
||||
ttl_cutoff_height,
|
||||
participant_data,
|
||||
payment_proof,
|
||||
feat,
|
||||
ttl: ttl_cutoff_height,
|
||||
off: tx.offset,
|
||||
sigs: participant_data,
|
||||
proof: payment_proof,
|
||||
feat_args,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&SlateV3> for Option<Vec<CommitsV4>> {
|
||||
fn from(slate: &SlateV3) -> Option<Vec<CommitsV4>> {
|
||||
let mut ret_vec = vec![];
|
||||
for i in slate.tx.body.inputs.iter() {
|
||||
ret_vec.push(CommitsV4 {
|
||||
f: i.features.into(),
|
||||
c: i.commit,
|
||||
p: None,
|
||||
});
|
||||
}
|
||||
for o in slate.tx.body.outputs.iter() {
|
||||
ret_vec.push(CommitsV4 {
|
||||
f: o.features.into(),
|
||||
c: o.commit,
|
||||
p: Some(o.proof),
|
||||
});
|
||||
}
|
||||
Some(ret_vec)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&ParticipantDataV3> for ParticipantDataV4 {
|
||||
fn from(data: &ParticipantDataV3) -> ParticipantDataV4 {
|
||||
let ParticipantDataV3 {
|
||||
|
@ -262,19 +551,16 @@ impl From<&ParticipantDataV3> for ParticipantDataV4 {
|
|||
message,
|
||||
message_sig,
|
||||
} = data;
|
||||
let id = *id;
|
||||
let _id = *id;
|
||||
let public_blind_excess = *public_blind_excess;
|
||||
let public_nonce = *public_nonce;
|
||||
let part_sig = *part_sig;
|
||||
let message: Option<String> = message.as_ref().map(|t| String::from(&**t));
|
||||
let message_sig = *message_sig;
|
||||
let _message: Option<String> = message.as_ref().map(|t| String::from(&**t));
|
||||
let _message_sig = *message_sig;
|
||||
ParticipantDataV4 {
|
||||
id,
|
||||
public_blind_excess,
|
||||
public_nonce,
|
||||
part_sig,
|
||||
message,
|
||||
message_sig,
|
||||
xs: public_blind_excess,
|
||||
nonce: public_nonce,
|
||||
part: part_sig,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -287,11 +573,10 @@ impl From<&VersionCompatInfoV3> for VersionCompatInfoV4 {
|
|||
block_header_version,
|
||||
} = data;
|
||||
let version = *version;
|
||||
let orig_version = *orig_version;
|
||||
let _orig_version = *orig_version;
|
||||
let block_header_version = *block_header_version;
|
||||
VersionCompatInfoV4 {
|
||||
version,
|
||||
orig_version,
|
||||
block_header_version,
|
||||
}
|
||||
}
|
||||
|
@ -317,9 +602,9 @@ impl From<&TransactionBodyV3> for TransactionBodyV4 {
|
|||
let outputs = map_vec!(outputs, |out| OutputV4::from(out));
|
||||
let kernels = map_vec!(kernels, |kern| TxKernelV4::from(kern));
|
||||
TransactionBodyV4 {
|
||||
inputs,
|
||||
outputs,
|
||||
kernels,
|
||||
ins: inputs,
|
||||
outs: outputs,
|
||||
kers: kernels,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -327,7 +612,10 @@ impl From<&TransactionBodyV3> for TransactionBodyV4 {
|
|||
impl From<&InputV3> for InputV4 {
|
||||
fn from(input: &InputV3) -> InputV4 {
|
||||
let InputV3 { features, commit } = *input;
|
||||
InputV4 { features, commit }
|
||||
InputV4 {
|
||||
features: features.into(),
|
||||
commit,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -339,9 +627,9 @@ impl From<&OutputV3> for OutputV4 {
|
|||
proof,
|
||||
} = *output;
|
||||
OutputV4 {
|
||||
features,
|
||||
commit,
|
||||
proof,
|
||||
features: features.into(),
|
||||
com: commit,
|
||||
prf: proof,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -367,9 +655,9 @@ impl From<&PaymentInfoV3> for PaymentInfoV4 {
|
|||
receiver_signature,
|
||||
} = *input;
|
||||
PaymentInfoV4 {
|
||||
sender_address,
|
||||
receiver_address,
|
||||
receiver_signature,
|
||||
saddr: sender_address,
|
||||
raddr: receiver_address,
|
||||
rsig: receiver_signature,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -380,30 +668,42 @@ impl TryFrom<&SlateV4> for SlateV3 {
|
|||
type Error = Error;
|
||||
fn try_from(slate: &SlateV4) -> Result<SlateV3, Error> {
|
||||
let SlateV4 {
|
||||
num_participants,
|
||||
num_parts: num_participants,
|
||||
id,
|
||||
tx,
|
||||
amount,
|
||||
sta: _,
|
||||
coms,
|
||||
amt: amount,
|
||||
fee,
|
||||
height,
|
||||
lock_height,
|
||||
ttl_cutoff_height,
|
||||
participant_data,
|
||||
version_info,
|
||||
payment_proof,
|
||||
feat,
|
||||
ttl: ttl_cutoff_height,
|
||||
off: offset,
|
||||
sigs: participant_data,
|
||||
ver,
|
||||
proof: payment_proof,
|
||||
feat_args,
|
||||
} = slate;
|
||||
let num_participants = *num_participants;
|
||||
let num_participants = match *num_participants {
|
||||
0 => 2,
|
||||
n => n,
|
||||
};
|
||||
let id = *id;
|
||||
let amount = *amount;
|
||||
let fee = *fee;
|
||||
let height = *height;
|
||||
let lock_height = *lock_height;
|
||||
let lock_height = match feat {
|
||||
0 => 0,
|
||||
1 => match feat_args {
|
||||
None => return Err(ErrorKind::KernelFeaturesMissing("lock_hgt".to_owned()).into()),
|
||||
Some(h) => h.lock_hgt,
|
||||
},
|
||||
n => return Err(ErrorKind::UnknownKernelFeatures(*n).into()),
|
||||
};
|
||||
let participant_data = map_vec!(participant_data, |data| ParticipantDataV3::from(data));
|
||||
let version_info = VersionCompatInfoV3::from(version_info);
|
||||
let version_info = VersionCompatInfoV3::from(ver);
|
||||
let payment_proof = match payment_proof {
|
||||
Some(p) => Some(PaymentInfoV3::from(p)),
|
||||
None => None,
|
||||
};
|
||||
let tx: Option<TransactionV3> = slate.into();
|
||||
let tx = match tx {
|
||||
Some(t) => TransactionV3::from(t),
|
||||
None => {
|
||||
|
@ -414,14 +714,18 @@ impl TryFrom<&SlateV4> for SlateV3 {
|
|||
}
|
||||
};
|
||||
|
||||
let ttl_cutoff_height = *ttl_cutoff_height;
|
||||
let ttl_cutoff_height = match *ttl_cutoff_height {
|
||||
0 => None,
|
||||
n => Some(n),
|
||||
};
|
||||
|
||||
Ok(SlateV3 {
|
||||
num_participants,
|
||||
num_participants: num_participants as usize,
|
||||
id,
|
||||
tx,
|
||||
amount,
|
||||
fee,
|
||||
height,
|
||||
height: 0,
|
||||
lock_height,
|
||||
ttl_cutoff_height,
|
||||
participant_data,
|
||||
|
@ -431,29 +735,84 @@ impl TryFrom<&SlateV4> for SlateV3 {
|
|||
}
|
||||
}
|
||||
|
||||
// Node's Transaction object and lock height to SlateV4 `coms`
|
||||
impl From<&SlateV4> for Option<TransactionV3> {
|
||||
fn from(slate: &SlateV4) -> Option<TransactionV3> {
|
||||
let res = crate::slate::tx_from_slate_v4(slate);
|
||||
let tx = match res {
|
||||
Some(tx) => tx,
|
||||
None => return None,
|
||||
};
|
||||
let mut out_fee = 0;
|
||||
let mut out_lock_height = 0;
|
||||
let txv3 = TransactionV3 {
|
||||
offset: tx.offset,
|
||||
body: TransactionBodyV3 {
|
||||
inputs: tx
|
||||
.body
|
||||
.inputs
|
||||
.iter()
|
||||
.map(|i| InputV3 {
|
||||
features: i.features,
|
||||
commit: i.commit,
|
||||
})
|
||||
.collect(),
|
||||
outputs: tx
|
||||
.body
|
||||
.outputs
|
||||
.iter()
|
||||
.map(|o| OutputV3 {
|
||||
features: o.features,
|
||||
commit: o.commit,
|
||||
proof: o.proof,
|
||||
})
|
||||
.collect(),
|
||||
kernels: tx
|
||||
.body
|
||||
.kernels
|
||||
.iter()
|
||||
.map(|k| TxKernelV3 {
|
||||
features: match k.features {
|
||||
KernelFeatures::Plain { fee } => {
|
||||
out_fee = fee;
|
||||
CompatKernelFeatures::Plain
|
||||
}
|
||||
KernelFeatures::Coinbase => CompatKernelFeatures::Coinbase,
|
||||
KernelFeatures::HeightLocked { fee, lock_height } => {
|
||||
out_fee = fee;
|
||||
out_lock_height = lock_height;
|
||||
CompatKernelFeatures::HeightLocked
|
||||
}
|
||||
},
|
||||
fee: out_fee,
|
||||
lock_height: out_lock_height,
|
||||
excess: k.excess,
|
||||
excess_sig: k.excess_sig,
|
||||
})
|
||||
.collect(),
|
||||
},
|
||||
};
|
||||
Some(txv3)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&ParticipantDataV4> for ParticipantDataV3 {
|
||||
fn from(data: &ParticipantDataV4) -> ParticipantDataV3 {
|
||||
let ParticipantDataV4 {
|
||||
id,
|
||||
public_blind_excess,
|
||||
public_nonce,
|
||||
part_sig,
|
||||
message,
|
||||
message_sig,
|
||||
xs: public_blind_excess,
|
||||
nonce: public_nonce,
|
||||
part: part_sig,
|
||||
} = data;
|
||||
let id = *id;
|
||||
let public_blind_excess = *public_blind_excess;
|
||||
let public_nonce = *public_nonce;
|
||||
let part_sig = *part_sig;
|
||||
let message: Option<String> = message.as_ref().map(|t| String::from(&**t));
|
||||
let message_sig = *message_sig;
|
||||
ParticipantDataV3 {
|
||||
id,
|
||||
id: 0,
|
||||
public_blind_excess,
|
||||
public_nonce,
|
||||
part_sig,
|
||||
message,
|
||||
message_sig,
|
||||
message: None,
|
||||
message_sig: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -462,11 +821,10 @@ impl From<&VersionCompatInfoV4> for VersionCompatInfoV3 {
|
|||
fn from(data: &VersionCompatInfoV4) -> VersionCompatInfoV3 {
|
||||
let VersionCompatInfoV4 {
|
||||
version,
|
||||
orig_version,
|
||||
block_header_version,
|
||||
} = data;
|
||||
let version = *version;
|
||||
let orig_version = *orig_version;
|
||||
let orig_version = version;
|
||||
let block_header_version = *block_header_version;
|
||||
VersionCompatInfoV3 {
|
||||
version,
|
||||
|
@ -495,15 +853,11 @@ impl From<&TransactionV4> for TransactionV3 {
|
|||
|
||||
impl From<&TransactionBodyV4> for TransactionBodyV3 {
|
||||
fn from(body: &TransactionBodyV4) -> TransactionBodyV3 {
|
||||
let TransactionBodyV4 {
|
||||
inputs,
|
||||
outputs,
|
||||
kernels,
|
||||
} = body;
|
||||
let TransactionBodyV4 { ins, outs, kers } = body;
|
||||
|
||||
let inputs = map_vec!(inputs, |inp| InputV3::from(inp));
|
||||
let outputs = map_vec!(outputs, |out| OutputV3::from(out));
|
||||
let kernels = map_vec!(kernels, |kern| TxKernelV3::from(kern));
|
||||
let inputs = map_vec!(ins, |inp| InputV3::from(inp));
|
||||
let outputs = map_vec!(outs, |out| OutputV3::from(out));
|
||||
let kernels = map_vec!(kers, |kern| TxKernelV3::from(kern));
|
||||
TransactionBodyV3 {
|
||||
inputs,
|
||||
outputs,
|
||||
|
@ -515,7 +869,10 @@ impl From<&TransactionBodyV4> for TransactionBodyV3 {
|
|||
impl From<&InputV4> for InputV3 {
|
||||
fn from(input: &InputV4) -> InputV3 {
|
||||
let InputV4 { features, commit } = *input;
|
||||
InputV3 { features, commit }
|
||||
InputV3 {
|
||||
features: features.into(),
|
||||
commit,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -523,11 +880,11 @@ impl From<&OutputV4> for OutputV3 {
|
|||
fn from(output: &OutputV4) -> OutputV3 {
|
||||
let OutputV4 {
|
||||
features,
|
||||
commit,
|
||||
proof,
|
||||
com: commit,
|
||||
prf: proof,
|
||||
} = *output;
|
||||
OutputV3 {
|
||||
features,
|
||||
features: features.into(),
|
||||
commit,
|
||||
proof,
|
||||
}
|
||||
|
@ -549,9 +906,9 @@ impl From<&TxKernelV4> for TxKernelV3 {
|
|||
impl From<&PaymentInfoV4> for PaymentInfoV3 {
|
||||
fn from(input: &PaymentInfoV4) -> PaymentInfoV3 {
|
||||
let PaymentInfoV4 {
|
||||
sender_address,
|
||||
receiver_address,
|
||||
receiver_signature,
|
||||
saddr: sender_address,
|
||||
raddr: receiver_address,
|
||||
rsig: receiver_signature,
|
||||
} = *input;
|
||||
PaymentInfoV3 {
|
||||
sender_address,
|
||||
|
|
584
libwallet/src/slate_versions/v4_bin.rs
Normal file
584
libwallet/src/slate_versions/v4_bin.rs
Normal file
|
@ -0,0 +1,584 @@
|
|||
// Copyright 2020 The Grin Developers
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
//! Wraps a V4 Slate into a V4 Binary slate
|
||||
|
||||
use crate::grin_core::core::transaction::OutputFeatures;
|
||||
use crate::grin_core::ser as grin_ser;
|
||||
use crate::grin_core::ser::{Readable, Reader, Writeable, Writer};
|
||||
use crate::grin_keychain::BlindingFactor;
|
||||
use crate::grin_util::secp::key::PublicKey;
|
||||
use crate::grin_util::secp::pedersen::{Commitment, RangeProof};
|
||||
use crate::grin_util::secp::Signature;
|
||||
use ed25519_dalek::PublicKey as DalekPublicKey;
|
||||
use ed25519_dalek::Signature as DalekSignature;
|
||||
use uuid::Uuid;
|
||||
|
||||
use crate::slate_versions::v4::{
|
||||
CommitsV4, KernelFeaturesArgsV4, ParticipantDataV4, PaymentInfoV4, SlateStateV4, SlateV4,
|
||||
VersionCompatInfoV4,
|
||||
};
|
||||
|
||||
impl Writeable for SlateStateV4 {
|
||||
fn write<W: Writer>(&self, writer: &mut W) -> Result<(), grin_ser::Error> {
|
||||
let b = match self {
|
||||
SlateStateV4::Unknown => 0,
|
||||
SlateStateV4::Standard1 => 1,
|
||||
SlateStateV4::Standard2 => 2,
|
||||
SlateStateV4::Standard3 => 3,
|
||||
SlateStateV4::Invoice1 => 4,
|
||||
SlateStateV4::Invoice2 => 5,
|
||||
SlateStateV4::Invoice3 => 6,
|
||||
};
|
||||
writer.write_u8(b)
|
||||
}
|
||||
}
|
||||
|
||||
impl Readable for SlateStateV4 {
|
||||
fn read<R: Reader>(reader: &mut R) -> Result<SlateStateV4, grin_ser::Error> {
|
||||
let b = reader.read_u8()?;
|
||||
let sta = match b {
|
||||
0 => SlateStateV4::Unknown,
|
||||
1 => SlateStateV4::Standard1,
|
||||
2 => SlateStateV4::Standard2,
|
||||
3 => SlateStateV4::Standard3,
|
||||
4 => SlateStateV4::Invoice1,
|
||||
5 => SlateStateV4::Invoice2,
|
||||
6 => SlateStateV4::Invoice3,
|
||||
_ => SlateStateV4::Unknown,
|
||||
};
|
||||
Ok(sta)
|
||||
}
|
||||
}
|
||||
|
||||
/// Allow serializing of Uuids not defined in crate
|
||||
struct UuidWrap(Uuid);
|
||||
|
||||
impl Writeable for UuidWrap {
|
||||
fn write<W: Writer>(&self, writer: &mut W) -> Result<(), grin_ser::Error> {
|
||||
writer.write_fixed_bytes(&self.0.as_bytes())
|
||||
}
|
||||
}
|
||||
|
||||
impl Readable for UuidWrap {
|
||||
fn read<R: Reader>(reader: &mut R) -> Result<UuidWrap, grin_ser::Error> {
|
||||
let bytes = reader.read_fixed_bytes(16)?;
|
||||
let mut b = [0u8; 16];
|
||||
b.copy_from_slice(&bytes[0..16]);
|
||||
Ok(UuidWrap(Uuid::from_bytes(b)))
|
||||
}
|
||||
}
|
||||
|
||||
/// Helper struct to serialize optional fields efficiently
|
||||
struct SlateOptFields {
|
||||
/// num parts, default 2
|
||||
pub num_parts: u8,
|
||||
/// amt, default 0
|
||||
pub amt: u64,
|
||||
/// fee, default 0
|
||||
pub fee: u64,
|
||||
/// kernel features, default 0
|
||||
pub feat: u8,
|
||||
/// ttl, default 0
|
||||
pub ttl: u64,
|
||||
}
|
||||
|
||||
impl Writeable for SlateOptFields {
|
||||
fn write<W: Writer>(&self, writer: &mut W) -> Result<(), grin_ser::Error> {
|
||||
// Status byte, bits determing which optional fields are serialized
|
||||
// 0 0 0 1 1 1 1 1
|
||||
// t f f a n
|
||||
let mut status = 0u8;
|
||||
if self.num_parts != 2 {
|
||||
status |= 0x01;
|
||||
}
|
||||
if self.amt > 0 {
|
||||
status |= 0x02;
|
||||
}
|
||||
if self.fee > 0 {
|
||||
status |= 0x04;
|
||||
}
|
||||
if self.feat > 0 {
|
||||
status |= 0x08;
|
||||
}
|
||||
if self.ttl > 0 {
|
||||
status |= 0x10;
|
||||
}
|
||||
writer.write_u8(status)?;
|
||||
if status & 0x01 > 0 {
|
||||
writer.write_u8(self.num_parts)?;
|
||||
}
|
||||
if status & 0x02 > 0 {
|
||||
writer.write_u64(self.amt)?;
|
||||
}
|
||||
if status & 0x04 > 0 {
|
||||
writer.write_u64(self.fee)?;
|
||||
}
|
||||
if status & 0x08 > 0 {
|
||||
writer.write_u8(self.feat)?;
|
||||
}
|
||||
if status & 0x10 > 0 {
|
||||
writer.write_u64(self.ttl)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Readable for SlateOptFields {
|
||||
fn read<R: Reader>(reader: &mut R) -> Result<SlateOptFields, grin_ser::Error> {
|
||||
let status = reader.read_u8()?;
|
||||
let num_parts = if status & 0x01 > 0 {
|
||||
reader.read_u8()?
|
||||
} else {
|
||||
2
|
||||
};
|
||||
let amt = if status & 0x02 > 0 {
|
||||
reader.read_u64()?
|
||||
} else {
|
||||
0
|
||||
};
|
||||
let fee = if status & 0x04 > 0 {
|
||||
reader.read_u64()?
|
||||
} else {
|
||||
0
|
||||
};
|
||||
let feat = if status & 0x08 > 0 {
|
||||
reader.read_u8()?
|
||||
} else {
|
||||
0
|
||||
};
|
||||
let ttl = if status & 0x10 > 0 {
|
||||
reader.read_u64()?
|
||||
} else {
|
||||
0
|
||||
};
|
||||
Ok(SlateOptFields {
|
||||
num_parts,
|
||||
amt,
|
||||
fee,
|
||||
feat,
|
||||
ttl,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
struct SigsWrap(Vec<ParticipantDataV4>);
|
||||
struct SigsWrapRef<'a>(&'a Vec<ParticipantDataV4>);
|
||||
|
||||
impl<'a> Writeable for SigsWrapRef<'a> {
|
||||
fn write<W: Writer>(&self, writer: &mut W) -> Result<(), grin_ser::Error> {
|
||||
writer.write_u8(self.0.len() as u8)?;
|
||||
for s in self.0.iter() {
|
||||
//0 means part sig is not yet included
|
||||
//1 means part sig included
|
||||
if s.part.is_some() {
|
||||
writer.write_u8(1)?;
|
||||
} else {
|
||||
writer.write_u8(0)?;
|
||||
}
|
||||
s.xs.write(writer)?;
|
||||
s.nonce.write(writer)?;
|
||||
if let Some(s) = s.part.clone() {
|
||||
s.write(writer)?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Readable for SigsWrap {
|
||||
fn read<R: Reader>(reader: &mut R) -> Result<SigsWrap, grin_ser::Error> {
|
||||
let sigs_len = reader.read_u8()?;
|
||||
let sigs = {
|
||||
let mut ret = vec![];
|
||||
for _ in 0..sigs_len as usize {
|
||||
let has_partial = reader.read_u8()?;
|
||||
let c = ParticipantDataV4 {
|
||||
xs: PublicKey::read(reader)?,
|
||||
nonce: PublicKey::read(reader)?,
|
||||
part: match has_partial {
|
||||
1 => Some(Signature::read(reader)?),
|
||||
0 | _ => None,
|
||||
},
|
||||
};
|
||||
ret.push(c);
|
||||
}
|
||||
ret
|
||||
};
|
||||
Ok(SigsWrap(sigs))
|
||||
}
|
||||
}
|
||||
|
||||
/// Serialization of optional structs
|
||||
struct SlateOptStructsRef<'a> {
|
||||
/// coms, default none
|
||||
pub coms: &'a Option<Vec<CommitsV4>>,
|
||||
///// proof, default none
|
||||
pub proof: &'a Option<PaymentInfoV4>,
|
||||
}
|
||||
|
||||
/// Serialization of optional structs
|
||||
struct SlateOptStructs {
|
||||
/// coms, default none
|
||||
pub coms: Option<Vec<CommitsV4>>,
|
||||
/// proof, default none
|
||||
pub proof: Option<PaymentInfoV4>,
|
||||
}
|
||||
|
||||
impl<'a> Writeable for SlateOptStructsRef<'a> {
|
||||
fn write<W: Writer>(&self, writer: &mut W) -> Result<(), grin_ser::Error> {
|
||||
// Status byte, bits determing which optional structs are serialized
|
||||
// 0 0 0 0 0 0 1 1
|
||||
// p c
|
||||
let mut status = 0u8;
|
||||
if self.coms.is_some() {
|
||||
status |= 0x01
|
||||
};
|
||||
if self.proof.is_some() {
|
||||
status |= 0x02
|
||||
};
|
||||
writer.write_u8(status)?;
|
||||
if let Some(c) = self.coms {
|
||||
ComsWrapRef(&c).write(writer)?;
|
||||
}
|
||||
if let Some(p) = self.proof {
|
||||
ProofWrapRef(&p).write(writer)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Readable for SlateOptStructs {
|
||||
fn read<R: Reader>(reader: &mut R) -> Result<SlateOptStructs, grin_ser::Error> {
|
||||
let status = reader.read_u8()?;
|
||||
let coms = if status & 0x01 > 0 {
|
||||
Some(ComsWrap::read(reader)?.0)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let proof = if status & 0x02 > 0 {
|
||||
Some(ProofWrap::read(reader)?.0)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
Ok(SlateOptStructs { coms, proof })
|
||||
}
|
||||
}
|
||||
|
||||
struct ComsWrap(Vec<CommitsV4>);
|
||||
struct ComsWrapRef<'a>(&'a Vec<CommitsV4>);
|
||||
|
||||
impl<'a> Writeable for ComsWrapRef<'a> {
|
||||
fn write<W: Writer>(&self, writer: &mut W) -> Result<(), grin_ser::Error> {
|
||||
writer.write_u16(self.0.len() as u16)?;
|
||||
for o in self.0.iter() {
|
||||
//0 means input
|
||||
//1 means output with proof
|
||||
if o.p.is_some() {
|
||||
writer.write_u8(1)?;
|
||||
} else {
|
||||
writer.write_u8(0)?;
|
||||
}
|
||||
OutputFeatures::from(o.f).write(writer)?;
|
||||
o.c.write(writer)?;
|
||||
if let Some(p) = o.p.clone() {
|
||||
p.write(writer)?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Readable for ComsWrap {
|
||||
fn read<R: Reader>(reader: &mut R) -> Result<ComsWrap, grin_ser::Error> {
|
||||
let coms_len = reader.read_u16()?;
|
||||
let coms = {
|
||||
let mut ret = vec![];
|
||||
for _ in 0..coms_len as usize {
|
||||
let is_output = reader.read_u8()?;
|
||||
let c = CommitsV4 {
|
||||
f: OutputFeatures::read(reader)?.into(),
|
||||
c: Commitment::read(reader)?,
|
||||
p: match is_output {
|
||||
1 => Some(RangeProof::read(reader)?),
|
||||
0 | _ => None,
|
||||
},
|
||||
};
|
||||
ret.push(c);
|
||||
}
|
||||
ret
|
||||
};
|
||||
Ok(ComsWrap(coms))
|
||||
}
|
||||
}
|
||||
|
||||
struct ProofWrap(PaymentInfoV4);
|
||||
struct ProofWrapRef<'a>(&'a PaymentInfoV4);
|
||||
|
||||
impl<'a> Writeable for ProofWrapRef<'a> {
|
||||
fn write<W: Writer>(&self, writer: &mut W) -> Result<(), grin_ser::Error> {
|
||||
writer.write_fixed_bytes(self.0.saddr.to_bytes())?;
|
||||
writer.write_fixed_bytes(self.0.raddr.to_bytes())?;
|
||||
match self.0.rsig {
|
||||
Some(s) => {
|
||||
writer.write_u8(1)?;
|
||||
writer.write_fixed_bytes(&s.to_bytes().to_vec())?;
|
||||
}
|
||||
None => writer.write_u8(0)?,
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Readable for ProofWrap {
|
||||
fn read<R: Reader>(reader: &mut R) -> Result<ProofWrap, grin_ser::Error> {
|
||||
let saddr = DalekPublicKey::from_bytes(&reader.read_fixed_bytes(32)?).unwrap();
|
||||
let raddr = DalekPublicKey::from_bytes(&reader.read_fixed_bytes(32)?).unwrap();
|
||||
let rsig = match reader.read_u8()? {
|
||||
0 => None,
|
||||
1 | _ => Some(DalekSignature::from_bytes(&reader.read_fixed_bytes(64)?).unwrap()),
|
||||
};
|
||||
Ok(ProofWrap(PaymentInfoV4 { saddr, raddr, rsig }))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct SlateV4Bin(pub SlateV4);
|
||||
|
||||
impl From<SlateV4> for SlateV4Bin {
|
||||
fn from(slate: SlateV4) -> SlateV4Bin {
|
||||
SlateV4Bin(slate)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<SlateV4Bin> for SlateV4 {
|
||||
fn from(slate: SlateV4Bin) -> SlateV4 {
|
||||
slate.0
|
||||
}
|
||||
}
|
||||
|
||||
impl serde::Serialize for SlateV4Bin {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: serde::Serializer,
|
||||
{
|
||||
let mut vec = vec![];
|
||||
grin_ser::serialize(&mut vec, grin_ser::ProtocolVersion(4), self)
|
||||
.map_err(|err| serde::ser::Error::custom(err.to_string()))?;
|
||||
serializer.serialize_bytes(&vec)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de> serde::Deserialize<'de> for SlateV4Bin {
|
||||
fn deserialize<D>(deserializer: D) -> Result<SlateV4Bin, D::Error>
|
||||
where
|
||||
D: serde::Deserializer<'de>,
|
||||
{
|
||||
struct SlateV4BinVisitor;
|
||||
|
||||
impl<'de> serde::de::Visitor<'de> for SlateV4BinVisitor {
|
||||
type Value = SlateV4Bin;
|
||||
|
||||
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
write!(formatter, "a serialised binary V4 slate")
|
||||
}
|
||||
|
||||
fn visit_bytes<E>(self, value: &[u8]) -> Result<SlateV4Bin, E>
|
||||
where
|
||||
E: serde::de::Error,
|
||||
{
|
||||
let mut reader = std::io::Cursor::new(value.to_vec());
|
||||
let s = grin_ser::deserialize(&mut reader, grin_ser::ProtocolVersion(4))
|
||||
.map_err(|err| serde::de::Error::custom(err.to_string()))?;
|
||||
Ok(s)
|
||||
}
|
||||
}
|
||||
deserializer.deserialize_bytes(SlateV4BinVisitor)
|
||||
}
|
||||
}
|
||||
|
||||
impl Writeable for SlateV4Bin {
|
||||
fn write<W: Writer>(&self, writer: &mut W) -> Result<(), grin_ser::Error> {
|
||||
let v4 = &self.0;
|
||||
writer.write_u16(v4.ver.version)?;
|
||||
writer.write_u16(v4.ver.block_header_version)?;
|
||||
(UuidWrap(v4.id)).write(writer)?;
|
||||
v4.sta.write(writer)?;
|
||||
v4.off.write(writer)?;
|
||||
SlateOptFields {
|
||||
num_parts: v4.num_parts,
|
||||
amt: v4.amt,
|
||||
fee: v4.fee,
|
||||
feat: v4.feat,
|
||||
ttl: v4.ttl,
|
||||
}
|
||||
.write(writer)?;
|
||||
(SigsWrapRef(&v4.sigs)).write(writer)?;
|
||||
SlateOptStructsRef {
|
||||
coms: &v4.coms,
|
||||
proof: &v4.proof,
|
||||
}
|
||||
.write(writer)?;
|
||||
// Write lock height for height locked kernels
|
||||
if v4.feat == 1 {
|
||||
let lock_hgt = match &v4.feat_args {
|
||||
Some(l) => l.lock_hgt,
|
||||
None => 0,
|
||||
};
|
||||
writer.write_u64(lock_hgt)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Readable for SlateV4Bin {
|
||||
fn read<R: Reader>(reader: &mut R) -> Result<SlateV4Bin, grin_ser::Error> {
|
||||
let ver = VersionCompatInfoV4 {
|
||||
version: reader.read_u16()?,
|
||||
block_header_version: reader.read_u16()?,
|
||||
};
|
||||
let id = UuidWrap::read(reader)?.0;
|
||||
let sta = SlateStateV4::read(reader)?;
|
||||
let off = BlindingFactor::read(reader)?;
|
||||
|
||||
let opts = SlateOptFields::read(reader)?;
|
||||
let sigs = SigsWrap::read(reader)?.0;
|
||||
let opt_structs = SlateOptStructs::read(reader)?;
|
||||
|
||||
let feat_args = if opts.feat == 1 {
|
||||
Some(KernelFeaturesArgsV4 {
|
||||
lock_hgt: reader.read_u64()?,
|
||||
})
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
Ok(SlateV4Bin(SlateV4 {
|
||||
ver,
|
||||
id,
|
||||
sta,
|
||||
off,
|
||||
num_parts: opts.num_parts,
|
||||
amt: opts.amt,
|
||||
fee: opts.fee,
|
||||
feat: opts.feat,
|
||||
ttl: opts.ttl,
|
||||
sigs,
|
||||
coms: opt_structs.coms,
|
||||
proof: opt_structs.proof,
|
||||
feat_args,
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn slate_v4_serialize_deserialize() {
|
||||
use crate::grin_util::from_hex;
|
||||
use crate::grin_util::secp::key::PublicKey;
|
||||
use crate::Slate;
|
||||
use grin_wallet_util::grin_keychain::{ExtKeychain, Keychain, SwitchCommitmentType};
|
||||
let slate = Slate::blank(1, false);
|
||||
let mut v4 = SlateV4::from(slate);
|
||||
|
||||
let keychain = ExtKeychain::from_random_seed(true).unwrap();
|
||||
let switch = SwitchCommitmentType::Regular;
|
||||
// add some sig data
|
||||
let id1 = ExtKeychain::derive_key_id(1, 1, 0, 0, 0);
|
||||
let id2 = ExtKeychain::derive_key_id(1, 1, 1, 0, 0);
|
||||
let skey1 = keychain.derive_key(0, &id1, switch).unwrap();
|
||||
let skey2 = keychain.derive_key(0, &id2, switch).unwrap();
|
||||
let xs = PublicKey::from_secret_key(keychain.secp(), &skey1).unwrap();
|
||||
let nonce = PublicKey::from_secret_key(keychain.secp(), &skey2).unwrap();
|
||||
let part = ParticipantDataV4 {
|
||||
xs,
|
||||
nonce,
|
||||
part: None,
|
||||
};
|
||||
let part2 = ParticipantDataV4 {
|
||||
xs,
|
||||
nonce,
|
||||
part: Some(Signature::from_raw_data(&[11; 64]).unwrap()),
|
||||
};
|
||||
v4.sigs.push(part.clone());
|
||||
v4.sigs.push(part2);
|
||||
v4.sigs.push(part);
|
||||
|
||||
// add some random commit data
|
||||
let com1 = CommitsV4 {
|
||||
f: OutputFeatures::Plain.into(),
|
||||
c: Commitment::from_vec([3u8; 1].to_vec()),
|
||||
p: None,
|
||||
};
|
||||
let com2 = CommitsV4 {
|
||||
f: OutputFeatures::Plain.into(),
|
||||
c: Commitment::from_vec([4u8; 1].to_vec()),
|
||||
p: Some(RangeProof::zero()),
|
||||
};
|
||||
let mut coms = vec![];
|
||||
coms.push(com1.clone());
|
||||
coms.push(com1.clone());
|
||||
coms.push(com1.clone());
|
||||
coms.push(com2);
|
||||
|
||||
v4.coms = Some(coms);
|
||||
v4.amt = 234324899824;
|
||||
v4.feat = 1;
|
||||
v4.num_parts = 2;
|
||||
v4.feat_args = Some(KernelFeaturesArgsV4 { lock_hgt: 23092039 });
|
||||
let v4_1 = v4.clone();
|
||||
let v4_1_copy = v4.clone();
|
||||
|
||||
let v4_bin = SlateV4Bin(v4);
|
||||
let mut vec = Vec::new();
|
||||
let _ = grin_ser::serialize_default(&mut vec, &v4_bin).expect("serialization failed");
|
||||
let b4_bin_2: SlateV4Bin = grin_ser::deserialize_default(&mut &vec[..]).unwrap();
|
||||
let v4_2 = b4_bin_2.0.clone();
|
||||
assert_eq!(v4_1.ver, v4_2.ver);
|
||||
assert_eq!(v4_1.id, v4_2.id);
|
||||
assert_eq!(v4_1.amt, v4_2.amt);
|
||||
assert_eq!(v4_1.fee, v4_2.fee);
|
||||
let v4_2_coms = v4_2.coms.as_ref().unwrap().clone();
|
||||
for (i, c) in v4_1.coms.unwrap().iter().enumerate() {
|
||||
assert_eq!(c.f, v4_2_coms[i].f);
|
||||
assert_eq!(c.c, v4_2_coms[i].c);
|
||||
assert_eq!(c.p, v4_2_coms[i].p);
|
||||
}
|
||||
assert_eq!(v4_1.sigs, v4_2.sigs);
|
||||
assert_eq!(v4_1.proof, v4_2.proof);
|
||||
|
||||
// Include Payment proof, remove coms to mix it up a bit
|
||||
let mut v4 = v4_1_copy;
|
||||
let raw_pubkey_str = "d03c09e9c19bb74aa9ea44e0fe5ae237a9bf40bddf0941064a80913a4459c8bb";
|
||||
let b = from_hex(raw_pubkey_str).unwrap();
|
||||
let d_pkey = DalekPublicKey::from_bytes(&b).unwrap();
|
||||
v4.proof = Some(PaymentInfoV4 {
|
||||
raddr: d_pkey.clone(),
|
||||
saddr: d_pkey.clone(),
|
||||
rsig: None,
|
||||
});
|
||||
v4.coms = None;
|
||||
let v4_1 = v4.clone();
|
||||
let v4_bin = SlateV4Bin(v4);
|
||||
let mut vec = Vec::new();
|
||||
let _ = grin_ser::serialize_default(&mut vec, &v4_bin).expect("serialization failed");
|
||||
let b4_bin_2: SlateV4Bin = grin_ser::deserialize_default(&mut &vec[..]).unwrap();
|
||||
let v4_2 = b4_bin_2.0.clone();
|
||||
assert_eq!(v4_1.ver, v4_2.ver);
|
||||
assert_eq!(v4_1.id, v4_2.id);
|
||||
assert_eq!(v4_1.amt, v4_2.amt);
|
||||
assert_eq!(v4_1.fee, v4_2.fee);
|
||||
assert!(v4_1.coms.is_none());
|
||||
assert_eq!(v4_1.sigs, v4_2.sigs);
|
||||
assert_eq!(v4_1.proof, v4_2.proof);
|
||||
}
|
|
@ -26,7 +26,6 @@ use crate::grin_util::logger::LoggingConfig;
|
|||
use crate::grin_util::secp::key::{PublicKey, SecretKey};
|
||||
use crate::grin_util::secp::{self, pedersen, Secp256k1};
|
||||
use crate::grin_util::{ToHex, ZeroingString};
|
||||
use crate::slate::ParticipantMessages;
|
||||
use crate::slate_versions::ser as dalek_ser;
|
||||
use chrono::prelude::*;
|
||||
use ed25519_dalek::PublicKey as DalekPublicKey;
|
||||
|
@ -194,7 +193,6 @@ where
|
|||
&mut self,
|
||||
keychain_mask: Option<&SecretKey>,
|
||||
slate_id: &[u8],
|
||||
participant_id: usize,
|
||||
) -> Result<Context, Error>;
|
||||
|
||||
/// Iterate over all output data stored by the backend
|
||||
|
@ -210,7 +208,7 @@ where
|
|||
fn store_tx(&self, uuid: &str, tx: &Transaction) -> Result<(), Error>;
|
||||
|
||||
/// Retrieves a stored transaction from a TxLogEntry
|
||||
fn get_stored_tx(&self, entry: &TxLogEntry) -> Result<Option<Transaction>, Error>;
|
||||
fn get_stored_tx(&self, uuid: &str) -> Result<Option<Transaction>, Error>;
|
||||
|
||||
/// Create a new write batch to update or remove output data
|
||||
fn batch<'a>(
|
||||
|
@ -296,19 +294,10 @@ where
|
|||
fn lock_output(&mut self, out: &mut OutputData) -> Result<(), Error>;
|
||||
|
||||
/// Saves the private context associated with a slate id
|
||||
fn save_private_context(
|
||||
&mut self,
|
||||
slate_id: &[u8],
|
||||
participant_id: usize,
|
||||
ctx: &Context,
|
||||
) -> Result<(), Error>;
|
||||
fn save_private_context(&mut self, slate_id: &[u8], ctx: &Context) -> Result<(), Error>;
|
||||
|
||||
/// Delete the private context associated with the slate id
|
||||
fn delete_private_context(
|
||||
&mut self,
|
||||
slate_id: &[u8],
|
||||
participant_id: usize,
|
||||
) -> Result<(), Error>;
|
||||
fn delete_private_context(&mut self, slate_id: &[u8]) -> Result<(), Error>;
|
||||
|
||||
/// Write the wallet data to backend file
|
||||
fn commit(&self) -> Result<(), Error>;
|
||||
|
@ -548,18 +537,28 @@ pub struct Context {
|
|||
/// Secret nonce (of which public is shared)
|
||||
/// (basically a SecretKey)
|
||||
pub sec_nonce: SecretKey,
|
||||
/// only used if self-sending an invoice
|
||||
pub initial_sec_key: SecretKey,
|
||||
/// as above
|
||||
pub initial_sec_nonce: SecretKey,
|
||||
/// store my outputs + amounts between invocations
|
||||
/// Id, mmr_index (if known), amount
|
||||
pub output_ids: Vec<(Identifier, Option<u64>, u64)>,
|
||||
/// store my inputs
|
||||
/// Id, mmr_index (if known), amount
|
||||
pub input_ids: Vec<(Identifier, Option<u64>, u64)>,
|
||||
/// store amount, so we can remove from slate if not
|
||||
/// needed by the other party
|
||||
pub amount: u64,
|
||||
/// store the calculated fee
|
||||
pub fee: u64,
|
||||
/// keep track of the participant id
|
||||
pub participant_id: usize,
|
||||
/// Payment proof sender address derivation path, if needed
|
||||
pub payment_proof_derivation_index: Option<u32>,
|
||||
/// whether this was an invoice transaction
|
||||
pub is_invoice: bool,
|
||||
/// for invoice I2 Only, store the tx excess so we can
|
||||
/// remove it from the slate on return
|
||||
pub calculated_excess: Option<pedersen::Commitment>,
|
||||
}
|
||||
|
||||
impl Context {
|
||||
|
@ -569,7 +568,7 @@ impl Context {
|
|||
sec_key: SecretKey,
|
||||
parent_key_id: &Identifier,
|
||||
use_test_rng: bool,
|
||||
participant_id: usize,
|
||||
is_invoice: bool,
|
||||
) -> Context {
|
||||
let sec_nonce = match use_test_rng {
|
||||
false => aggsig::create_secnonce(secp).unwrap(),
|
||||
|
@ -577,13 +576,17 @@ impl Context {
|
|||
};
|
||||
Context {
|
||||
parent_key_id: parent_key_id.clone(),
|
||||
sec_key: sec_key,
|
||||
sec_nonce,
|
||||
sec_key: sec_key.clone(),
|
||||
sec_nonce: sec_nonce.clone(),
|
||||
initial_sec_key: sec_key.clone(),
|
||||
initial_sec_nonce: sec_nonce.clone(),
|
||||
input_ids: vec![],
|
||||
output_ids: vec![],
|
||||
amount: 0,
|
||||
fee: 0,
|
||||
participant_id: participant_id,
|
||||
payment_proof_derivation_index: None,
|
||||
is_invoice,
|
||||
calculated_excess: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -796,8 +799,6 @@ pub struct TxLogEntry {
|
|||
#[serde(with = "secp_ser::opt_string_or_u64")]
|
||||
#[serde(default)]
|
||||
pub ttl_cutoff_height: Option<u64>,
|
||||
/// Message data, stored as json
|
||||
pub messages: Option<ParticipantMessages>,
|
||||
/// Location of the store transaction, (reference or resending)
|
||||
pub stored_tx: Option<String>,
|
||||
/// Associated kernel excess, for later lookup if necessary
|
||||
|
@ -846,7 +847,6 @@ impl TxLogEntry {
|
|||
num_outputs: 0,
|
||||
fee: None,
|
||||
ttl_cutoff_height: None,
|
||||
messages: None,
|
||||
stored_tx: None,
|
||||
kernel_excess: None,
|
||||
kernel_lookup_min_height: None,
|
||||
|
|
|
@ -74,7 +74,7 @@ fn aggsig_sender_receiver_interaction() {
|
|||
|
||||
let blind = blinding_factor.secret_key(&keychain.secp()).unwrap();
|
||||
|
||||
s_cx = Context::new(&keychain.secp(), blind, &parent, false, 0);
|
||||
s_cx = Context::new(&keychain.secp(), blind, &parent, false, false);
|
||||
s_cx.get_public_keys(&keychain.secp())
|
||||
};
|
||||
|
||||
|
@ -88,7 +88,7 @@ fn aggsig_sender_receiver_interaction() {
|
|||
// let blind = blind_sum.secret_key(&keychain.secp())?;
|
||||
let blind = keychain.derive_key(0, &key_id, switch).unwrap();
|
||||
|
||||
rx_cx = Context::new(&keychain.secp(), blind, &parent, false, 1);
|
||||
rx_cx = Context::new(&keychain.secp(), blind, &parent, false, false);
|
||||
let (pub_excess, pub_nonce) = rx_cx.get_public_keys(&keychain.secp());
|
||||
rx_cx.add_output(&key_id, &None, 0);
|
||||
|
||||
|
@ -293,7 +293,7 @@ fn aggsig_sender_receiver_interaction_offset() {
|
|||
|
||||
let blind = blinding_factor.secret_key(&keychain.secp()).unwrap();
|
||||
|
||||
s_cx = Context::new(&keychain.secp(), blind, &parent, false, 0);
|
||||
s_cx = Context::new(&keychain.secp(), blind, &parent, false, false);
|
||||
s_cx.get_public_keys(&keychain.secp())
|
||||
};
|
||||
|
||||
|
@ -306,7 +306,7 @@ fn aggsig_sender_receiver_interaction_offset() {
|
|||
|
||||
let blind = keychain.derive_key(0, &key_id, switch).unwrap();
|
||||
|
||||
rx_cx = Context::new(&keychain.secp(), blind, &parent, false, 1);
|
||||
rx_cx = Context::new(&keychain.secp(), blind, &parent, false, false);
|
||||
let (pub_excess, pub_nonce) = rx_cx.get_public_keys(&keychain.secp());
|
||||
rx_cx.add_output(&key_id, &None, 0);
|
||||
|
||||
|
|
|
@ -125,6 +125,7 @@ subcommands:
|
|||
possible_values:
|
||||
- http
|
||||
- file
|
||||
- binfile
|
||||
- self
|
||||
- keybase
|
||||
default_value: http
|
||||
|
@ -147,11 +148,6 @@ subcommands:
|
|||
help: Fluff the transaction (ignore Dandelion relay protocol)
|
||||
short: f
|
||||
long: fluff
|
||||
- message:
|
||||
help: Optional participant message to include
|
||||
short: g
|
||||
long: message
|
||||
takes_value: true
|
||||
- stored_tx:
|
||||
help: If present, use the previously stored Unconfirmed transaction with given id
|
||||
short: t
|
||||
|
@ -162,14 +158,14 @@ subcommands:
|
|||
short: b
|
||||
long: ttl_blocks
|
||||
takes_value: true
|
||||
#TODO: Remove HF3
|
||||
- v4:
|
||||
help: Output a V4 slate prior to HF3 block
|
||||
long: v4
|
||||
takes_value: false
|
||||
- receive:
|
||||
about: Processes a transaction file to accept a transfer from a sender
|
||||
args:
|
||||
- message:
|
||||
help: Optional participant message to include
|
||||
short: g
|
||||
long: message
|
||||
takes_value: true
|
||||
- input:
|
||||
help: Partial transaction to process, expects the sender's transaction file.
|
||||
short: i
|
||||
|
@ -202,16 +198,20 @@ subcommands:
|
|||
- amount:
|
||||
help: Number of coins to invoice with optional fraction, e.g. 12.423
|
||||
index: 1
|
||||
- message:
|
||||
help: Optional participant message to include
|
||||
short: g
|
||||
long: message
|
||||
takes_value: true
|
||||
- dest:
|
||||
help: Name of destination slate output file
|
||||
short: d
|
||||
long: dest
|
||||
takes_value: true
|
||||
- bin:
|
||||
help: Whether to output file as binary
|
||||
short: b
|
||||
long: bin
|
||||
#TODO: Remove HF3
|
||||
- v4:
|
||||
help: Output a V4 slate prior to HF3 block
|
||||
long: v4
|
||||
takes_value: false
|
||||
- pay:
|
||||
about: Spend coins to pay the provided invoice transaction
|
||||
args:
|
||||
|
@ -239,6 +239,7 @@ subcommands:
|
|||
short: m
|
||||
long: method
|
||||
possible_values:
|
||||
- filebin
|
||||
- file
|
||||
- http
|
||||
- self
|
||||
|
@ -249,11 +250,6 @@ subcommands:
|
|||
short: d
|
||||
long: dest
|
||||
takes_value: true
|
||||
- message:
|
||||
help: Optional participant message to include
|
||||
short: g
|
||||
long: message
|
||||
takes_value: true
|
||||
- input:
|
||||
help: Partial transaction to process, expects the invoicer's transaction file.
|
||||
short: i
|
||||
|
|
|
@ -161,10 +161,6 @@ fn prompt_pay_invoice(slate: &Slate, method: &str, dest: &str) -> Result<bool, P
|
|||
println!("* The resulting transaction will be saved to the file '{}', which you can manually send back to the invoice creator.", dest);
|
||||
}
|
||||
println!();
|
||||
println!("The invoice slate's participant info is:");
|
||||
for m in slate.participant_messages().messages {
|
||||
println!("{}", m);
|
||||
}
|
||||
println!("Please review the above information carefully before proceeding");
|
||||
println!();
|
||||
loop {
|
||||
|
@ -408,12 +404,6 @@ pub fn parse_send_args(args: &ArgMatches) -> Result<command::SendArgs, ParseErro
|
|||
}
|
||||
};
|
||||
|
||||
// message
|
||||
let message = match args.is_present("message") {
|
||||
true => Some(args.value_of("message").unwrap().to_owned()),
|
||||
false => None,
|
||||
};
|
||||
|
||||
// minimum_confirmations
|
||||
let min_c = parse_required(args, "minimum_confirmations")?;
|
||||
let min_c = parse_u64(min_c, "minimum_confirmations")?;
|
||||
|
@ -469,6 +459,9 @@ pub fn parse_send_args(args: &ArgMatches) -> Result<command::SendArgs, ParseErro
|
|||
// max_outputs
|
||||
let max_outputs = 500;
|
||||
|
||||
// TODO: Remove HF3
|
||||
let output_v4_slate = args.is_present("v4");
|
||||
|
||||
// target slate version to create/send
|
||||
let target_slate_version = {
|
||||
match args.is_present("slate_version") {
|
||||
|
@ -505,7 +498,6 @@ pub fn parse_send_args(args: &ArgMatches) -> Result<command::SendArgs, ParseErro
|
|||
|
||||
Ok(command::SendArgs {
|
||||
amount: amount,
|
||||
message: message,
|
||||
minimum_confirmations: min_c,
|
||||
selection_strategy: selection_strategy.to_owned(),
|
||||
estimate_selection_strategies,
|
||||
|
@ -517,16 +509,11 @@ pub fn parse_send_args(args: &ArgMatches) -> Result<command::SendArgs, ParseErro
|
|||
payment_proof_address,
|
||||
ttl_blocks,
|
||||
target_slate_version: target_slate_version,
|
||||
output_v4_slate,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn parse_receive_args(receive_args: &ArgMatches) -> Result<command::ReceiveArgs, ParseError> {
|
||||
// message
|
||||
let message = match receive_args.is_present("message") {
|
||||
true => Some(receive_args.value_of("message").unwrap().to_owned()),
|
||||
false => None,
|
||||
};
|
||||
|
||||
// input
|
||||
let tx_file = parse_required(receive_args, "input")?;
|
||||
|
||||
|
@ -538,7 +525,6 @@ pub fn parse_receive_args(receive_args: &ArgMatches) -> Result<command::ReceiveA
|
|||
|
||||
Ok(command::ReceiveArgs {
|
||||
input: tx_file.to_owned(),
|
||||
message: message,
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -580,11 +566,11 @@ pub fn parse_issue_invoice_args(
|
|||
return Err(ParseError::ArgumentError(msg));
|
||||
}
|
||||
};
|
||||
// message
|
||||
let message = match args.is_present("message") {
|
||||
true => Some(args.value_of("message").unwrap().to_owned()),
|
||||
false => None,
|
||||
};
|
||||
let bin = args.is_present("bin");
|
||||
|
||||
// TODO: Remove HF3
|
||||
let output_v4_slate = args.is_present("v4");
|
||||
|
||||
// target slate version to create
|
||||
let target_slate_version = {
|
||||
match args.is_present("slate_version") {
|
||||
|
@ -599,10 +585,11 @@ pub fn parse_issue_invoice_args(
|
|||
let dest = parse_required(args, "dest")?;
|
||||
Ok(command::IssueInvoiceArgs {
|
||||
dest: dest.into(),
|
||||
bin,
|
||||
output_v4_slate,
|
||||
issue_args: IssueInvoiceTxArgs {
|
||||
dest_acct_name: None,
|
||||
amount,
|
||||
message,
|
||||
target_slate_version,
|
||||
},
|
||||
})
|
||||
|
@ -612,13 +599,6 @@ pub fn parse_process_invoice_args(
|
|||
args: &ArgMatches,
|
||||
prompt: bool,
|
||||
) -> Result<command::ProcessInvoiceArgs, ParseError> {
|
||||
// TODO: display and prompt for confirmation of what we're doing
|
||||
// message
|
||||
let message = match args.is_present("message") {
|
||||
true => Some(args.value_of("message").unwrap().to_owned()),
|
||||
false => None,
|
||||
};
|
||||
|
||||
// minimum_confirmations
|
||||
let min_c = parse_required(args, "minimum_confirmations")?;
|
||||
let min_c = parse_u64(min_c, "minimum_confirmations")?;
|
||||
|
@ -673,7 +653,7 @@ pub fn parse_process_invoice_args(
|
|||
// which requires reading the slate
|
||||
|
||||
let slate = match PathToSlate((&tx_file).into()).get_tx() {
|
||||
Ok(s) => s,
|
||||
Ok(s) => s.0,
|
||||
Err(e) => return Err(ParseError::ArgumentError(format!("{}", e))),
|
||||
};
|
||||
|
||||
|
@ -681,7 +661,6 @@ pub fn parse_process_invoice_args(
|
|||
}
|
||||
|
||||
Ok(command::ProcessInvoiceArgs {
|
||||
message: message,
|
||||
minimum_confirmations: min_c,
|
||||
selection_strategy: selection_strategy.to_owned(),
|
||||
estimate_selection_strategies,
|
||||
|
|
|
@ -165,16 +165,6 @@ fn command_line_test_impl(test_dir: &str) -> Result<(), grin_wallet_controller::
|
|||
let _ =
|
||||
test_framework::award_blocks_to_wallet(&chain, wallet1.clone(), mask1, bh as usize, false);
|
||||
|
||||
let very_long_message = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdef\
|
||||
ABCDEFGHIJKLMNOPQRSTUVWXYZabcdef\
|
||||
ABCDEFGHIJKLMNOPQRSTUVWXYZabcdef\
|
||||
ABCDEFGHIJKLMNOPQRSTUVWXYZabcdef\
|
||||
ABCDEFGHIJKLMNOPQRSTUVWXYZabcdef\
|
||||
ABCDEFGHIJKLMNOPQRSTUVWXYZabcdef\
|
||||
ABCDEFGHIJKLMNOPQRSTUVWXYZabcdef\
|
||||
ABCDEFGHIJKLMNOPQRSTUVWXYZabcdef\
|
||||
This part should all be truncated";
|
||||
|
||||
// Update info and check
|
||||
let arg_vec = vec!["grin-wallet", "-p", "password", "-a", "mining", "info"];
|
||||
execute_command(&app, test_dir, "wallet1", &client1, arg_vec)?;
|
||||
|
@ -193,8 +183,6 @@ fn command_line_test_impl(test_dir: &str) -> Result<(), grin_wallet_controller::
|
|||
"file",
|
||||
"-d",
|
||||
&file_name,
|
||||
"-g",
|
||||
very_long_message,
|
||||
"10",
|
||||
];
|
||||
execute_command(&app, test_dir, "wallet1", &client1, arg_vec)?;
|
||||
|
@ -208,8 +196,6 @@ fn command_line_test_impl(test_dir: &str) -> Result<(), grin_wallet_controller::
|
|||
"receive",
|
||||
"-i",
|
||||
&file_name,
|
||||
"-g",
|
||||
"Thanks, Yeast!",
|
||||
];
|
||||
execute_command(&app, test_dir, "wallet2", &client2, arg_vec.clone())?;
|
||||
|
||||
|
@ -300,8 +286,6 @@ fn command_line_test_impl(test_dir: &str) -> Result<(), grin_wallet_controller::
|
|||
"file",
|
||||
"-d",
|
||||
&file_name,
|
||||
"-g",
|
||||
"Love, Yeast, Smallest",
|
||||
"-s",
|
||||
"smallest",
|
||||
"10",
|
||||
|
@ -317,8 +301,6 @@ fn command_line_test_impl(test_dir: &str) -> Result<(), grin_wallet_controller::
|
|||
"receive",
|
||||
"-i",
|
||||
&file_name,
|
||||
"-g",
|
||||
"Thanks, Yeast!",
|
||||
];
|
||||
execute_command(&app, test_dir, "wallet1", &client1, arg_vec.clone())?;
|
||||
|
||||
|
@ -370,8 +352,6 @@ fn command_line_test_impl(test_dir: &str) -> Result<(), grin_wallet_controller::
|
|||
"self",
|
||||
"-d",
|
||||
"mining",
|
||||
"-g",
|
||||
"Self love",
|
||||
"-o",
|
||||
"3",
|
||||
"-s",
|
||||
|
@ -416,8 +396,6 @@ fn command_line_test_impl(test_dir: &str) -> Result<(), grin_wallet_controller::
|
|||
"file",
|
||||
"-d",
|
||||
&file_name,
|
||||
"-g",
|
||||
"Ain't sending",
|
||||
"10",
|
||||
];
|
||||
execute_command(&app, test_dir, "wallet1", &client1, arg_vec)?;
|
||||
|
@ -437,8 +415,6 @@ fn command_line_test_impl(test_dir: &str) -> Result<(), grin_wallet_controller::
|
|||
"file",
|
||||
"-d",
|
||||
&file_name,
|
||||
"-g",
|
||||
"Ain't sending 2",
|
||||
"10",
|
||||
];
|
||||
execute_command(&app, test_dir, "wallet1", &client1, arg_vec)?;
|
||||
|
@ -464,8 +440,7 @@ fn command_line_test_impl(test_dir: &str) -> Result<(), grin_wallet_controller::
|
|||
"invoice",
|
||||
"-d",
|
||||
&file_name,
|
||||
"-g",
|
||||
"Please give me your precious grins. Love, Yeast",
|
||||
"-b",
|
||||
"65",
|
||||
];
|
||||
execute_command(&app, test_dir, "wallet2", &client2, arg_vec)?;
|
||||
|
@ -483,8 +458,6 @@ fn command_line_test_impl(test_dir: &str) -> Result<(), grin_wallet_controller::
|
|||
&file_name,
|
||||
"-d",
|
||||
&output_file_name,
|
||||
"-g",
|
||||
"Here you go",
|
||||
];
|
||||
execute_command(&app, test_dir, "wallet1", &client1, arg_vec)?;
|
||||
|
||||
|
|
|
@ -396,7 +396,7 @@ fn owner_v3_lifecycle() -> Result<(), grin_wallet_controller::Error> {
|
|||
let res = api.process_invoice_tx(m, &slate, args);
|
||||
assert!(res.is_ok());
|
||||
slate = res.unwrap();
|
||||
api.tx_lock_outputs(m, &slate, 0)?;
|
||||
api.tx_lock_outputs(m, &slate)?;
|
||||
|
||||
Ok(())
|
||||
},
|
||||
|
|
|
@ -43,12 +43,12 @@ grin_api = { git = "https://github.com/mimblewimble/grin", branch = "master" }
|
|||
grin_store = { git = "https://github.com/mimblewimble/grin", branch = "master" }
|
||||
|
||||
# For local testing
|
||||
# grin_core = { path = "../../grin/core"}
|
||||
# grin_keychain = { path = "../../grin/keychain"}
|
||||
# grin_chain = { path = "../../grin/chain"}
|
||||
# grin_util = { path = "../../grin/util"}
|
||||
# grin_api = { path = "../../grin/api"}
|
||||
# grin_store = { path = "../../grin/store"}
|
||||
#grin_core = { path = "../../grin/core"}
|
||||
#grin_keychain = { path = "../../grin/keychain"}
|
||||
#grin_chain = { path = "../../grin/chain"}
|
||||
#grin_util = { path = "../../grin/util"}
|
||||
#grin_api = { path = "../../grin/api"}
|
||||
#grin_store = { path = "../../grin/store"}
|
||||
|
||||
[dev-dependencies]
|
||||
pretty_assertions = "0.5.1"
|
||||
|
|
Loading…
Reference in a new issue