Skip to content

Commit

Permalink
feat: wallet selects previous checkpoint for spending (#4236)
Browse files Browse the repository at this point in the history
Description
---

- adds ContractOutput filter to UtxoSelectionCriteria
- adds output features param to send_transaction functions
- fixes input metadata mismatch when spending contract utxos
- changes dan to submit initial checkpoint for first checkpoint (todo: dan needs to keep track of checkpoints)

Motivation and Context
---
Allows wallet to select contract utxos when spending and allows for particular output features to be specified for the non-change output. 

How Has This Been Tested?
---
Basic unit test. 
Manually - VN posts initial and follow up checkpoint that spends previous checkpoint

Commits
---

* feat(wallet): add ContractOutput selection filter

* feat(wallet): spend contract utxos and allow output features to be specified

* chore(lmdb-db): add more debug info to contract index

* fix(dan): use new contract output metadata for checkpoints

* fix(dan): create initial checkpoint
  • Loading branch information
sdbondi committed Jun 29, 2022
1 parent 92ae4ab commit 90a5ec3
Show file tree
Hide file tree
Showing 32 changed files with 492 additions and 314 deletions.
17 changes: 15 additions & 2 deletions applications/tari_console_wallet/src/automation/commands.rs
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ use tari_core::transactions::{
ContractAmendment,
ContractDefinition,
ContractUpdateProposal,
OutputFeatures,
SideChainConsensus,
SideChainFeatures,
TransactionOutput,
Expand Down Expand Up @@ -141,7 +142,13 @@ pub async fn send_tari(
message: String,
) -> Result<TxId, CommandError> {
wallet_transaction_service
.send_transaction(dest_pubkey, amount, fee_per_gram * uT, message)
.send_transaction(
dest_pubkey,
amount,
OutputFeatures::default(),
fee_per_gram * uT,
message,
)
.await
.map_err(CommandError::TransactionServiceError)
}
Expand Down Expand Up @@ -205,7 +212,13 @@ pub async fn send_one_sided(
message: String,
) -> Result<TxId, CommandError> {
wallet_transaction_service
.send_one_sided_transaction(dest_pubkey, amount, fee_per_gram * uT, message)
.send_one_sided_transaction(
dest_pubkey,
amount,
OutputFeatures::default(),
fee_per_gram * uT,
message,
)
.await
.map_err(CommandError::TransactionServiceError)
}
Expand Down
20 changes: 16 additions & 4 deletions applications/tari_console_wallet/src/grpc/wallet_grpc_server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -458,7 +458,13 @@ impl wallet_server::Wallet for WalletGrpcServer {
(
address,
transaction_service
.send_transaction(pk, amount.into(), fee_per_gram.into(), message)
.send_transaction(
pk,
amount.into(),
OutputFeatures::default(),
fee_per_gram.into(),
message,
)
.await,
)
});
Expand All @@ -467,7 +473,13 @@ impl wallet_server::Wallet for WalletGrpcServer {
(
address,
transaction_service
.send_one_sided_transaction(pk, amount.into(), fee_per_gram.into(), message)
.send_one_sided_transaction(
pk,
amount.into(),
OutputFeatures::default(),
fee_per_gram.into(),
message,
)
.await,
)
});
Expand Down Expand Up @@ -857,8 +869,8 @@ impl wallet_server::Wallet for WalletGrpcServer {
.map_err(|e| Status::internal(e.to_string()))?;

let message = format!("Sidechain state checkpoint for {}", contract_id);
let _ = transaction_service
.submit_transaction(tx_id, transaction, 0.into(), message)
transaction_service
.submit_transaction(tx_id, transaction, 10.into(), message)
.await
.map_err(|e| Status::internal(e.to_string()))?;

Expand Down
19 changes: 15 additions & 4 deletions applications/tari_console_wallet/src/ui/state/app_state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ use tari_comms::{
};
use tari_core::transactions::{
tari_amount::{uT, MicroTari},
transaction_components::OutputFeatures,
weight::TransactionWeight,
};
use tari_crypto::ristretto::RistrettoPublicKey;
Expand Down Expand Up @@ -295,13 +296,18 @@ impl AppState {
Err(_) => EmojiId::str_to_pubkey(public_key.as_str()).map_err(|_| UiError::PublicKeyParseError)?,
};

let output_features = OutputFeatures {
unique_id,
parent_public_key,
..Default::default()
};

let fee_per_gram = fee_per_gram * uT;
let tx_service_handle = inner.wallet.transaction_service.clone();
tokio::spawn(send_transaction_task(
public_key,
MicroTari::from(amount),
unique_id,
parent_public_key,
output_features,
message,
fee_per_gram,
tx_service_handle,
Expand All @@ -327,13 +333,18 @@ impl AppState {
Err(_) => EmojiId::str_to_pubkey(public_key.as_str()).map_err(|_| UiError::PublicKeyParseError)?,
};

let output_features = OutputFeatures {
unique_id,
parent_public_key,
..Default::default()
};

let fee_per_gram = fee_per_gram * uT;
let tx_service_handle = inner.wallet.transaction_service.clone();
tokio::spawn(send_one_sided_transaction_task(
public_key,
MicroTari::from(amount),
unique_id,
parent_public_key,
output_features,
message,
fee_per_gram,
tx_service_handle,
Expand Down
13 changes: 5 additions & 8 deletions applications/tari_console_wallet/src/ui/state/tasks.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,8 @@
// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

use tari_common_types::types::PublicKey;
use tari_comms::types::CommsPublicKey;
use tari_core::transactions::tari_amount::MicroTari;
use tari_core::transactions::{tari_amount::MicroTari, transaction_components::OutputFeatures};
use tari_wallet::transaction_service::handle::{TransactionEvent, TransactionSendStatus, TransactionServiceHandle};
use tokio::sync::{broadcast, watch};

Expand All @@ -33,8 +32,7 @@ const LOG_TARGET: &str = "wallet::console_wallet::tasks ";
pub async fn send_transaction_task(
public_key: CommsPublicKey,
amount: MicroTari,
unique_id: Option<Vec<u8>>,
parent_public_key: Option<PublicKey>,
output_features: OutputFeatures,
message: String,
fee_per_gram: MicroTari,
mut transaction_service_handle: TransactionServiceHandle,
Expand All @@ -44,7 +42,7 @@ pub async fn send_transaction_task(
let mut event_stream = transaction_service_handle.get_event_stream();
let mut send_status = TransactionSendStatus::default();
match transaction_service_handle
.send_transaction_or_token(public_key, amount, unique_id, parent_public_key, fee_per_gram, message)
.send_transaction(public_key, amount, output_features, fee_per_gram, message)
.await
{
Err(e) => {
Expand Down Expand Up @@ -102,8 +100,7 @@ pub async fn send_transaction_task(
pub async fn send_one_sided_transaction_task(
public_key: CommsPublicKey,
amount: MicroTari,
unique_id: Option<Vec<u8>>,
parent_public_key: Option<PublicKey>,
output_features: OutputFeatures,
message: String,
fee_per_gram: MicroTari,
mut transaction_service_handle: TransactionServiceHandle,
Expand All @@ -112,7 +109,7 @@ pub async fn send_one_sided_transaction_task(
let _result = result_tx.send(UiTransactionSendStatus::Initiated);
let mut event_stream = transaction_service_handle.get_event_stream();
match transaction_service_handle
.send_one_sided_transaction_or_token(public_key, amount, unique_id, parent_public_key, fee_per_gram, message)
.send_one_sided_transaction(public_key, amount, output_features, fee_per_gram, message)
.await
{
Err(e) => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ use tari_app_grpc::{
tari_rpc as grpc,
tari_rpc::{
CreateFollowOnAssetCheckpointRequest,
CreateInitialAssetCheckpointRequest,
SubmitContractAcceptanceRequest,
SubmitContractUpdateProposalAcceptanceRequest,
},
Expand Down Expand Up @@ -66,18 +67,32 @@ impl WalletClient for GrpcWalletClient {
&mut self,
contract_id: &FixedHash,
state_root: &StateRoot,
is_initial: bool,
) -> Result<(), DigitalAssetError> {
let inner = self.connection().await?;

let request = CreateFollowOnAssetCheckpointRequest {
contract_id: contract_id.to_vec(),
merkle_root: state_root.as_bytes().to_vec(),
};

let _res = inner
.create_follow_on_asset_checkpoint(request)
.await
.map_err(|e| DigitalAssetError::FatalError(format!("Could not create checkpoint:{}", e)))?;
if is_initial {
let request = CreateInitialAssetCheckpointRequest {
contract_id: contract_id.to_vec(),
merkle_root: state_root.as_bytes().to_vec(),
committee: vec![],
};

let _res = inner
.create_initial_asset_checkpoint(request)
.await
.map_err(|e| DigitalAssetError::FatalError(format!("Could not create checkpoint:{}", e)))?;
} else {
let request = CreateFollowOnAssetCheckpointRequest {
contract_id: contract_id.to_vec(),
merkle_root: state_root.as_bytes().to_vec(),
};

let _res = inner
.create_follow_on_asset_checkpoint(request)
.await
.map_err(|e| DigitalAssetError::FatalError(format!("Could not create checkpoint:{}", e)))?;
}

Ok(())
}
Expand Down
32 changes: 31 additions & 1 deletion base_layer/core/src/chain_storage/lmdb_db/contract_index.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
use std::{
collections::{hash_map::DefaultHasher, HashSet},
convert::{TryFrom, TryInto},
fmt::Debug,
fmt::{Debug, Display, Formatter},
hash::{BuildHasherDefault, Hash},
ops::Deref,
};
Expand Down Expand Up @@ -215,6 +215,7 @@ impl<'a> ContractIndex<'a, WriteTransaction<'a>> {
output_hash: FixedHash,
) -> Result<(), ChainStorageError> {
let contract_key = ContractIndexKey::new(contract_id, output_type);
debug!(target: LOG_TARGET, "Adding contract key {} to index", contract_key,);
let block_key = BlockContractIndexKey::new(block_hash, output_type, contract_id);
match output_type {
OutputType::ContractDefinition => {
Expand Down Expand Up @@ -267,6 +268,7 @@ impl<'a> ContractIndex<'a, WriteTransaction<'a>> {
) -> Result<(), ChainStorageError> {
let contract_key = ContractIndexKey::new(contract_id, output_type);

debug!(target: LOG_TARGET, "Removing contract key {} from index", contract_key,);
match output_type {
OutputType::ContractDefinition => {
if self.has_dependent_outputs(&contract_key)? {
Expand Down Expand Up @@ -488,6 +490,22 @@ impl ContractIndexKey {
key.key[FixedHash::byte_size() + 1] = output_type.as_byte();
key
}

pub fn key_type(&self) -> KeyType {
match self.key[0] {
0 => KeyType::PerContract,
1 => KeyType::PerBlock,
_ => unreachable!(),
}
}

pub fn contract_id(&self) -> FixedHash {
FixedHash::try_from(&self.key[1..=32]).expect("32 bytes cannot fail")
}

pub fn output_type(&self) -> OutputType {
OutputType::from_byte(self.key[33]).expect("Contract key set with invalid OutputType")
}
}

impl Deref for ContractIndexKey {
Expand All @@ -504,6 +522,18 @@ impl AsLmdbBytes for ContractIndexKey {
}
}

impl Display for ContractIndexKey {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(
f,
"{:?}, {}, {}",
self.key_type(),
self.contract_id(),
self.output_type()
)
}
}

#[cfg(test)]
mod tests {
use digest::Digest;
Expand Down
1 change: 1 addition & 0 deletions base_layer/core/src/chain_storage/lmdb_db/lmdb_db.rs
Original file line number Diff line number Diff line change
Expand Up @@ -762,6 +762,7 @@ impl LMDBDatabase {
}

if input.features()?.is_sidechain_contract() {
// TODO: 0-conf not supported for contract outputs
self.get_contract_index(txn).spend(input)?;
}

Expand Down
1 change: 1 addition & 0 deletions base_layer/core/src/proto/transaction.rs
Original file line number Diff line number Diff line change
Expand Up @@ -216,6 +216,7 @@ impl TryFrom<TransactionInput> for proto::types::TransactionInput {
.map_err(|_| "Non-compact Transaction input should contain sender_offset_public_key".to_string())?
.as_bytes()
.to_vec(),
// Output hash is only used in compact form
output_hash: Vec::new(),
covenant: input
.covenant()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,13 @@ impl CommitteeSignatures {
Self { signatures }
}

pub fn empty() -> Self {
Self {
// Panic: vec is size 0 < 512
signatures: vec![].try_into().unwrap(),
}
}

pub fn signatures(&self) -> Vec<Signature> {
self.signatures.to_vec()
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,10 @@ impl UnblindedOutputBuilder {
self.script_private_key = Some(script_private_key);
self
}

pub fn covenant(&self) -> &Covenant {
&self.covenant
}
}

#[cfg(test)]
Expand Down
Loading

0 comments on commit 90a5ec3

Please sign in to comment.