Slatepack address index on send (#821)

* Store address_index into tx log entry to get address and decrypt slatepack message, increment over all values

* Provide address index on send and issue_invoice command

* Keep 0 index for receive

* Use sender address index at payment proof

Rust API users need to update to SlatepackAddressIndex and the Slatepack methods
Keep a wallet database backup for pending transactions and saved address indices. Seed recovery alone does not restore these, and the address counter restarts at 1.
This commit is contained in:
ardocrat
2026-10-13 16:41:29 +03:00
committed by GitHub
parent dce4e5eecc
commit b8ddf13001
31 changed files with 1171 additions and 735 deletions
+75 -38
View File
@@ -23,10 +23,11 @@ use crate::keychain::{Identifier, Keychain};
use crate::libwallet::api_impl::owner_updater::{start_updater_log_thread, StatusMessage};
use crate::libwallet::api_impl::types::update_tx_slate_state;
use crate::libwallet::api_impl::{owner, owner_updater};
use crate::libwallet::wallet_lock;
use crate::libwallet::{
AcctPathMapping, BuiltOutput, Error, InitTxArgs, IssueInvoiceTxArgs, NodeClient,
NodeHeightResult, OutputCommitMapping, PaymentProof, Slate, Slatepack, SlatepackAddress,
TxLogEntry, ViewWallet, WalletInfo, WalletLCProvider,
TxLogEntry, ViewWallet, WalletBackend, WalletInfo, WalletLCProvider,
};
use crate::util::logger::LoggingConfig;
use crate::util::secp::{key::SecretKey, pedersen::Commitment};
@@ -35,12 +36,12 @@ use grin_wallet_config::config::{
reload_global_config, update_global_config, WALLET_CONFIG_FILE_NAME,
};
use grin_wallet_libwallet::mwixnet::{MixnetReqCreationParams, SwapReq};
use grin_wallet_libwallet::slatepack::SlatepackAddressIndex;
use grin_wallet_libwallet::RetrieveTxQueryArgs;
use grin_wallet_util::OnionV3Address;
use chrono::prelude::*;
use ed25519_dalek::SigningKey as DalekSecretKey;
use libwallet::wallet_lock;
use std::convert::TryFrom;
use std::fs::File;
use std::io::Write;
@@ -782,7 +783,7 @@ where
pub fn init_send_tx(
&self,
keychain_mask: Option<&SecretKey>,
args: InitTxArgs,
mut args: InitTxArgs,
) -> Result<Slate, Error> {
let send_args = args.send_args.clone();
let dest = match send_args.as_ref() {
@@ -797,6 +798,18 @@ where
.map(|_| crate::tor_config::load(&self.config_path()))
.transpose()?;
let init_send_tx = |w: &mut WalletBackend<C, K>, args: &mut InitTxArgs| {
if args.address_index.is_none() && args.estimate_only != Some(true) {
let parent_key_id = match &args.src_acct_name {
Some(name) => w.get_acct_path(name.clone())?.map(|account| account.path),
None => None,
}
.unwrap_or_else(|| w.parent_key_id());
args.address_index = Some(w.next_tx_slatepack_address_index(&parent_key_id)?);
}
owner::init_send_tx(w, keychain_mask, args.clone(), self.doctest_mode)
};
// Helper functionality. If send arguments exist, attempt to send sync and
// finalize
match send_args {
@@ -804,7 +817,7 @@ where
let slate = {
wallet_lock!(self.wallet_inst, w);
let late_lock = args.late_lock.unwrap_or(false);
let slate = owner::init_send_tx(w, keychain_mask, args, self.doctest_mode)?;
let slate = init_send_tx(w, &mut args)?;
let tc = tor_config.as_ref().ok_or_else(|| {
Error::TorConfig("Tor config was not loaded with send arguments".into())
})?;
@@ -815,12 +828,29 @@ where
if !late_lock {
owner::tx_lock_outputs(w, keychain_mask, &slate)?;
}
slate
};
let res =
try_slatepack_sync_workflow(&slate, &dest.unwrap(), tor_config, None, false);
let res = try_slatepack_sync_workflow(
&slate,
dest.as_ref().unwrap(),
tor_config,
None,
false,
);
match res {
Ok(s) => {
// Output slatepack message to file.
match output_slatepack_file(
self,
keychain_mask,
&s,
dest,
args.address_index,
) {
Ok(_) => {}
Err(e) => error!("Error on saving output slatepack message: {}", e),
}
let ret_slate = self.finalize_tx(keychain_mask, &s)?;
if sa.post_tx {
let result = self.post_tx(keychain_mask, &ret_slate, sa.fluff);
@@ -846,7 +876,7 @@ where
}
None => {
wallet_lock!(self.wallet_inst, w);
let slate = owner::init_send_tx(w, keychain_mask, args, self.doctest_mode)?;
let slate = init_send_tx(w, &mut args)?;
Ok(slate)
}
}
@@ -938,7 +968,7 @@ where
/// let mut api_owner = Owner::new(wallet.clone(), None, std::path::PathBuf::from("grin-wallet.toml"));
///
/// // . . .
/// // The slate has been recieved from the invoicer, somehow
/// // The slate has been received from the invoicer, somehow
/// # let slate = Slate::blank(2, true);
/// let args = InitTxArgs {
/// src_acct_name: None,
@@ -970,19 +1000,17 @@ where
}
_ => None,
};
let (slate, tor_config) = {
let tor_config = send_args
.as_ref()
.map(|_| crate::tor_config::load(&self.config_path()))
.transpose()?;
let slate = {
let mut w_lock = self.wallet_inst.lock();
let tor_config = send_args
.as_ref()
.map(|_| crate::tor_config::load(&self.config_path()))
.transpose()?;
let w = w_lock.lc_provider()?.wallet_inst()?;
let slate =
owner::process_invoice_tx(w, keychain_mask, slate, args, self.doctest_mode)?;
(slate, tor_config)
owner::process_invoice_tx(w, keychain_mask, slate, args.clone(), self.doctest_mode)?
};
// Helper functionality. If send arguments exist, attempt to send
match send_args {
match send_args.as_ref() {
Some(sa) => {
let tc = tor_config.ok_or_else(|| {
Error::TorConfig("Tor config was not loaded with send arguments".into())
@@ -1006,7 +1034,13 @@ where
}
}
// Output slatepack message to file.
match output_slatepack_file(self, keychain_mask, &s, Some(dest)) {
match output_slatepack_file(
self,
keychain_mask,
&s,
Some(dest),
args.address_index,
) {
Ok(_) => {}
Err(e) => error!("Error on saving output slatepack message: {}", e),
}
@@ -2193,7 +2227,7 @@ where
/// # Arguments
///
/// * `keychain_mask` - Wallet secret mask to XOR against the stored wallet seed before using, if
/// * `derivation_index` - The index along the derivation path to retrieve an address for
/// * `derivation_index` - The index along the derivation path to retrieve an address for, should be not higher than 2^31 - 1
///
/// # Returns
/// * Ok with a SlatepackAddress representing the address
@@ -2205,13 +2239,14 @@ where
/// # grin_wallet_api::doctest_helper_setup_doc_env!(wallet, wallet_config);
///
/// use grin_core::global::ChainTypes;
/// use grin_wallet_libwallet::slatepack::SlatepackAddressIndex;
///
/// use std::time::Duration;
///
/// // Set up as above
/// # let api_owner = Owner::new(wallet.clone(), None, std::path::PathBuf::from("grin-wallet.toml"));
///
/// let res = api_owner.get_slatepack_address(None, 0);
/// let res = api_owner.get_slatepack_address(None, SlatepackAddressIndex(0));
///
/// if let Ok(_) = res {
/// // ...
@@ -2221,7 +2256,7 @@ where
pub fn get_slatepack_address(
&self,
keychain_mask: Option<&SecretKey>,
derivation_index: u32,
derivation_index: SlatepackAddressIndex,
) -> Result<SlatepackAddress, Error> {
owner::get_slatepack_address(self.wallet_inst.clone(), keychain_mask, derivation_index)
}
@@ -2231,9 +2266,8 @@ where
///
/// # Arguments
///
/// * `keychain_mask` - Wallet secret mask to XOR against the stored wallet seed before using, if
/// * `derivation_index` - The index along the derivation path to for which to retrieve the secret key
///
/// * `keychain_mask` - Wallet secret mask to XOR against the stored wallet seed before using
/// * `derivation_index` - The index along the derivation path to for which to retrieve the secret key, should be not higher than 2^31 - 1
/// # Returns
/// * Ok with an ed25519_dalek::SecretKey if successful
/// * or [`libwallet::Error`](../grin_wallet_libwallet/struct.Error.html) if an error is encountered.
@@ -2244,13 +2278,14 @@ where
/// # grin_wallet_api::doctest_helper_setup_doc_env!(wallet, wallet_config);
///
/// use grin_core::global::ChainTypes;
/// use grin_wallet_libwallet::slatepack::SlatepackAddressIndex;
///
/// use std::time::Duration;
///
/// // Set up as above
/// # let api_owner = Owner::new(wallet.clone(), None, std::path::PathBuf::from("grin-wallet.toml"));
///
/// let res = api_owner.get_slatepack_secret_key(None, 0);
/// let res = api_owner.get_slatepack_secret_key(None, SlatepackAddressIndex(0));
///
/// if let Ok(_) = res {
/// // ...
@@ -2260,7 +2295,7 @@ where
pub fn get_slatepack_secret_key(
&self,
keychain_mask: Option<&SecretKey>,
derivation_index: u32,
derivation_index: SlatepackAddressIndex,
) -> Result<DalekSecretKey, Error> {
owner::get_slatepack_secret_key(self.wallet_inst.clone(), keychain_mask, derivation_index)
}
@@ -2271,7 +2306,7 @@ where
/// # Arguments
///
/// * `keychain_mask` - Wallet secret mask to XOR against the stored wallet seed before using, if
/// * `sender_index` - If Some(n), the index along the derivation path to include as the sender
/// * `sender_index` - If Some(n), the index along the derivation path to include as the sender, should be not higher than 2^31 - 1
/// * `recipients` - Optional recipients for which to encrypt the slatepack's payload (i.e. the
/// slate). If an empty vec, the payload will remain unencrypted
///
@@ -2285,6 +2320,7 @@ where
/// # grin_wallet_api::doctest_helper_setup_doc_env!(wallet, wallet_config);
///
/// use grin_core::global::ChainTypes;
/// use grin_wallet_libwallet::slatepack::SlatepackAddressIndex;
///
/// use std::time::Duration;
///
@@ -2310,7 +2346,7 @@ where
/// let slatepack = api_owner.create_slatepack_message(
/// None,
/// &slate,
/// Some(0),
/// Some(SlatepackAddressIndex(0)),
/// vec![],
/// );
/// }
@@ -2320,7 +2356,7 @@ where
&self,
keychain_mask: Option<&SecretKey>,
slate: &Slate,
sender_index: Option<u32>,
sender_index: Option<SlatepackAddressIndex>,
recipients: Vec<SlatepackAddress>,
) -> Result<String, Error> {
owner::create_slatepack_message(
@@ -2340,8 +2376,7 @@ where
/// * `keychain_mask` - Wallet secret mask to XOR against the stored wallet seed before using, if
/// * `slatepack` - A string representing an armored slatepack
/// * `secret_indices` - Indices along this wallet's derivation path with which to attempt
/// decryption. This function will attempt to use secret keys at each index along this path
/// to attempt to decrypt the payload, returning an error if none of the keys match.
/// decryption. This function will attempt to use secret keys at each index along this path to attempt to decrypt the payload, returning an error if none of the keys match. Should be not higher than 2^31 - 1 each.
///
/// # Returns
/// * Ok with a [Slate](../grin_wallet_libwallet/slate/struct.Slate.html) if successful
@@ -2353,6 +2388,7 @@ where
/// # grin_wallet_api::doctest_helper_setup_doc_env!(wallet, wallet_config);
///
/// use grin_core::global::ChainTypes;
/// use grin_wallet_libwallet::slatepack::SlatepackAddressIndex;
///
/// use std::time::Duration;
///
@@ -2363,14 +2399,14 @@ where
/// let res = api_owner.slate_from_slatepack_message(
/// None,
/// slatepack_string,
/// vec![0, 1, 2],
/// vec![SlatepackAddressIndex(0), SlatepackAddressIndex(1), SlatepackAddressIndex(2)],
/// );
/// ```
pub fn slate_from_slatepack_message(
&self,
keychain_mask: Option<&SecretKey>,
slatepack: String,
secret_indices: Vec<u32>,
secret_indices: Vec<SlatepackAddressIndex>,
) -> Result<Slate, Error> {
owner::slate_from_slatepack_message(
self.wallet_inst.clone(),
@@ -2389,8 +2425,7 @@ where
/// * `keychain_mask` - Wallet secret mask to XOR against the stored wallet seed before using
/// * `slatepack` - A string representing an armored slatepack
/// * `secret_indices` - Indices along this wallet's derivation path with which to attempt
/// decryption. If this wallet can't decrypt this slatepack, the payload of the returned
/// Slatepack will remain encrypted.
/// decryption. If this wallet can't decrypt this slatepack, the payload of the returned Slatepack will remain encrypted. Should be not higher than 2^31 - 1 each.
///
/// # Returns
/// * Ok with a [Slatepack](../grin_wallet_libwallet/slatepack/types/struct.Slatepack.html) if successful
@@ -2402,6 +2437,7 @@ where
/// # grin_wallet_api::doctest_helper_setup_doc_env!(wallet, wallet_config);
///
/// use grin_core::global::ChainTypes;
/// use grin_wallet_libwallet::slatepack::SlatepackAddressIndex;
///
/// use std::time::Duration;
///
@@ -2412,7 +2448,7 @@ where
/// let res = api_owner.decode_slatepack_message(
/// None,
/// slatepack_string,
/// vec![0, 1, 2],
/// vec![SlatepackAddressIndex(0), SlatepackAddressIndex(1), SlatepackAddressIndex(2)],
/// );
///
/// ```
@@ -2420,7 +2456,7 @@ where
&self,
keychain_mask: Option<&SecretKey>,
slatepack: String,
secret_indices: Vec<u32>,
secret_indices: Vec<SlatepackAddressIndex>,
) -> Result<Slatepack, Error> {
owner::decode_slatepack_message(
self.wallet_inst.clone(),
@@ -2758,6 +2794,7 @@ fn output_slatepack_file<L, C, K>(
keychain_mask: Option<&SecretKey>,
slate: &Slate,
dest: Option<SlatepackAddress>,
sender_index: Option<SlatepackAddressIndex>,
) -> Result<(), Error>
where
L: WalletLCProvider<'static, C, K> + 'static,
@@ -2769,7 +2806,7 @@ where
Some(a) => vec![a],
None => vec![],
};
let message = api.create_slatepack_message(keychain_mask, slate, Some(0), recipients)?;
let message = api.create_slatepack_message(keychain_mask, slate, sender_index, recipients)?;
let tld = api.get_top_level_directory()?;
// Create a directory to which files will be output.
+18 -11
View File
@@ -33,6 +33,7 @@ use crate::util::secp::pedersen::Commitment;
use crate::util::{from_hex, static_secp_instance, Mutex, ZeroingString};
use crate::{ECDHPubkey, Ed25519SecretKey, Owner, Token};
use grin_wallet_util::OnionV3Address;
use libwallet::slatepack::SlatepackAddressIndex;
use rand::thread_rng;
use std::convert::TryFrom;
use std::path::PathBuf;
@@ -359,6 +360,7 @@ pub trait OwnerRpc {
true,
[
{
"address_index": null,
"amount_credited": "60000000000",
"amount_debited": "0",
"confirmation_ts": "2019-01-15T16:01:26Z",
@@ -380,6 +382,7 @@ pub trait OwnerRpc {
"tx_type": "ConfirmedCoinbase"
},
{
"address_index": null,
"amount_credited": "60000000000",
"amount_debited": "0",
"confirmation_ts": "2019-01-15T16:01:26Z",
@@ -451,6 +454,7 @@ pub trait OwnerRpc {
true,
[
{
"address_index": null,
"amount_credited": "60000000000",
"amount_debited": "0",
"confirmation_ts": "2019-01-15T16:01:26Z",
@@ -472,6 +476,7 @@ pub trait OwnerRpc {
"tx_type": "ConfirmedCoinbase"
},
{
"address_index": null,
"amount_credited": "60000000000",
"amount_debited": "0",
"confirmation_ts": "2019-01-15T16:01:26Z",
@@ -598,7 +603,7 @@ pub trait OwnerRpc {
"id": "0436430c-2b02-624c-2032-570501212b00",
"proof": {
"raddr": "32cdd63928854f8b2628b1dce4626ddcdf35d56cb7cfdf7d64cca5822b78d4d3",
"saddr": "32cdd63928854f8b2628b1dce4626ddcdf35d56cb7cfdf7d64cca5822b78d4d3"
"saddr": "4b2adcc4efc1d547c08ed7c9f7d987ff085f1788d62805e73c8f632e4b065039"
},
"sigs": [
{
@@ -1706,7 +1711,7 @@ pub trait OwnerRpc {
fn get_slatepack_address(
&self,
token: Token,
derivation_index: u32,
derivation_index: SlatepackAddressIndex,
) -> Result<SlatepackAddress, Error>;
/**
@@ -1740,7 +1745,7 @@ pub trait OwnerRpc {
fn get_slatepack_secret_key(
&self,
token: Token,
derivation_index: u32,
derivation_index: SlatepackAddressIndex,
) -> Result<Ed25519SecretKey, Error>;
/**
@@ -1790,7 +1795,7 @@ pub trait OwnerRpc {
&self,
token: Token,
slate: VersionedSlate,
sender_index: Option<u32>,
sender_index: Option<SlatepackAddressIndex>,
recipients: Vec<SlatepackAddress>,
) -> Result<String, Error>;
@@ -1844,7 +1849,7 @@ pub trait OwnerRpc {
&self,
token: Token,
message: String,
secret_indices: Vec<u32>,
secret_indices: Vec<SlatepackAddressIndex>,
) -> Result<VersionedSlate, Error>;
/**
@@ -1885,7 +1890,7 @@ pub trait OwnerRpc {
&self,
token: Token,
message: String,
secret_indices: Vec<u32>,
secret_indices: Vec<SlatepackAddressIndex>,
) -> Result<Slatepack, Error>;
/**
@@ -1917,6 +1922,7 @@ pub trait OwnerRpc {
"recipient_address": "tgrin10qlk22rxjap2ny8qltc2tl996kenxr3hhwuu6hrzs6tdq08yaqgqq6t83r",
"recipient_sig": "02868f2d2b983981f8f98043701687a8531ed2de564ea3df48e9e7e0229ccbe8359efe506896df2efbe3528e977252c50e4a41ca3cc9896e7c5a30bbb1d33604",
"sender_address": "tgrin1xtxavwfgs48ckf3gk8wwgcndmn0nt4tvkl8a7ltyejjcy2mc6nfs9gm2lp",
"sender_address_path": 0,
"sender_sig": "c511764f3f61ed3d1cbca9514df8bc6811fad5662b1cb0e0587b9c9e49db9f33183cce71af6cb24b507fabf525a2bc405c6e84e63a60334edff0b451ae5e6102"
}
}
@@ -1949,6 +1955,7 @@ pub trait OwnerRpc {
"recipient_address": "tgrin10qlk22rxjap2ny8qltc2tl996kenxr3hhwuu6hrzs6tdq08yaqgqq6t83r",
"recipient_sig": "02868f2d2b983981f8f98043701687a8531ed2de564ea3df48e9e7e0229ccbe8359efe506896df2efbe3528e977252c50e4a41ca3cc9896e7c5a30bbb1d33604",
"sender_address": "tgrin1xtxavwfgs48ckf3gk8wwgcndmn0nt4tvkl8a7ltyejjcy2mc6nfs9gm2lp",
"sender_address_path": 0,
"sender_sig": "c511764f3f61ed3d1cbca9514df8bc6811fad5662b1cb0e0587b9c9e49db9f33183cce71af6cb24b507fabf525a2bc405c6e84e63a60334edff0b451ae5e6102"
}
},
@@ -2424,7 +2431,7 @@ where
fn get_slatepack_address(
&self,
token: Token,
derivation_index: u32,
derivation_index: SlatepackAddressIndex,
) -> Result<SlatepackAddress, Error> {
Owner::get_slatepack_address(self, token.keychain_mask.as_ref(), derivation_index)
}
@@ -2432,7 +2439,7 @@ where
fn get_slatepack_secret_key(
&self,
token: Token,
derivation_index: u32,
derivation_index: SlatepackAddressIndex,
) -> Result<Ed25519SecretKey, Error> {
let key =
Owner::get_slatepack_secret_key(self, token.keychain_mask.as_ref(), derivation_index)?;
@@ -2443,7 +2450,7 @@ where
&self,
token: Token,
slate: VersionedSlate,
sender_index: Option<u32>,
sender_index: Option<SlatepackAddressIndex>,
recipients: Vec<SlatepackAddress>,
) -> Result<String, Error> {
let res = Owner::create_slatepack_message(
@@ -2460,7 +2467,7 @@ where
&self,
token: Token,
message: String,
secret_indices: Vec<u32>,
secret_indices: Vec<SlatepackAddressIndex>,
) -> Result<VersionedSlate, Error> {
let slate = Owner::slate_from_slatepack_message(
self,
@@ -2476,7 +2483,7 @@ where
&self,
token: Token,
message: String,
secret_indices: Vec<u32>,
secret_indices: Vec<SlatepackAddressIndex>,
) -> Result<Slatepack, Error> {
Owner::decode_slatepack_message(self, token.keychain_mask.as_ref(), message, secret_indices)
}