瀏覽代碼

money/client/transfer: Fix integer truncation issues when dividing for half-split

- Fix issue where `half` was calculated incorrectly for odd values of
  `value`
- Fix issue where sending a `value` of 1 and `half_split` of true would
  result in two empty value transfers

Note that this is only a client issue. The wasm back-end would cause the
invalid transfer to fail as the tx outputs from the client would not
match the inputs
foo 2 年之前
父節點
當前提交
302d482475
共有 1 個文件被更改,包括 21 次插入3 次删除
  1. 21 3
      src/contract/money/src/client/transfer_v1/mod.rs

+ 21 - 3
src/contract/money/src/client/transfer_v1/mod.rs

@@ -109,6 +109,12 @@ pub fn make_transfer_call(
         return Err(ClientFailed::InvalidAmount(value).into())
         return Err(ClientFailed::InvalidAmount(value).into())
     }
     }
 
 
+    // Using integer division via `half_split` causes the evaluation of `1 / 2` which is equal to
+    // 0. This would cause us to send two outputs of 0 value which is not what we want.
+    if half_split && value == 1 {
+        return Err(ClientFailed::InvalidAmount(value).into())
+    }
+
     if token_id.inner() == pallas::Base::ZERO {
     if token_id.inner() == pallas::Base::ZERO {
         return Err(ClientFailed::InvalidTokenId(token_id.to_string()).into())
         return Err(ClientFailed::InvalidTokenId(token_id.to_string()).into())
     }
     }
@@ -142,18 +148,30 @@ pub fn make_transfer_call(
 
 
     // Check if we should split the output into two equal halves
     // Check if we should split the output into two equal halves
     if half_split {
     if half_split {
-        let value = value / 2;
+        // Integer division is safe here as we are dividing by a constant.
+        #[allow(clippy::integer_division)]
+        let mut half = value / 2;
+
+        // Add the first half
         outputs.push(TransferCallOutput {
         outputs.push(TransferCallOutput {
             public_key: recipient,
             public_key: recipient,
-            value,
+            value: half,
             token_id,
             token_id,
             spend_hook: output_spend_hook.unwrap_or(FuncId::none()),
             spend_hook: output_spend_hook.unwrap_or(FuncId::none()),
             user_data: output_user_data.unwrap_or(pallas::Base::ZERO),
             user_data: output_user_data.unwrap_or(pallas::Base::ZERO),
             blind: Blind::random(&mut OsRng),
             blind: Blind::random(&mut OsRng),
         });
         });
+
+        // Handle the case where value is odd. If so, division by 2 will truncate the amount.
+        // e.g. in integer division, 3 / 2 == 1.
+        // Arithmetic side effects are safe here: no risk of overflow or panic.
+        #[allow(clippy::arithmetic_side_effects)]
+        if value % 2 != 0 {
+            half += 1;
+        }
         outputs.push(TransferCallOutput {
         outputs.push(TransferCallOutput {
             public_key: recipient,
             public_key: recipient,
-            value,
+            value: half,
             token_id,
             token_id,
             spend_hook: output_spend_hook.unwrap_or(FuncId::none()),
             spend_hook: output_spend_hook.unwrap_or(FuncId::none()),
             user_data: output_user_data.unwrap_or(pallas::Base::ZERO),
             user_data: output_user_data.unwrap_or(pallas::Base::ZERO),