Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
4 changes: 2 additions & 2 deletions applications/tari_validator_node/src/bootstrap.rs
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ use crate::consensus::metrics::PrometheusConsensusMetrics;
use crate::{
consensus::{self, ConsensusHandle, TarBlockTransactionExecutor, ValidationContext},
file_l1_submitter::FileLayerOneSubmitter,
genesis_state::create_genesis_state,
p2p::{
create_tari_validator_node_rpc_service,
services::{
Expand All @@ -90,7 +91,6 @@ use crate::{
},
NopLogger,
},
state_bootstrap::bootstrap_state,
transaction_validators::{
EpochRangeValidator,
FeeTransactionValidator,
Expand Down Expand Up @@ -218,7 +218,7 @@ pub async fn spawn_services(

let state_store = ValidatorNodeStateStore::open(&config.validator_node.state_db_path, DatabaseOptions::default())?;

state_store.with_write_tx(|tx| bootstrap_state(tx, config.network, consensus_constants.num_preshards))?;
state_store.with_write_tx(|tx| create_genesis_state(tx, config.network, consensus_constants.num_preshards))?;

info!(target: LOG_TARGET, "Epoch manager initializing");
let epoch_manager_config = EpochManagerConfig {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,11 @@ pub fn has_bootstrapped<TTx: StateStoreReadTransaction>(tx: &TTx) -> Result<bool
)
}

pub fn bootstrap_state<TTx>(tx: &mut TTx, network: Network, num_preshards: NumPreshards) -> Result<(), StorageError>
pub fn create_genesis_state<TTx>(
tx: &mut TTx,
network: Network,
num_preshards: NumPreshards,
) -> Result<(), StorageError>
where
TTx: StateStoreWriteTransaction + Deref,
TTx::Target: StateStoreReadTransaction,
Expand Down Expand Up @@ -94,7 +98,11 @@ where
None,
None,
6,
true,
// Disable total supply tracking for XTR. This is because it is not feasible to include "the fee exhaust" in
// the tracking (as that would require mutating the resource on every transaction). Tracking supply can
// be done by summing up the total burn claims (ClaimedOutputTombstone) and subtracting the total exhaust in
// fee receipts.
false,
);

if is_testnet {
Expand Down
2 changes: 1 addition & 1 deletion applications/tari_validator_node/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,14 +25,14 @@ mod config;
pub mod consensus;
mod event_subscription;
mod file_l1_submitter;
mod genesis_state;
#[cfg(feature = "web_ui")]
mod http_ui;
mod json_rpc;
#[cfg(feature = "metrics")]
mod metrics;
mod node;
mod p2p;
mod state_bootstrap;
pub mod transaction_validators;
mod validator;

Expand Down
4 changes: 3 additions & 1 deletion bindings/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,11 @@ export * from "./types/ComponentBody";
export * from "./types/ComponentCall";
export * from "./types/ComponentHeader";
export * from "./types/ComponentKey";
export * from "./types/CompressedElgamalVerifiableBalance";
export * from "./types/ConfidentialOutputStatement";
export * from "./types/ConfidentialTransferInputSelection";
export * from "./types/ConfidentialWithdrawProof";
export * from "./types/Decision";
export * from "./types/ElgamalVerifiableBalanceBytes";
export * from "./types/EncodedMerkleProof";
export * from "./types/EncryptedData";
export * from "./types/EntityId";
Expand Down Expand Up @@ -110,6 +110,7 @@ export * from "./types/StealthInput";
export * from "./types/StealthOutputsStatement";
export * from "./types/StealthTransferStatement";
export * from "./types/StealthUnspentOutput";
export * from "./types/StealthValueProof";
export * from "./types/SubstateAddress";
export * from "./types/SubstateCreated";
export * from "./types/SubstateDestroyed";
Expand Down Expand Up @@ -157,6 +158,7 @@ export * from "./types/ValidatorFeePoolAddress";
export * from "./types/ValidatorFeePool";
export * from "./types/ValidatorFeeWithdrawal";
export * from "./types/ValidatorSignatureBytes";
export * from "./types/ValueKnowledgeProof";
export * from "./types/VaultFreezeFlags";
export * from "./types/VaultId";
export * from "./types/Vault";
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
import type { RistrettoPublicKeyBytes } from "./RistrettoPublicKeyBytes";

export type CompressedElgamalVerifiableBalance = {
export type ElgamalVerifiableBalanceBytes = {
encrypted: RistrettoPublicKeyBytes;
public_nonce: RistrettoPublicKeyBytes;
};
4 changes: 2 additions & 2 deletions bindings/src/types/PrivateOutput.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
import type { CompressedElgamalVerifiableBalance } from "./CompressedElgamalVerifiableBalance";
import type { ElgamalVerifiableBalanceBytes } from "./ElgamalVerifiableBalanceBytes";
import type { EncryptedData } from "./EncryptedData";
import type { RistrettoPublicKeyBytes } from "./RistrettoPublicKeyBytes";

export type PrivateOutput = {
public_nonce: RistrettoPublicKeyBytes;
encrypted_data: EncryptedData;
minimum_value_promise: number | bigint;
viewable_balance: CompressedElgamalVerifiableBalance | null;
viewable_balance: ElgamalVerifiableBalanceBytes | null;
};
15 changes: 15 additions & 0 deletions bindings/src/types/StealthValueProof.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
import type { Amount } from "./Amount";
import type { ValueKnowledgeProof } from "./ValueKnowledgeProof";

/**
* Proof of knowledge of the opening to a commitment and that the commitment commits to a specific value.
* Currently used when burning UTXOs to allow the total supply to be adjusted.
*/
export type StealthValueProof = {
/**
* The claimed value to prove
*/
value: Amount;
knowledge_proof: ValueKnowledgeProof;
};
23 changes: 23 additions & 0 deletions bindings/src/types/ValueKnowledgeProof.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
import type { RistrettoPublicKeyBytes } from "./RistrettoPublicKeyBytes";
import type { SchnorrSignatureBytes } from "./SchnorrSignatureBytes";

export type ValueKnowledgeProof =
| {
Commitment: {
/**
* Signed by C - v.H, where C is the commitment being proven and v is the claimed value
* Proving knowledge of the opening to C, and that the commitment C = m.G + v.H
*/
mask_knowledge_proof: SchnorrSignatureBytes;
};
}
| {
ElgamalEncrypted: {
/**
* The R.p term of the ElGamal encryption. This allows validators to check the provided value is correct using
* the viewable balance. This assumes that the verifiable proof was originally validated correctly.
*/
reveal_key: RistrettoPublicKeyBytes;
};
};
3 changes: 2 additions & 1 deletion bindings/src/types/ViewableBalanceProof.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,8 @@ import type { Scalar32Bytes } from "./Scalar32Bytes";
export type ViewableBalanceProof = {
/**
* The encrypted value that takes the form: E = v.G + r.P
* where v is the value, G is the generator, r is the secret_nonce and P is the view key
* where v is the value, G is the generator, r is the secret_nonce and P is the view key.
* The value is decrypted by brute forcing E - R.p = v.G
*/
elgamal_encrypted: RistrettoPublicKeyBytes;
/**
Expand Down
4 changes: 4 additions & 0 deletions crates/common_types/src/engine_signature.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,10 @@ impl Verifier for RistrettoSchnorrBlake2bVerifier {
return false;
};

if sig.public_nonce().is_zero() {
return false;
}

let Ok(sig) = RistrettoSchnorr::convert_from_byte_type(sig) else {
return false;
};
Expand Down
129 changes: 95 additions & 34 deletions crates/engine/src/runtime/impl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ use tari_engine_types::{
published_template::{PublishedTemplate, PublishedTemplateAddress},
resource::Resource,
resource_container::{ResourceContainer, ResourceError},
stealth,
substate::{SubstateId, SubstateValue},
vault::Vault,
Utxo,
Expand All @@ -56,6 +57,7 @@ use tari_template_lib::{
BucketAction,
BucketRef,
BuiltinTemplateAction,
BurnStealthUtxoArg,
CallAction,
CallFunctionArg,
CallMethodArg,
Expand Down Expand Up @@ -959,14 +961,6 @@ impl<TTemplateProvider: TemplateProvider<Template = LoadedTemplate>> RuntimeInte
let resource_lock = state_mut.read_lock_substate(&SubstateId::Resource(resource_address))?;

let resource = state_mut.get_resource(&resource_lock)?;
if resource.resource_type().is_stealth() {
return Err(ResourceError::OperationNotAllowed(format!(
"Cannot recall stealth resources: {}",
resource_address
))
.into());
}

state_mut.authorization().check_resource_access_rules(
ResourceAuthAction::Recall,
resource.as_ownership(),
Expand Down Expand Up @@ -1222,6 +1216,13 @@ impl<TTemplateProvider: TemplateProvider<Template = LoadedTemplate>> RuntimeInte
})?;
let arg: SetFreezeStealthUtxosArg = args.assert_one_arg()?;

if arg.utxos.is_empty() {
return Err(RuntimeError::InvalidArgument {
argument: "SetFreezeStealthUtxosArg",
reason: "Utxos list cannot be empty".to_string(),
});
}

self.tracker.write_with(|state_mut| {
let resource_lock = state_mut.read_lock_substate(&SubstateId::Resource(resource_address))?;

Expand All @@ -1234,13 +1235,6 @@ impl<TTemplateProvider: TemplateProvider<Template = LoadedTemplate>> RuntimeInte
});
}

if arg.utxos.is_empty() {
return Err(RuntimeError::InvalidArgument {
argument: "SetFreezeStealthUtxosArg",
reason: "Utxos list cannot be empty".to_string(),
});
}

state_mut.authorization().check_resource_access_rules(
ResourceAuthAction::Freeze,
resource.as_ownership(),
Expand All @@ -1251,7 +1245,7 @@ impl<TTemplateProvider: TemplateProvider<Template = LoadedTemplate>> RuntimeInte
let id = SubstateId::Utxo(UtxoAddress::new(resource_address, utxo));
let locked = state_mut.write_lock_substate(&id)?;

let utxo = state_mut
let utxo_mut = state_mut
.get_locked_substate_mut(&locked)?
.as_utxo_mut()
.ok_or_else(|| RuntimeError::LockSubstateMismatch {
Expand All @@ -1262,15 +1256,98 @@ impl<TTemplateProvider: TemplateProvider<Template = LoadedTemplate>> RuntimeInte

// Freeze is idempotent.
if arg.freeze {
utxo.freeze();
utxo_mut.freeze();
} else {
utxo.unfreeze();
utxo_mut.unfreeze();
}
state_mut.unlock_substate(locked)?;
}

state_mut.unlock_substate(resource_lock)?;

Ok(InvokeResult::unit())
})
},
ResourceAction::StealthUtxoBurn => {
let resource_address =
resource_ref
.as_resource_address()
.ok_or_else(|| RuntimeError::InvalidArgument {
argument: "resource_ref",
reason: "BurnStealthUtxo resource action requires a resource address".to_string(),
})?;
let arg: BurnStealthUtxoArg = args.assert_one_arg()?;

self.tracker.write_with(|state_mut| {
let resource_lock = state_mut.read_lock_substate(&SubstateId::Resource(resource_address))?;

let resource = state_mut.get_resource(&resource_lock)?;

if !resource.resource_type().is_stealth() {
return Err(RuntimeError::InvalidArgument {
argument: "resource_ref",
reason: "FreezeStealthUtxo can only be called on stealth resources".to_string(),
});
}

let is_total_supply_tracking_enabled = resource.is_supply_tracking_enabled();
if is_total_supply_tracking_enabled && arg.value_proof.is_none() {
return Err(RuntimeError::InvalidArgument {
argument: "BurnStealthUtxoArg",
reason: "Burning from a total supply tracking resource requires a value proof".to_string(),
});
}

state_mut.authorization().check_resource_access_rules(
ResourceAuthAction::Burn,
resource.as_ownership(),
resource.access_rules(),
)?;

state_mut.unlock_substate(resource_lock)?;

let id = SubstateId::Utxo(UtxoAddress::new(resource_address, arg.utxo_id));
let utxo_lock = state_mut.write_lock_substate(&id)?;

let utxo_mut = state_mut
.get_locked_substate_mut(&utxo_lock)?
.as_utxo_mut()
.ok_or_else(|| RuntimeError::LockSubstateMismatch {
lock_id: utxo_lock.lock_id(),
expected_type: "Utxo",
id,
})?;

if utxo_mut.is_burnt() {
return Err(RuntimeError::ResourceError(ResourceError::UtxoBurnFailed {
id: arg.utxo_id,
details: "already burnt".to_string(),
}));
}

utxo_mut.burn();

if is_total_supply_tracking_enabled {
let value_proof = arg.value_proof.as_ref().expect(
"BUG: is_total_supply_tracking_enabled is true and value proof is some has been checked",
);
let commitment = arg.utxo_id.into_commitment_bytes();
let elgamal_proof = utxo_mut
.output
.as_ref()
.and_then(|o| o.output.viewable_balance.as_ref());
let value = stealth::validate_value_proof(&commitment, elgamal_proof, value_proof)?;
if value.is_positive() {
let resource_lock =
state_mut.write_lock_substate(&SubstateId::Resource(resource_address))?;
let resource_mut = state_mut.get_resource_mut(&resource_lock)?;
resource_mut.decrease_total_supply(value);
state_mut.unlock_substate(resource_lock)?;
}
}

state_mut.unlock_substate(utxo_lock)?;

Ok(InvokeResult::unit())
})
},
Expand Down Expand Up @@ -2392,22 +2469,6 @@ impl<TTemplateProvider: TemplateProvider<Template = LoadedTemplate>> RuntimeInte

state_mut.new_substate(address, utxo)?;

// 4. Update the total supply
{
let resource_lock = state_mut.write_lock_substate(&SubstateId::Resource(XTR))?;
let resource_mut = state_mut.get_resource_mut(&resource_lock)?;
if !resource_mut.increase_total_supply(claim.value.into()) {
return Err(RuntimeError::ResourceSupplyWouldOverflow {
resource_address: XTR,
current_supply: resource_mut
.total_supply()
.expect("Resource supply tracking is enabled"),
amount: claim.value.into(),
});
}
state_mut.unlock_substate(resource_lock)?;
}

Ok::<_, RuntimeError>(())
})?;

Expand Down
2 changes: 1 addition & 1 deletion crates/engine/src/runtime/working_state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -708,7 +708,7 @@ impl WorkingState {
});
}

if !vault_mut.resource_type().is_fungible() {
if !vault_mut.resource_type().is_fungible() && !vault_mut.resource_type().is_stealth() {
return Err(RuntimeError::InvalidArgument {
argument: "resource",
reason: format!(
Expand Down
Loading
Loading