add spend_hook to money contracts, and checks for DAO::exec()

This commit is contained in:
x
2023-01-09 14:18:50 +01:00
parent f03966069b
commit 0c292e524e
5 changed files with 54 additions and 8 deletions

View File

@@ -148,8 +148,23 @@ fn process_instruction(cid: ContractId, ix: &[u8]) -> ContractResult {
assert!(call_idx < call.len() as u32);
let self_ = &call[call_idx as usize];
let func = DaoFunction::try_from(self_.data[0])?;
match DaoFunction::try_from(self_.data[0])? {
if call.len() != 1 {
// Enforce a strict structure for our tx
assert_eq!(call.len(), 2);
assert_eq!(call_idx, 1);
// We can unpack user_data and check the function call is correct.
// But in this contract, only DAO::exec() can be invoked by other ones.
// So just check the function call is correct.
// NOTE: we may wish to improve this since it cripples user composability.
assert_eq!(func, DaoFunction::Exec);
}
match func {
DaoFunction::Mint => {
let params: MintCallParams = deserialize(&self_.data[1..])?;
let dao_bulla = params.dao_bulla.inner();

View File

@@ -47,6 +47,7 @@ pub const DAO_CONTRACT_ZKAS_DAO_PROPOSE_BURN_NS: &str = "DaoProposeInput";
pub const DAO_CONTRACT_ZKAS_DAO_PROPOSE_MAIN_NS: &str = "DaoProposeMain";
#[repr(u8)]
#[derive(PartialEq, Debug)]
pub enum DaoFunction {
Mint = 0x00,
Propose = 0x01,

View File

@@ -755,7 +755,7 @@ async fn integration_test() -> Result<()> {
let dao_coin_blind = pallas::Base::random(&mut OsRng);
let input_value = treasury_note.value;
let input_value_blind = pallas::Scalar::random(&mut OsRng);
let tx_signature_secret = SecretKey::random(&mut OsRng);
let xfer_signature_secret = SecretKey::random(&mut OsRng);
let exec_signature_secret = SecretKey::random(&mut OsRng);
let (treasury_leaf_position, treasury_merkle_path) = {
@@ -783,7 +783,7 @@ async fn integration_test() -> Result<()> {
note: treasury_note,
user_data_blind,
value_blind: input_value_blind,
signature_secret: tx_signature_secret,
signature_secret: xfer_signature_secret,
}],
outputs: vec![
// Sending money
@@ -847,7 +847,7 @@ async fn integration_test() -> Result<()> {
proofs: vec![xfer_proofs, exec_proofs],
signatures: vec![],
};
let xfer_sigs = tx.create_sigs(&mut OsRng, &vec![tx_signature_secret])?;
let xfer_sigs = tx.create_sigs(&mut OsRng, &vec![xfer_signature_secret])?;
let exec_sigs = tx.create_sigs(&mut OsRng, &vec![exec_signature_secret])?;
tx.signatures = vec![xfer_sigs, exec_sigs];

View File

@@ -426,6 +426,32 @@ fn process_instruction(cid: ContractId, ix: &[u8]) -> ContractResult {
return Err(ContractError::Custom(22))
}
msg!("XXXX: spend_hook = {:?}", input.spend_hook);
// 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;
}
@@ -436,7 +462,7 @@ fn process_instruction(cid: ContractId, ix: &[u8]) -> ContractResult {
// 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(23))
return Err(ContractError::Custom(25))
}
// FIXME: Needs some work on types and their place within all these libraries
@@ -447,7 +473,7 @@ fn process_instruction(cid: ContractId, ix: &[u8]) -> ContractResult {
// 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(24))
return Err(ContractError::Custom(26))
}
// Verify that the token commitments are all for the same token

View File

@@ -126,9 +126,13 @@ pub struct Input {
pub nullifier: Nullifier,
/// Revealed Merkle root
pub merkle_root: MerkleNode,
/// spend hook (TODO: document)
/// Spend hook used to invoke other contracts.
/// If this value is nonzero then the subsequent contract call in the tx
/// must have this value as its ID.
pub spend_hook: pallas::Base,
/// user data enc (TODO: document)
/// Encrypted user data field. An encrypted commitment to arbitrary data.
/// When spend hook is set (it is nonzero), then this field may be used
/// to pass data to the invoked contract.
pub user_data_enc: pallas::Base,
/// Public key for the signature
pub signature_public: PublicKey,