proto-kit / starter-kit

Starter kit for privacy enabled application chains
41 stars 23 forks source link

Provable.if in runtimeMethod does not work #26

Open dloghin opened 1 month ago

dloghin commented 1 month ago

We discovered this issue during the ETHGlobal Singapore Hackathon.

Suppose we want to update a balance based on a certain amount, such that if amount > balance we top up the balance (balance += amount - balance), else we withdraw (balance -= balance - amount). We do this with provable if:

    const condition = amount.greaterThan(currBalance);
    const diff = Provable.if(
      condition,
      Balance,
      amount.sub(currBalance),
      currBalance.sub(amount)
    );
    const to = Provable.if(
      condition,
      PublicKey,
      address,
      addressPool
    );
    const from = Provable.if(
      condition,
      PublicKey,
      addressPool,
      address
    );
    const d = UInt64.Unsafe.fromField(diff.value);
    await this.transfer(tokenId, from, to, d);

(see balances.ts in my fork)

The test fails either when amount > balance or amount < balance. It only passes when amount == balance (see the test at line 50 of balances.test.ts in my fork).

starter-kit commit: ed6ced595e6889d607be50e1840f27e722015bcb node version: v18.20.4 pnpm version: 9.8.0

rpanic commented 1 month ago

@dloghin In o1js and protokit, all of your code will always be executed, this means both branches of all Provable.if calls will be called, but only one will be returned. And the UInt64 is built in a way that protects you from underflows, for example 2 - 5 = -3 -> we can't have minus values since it's a uint. However, you built your code in a way that one of your branches will always underflow (in the if that computes diff), no matter the inputs