transfer.rs 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133
  1. use darkfi_sdk::{
  2. crypto::{
  3. pedersen::{pedersen_commitment_base, pedersen_commitment_u64, ValueCommit},
  4. MerkleNode,
  5. },
  6. error::{ContractError, ContractResult},
  7. incrementalmerkletree::Tree,
  8. msg,
  9. pasta::{group::Group, pallas},
  10. state::Verification,
  11. };
  12. use super::State;
  13. /// This function is the execution of the `Transfer` functionality.
  14. pub fn exec(state: &mut State, tx: Transaction) -> ContractResult {
  15. // TODO: Clear inputs. Cashier+Faucet logic is bad and needs to
  16. // be solved in another better way.
  17. // Nullifiers in the transaction
  18. let mut nullifiers = Vec::with_capacity(tx.inputs.len());
  19. msg!("Iterating over inputs");
  20. for (i, input) in tx.inputs.iter().enumerate() {
  21. let merkle_root = input.revealed.merkle_root;
  22. let spend_hook = input.revealed.spend_hook;
  23. let nullifier = input.revealed.nullifier;
  24. // The Merkle root is used to know whether this is a coin
  25. // that existed in a previous state.
  26. if !state.is_valid_merkle(&merkle_root) {
  27. msg!("Error: Invalid Merkle root (input {})", i);
  28. msg!("Root: {:?}", merkle_root);
  29. return Err(ContractError::Custom(30))
  30. }
  31. // Check the spend_hook is satisfied.
  32. // The spend_hook says a coin must invoke another contract
  33. // function when being spent. If the value is set, then we
  34. // check the function call exists.
  35. if spend_hook != pallas::Base::zero() {
  36. todo!();
  37. }
  38. // The nullifiers should not already exist. This gives us
  39. // protection against double-spending.
  40. if state.nullifier_exists(&nullifier) || nullifiers.contains(&nullifier) {
  41. msg!("Duplicate nulliier found (input {})", i);
  42. msg!("Nullifier: {:?}", nullifier);
  43. return Err(ContractError::Custom(31))
  44. }
  45. // Add the nullifier to the list of seen nullifiers.
  46. nullifiers.push(nullifier);
  47. }
  48. // Verify transaction
  49. match tx.verify() {
  50. Ok(()) => msg!("Transaction verified successfully"),
  51. Err(e) => {
  52. msg!("Transaction failed to verify");
  53. return Err(e)
  54. }
  55. }
  56. msg!("Applying state update");
  57. state.nullifiers.extend_from_slice(&nullifiers);
  58. for output in tx.outputs {
  59. state.tree.append(&MerkleNode::from(output.coin.inner()));
  60. state.merkle_roots.push(state.tree.root(0).unwrap());
  61. }
  62. Ok(())
  63. }
  64. // `Verification` could be a generic trait we implement for doing
  65. // arbitrary verification in contracts.
  66. impl Verification for Transaction {
  67. fn verify(&self) -> ContractResult {
  68. // Must have minimum 1 clear or anon input
  69. if self.clear_inputs.len() + self.inputs.len() == 0 {
  70. msg!("Error: Missing inputs in transaction");
  71. return Err(ContractError::Custom(32))
  72. }
  73. // Also minimum 1 output
  74. if self.outputs.is_empty() {
  75. msg!("Error: Missing outputs in transaction");
  76. return Err(ContractError::Custom(33))
  77. }
  78. // Accumulator for the value commitments
  79. let mut valcom_total = ValueCommit::identity();
  80. // Add values from the clear inputs
  81. for input in &self.clear_inputs {
  82. valcom_total += pedersen_commitment_u64(input.value, input.value_blind);
  83. }
  84. // Add values from the inputs
  85. for input in &self.inputs {
  86. valcom_total += input.revealed.value_commit;
  87. }
  88. // Subtract values from the outputs
  89. for output in &self.outputs {
  90. valcom_total -= output.revealed.value_commit;
  91. }
  92. // If the accumulator is not back in its initial state,
  93. // there's a value mismatch.
  94. if valcom_total != ValueCommit::identity() {
  95. msg!("Error: Missing funds");
  96. return Err(ContractError::Custom(34))
  97. }
  98. // Verify that the token commitments match
  99. let tokval = self.outputs[0].revealed.token_commit;
  100. let mut failed = self.inputs.iter().any(|input| input.revealed.token_commit != tokval);
  101. failed = failed || self.outputs.iter().any(|output| output.revealed.token_commit != tokval);
  102. failed = failed ||
  103. self.clear_inputs.iter().any(|input| {
  104. pedersen_commitment_base(input.token_id, input.token_blind) != tokval
  105. });
  106. if failed {
  107. msg!("Error: Token ID mismatch");
  108. return Err(ContractError::Custom(35))
  109. }
  110. Ok(())
  111. }
  112. }