Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

feat!: add burned outputs #4364

Merged
merged 5 commits into from
Aug 2, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions applications/tari_app_grpc/proto/types.proto
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,8 @@ message TransactionKernel {
bytes hash = 8;
// Version
uint32 version = 9;
// Optional burned commitment
bytes burn_commitment = 10;
}

// A transaction input.
Expand Down
14 changes: 14 additions & 0 deletions applications/tari_app_grpc/proto/wallet.proto
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,8 @@ service Wallet {
rpc RevalidateAllTransactions (RevalidateRequest) returns (RevalidateResponse);
// This will send a XTR SHA Atomic swap transaction
rpc SendShaAtomicSwapTransaction(SendShaAtomicSwapRequest) returns (SendShaAtomicSwapResponse);
// This will create a burn transaction
rpc CreateBurnTransaction(CreateBurnTransactionRequest) returns (CreateBurnTransactionResponse);
// This will claim a XTR SHA Atomic swap transaction
rpc ClaimShaAtomicSwapTransaction(ClaimShaAtomicSwapRequest) returns (ClaimShaAtomicSwapResponse);
// This will claim a HTLC refund transaction
Expand Down Expand Up @@ -106,6 +108,12 @@ message SendShaAtomicSwapRequest {
PaymentRecipient recipient = 1;
}

message CreateBurnTransactionRequest{
uint64 amount = 1;
uint64 fee_per_gram = 2;
string message = 3;
}

message PaymentRecipient {
string address = 1;
uint64 amount = 2;
Expand All @@ -131,6 +139,12 @@ message SendShaAtomicSwapResponse {
string failure_message = 5;
}

message CreateBurnTransactionResponse{
uint64 transaction_id = 1;
bool is_success = 2;
string failure_message = 3;
}

message TransferResult {
string address = 1;
uint64 transaction_id = 2;
Expand Down
14 changes: 14 additions & 0 deletions applications/tari_app_grpc/src/conversions/transaction_kernel.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,14 @@ impl TryFrom<grpc::TransactionKernel> for TransactionKernel {
.map_err(|_| "excess_sig could not be converted".to_string())?;

let kernel_features = u8::try_from(kernel.features).map_err(|_| "kernel features must be a single byte")?;
let commitment = if kernel.burn_commitment.is_empty() {
None
} else {
Some(
Commitment::from_bytes(&kernel.burn_commitment)
.map_err(|err| format!("Burn commitment could not be converted:{}", err))?,
)
};

Ok(Self::new(
TransactionKernelVersion::try_from(
Expand All @@ -56,13 +64,18 @@ impl TryFrom<grpc::TransactionKernel> for TransactionKernel {
kernel.lock_height,
excess,
excess_sig,
commitment,
))
}
}

impl From<TransactionKernel> for grpc::TransactionKernel {
fn from(kernel: TransactionKernel) -> Self {
let hash = kernel.hash();
let commitment = match kernel.burn_commitment {
Some(c) => c.as_bytes().to_vec(),
None => vec![],
};

grpc::TransactionKernel {
features: u32::from(kernel.features.bits()),
Expand All @@ -75,6 +88,7 @@ impl From<TransactionKernel> for grpc::TransactionKernel {
}),
hash,
version: kernel.version as u32,
burn_commitment: commitment,
}
}
}
35 changes: 35 additions & 0 deletions applications/tari_console_wallet/src/grpc/wallet_grpc_server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@ use tari_app_grpc::{
ClaimShaAtomicSwapResponse,
CoinSplitRequest,
CoinSplitResponse,
CreateBurnTransactionRequest,
CreateBurnTransactionResponse,
CreateConstitutionDefinitionRequest,
CreateConstitutionDefinitionResponse,
CreateFollowOnAssetCheckpointRequest,
Expand Down Expand Up @@ -560,6 +562,39 @@ impl wallet_server::Wallet for WalletGrpcServer {
Ok(Response::new(TransferResponse { results }))
}

async fn create_burn_transaction(
&self,
request: Request<CreateBurnTransactionRequest>,
) -> Result<Response<CreateBurnTransactionResponse>, Status> {
let message = request.into_inner();

let mut transaction_service = self.get_transaction_service();
debug!(target: LOG_TARGET, "Trying to burn {} Tari", message.amount);
let response = match transaction_service
.burn_tari(message.amount.into(), message.fee_per_gram.into(), message.message)
.await
{
Ok(tx_id) => {
debug!(target: LOG_TARGET, "Transaction broadcast: {}", tx_id,);
CreateBurnTransactionResponse {
transaction_id: tx_id.as_u64(),
is_success: true,
failure_message: Default::default(),
}
},
Err(e) => {
warn!(target: LOG_TARGET, "Failed to burn Tarid: {}", e);
CreateBurnTransactionResponse {
transaction_id: Default::default(),
is_success: false,
failure_message: e.to_string(),
}
},
};

Ok(Response::new(response))
}

async fn get_transaction_info(
&self,
request: Request<GetTransactionInfoRequest>,
Expand Down
3 changes: 2 additions & 1 deletion applications/test_faucet/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,8 @@ async fn write_keys(mut rx: mpsc::Receiver<(TransactionOutput, PrivateKey, Micro
}
let (pk, sig) = test_helpers::create_random_signature_from_s_key(key_sum, 0.into(), 0);
let excess = Commitment::from_public_key(&pk);
let kernel = TransactionKernel::new_current_version(KernelFeatures::empty(), MicroTari::from(0), 0, excess, sig);
let kernel =
TransactionKernel::new_current_version(KernelFeatures::empty(), MicroTari::from(0), 0, excess, sig, None);
let kernel = serde_json::to_string(&kernel).unwrap();
let _result = utxo_file.write_all(format!("{}\n", kernel).as_bytes());

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,8 @@ pub enum HorizonSyncError {
JoinError(#[from] task::JoinError),
#[error("A range proof verification has produced an error: {0}")]
RangeProofError(#[from] RangeProofError),
#[error("An invalid transaction has been encountered: {0}")]
TransactionError(#[from] TransactionError),
#[error("Invalid kernel signature: {0}")]
InvalidKernelSignature(TransactionError),
#[error("MMR did not match for {mmr_tree} at height {at_height}. Expected {actual_hex} to equal {expected_hex}")]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -743,14 +743,15 @@ impl<'a, B: BlockchainBackend + 'static> HorizonStateSynchronization<'a, B> {
));

let header = self.db().fetch_chain_header(self.horizon_sync_height).await?;
let (calc_utxo_sum, calc_kernel_sum) = self.calculate_commitment_sums(&header).await?;
let (calc_utxo_sum, calc_kernel_sum, calc_burned_sum) = self.calculate_commitment_sums(&header).await?;

self.final_state_validator
.validate(
&*self.db().inner().db_read_access()?,
header.height(),
&calc_utxo_sum,
&calc_kernel_sum,
&calc_burned_sum,
)
.map_err(HorizonSyncError::FinalStateValidationFailed)?;

Expand Down Expand Up @@ -793,9 +794,10 @@ impl<'a, B: BlockchainBackend + 'static> HorizonStateSynchronization<'a, B> {
async fn calculate_commitment_sums(
&mut self,
header: &ChainHeader,
) -> Result<(Commitment, Commitment), HorizonSyncError> {
) -> Result<(Commitment, Commitment, Commitment), HorizonSyncError> {
let mut utxo_sum = HomomorphicCommitment::default();
let mut kernel_sum = HomomorphicCommitment::default();
let mut burned_sum = HomomorphicCommitment::default();

let mut prev_mmr = 0;
let mut prev_kernel_mmr = 0;
Expand All @@ -810,7 +812,6 @@ impl<'a, B: BlockchainBackend + 'static> HorizonStateSynchronization<'a, B> {

for h in 0..=height {
let curr_header = db.fetch_chain_header(h)?;

trace!(
target: LOG_TARGET,
"Fetching utxos from db: height:{}, header.output_mmr:{}, prev_mmr:{}, end:{}",
Expand Down Expand Up @@ -866,6 +867,9 @@ impl<'a, B: BlockchainBackend + 'static> HorizonStateSynchronization<'a, B> {
trace!(target: LOG_TARGET, "Number of kernels returned: {}", kernels.len());
for k in kernels {
kernel_sum = &k.excess + &kernel_sum;
if k.is_burned() {
burned_sum = k.get_burn_commitment()? + &burned_sum;
}
}
prev_kernel_mmr = curr_header.header().kernel_mmr_size;

Expand All @@ -888,7 +892,7 @@ impl<'a, B: BlockchainBackend + 'static> HorizonStateSynchronization<'a, B> {
db.write(txn)?;
}

Ok((utxo_sum, kernel_sum))
Ok((utxo_sum, kernel_sum, burned_sum))
})
.await?
}
Expand Down
10 changes: 6 additions & 4 deletions base_layer/core/src/blocks/genesis_block.rs
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,8 @@ fn get_igor_genesis_block_raw() -> Block {
"9474ba70976e2fa06f970bb83f7d0a4d4b45e6e29f834847b659d32102f90b51",
)
.unwrap(),
sig,
sig,None

)],
);
body.sort();
Expand Down Expand Up @@ -216,7 +217,7 @@ pub fn get_dibbler_genesis_block() -> ChainBlock {
// println!("output mr: {}", block.header.output_mr.to_hex());

// Hardcode the Merkle roots once they've been computed above
block.header.kernel_mr = from_hex("8bec1140bfac718ab3acd8a5e19c1bb28e0e4a57663c2fc7e8c7155cc355aac3").unwrap();
block.header.kernel_mr = from_hex("51acb4b74cc2e43a11be4f283b653a6fc95666dcf90f66f0c32742c5fb77e640").unwrap();
block.header.witness_mr = from_hex("1df4a4200338686763c784187f7077148986e088586cf4839147a3f56adc4af6").unwrap();
block.header.output_mr = from_hex("f9616ca84e798022f638546e6ce372d1344eee56e5cf47ba7e2bf58b5e28bf45").unwrap();

Expand Down Expand Up @@ -274,6 +275,7 @@ fn get_dibbler_genesis_block_raw() -> Block {
0,
Commitment::from_hex("0cff7e89fa0468aa68f777cf600ae6f9e46fdc6e4e33540077e7303e8929295c").unwrap(),
excess_sig,
None,
);
let mut body = AggregateBody::new(vec![], vec![coinbase], vec![kernel]);
body.sort();
Expand Down Expand Up @@ -314,7 +316,7 @@ fn get_dibbler_genesis_block_raw() -> Block {
#[cfg(test)]
mod test {
use croaring::Bitmap;
use tari_common_types::types::HashDigest;
use tari_common_types::types::{Commitment, HashDigest};
use tari_mmr::{MerkleMountainRange, MutableMmr};

use super::*;
Expand Down Expand Up @@ -385,7 +387,7 @@ mod test {

let lock = db.db_read_access().unwrap();
ChainBalanceValidator::new(ConsensusManager::builder(Network::Dibbler).build(), Default::default())
.validate(&*lock, 0, &utxo_sum, &kernel_sum)
.validate(&*lock, 0, &utxo_sum, &kernel_sum, &Commitment::default())
.unwrap();
}
}
31 changes: 31 additions & 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 @@ -1302,12 +1302,35 @@ impl LMDBDatabase {
let leaf_count = witness_mmr.get_leaf_count()?;

// Output hashes added before inputs so that inputs can spend outputs in this transaction (0-conf and combined)
let mut burned_outputs = Vec::new();
let outputs = outputs
.into_iter()
.enumerate()
.map(|(i, output)| {
output_mmr.push(output.hash())?;
witness_mmr.push(output.witness_hash())?;
// lets check burn
if output.is_burned() {
let index = match output_mmr.find_leaf_index(&output.hash())? {
Some(index) => {
debug!(target: LOG_TARGET, "Output {} burned in current block", output);
burned_outputs.push(output.commitment.clone());
index
},
None => {
return Err(ChainStorageError::UnexpectedResult(
"Output MMR did not contain the expected output".to_string(),
))
},
};
// We need to mark this as spent as well.
if !output_mmr.delete(index) {
return Err(ChainStorageError::InvalidOperation(format!(
"Could not delete index {} from the output MMR",
index
)));
}
};
Ok((output, leaf_count + i + 1))
})
.collect::<Result<Vec<_>, ChainStorageError>>()?;
Expand Down Expand Up @@ -1366,6 +1389,14 @@ impl LMDBDatabase {
"utxo_commitment_index",
)?;
}
for commitment in burned_outputs {
sdbondi marked this conversation as resolved.
Show resolved Hide resolved
lmdb_delete(
txn,
&self.utxo_commitment_index,
commitment.as_bytes(),
"utxo_commitment_index",
)?;
}
// Merge current deletions with the tip bitmap
let deleted_at_current_height = output_mmr.deleted().clone();
// Merge the new indexes with the blockchain deleted bitmap
Expand Down
2 changes: 2 additions & 0 deletions base_layer/core/src/proto/transaction.proto
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ message TransactionKernel {
Signature excess_sig = 7;
// Version
uint32 version = 8;
// Optional burned commitment
Commitment burn_commitment = 9;
}

// A transaction input.
Expand Down
7 changes: 7 additions & 0 deletions base_layer/core/src/proto/transaction.rs
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,10 @@ impl TryFrom<proto::types::TransactionKernel> for TransactionKernel {
.ok_or_else(|| "excess_sig not provided".to_string())?
.try_into()?;
let kernel_features = u8::try_from(kernel.features).map_err(|_| "Kernel features must be a single byte")?;
let commitment = match kernel.burn_commitment {
Some(burn_commitment) => Some(Commitment::from_bytes(&burn_commitment.data).map_err(|e| e.to_string())?),
None => None,
};

Ok(TransactionKernel::new(
TransactionKernelVersion::try_from(
Expand All @@ -111,19 +115,22 @@ impl TryFrom<proto::types::TransactionKernel> for TransactionKernel {
kernel.lock_height,
excess,
excess_sig,
commitment,
))
}
}

impl From<TransactionKernel> for proto::types::TransactionKernel {
fn from(kernel: TransactionKernel) -> Self {
let commitment = kernel.burn_commitment.map(|commitment| commitment.into());
Self {
features: u32::from(kernel.features.bits()),
excess: Some(kernel.excess.into()),
excess_sig: Some(kernel.excess_sig.into()),
fee: kernel.fee.into(),
lock_height: kernel.lock_height,
version: kernel.version as u32,
burn_commitment: commitment,
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion base_layer/core/src/transactions/aggregated_body.rs
Original file line number Diff line number Diff line change
Expand Up @@ -264,7 +264,7 @@ impl AggregateBody {
for kernel in self.kernels() {
if kernel.lock_height > height {
warn!(target: LOG_TARGET, "Kernel lock height was not reached: {}", kernel);
return Err(TransactionError::InvalidKernel);
return Err(TransactionError::InvalidKernel("Invalid lock height".to_string()));
}
}
Ok(())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,8 @@ pub enum TransactionError {
RangeProofError(#[from] RangeProofError),
#[error("An error occurred while performing a commitment signature: {0}")]
SigningError(#[from] CommitmentSignatureError),
#[error("Invalid kernel in body")]
InvalidKernel,
#[error("Invalid kernel in body : {0}")]
InvalidKernel(String),
#[error("Invalid coinbase in body")]
InvalidCoinbase,
#[error("Invalid coinbase maturity in body")]
Expand Down
Loading