contracts: use proper indexes in money and consensus calls

This commit is contained in:
aggstam
2023-12-18 16:35:00 +02:00
parent a84a3e3f70
commit 5711ea1370
6 changed files with 137 additions and 108 deletions

View File

@@ -82,35 +82,40 @@ pub(crate) fn consensus_stake_process_instruction_v1(
call_idx: u32,
calls: Vec<DarkLeaf<ContractCall>>,
) -> Result<Vec<u8>, ContractError> {
let self_ = &calls[call_idx as usize].data;
let params: ConsensusStakeParamsV1 = deserialize(&self_.data[1..])?;
let self_ = &calls[call_idx as usize];
let params: ConsensusStakeParamsV1 = deserialize(&self_.data.data[1..])?;
// Check previous call is money contract
// FIXME: This changes with Money::Fee
// Check child call is money contract
if call_idx == 0 {
msg!("[ConsensusStakeV1] Error: previous_call_idx will be out of bounds");
msg!("[ConsensusStakeV1] Error: child_call_idx will be out of bounds");
return Err(MoneyError::CallIdxOutOfBounds.into())
}
// Verify previous call corresponds to Money::StakeV1
let previous_call_idx = call_idx - 1;
let previous = &calls[previous_call_idx as usize].data;
if previous.contract_id.inner() != MONEY_CONTRACT_ID.inner() {
msg!("[ConsensusStakeV1] Error: Previous contract call is not money contract");
return Err(MoneyError::StakePreviousCallNotMoneyContract.into())
let child_call_indexes = &self_.children_indexes;
if child_call_indexes.len() != 1 {
msg!("[ConsensusStakeV1] Error: child_call_idx is missing");
return Err(MoneyError::StakeChildCallNotMoneyContract.into())
}
let child_call_idx = child_call_indexes[0];
// Verify child call corresponds to Money::StakeV1
let child = &calls[child_call_idx].data;
if child.contract_id.inner() != MONEY_CONTRACT_ID.inner() {
msg!("[ConsensusStakeV1] Error: Child contract call is not money contract");
return Err(MoneyError::StakeChildCallNotMoneyContract.into())
}
if previous.data[0] != MoneyFunction::StakeV1 as u8 {
msg!("[ConsensusStakeV1] Error: Previous call function mismatch");
return Err(MoneyError::PreviousCallFunctionMismatch.into())
if child.data[0] != MoneyFunction::StakeV1 as u8 {
msg!("[ConsensusStakeV1] Error: Child call function mismatch");
return Err(MoneyError::ChildCallFunctionMismatch.into())
}
// Verify that the previous call's input is the same as this one's
let previous_params: MoneyStakeParamsV1 = deserialize(&previous.data[1..])?;
let previous_input = &previous_params.input;
if previous_input != &params.input {
msg!("[ConsensusStakeV1] Error: Previous call input mismatch");
return Err(MoneyError::PreviousCallInputMismatch.into())
// Verify that the child call's input is the same as this one's
let child_params: MoneyStakeParamsV1 = deserialize(&child.data[1..])?;
let child_input = &child_params.input;
if child_input != &params.input {
msg!("[ConsensusStakeV1] Error: Child call input mismatch");
return Err(MoneyError::ChildCallInputMismatch.into())
}
// Access the necessary databases where there is information to

View File

@@ -85,8 +85,8 @@ pub(crate) fn consensus_unstake_process_instruction_v1(
call_idx: u32,
calls: Vec<DarkLeaf<ContractCall>>,
) -> Result<Vec<u8>, ContractError> {
let self_ = &calls[call_idx as usize].data;
let params: ConsensusUnstakeParamsV1 = deserialize(&self_.data[1..])?;
let self_ = &calls[call_idx as usize];
let params: ConsensusUnstakeParamsV1 = deserialize(&self_.data.data[1..])?;
let input = &params.input;
// Access the necessary databases where there is information to
@@ -98,30 +98,36 @@ pub(crate) fn consensus_unstake_process_instruction_v1(
// Perform the actual state transition
// ===================================
// Check next call is money contract
let next_call_idx = call_idx + 1;
if next_call_idx >= calls.len() as u32 {
msg!("[ConsensusUnstakeV1] Error: next_call_idx out of bounds");
// Check parent call is money contract
let parent_call_idx = self_.parent_index;
if parent_call_idx.is_none() {
msg!("[ConsensusUnstakeV1] Error: parent_call_idx is missing");
return Err(MoneyError::UnstakeParentCallNotMoneyContract.into())
}
let parent_call_idx = parent_call_idx.unwrap();
if parent_call_idx >= calls.len() {
msg!("[ConsensusUnstakeV1] Error: parent_call_idx out of bounds");
return Err(MoneyError::CallIdxOutOfBounds.into())
}
let next = &calls[next_call_idx as usize].data;
if next.contract_id.inner() != MONEY_CONTRACT_ID.inner() {
msg!("[ConsensusUnstakeV1] Error: Next contract call is not money contract");
return Err(MoneyError::UnstakeNextCallNotMoneyContract.into())
let parent = &calls[parent_call_idx].data;
if parent.contract_id.inner() != MONEY_CONTRACT_ID.inner() {
msg!("[ConsensusUnstakeV1] Error: Parent contract call is not money contract");
return Err(MoneyError::UnstakeParentCallNotMoneyContract.into())
}
// Verify next call corresponds to Money::UnstakeV1
if next.data[0] != MoneyFunction::UnstakeV1 as u8 {
msg!("[ConsensusUnstakeV1] Error: Next call function mismatch");
return Err(MoneyError::NextCallFunctionMismatch.into())
// Verify parent call corresponds to Money::UnstakeV1
if parent.data[0] != MoneyFunction::UnstakeV1 as u8 {
msg!("[ConsensusUnstakeV1] Error: Parent call function mismatch");
return Err(MoneyError::ParentCallFunctionMismatch.into())
}
// Verify next call input is the same as this calls input
let next_params: MoneyUnstakeParamsV1 = deserialize(&next.data[1..])?;
if input != &next_params.input {
msg!("[ConsensusUnstakeV1] Error: Next call input mismatch");
return Err(MoneyError::NextCallInputMismatch.into())
// Verify parent call input is the same as this calls input
let parent_params: MoneyUnstakeParamsV1 = deserialize(&parent.data[1..])?;
if input != &parent_params.input {
msg!("[ConsensusUnstakeV1] Error: Parent call input mismatch");
return Err(MoneyError::ParentCallInputMismatch.into())
}
msg!("[ConsensusUnstakeV1] Validating anonymous input");

View File

@@ -86,8 +86,8 @@ pub(crate) fn money_stake_process_instruction_v1(
call_idx: u32,
calls: Vec<DarkLeaf<ContractCall>>,
) -> Result<Vec<u8>, ContractError> {
let self_ = &calls[call_idx as usize].data;
let params: MoneyStakeParamsV1 = deserialize(&self_.data[1..])?;
let self_ = &calls[call_idx as usize];
let params: MoneyStakeParamsV1 = deserialize(&self_.data.data[1..])?;
// Access the necessary databases where there is information to
// validate this state transition.
@@ -126,30 +126,36 @@ pub(crate) fn money_stake_process_instruction_v1(
return Err(MoneyError::DuplicateNullifier.into())
}
// Check next call is consensus contract
let next_call_idx = call_idx + 1;
if next_call_idx >= calls.len() as u32 {
// Check parent call is consensus contract
let parent_call_idx = self_.parent_index;
if parent_call_idx.is_none() {
msg!("[MoneyStakeV1] Error: parent_call_idx is missing");
return Err(MoneyError::StakeParentCallNotConsensusContract.into())
}
let parent_call_idx = parent_call_idx.unwrap();
if parent_call_idx >= calls.len() {
msg!("[MoneyStakeV1] Error: next_call_idx out of bounds");
return Err(MoneyError::CallIdxOutOfBounds.into())
}
// Verify next call corresponds to Consensus::StakeV1 (0x01)
let next = &calls[next_call_idx as usize].data;
if next.contract_id.inner() != CONSENSUS_CONTRACT_ID.inner() {
msg!("[MoneyStakeV1] Error: Next contract call is not consensus contract");
return Err(MoneyError::StakeNextCallNotConsensusContract.into())
// Verify parent call corresponds to Consensus::StakeV1 (0x01)
let parent = &calls[parent_call_idx].data;
if parent.contract_id.inner() != CONSENSUS_CONTRACT_ID.inner() {
msg!("[MoneyStakeV1] Error: Parent contract call is not consensus contract");
return Err(MoneyError::StakeParentCallNotConsensusContract.into())
}
if next.data[0] != 0x01 {
msg!("[MoneyStakeV1] Error: Next call function mismatch");
return Err(MoneyError::NextCallFunctionMismatch.into())
if parent.data[0] != 0x01 {
msg!("[MoneyStakeV1] Error: Parent call function mismatch");
return Err(MoneyError::ParentCallFunctionMismatch.into())
}
// Verify next call ConsensusInput is the same as this calls input
let next_params: ConsensusStakeParamsV1 = deserialize(&next.data[1..])?;
if input != &next_params.input {
msg!("[MoneyStakeV1] Error: Next call input mismatch");
return Err(MoneyError::NextCallInputMismatch.into())
// Verify parent call ConsensusInput is the same as this calls input
let parent_params: ConsensusStakeParamsV1 = deserialize(&parent.data[1..])?;
if input != &parent_params.input {
msg!("[MoneyStakeV1] Error: Parent call input mismatch");
return Err(MoneyError::ParentCallInputMismatch.into())
}
// At this point the state transition has passed, so we create a state update

View File

@@ -109,8 +109,8 @@ pub(crate) fn money_transfer_process_instruction_v1(
call_idx: u32,
calls: Vec<DarkLeaf<ContractCall>>,
) -> Result<Vec<u8>, ContractError> {
let self_ = &calls[call_idx as usize].data;
let params: MoneyTransferParamsV1 = deserialize(&self_.data[1..])?;
let self_ = &calls[call_idx as usize];
let params: MoneyTransferParamsV1 = deserialize(&self_.data.data[1..])?;
if params.clear_inputs.len() + params.inputs.len() < 1 {
msg!("[TransferV1] Error: No inputs in the call");
@@ -188,14 +188,20 @@ pub(crate) fn money_transfer_process_instruction_v1(
// If spend hook is set, check its correctness
if input.spend_hook != pallas::Base::ZERO {
let next_call_idx = call_idx + 1;
if next_call_idx >= calls.len() as u32 {
msg!("[TransferV1] Error: next_call_idx out of bounds (input {})", i);
let parent_call_idx = self_.parent_index;
if parent_call_idx.is_none() {
msg!("[TransferV1] Error: parent_call_idx is missing");
return Err(MoneyError::CallIdxOutOfBounds.into())
}
let parent_call_idx = parent_call_idx.unwrap();
if parent_call_idx >= calls.len() {
msg!("[TransferV1] Error: parent_call_idx out of bounds (input {})", i);
return Err(MoneyError::CallIdxOutOfBounds.into())
}
let next = &calls[next_call_idx as usize].data;
if next.contract_id.inner() != input.spend_hook {
let parent = &calls[parent_call_idx].data;
if parent.contract_id.inner() != input.spend_hook {
msg!("[TransferV1] Error: Invoked contract call does not match spend hook in input {}", i);
return Err(MoneyError::SpendHookMismatch.into())
}

View File

@@ -79,8 +79,8 @@ pub(crate) fn money_unstake_process_instruction_v1(
call_idx: u32,
calls: Vec<DarkLeaf<ContractCall>>,
) -> Result<Vec<u8>, ContractError> {
let self_ = &calls[call_idx as usize].data;
let params: MoneyUnstakeParamsV1 = deserialize(&self_.data[1..])?;
let self_ = &calls[call_idx as usize];
let params: MoneyUnstakeParamsV1 = deserialize(&self_.data.data[1..])?;
let input = &params.input;
let output = &params.output;
@@ -96,31 +96,37 @@ pub(crate) fn money_unstake_process_instruction_v1(
// Perform the actual state transition
// ===================================
// Check previous call is consensus contract
// Check child call is consensus contract
if call_idx == 0 {
msg!("[MoneyUnstakeV1] Error: previous_call_idx will be out of bounds");
msg!("[MoneyUnstakeV1] Error: child_call_idx will be out of bounds");
return Err(MoneyError::CallIdxOutOfBounds.into())
}
let previous_call_idx = call_idx - 1;
let previous = &calls[previous_call_idx as usize].data;
if previous.contract_id.inner() != CONSENSUS_CONTRACT_ID.inner() {
msg!("[MoneyUnstakeV1] Error: Previous contract call is not consensus contract");
return Err(MoneyError::UnstakePreviousCallNotConsensusContract.into())
let child_call_indexes = &self_.children_indexes;
if child_call_indexes.len() != 1 {
msg!("[MoneyUnstakeV1] Error: child_call_idx is missing");
return Err(MoneyError::UnstakeChildCallNotConsensusContract.into())
}
let child_call_idx = child_call_indexes[0];
let child = &calls[child_call_idx].data;
if child.contract_id.inner() != CONSENSUS_CONTRACT_ID.inner() {
msg!("[MoneyUnstakeV1] Error: Child contract call is not consensus contract");
return Err(MoneyError::UnstakeChildCallNotConsensusContract.into())
}
// Verify previous call corresponds to Consensus::UnstakeV1 (0x04)
if previous.data[0] != 0x04 {
msg!("[MoneyUnstakeV1] Error: Previous call function mismatch");
return Err(MoneyError::PreviousCallFunctionMismatch.into())
// Verify child call corresponds to Consensus::UnstakeV1 (0x04)
if child.data[0] != 0x04 {
msg!("[MoneyUnstakeV1] Error: Child call function mismatch");
return Err(MoneyError::ChildCallFunctionMismatch.into())
}
// Verify previous call input is the same as this calls StakeInput
let previous_params: ConsensusUnstakeParamsV1 = deserialize(&previous.data[1..])?;
let previous_input = &previous_params.input;
if previous_input != input {
msg!("[MoneyUnstakeV1] Error: Previous call input mismatch");
return Err(MoneyError::PreviousCallInputMismatch.into())
// Verify child call input is the same as this calls StakeInput
let child_params: ConsensusUnstakeParamsV1 = deserialize(&child.data[1..])?;
let child_input = &child_params.input;
if child_input != input {
msg!("[MoneyUnstakeV1] Error: Child call input mismatch");
return Err(MoneyError::ChildCallInputMismatch.into())
}
msg!("[MoneyUnstakeV1] Validating anonymous output");

View File

@@ -20,7 +20,7 @@ use darkfi_sdk::error::ContractError;
#[derive(Debug, Clone, thiserror::Error)]
// TODO: Make generic contract common errors like
// NextCallFunctionMismatch
// ParentCallFunctionMismatch
pub enum MoneyError {
#[error("Missing inputs in transfer call")]
TransferMissingInputs,
@@ -85,32 +85,32 @@ pub enum MoneyError {
#[error("Missing nullifier")]
StakeMissingNullifier,
#[error("Next contract call is not consensus contract")]
StakeNextCallNotConsensusContract,
#[error("Parent contract call is not consensus contract")]
StakeParentCallNotConsensusContract,
#[error("Previous contract call is not money contract")]
StakePreviousCallNotMoneyContract,
#[error("Child contract call is not money contract")]
StakeChildCallNotMoneyContract,
#[error("Spend hook is not consensus contract")]
UnstakeSpendHookNotConsensusContract,
#[error("Next contract call is not money contract")]
UnstakeNextCallNotMoneyContract,
#[error("Parent contract call is not money contract")]
UnstakeParentCallNotMoneyContract,
#[error("Previous contract call is not consensus contract")]
UnstakePreviousCallNotConsensusContract,
#[error("Child contract call is not consensus contract")]
UnstakeChildCallNotConsensusContract,
#[error("Next call function mismatch")]
NextCallFunctionMismatch,
#[error("Parent call function mismatch")]
ParentCallFunctionMismatch,
#[error("Next call input mismatch")]
NextCallInputMismatch,
#[error("Parent call input mismatch")]
ParentCallInputMismatch,
#[error("Previous call function mismatch")]
PreviousCallFunctionMismatch,
#[error("Child call function mismatch")]
ChildCallFunctionMismatch,
#[error("Previous call input mismatch")]
PreviousCallInputMismatch,
#[error("Child call input mismatch")]
ChildCallInputMismatch,
#[error("Call is not executed on genesis slot")]
GenesisCallNonGenesisSlot,
@@ -155,15 +155,15 @@ impl From<MoneyError> for ContractError {
MoneyError::StakeInputNonNativeToken => Self::Custom(19),
MoneyError::StakeMissingSpendHook => Self::Custom(20),
MoneyError::StakeMissingNullifier => Self::Custom(21),
MoneyError::StakeNextCallNotConsensusContract => Self::Custom(22),
MoneyError::StakePreviousCallNotMoneyContract => Self::Custom(23),
MoneyError::StakeParentCallNotConsensusContract => Self::Custom(22),
MoneyError::StakeChildCallNotMoneyContract => Self::Custom(23),
MoneyError::UnstakeSpendHookNotConsensusContract => Self::Custom(24),
MoneyError::UnstakeNextCallNotMoneyContract => Self::Custom(25),
MoneyError::UnstakePreviousCallNotConsensusContract => Self::Custom(26),
MoneyError::NextCallFunctionMismatch => Self::Custom(27),
MoneyError::NextCallInputMismatch => Self::Custom(28),
MoneyError::PreviousCallFunctionMismatch => Self::Custom(29),
MoneyError::PreviousCallInputMismatch => Self::Custom(30),
MoneyError::UnstakeParentCallNotMoneyContract => Self::Custom(25),
MoneyError::UnstakeChildCallNotConsensusContract => Self::Custom(26),
MoneyError::ParentCallFunctionMismatch => Self::Custom(27),
MoneyError::ParentCallInputMismatch => Self::Custom(28),
MoneyError::ChildCallFunctionMismatch => Self::Custom(29),
MoneyError::ChildCallInputMismatch => Self::Custom(30),
MoneyError::GenesisCallNonGenesisSlot => Self::Custom(31),
MoneyError::MissingNullifier => Self::Custom(32),
MoneyError::PoWRewardCallAfterCutoffSlot => Self::Custom(33),