Skip to content
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
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
2 changes: 1 addition & 1 deletion bindings/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@tari-project/typescript-bindings",
"version": "1.17.1",
"version": "1.17.2",
"description": "TypeScript types synchronized to the Tari Ootle Rust codebase",
"homepage": "https://github.com/tari-project/tari-ootle#readme",
"bugs": {
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