|
|
@@ -19,12 +19,11 @@
|
|
|
#[cfg(not(feature = "no-entrypoint"))]
|
|
|
use darkfi_sdk::{
|
|
|
crypto::{
|
|
|
- pallas, pasta_prelude::*, pedersen_commitment_base, pedersen_commitment_u64, Coin,
|
|
|
- ContractId, MerkleNode, MerkleTree, PublicKey, DARK_TOKEN_ID,
|
|
|
+ pallas, pasta_prelude::*, pedersen_commitment_base, Coin, ContractId, MerkleNode,
|
|
|
+ MerkleTree, PublicKey, DARK_TOKEN_ID,
|
|
|
},
|
|
|
db::{
|
|
|
- db_contains_key, db_get, db_init, db_lookup, db_set, set_return_data,
|
|
|
- SMART_CONTRACT_ZKAS_DB_NAME,
|
|
|
+ db_contains_key, db_init, db_lookup, db_set, set_return_data, SMART_CONTRACT_ZKAS_DB_NAME,
|
|
|
},
|
|
|
error::ContractResult,
|
|
|
merkle::merkle_add,
|
|
|
@@ -66,10 +65,22 @@ impl TryFrom<u8> for MoneyFunction {
|
|
|
/// Structures and object definitions
|
|
|
pub mod model;
|
|
|
|
|
|
+// Contract functionalities
|
|
|
+mod mint;
|
|
|
+use mint::{money_mint_get_metadata, money_mint_process_instruction, money_mint_process_update};
|
|
|
+mod swap;
|
|
|
+use swap::{
|
|
|
+ money_otcswap_get_metadata, money_otcswap_process_instruction, money_otcswap_process_update,
|
|
|
+};
|
|
|
+mod transfer;
|
|
|
+use transfer::{
|
|
|
+ money_transfer_get_metadata, money_transfer_process_instruction, money_transfer_process_update,
|
|
|
+};
|
|
|
+
|
|
|
#[cfg(not(feature = "no-entrypoint"))]
|
|
|
use model::{
|
|
|
- MoneyStakeParams, MoneyStakeUpdate, MoneyTransferParams, MoneyTransferUpdate,
|
|
|
- MoneyUnstakeParams,
|
|
|
+ MoneyMintParams, MoneyMintUpdate, MoneyStakeParams, MoneyStakeUpdate, MoneyTransferParams,
|
|
|
+ MoneyTransferUpdate, MoneyUnstakeParams,
|
|
|
};
|
|
|
|
|
|
#[cfg(feature = "client")]
|
|
|
@@ -87,7 +98,6 @@ darkfi_sdk::define_contract!(
|
|
|
// These are the different sled trees that will be created
|
|
|
pub const MONEY_CONTRACT_COIN_ROOTS_TREE: &str = "coin_roots";
|
|
|
pub const MONEY_CONTRACT_NULLIFIERS_TREE: &str = "nullifiers";
|
|
|
-pub const MONEY_CONTRACT_TOKEN_ROOTS_TREE: &str = "token_roots";
|
|
|
pub const MONEY_CONTRACT_TOKEN_FREEZE_TREE: &str = "token_freezes";
|
|
|
pub const MONEY_CONTRACT_INFO_TREE: &str = "info";
|
|
|
// lead coin, nullifier sled trees.
|
|
|
@@ -137,8 +147,8 @@ fn init_contract(cid: ContractId, ix: &[u8]) -> ContractResult {
|
|
|
let lead_mint_v1_bincode = include_bytes!("../proof/lead_mint_v1.zk.bin");
|
|
|
let lead_burn_v1_bincode = include_bytes!("../proof/lead_burn_v1.zk.bin");
|
|
|
|
|
|
- /* TODO: Do I really want to make zkas a dependency? Yeah, in the future.
|
|
|
- For now we take anything.
|
|
|
+ /* For now we take anything, but the zkas db needs protection against
|
|
|
+ arbitrary data.
|
|
|
let zkbin = ZkBinary::decode(mint_bincode)?;
|
|
|
let mint_namespace = zkbin.namespace.clone();
|
|
|
assert_eq!(&mint_namespace, ZKAS_MINT_NS);
|
|
|
@@ -166,11 +176,6 @@ fn init_contract(cid: ContractId, ix: &[u8]) -> ContractResult {
|
|
|
db_init(cid, MONEY_CONTRACT_NULLIFIERS_TREE)?;
|
|
|
}
|
|
|
|
|
|
- // Set up a database tree to hold Merkle roots of all tokens
|
|
|
- if db_lookup(cid, MONEY_CONTRACT_TOKEN_ROOTS_TREE).is_err() {
|
|
|
- db_init(cid, MONEY_CONTRACT_TOKEN_ROOTS_TREE)?;
|
|
|
- }
|
|
|
-
|
|
|
// Set up a database tree to hold a set of frozen token mints
|
|
|
if db_lookup(cid, MONEY_CONTRACT_TOKEN_FREEZE_TREE).is_err() {
|
|
|
db_init(cid, MONEY_CONTRACT_TOKEN_FREEZE_TREE)?;
|
|
|
@@ -214,71 +219,26 @@ fn init_contract(cid: ContractId, ix: &[u8]) -> ContractResult {
|
|
|
/// This function is used by the VM's host to fetch the necessary metadata for
|
|
|
/// verifying signatures and zk proofs.
|
|
|
#[cfg(not(feature = "no-entrypoint"))]
|
|
|
-fn get_metadata(_cid: ContractId, ix: &[u8]) -> ContractResult {
|
|
|
- let (call_idx, call): (u32, Vec<ContractCall>) = deserialize(ix)?;
|
|
|
- assert!(call_idx < call.len() as u32);
|
|
|
+fn get_metadata(cid: ContractId, ix: &[u8]) -> ContractResult {
|
|
|
+ let (call_idx, calls): (u32, Vec<ContractCall>) = deserialize(ix)?;
|
|
|
+ assert!(call_idx < calls.len() as u32);
|
|
|
|
|
|
- let self_ = &call[call_idx as usize];
|
|
|
+ let self_ = &calls[call_idx as usize];
|
|
|
|
|
|
match MoneyFunction::try_from(self_.data[0])? {
|
|
|
- MoneyFunction::Transfer | MoneyFunction::OtcSwap => {
|
|
|
- let params: MoneyTransferParams = deserialize(&self_.data[1..])?;
|
|
|
-
|
|
|
- let mut zk_public_values: Vec<(String, Vec<pallas::Base>)> = vec![];
|
|
|
- let mut signature_pubkeys: Vec<PublicKey> = vec![];
|
|
|
-
|
|
|
- for input in ¶ms.clear_inputs {
|
|
|
- signature_pubkeys.push(input.signature_public);
|
|
|
- }
|
|
|
-
|
|
|
- for input in ¶ms.inputs {
|
|
|
- let value_coords = input.value_commit.to_affine().coordinates().unwrap();
|
|
|
- let token_coords = input.token_commit.to_affine().coordinates().unwrap();
|
|
|
- let (sig_x, sig_y) = input.signature_public.xy();
|
|
|
-
|
|
|
- zk_public_values.push((
|
|
|
- MONEY_CONTRACT_ZKAS_BURN_NS_V1.to_string(),
|
|
|
- vec![
|
|
|
- input.nullifier.inner(),
|
|
|
- *value_coords.x(),
|
|
|
- *value_coords.y(),
|
|
|
- *token_coords.x(),
|
|
|
- *token_coords.y(),
|
|
|
- input.merkle_root.inner(),
|
|
|
- input.user_data_enc,
|
|
|
- sig_x,
|
|
|
- sig_y,
|
|
|
- ],
|
|
|
- ));
|
|
|
-
|
|
|
- signature_pubkeys.push(input.signature_public);
|
|
|
- }
|
|
|
-
|
|
|
- for output in ¶ms.outputs {
|
|
|
- let value_coords = output.value_commit.to_affine().coordinates().unwrap();
|
|
|
- let token_coords = output.token_commit.to_affine().coordinates().unwrap();
|
|
|
-
|
|
|
- zk_public_values.push((
|
|
|
- MONEY_CONTRACT_ZKAS_MINT_NS_V1.to_string(),
|
|
|
- vec![
|
|
|
- output.coin,
|
|
|
- *value_coords.x(),
|
|
|
- *value_coords.y(),
|
|
|
- *token_coords.x(),
|
|
|
- *token_coords.y(),
|
|
|
- ],
|
|
|
- ));
|
|
|
- }
|
|
|
-
|
|
|
- let mut metadata = vec![];
|
|
|
- zk_public_values.encode(&mut metadata)?;
|
|
|
- signature_pubkeys.encode(&mut metadata)?;
|
|
|
-
|
|
|
+ MoneyFunction::Transfer => {
|
|
|
+ let metadata = money_transfer_get_metadata(cid, call_idx, calls)?;
|
|
|
// Using this, we pass the above data to the host.
|
|
|
set_return_data(&metadata)?;
|
|
|
Ok(())
|
|
|
}
|
|
|
|
|
|
+ MoneyFunction::OtcSwap => {
|
|
|
+ let metadata = money_otcswap_get_metadata(cid, call_idx, calls)?;
|
|
|
+ set_return_data(&metadata)?;
|
|
|
+ Ok(())
|
|
|
+ }
|
|
|
+
|
|
|
MoneyFunction::Stake => {
|
|
|
let params: MoneyStakeParams = deserialize(&self_.data[1..])?;
|
|
|
|
|
|
@@ -376,8 +336,10 @@ fn get_metadata(_cid: ContractId, ix: &[u8]) -> ContractResult {
|
|
|
}
|
|
|
|
|
|
MoneyFunction::Mint => {
|
|
|
- msg!("[Mint] Entered match arm");
|
|
|
- unimplemented!();
|
|
|
+ let metadata = money_mint_get_metadata(cid, call_idx, calls)?;
|
|
|
+ // Using this, we pass the above data to the host.
|
|
|
+ set_return_data(&metadata)?;
|
|
|
+ Ok(())
|
|
|
}
|
|
|
|
|
|
MoneyFunction::Freeze => {
|
|
|
@@ -391,222 +353,29 @@ fn get_metadata(_cid: ContractId, ix: &[u8]) -> ContractResult {
|
|
|
/// update if everything is successful.
|
|
|
#[cfg(not(feature = "no-entrypoint"))]
|
|
|
fn process_instruction(cid: ContractId, ix: &[u8]) -> ContractResult {
|
|
|
- let (call_idx, call): (u32, Vec<ContractCall>) = deserialize(ix)?;
|
|
|
- assert!(call_idx < call.len() as u32);
|
|
|
+ let (call_idx, calls): (u32, Vec<ContractCall>) = deserialize(ix)?;
|
|
|
+
|
|
|
+ if call_idx >= calls.len() as u32 {
|
|
|
+ msg!("Error: call_idx >= calls.len()");
|
|
|
+ return Err(ContractError::Internal)
|
|
|
+ }
|
|
|
|
|
|
- let self_ = &call[call_idx as usize];
|
|
|
+ let self_ = &calls[call_idx as usize];
|
|
|
|
|
|
match MoneyFunction::try_from(self_.data[0])? {
|
|
|
MoneyFunction::Transfer => {
|
|
|
msg!("[Transfer] Entered match arm");
|
|
|
- let params: MoneyTransferParams = deserialize(&self_.data[1..])?;
|
|
|
-
|
|
|
- assert!(params.clear_inputs.len() + params.inputs.len() > 0);
|
|
|
- assert!(!params.outputs.is_empty());
|
|
|
-
|
|
|
- let info_db = db_lookup(cid, MONEY_CONTRACT_INFO_TREE)?;
|
|
|
- let nullifiers_db = db_lookup(cid, MONEY_CONTRACT_NULLIFIERS_TREE)?;
|
|
|
- let coin_roots_db = db_lookup(cid, MONEY_CONTRACT_COIN_ROOTS_TREE)?;
|
|
|
-
|
|
|
- let Some(faucet_pubkeys) = db_get(info_db, &serialize(&MONEY_CONTRACT_FAUCET_PUBKEYS))? else {
|
|
|
- msg!("[Transfer] Error: Missing faucet pubkeys from info db");
|
|
|
- return Err(ContractError::Internal);
|
|
|
- };
|
|
|
- let faucet_pubkeys: Vec<PublicKey> = deserialize(&faucet_pubkeys)?;
|
|
|
-
|
|
|
- // Accumulator for the value commitments
|
|
|
- let mut valcom_total = pallas::Point::identity();
|
|
|
-
|
|
|
- // State transition for payments
|
|
|
- msg!("[Transfer] Iterating over clear inputs");
|
|
|
- for (i, input) in params.clear_inputs.iter().enumerate() {
|
|
|
- let pk = input.signature_public;
|
|
|
-
|
|
|
- if !faucet_pubkeys.contains(&pk) {
|
|
|
- msg!("[Transfer] Error: Clear input {} has invalid faucet pubkey", i);
|
|
|
- return Err(ContractError::Custom(20))
|
|
|
- }
|
|
|
-
|
|
|
- valcom_total += pedersen_commitment_u64(input.value, input.value_blind);
|
|
|
- }
|
|
|
-
|
|
|
- let mut new_nullifiers = Vec::with_capacity(params.inputs.len());
|
|
|
-
|
|
|
- msg!("[Transfer] Iterating over anonymous inputs");
|
|
|
- for (i, input) in params.inputs.iter().enumerate() {
|
|
|
- // The Merkle root is used to know whether this is a coin that existed
|
|
|
- // in a previous state.
|
|
|
- if !db_contains_key(coin_roots_db, &serialize(&input.merkle_root))? {
|
|
|
- msg!("[Transfer] Error: Merkle root not found in previous state (input {})", i);
|
|
|
- return Err(ContractError::Custom(21))
|
|
|
- }
|
|
|
-
|
|
|
- // The nullifiers should not already exist. It is the double-spend protection.
|
|
|
- if new_nullifiers.contains(&input.nullifier) ||
|
|
|
- db_contains_key(nullifiers_db, &serialize(&input.nullifier))?
|
|
|
- {
|
|
|
- msg!("[Transfer] Error: Duplicate nullifier found in input {}", i);
|
|
|
- return Err(ContractError::Custom(22))
|
|
|
- }
|
|
|
-
|
|
|
- // Check the invoked contract if spend hook is set
|
|
|
- if !bool::from(input.spend_hook.is_zero()) {
|
|
|
- let next_call_idx = call_idx + 1;
|
|
|
- if next_call_idx >= call.len() as u32 {
|
|
|
- msg!(
|
|
|
- "[Transfer] Error: next_call_idx = {} but len(calls) = {} in input {}",
|
|
|
- next_call_idx,
|
|
|
- call.len(),
|
|
|
- i
|
|
|
- );
|
|
|
- return Err(ContractError::Custom(23))
|
|
|
- }
|
|
|
-
|
|
|
- let next = &call[next_call_idx as usize];
|
|
|
- if next.contract_id.inner() != input.spend_hook {
|
|
|
- msg!(
|
|
|
- "[Transfer] Error: invoking contract call does not match spend hook\
|
|
|
- in input {}",
|
|
|
- i
|
|
|
- );
|
|
|
- return Err(ContractError::Custom(24))
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- new_nullifiers.push(input.nullifier);
|
|
|
- valcom_total += input.value_commit;
|
|
|
- }
|
|
|
-
|
|
|
- // Newly created coins for this transaction are in the outputs.
|
|
|
- let mut new_coins = Vec::with_capacity(params.outputs.len());
|
|
|
- for (i, output) in params.outputs.iter().enumerate() {
|
|
|
- // TODO: Should we have coins in a sled tree too to check dupes?
|
|
|
- if new_coins.contains(&Coin::from(output.coin)) {
|
|
|
- msg!("[Transfer] Error: Duplicate coin found in output {}", i);
|
|
|
- return Err(ContractError::Custom(25))
|
|
|
- }
|
|
|
-
|
|
|
- // FIXME: Needs some work on types and their place within all these libraries
|
|
|
- new_coins.push(Coin::from(output.coin));
|
|
|
- valcom_total -= output.value_commit;
|
|
|
- }
|
|
|
-
|
|
|
- // If the accumulator is not back in its initial state, there's a value mismatch.
|
|
|
- if valcom_total != pallas::Point::identity() {
|
|
|
- msg!("[Transfer] Error: Value commitments do not result in identity");
|
|
|
- return Err(ContractError::Custom(26))
|
|
|
- }
|
|
|
-
|
|
|
- // Verify that the token commitments are all for the same token
|
|
|
- let tokcom = params.outputs[0].token_commit;
|
|
|
- let mut failed_tokcom = params.inputs.iter().any(|input| input.token_commit != tokcom);
|
|
|
-
|
|
|
- failed_tokcom =
|
|
|
- failed_tokcom || params.outputs.iter().any(|output| output.token_commit != tokcom);
|
|
|
-
|
|
|
- failed_tokcom = failed_tokcom ||
|
|
|
- params.clear_inputs.iter().any(|input| {
|
|
|
- pedersen_commitment_base(input.token_id.inner(), input.token_blind) != tokcom
|
|
|
- });
|
|
|
-
|
|
|
- if failed_tokcom {
|
|
|
- msg!("[Transfer] Error: Token commitments do not match");
|
|
|
- return Err(ContractError::Custom(25))
|
|
|
- }
|
|
|
-
|
|
|
- // Create a state update
|
|
|
- let update = MoneyTransferUpdate { nullifiers: new_nullifiers, coins: new_coins };
|
|
|
- let mut update_data = vec![];
|
|
|
- update_data.write_u8(MoneyFunction::Transfer as u8)?;
|
|
|
- update.encode(&mut update_data)?;
|
|
|
+ let update_data = money_transfer_process_instruction(cid, call_idx, calls)?;
|
|
|
set_return_data(&update_data)?;
|
|
|
msg!("[Transfer] State update set!");
|
|
|
-
|
|
|
Ok(())
|
|
|
}
|
|
|
|
|
|
MoneyFunction::OtcSwap => {
|
|
|
msg!("[OtcSwap] Entered match arm");
|
|
|
- let params: MoneyTransferParams = deserialize(&self_.data[1..])?;
|
|
|
-
|
|
|
- let nullifiers_db = db_lookup(cid, MONEY_CONTRACT_NULLIFIERS_TREE)?;
|
|
|
- let coin_roots_db = db_lookup(cid, MONEY_CONTRACT_COIN_ROOTS_TREE)?;
|
|
|
-
|
|
|
- // State transition for OTC swaps
|
|
|
- // For now we enforce 2 inputs and 2 outputs, which means the coins
|
|
|
- // must be available beforehand. We might want to change this and
|
|
|
- // allow transactions including leftover change.
|
|
|
- assert!(params.clear_inputs.is_empty());
|
|
|
- assert!(params.inputs.len() == 2);
|
|
|
- assert!(params.outputs.len() == 2);
|
|
|
-
|
|
|
- let mut new_nullifiers = Vec::with_capacity(params.inputs.len());
|
|
|
-
|
|
|
- // inputs[0] is being swapped to outputs[1]
|
|
|
- // inputs[1] is being swapped to outputs[0]
|
|
|
- // So that's how we check the value and token commitments
|
|
|
- if params.inputs[0].value_commit != params.outputs[1].value_commit {
|
|
|
- msg!("[OtcSwap] Error: Value commitments for input 0 and output 1 do not match");
|
|
|
- return Err(ContractError::Custom(24))
|
|
|
- }
|
|
|
-
|
|
|
- if params.inputs[1].value_commit != params.outputs[0].value_commit {
|
|
|
- msg!("[OtcSwap] Error: Value commitments for input 1 and output 0 do not match");
|
|
|
- return Err(ContractError::Custom(24))
|
|
|
- }
|
|
|
-
|
|
|
- if params.inputs[0].token_commit != params.outputs[1].token_commit {
|
|
|
- msg!("[OtcSwap] Error: Token commitments for input 0 and output 1 do not match");
|
|
|
- return Err(ContractError::Custom(25))
|
|
|
- }
|
|
|
-
|
|
|
- if params.inputs[1].token_commit != params.outputs[0].token_commit {
|
|
|
- msg!("[OtcSwap] Error: Token commitments for input 1 and output 0 do not match");
|
|
|
- return Err(ContractError::Custom(25))
|
|
|
- }
|
|
|
-
|
|
|
- msg!("[OtcSwap] Iterating over anonymous inputs");
|
|
|
- for (i, input) in params.inputs.iter().enumerate() {
|
|
|
- // The Merkle root is used to know whether this is a coin that
|
|
|
- // existed in a previous state.
|
|
|
- if !db_contains_key(coin_roots_db, &serialize(&input.merkle_root))? {
|
|
|
- msg!("[OtcSwap] Error: Merkle root not found in previous state (input {})", i);
|
|
|
- return Err(ContractError::Custom(21))
|
|
|
- }
|
|
|
-
|
|
|
- // The nullifiers should not already exist. It is the double-spend protection.
|
|
|
- if new_nullifiers.contains(&input.nullifier) ||
|
|
|
- db_contains_key(nullifiers_db, &serialize(&input.nullifier))?
|
|
|
- {
|
|
|
- msg!("[OtcSwap] Error: Duplicate nullifier found in input {}", i);
|
|
|
- return Err(ContractError::Custom(22))
|
|
|
- }
|
|
|
-
|
|
|
- new_nullifiers.push(input.nullifier);
|
|
|
- }
|
|
|
-
|
|
|
- // Newly created coins for this transaction are in the outputs.
|
|
|
- let mut new_coins = Vec::with_capacity(params.outputs.len());
|
|
|
- for (i, output) in params.outputs.iter().enumerate() {
|
|
|
- // TODO: Should we have coins in a sled tree too to check dupes?
|
|
|
- if new_coins.contains(&Coin::from(output.coin)) {
|
|
|
- msg!("[OtcSwap] Error: Duplicate coin found in output {}", i);
|
|
|
- return Err(ContractError::Custom(23))
|
|
|
- }
|
|
|
-
|
|
|
- // FIXME: Needs some work on types and their place within all these libraries
|
|
|
- new_coins.push(Coin::from(output.coin));
|
|
|
- }
|
|
|
-
|
|
|
- // Create a state update. We also use the MoneyTransferUpdate because they're
|
|
|
- // essentially the same thing just with a different transition ruleset.
|
|
|
- let update = MoneyTransferUpdate { nullifiers: new_nullifiers, coins: new_coins };
|
|
|
- let mut update_data = vec![];
|
|
|
- update_data.write_u8(MoneyFunction::OtcSwap as u8)?;
|
|
|
- update.encode(&mut update_data)?;
|
|
|
+ let update_data = money_otcswap_process_instruction(cid, call_idx, calls)?;
|
|
|
set_return_data(&update_data)?;
|
|
|
msg!("[OtcSwap] State update set!");
|
|
|
-
|
|
|
Ok(())
|
|
|
}
|
|
|
|
|
|
@@ -766,7 +535,10 @@ fn process_instruction(cid: ContractId, ix: &[u8]) -> ContractResult {
|
|
|
|
|
|
MoneyFunction::Mint => {
|
|
|
msg!("[Mint] Entered match arm");
|
|
|
- unimplemented!();
|
|
|
+ let update_data = money_mint_process_instruction(cid, call_idx, calls)?;
|
|
|
+ set_return_data(&update_data)?;
|
|
|
+ msg!("[Mint] State update set!");
|
|
|
+ Ok(())
|
|
|
}
|
|
|
|
|
|
MoneyFunction::Freeze => {
|
|
|
@@ -779,26 +551,15 @@ fn process_instruction(cid: ContractId, ix: &[u8]) -> ContractResult {
|
|
|
#[cfg(not(feature = "no-entrypoint"))]
|
|
|
fn process_update(cid: ContractId, update_data: &[u8]) -> ContractResult {
|
|
|
match MoneyFunction::try_from(update_data[0])? {
|
|
|
- MoneyFunction::Transfer | MoneyFunction::OtcSwap => {
|
|
|
+ MoneyFunction::Transfer => {
|
|
|
let update: MoneyTransferUpdate = deserialize(&update_data[1..])?;
|
|
|
+ money_transfer_process_update(cid, update)?;
|
|
|
+ Ok(())
|
|
|
+ }
|
|
|
|
|
|
- let info_db = db_lookup(cid, MONEY_CONTRACT_INFO_TREE)?;
|
|
|
- let nullifiers_db = db_lookup(cid, MONEY_CONTRACT_NULLIFIERS_TREE)?;
|
|
|
- let coin_roots_db = db_lookup(cid, MONEY_CONTRACT_COIN_ROOTS_TREE)?;
|
|
|
-
|
|
|
- for nullifier in update.nullifiers {
|
|
|
- db_set(nullifiers_db, &serialize(&nullifier), &[])?;
|
|
|
- }
|
|
|
-
|
|
|
- msg!("Adding coins {:?} to Merkle tree", update.coins);
|
|
|
- let coins: Vec<_> = update.coins.iter().map(|x| MerkleNode::from(x.inner())).collect();
|
|
|
- merkle_add(
|
|
|
- info_db,
|
|
|
- coin_roots_db,
|
|
|
- &serialize(&MONEY_CONTRACT_COIN_MERKLE_TREE),
|
|
|
- &coins,
|
|
|
- )?;
|
|
|
-
|
|
|
+ MoneyFunction::OtcSwap => {
|
|
|
+ let update: MoneyTransferUpdate = deserialize(&update_data[1..])?;
|
|
|
+ money_otcswap_process_update(cid, update)?;
|
|
|
Ok(())
|
|
|
}
|
|
|
|
|
|
@@ -826,8 +587,9 @@ fn process_update(cid: ContractId, update_data: &[u8]) -> ContractResult {
|
|
|
}
|
|
|
|
|
|
MoneyFunction::Mint => {
|
|
|
- msg!("[Mint] Entered match arm");
|
|
|
- unimplemented!();
|
|
|
+ let update: MoneyMintUpdate = deserialize(&update_data[1..])?;
|
|
|
+ money_mint_process_update(cid, update)?;
|
|
|
+ Ok(())
|
|
|
}
|
|
|
|
|
|
MoneyFunction::Freeze => {
|