Skip to content
This repository has been archived by the owner on Mar 12, 2024. It is now read-only.

Commit

Permalink
feat(contracts)!: payable vouchers (WIP)
Browse files Browse the repository at this point in the history
* Add an `executeVoucher` function to `ICartesiDApp` and `CartesiDApp`
  with an extra `uint256` parameter called `_value`, which is the amount
  of Wei to be sent along the message call. Backwards compatibility is
  kept with the old signature by passing 0 Wei by default.

* Use a TEMPORARY workaround to test the validation of payable vouchers,
  by encoding outputs using `abi.encodePacked` instead of `abi.encode`
  and using the notice tree as the voucher tree. Changes related to this
  TEMPORARY workaround will be reverted when a new version of the
  machine that supports payable vouchers is available.

BREAKING CHANGE: Changes the encoding of vouchers from
`abi.encode(destination, payload)` to
`abi.encode(destination, value, payload)`. This change is planned but
not yet executed as we need the machine to encode vouchers in such a way
in order to validate them on-chain.
  • Loading branch information
guidanoli committed Jul 18, 2023
1 parent 757be26 commit d7156f5
Show file tree
Hide file tree
Showing 6 changed files with 70 additions and 19 deletions.
6 changes: 4 additions & 2 deletions onchain/rollups/contracts/common/OutputEncoding.sol
Original file line number Diff line number Diff line change
Expand Up @@ -22,17 +22,19 @@ library OutputEncoding {
function encodeNotice(
bytes calldata notice
) internal pure returns (bytes memory) {
return abi.encode(notice);
return abi.encodePacked(notice);
}

/// @notice Encode a voucher.
/// @param destination The address that will receive the payload through a message call
/// @param value The amount of Wei to be passed along the call
/// @param payload The payload, which—in the case of Solidity contracts—encodes a function call
/// @return The encoded output
function encodeVoucher(
address destination,
uint256 value,
bytes calldata payload
) internal pure returns (bytes memory) {
return abi.encode(destination, payload);
return abi.encodePacked(destination, value, payload);
}
}
20 changes: 17 additions & 3 deletions onchain/rollups/contracts/dapp/CartesiDApp.sol
Original file line number Diff line number Diff line change
Expand Up @@ -111,9 +111,10 @@ contract CartesiDApp is

function executeVoucher(
address _destination,
uint256 _value,
bytes calldata _payload,
Proof calldata _proof
) external override nonReentrant returns (bool) {
) public override nonReentrant returns (bool) {
bytes32 epochHash;
uint256 firstInputIndex;
uint256 lastInputIndex;
Expand All @@ -130,7 +131,12 @@ contract CartesiDApp is
);

// reverts if proof isn't valid
_proof.validity.validateVoucher(_destination, _payload, epochHash);
_proof.validity.validateVoucher(
_destination,
_value,
_payload,
epochHash
);

uint256 voucherPosition = LibOutputValidation.getBitMaskPosition(
_proof.validity.outputIndexWithinInput,
Expand All @@ -143,7 +149,7 @@ contract CartesiDApp is
}

// execute voucher
(bool succ, ) = _destination.call(_payload);
(bool succ, ) = _destination.call{value: _value}(_payload);

// if properly executed, mark it as executed and emit event
if (succ) {
Expand All @@ -154,6 +160,14 @@ contract CartesiDApp is
return succ;
}

function executeVoucher(
address _destination,
bytes calldata _payload,
Proof calldata _proof
) external override returns (bool) {
return executeVoucher(_destination, 0, _payload, _proof);
}

function wasVoucherExecuted(
uint256 _inboxInputIndex,
uint256 _outputIndexWithinInput
Expand Down
19 changes: 19 additions & 0 deletions onchain/rollups/contracts/dapp/ICartesiDApp.sol
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,25 @@ interface ICartesiDApp {
Proof calldata _proof
) external returns (bool);

/// @notice Try to execute a voucher.
///
/// Reverts if voucher was already successfully executed.
///
/// @param _destination The address that will receive the payload through a message call
/// @param _value The amount of Wei to be passed along the call
/// @param _payload The payload, which—in the case of Solidity contracts—encodes a function call
/// @param _proof The proof used to validate the voucher against
/// a claim submitted by the current consensus contract
/// @return Whether the execution was successful or not
/// @dev On a successful execution, emits a `VoucherExecuted` event.
/// Execution of already executed voucher will raise a `VoucherReexecutionNotAllowed` error.
function executeVoucher(
address _destination,
uint256 _value,
bytes calldata _payload,
Proof calldata _proof
) external returns (bool);

/// @notice Check whether a voucher has been executed.
/// @param _inboxInputIndex The index of the input in the input box
/// @param _outputIndexWithinInput The index of output emitted by the input
Expand Down
23 changes: 13 additions & 10 deletions onchain/rollups/contracts/library/LibOutputValidation.sol
Original file line number Diff line number Diff line change
Expand Up @@ -57,17 +57,17 @@ library LibOutputValidation {

/// @notice Make sure the output proof is valid, otherwise revert.
/// @param v The output validity proof
/// @param encodedOutput The encoded output
/// @param output The output
/// @param epochHash The hash of the epoch in which the output was generated
/// @param outputsEpochRootHash Either `v.vouchersEpochRootHash` (for vouchers)
/// or `v.noticesEpochRootHash` (for notices)
/// @param outputEpochLog2Size Either `EPOCH_VOUCHER_LOG2_SIZE` (for vouchers)
/// or `EPOCH_NOTICE_LOG2_SIZE` (for notices)
/// @param outputHashesLog2Size Either `VOUCHER_METADATA_LOG2_SIZE` (for vouchers)
/// or `NOTICE_METADATA_LOG2_SIZE` (for notices)
function validateEncodedOutput(
function validateOutput(
OutputValidityProof calldata v,
bytes memory encodedOutput,
bytes memory output,
bytes32 epochHash,
bytes32 outputsEpochRootHash,
uint256 outputEpochLog2Size,
Expand Down Expand Up @@ -120,7 +120,7 @@ library LibOutputValidation {
// is contained in it. We can't simply use hashOfOutput because the
// log2size of the leaf is three (8 bytes) not five (32 bytes)
bytes32 merkleRootOfHashOfOutput = MerkleV2.getMerkleRootFromBytes(
abi.encodePacked(keccak256(encodedOutput)),
abi.encodePacked(keccak256(abi.encode(output))),
CanonicalMachine.KECCAK_LOG2_SIZE.uint64OfSize()
);

Expand All @@ -145,21 +145,24 @@ library LibOutputValidation {
/// @notice Make sure the output proof is valid, otherwise revert.
/// @param v The output validity proof
/// @param destination The address that will receive the payload through a message call
/// @param value The amount of Wei to be passed along the call
/// @param payload The payload, which—in the case of Solidity contracts—encodes a function call
/// @param epochHash The hash of the epoch in which the output was generated
function validateVoucher(
OutputValidityProof calldata v,
address destination,
uint256 value,
bytes calldata payload,
bytes32 epochHash
) internal pure {
bytes memory encodedVoucher = OutputEncoding.encodeVoucher(
bytes memory output = OutputEncoding.encodeVoucher(
destination,
value,
payload
);
validateEncodedOutput(
validateOutput(
v,
encodedVoucher,
output,
epochHash,
v.vouchersEpochRootHash,
CanonicalMachine.EPOCH_VOUCHER_LOG2_SIZE.uint64OfSize(),
Expand All @@ -176,10 +179,10 @@ library LibOutputValidation {
bytes calldata notice,
bytes32 epochHash
) internal pure {
bytes memory encodedNotice = OutputEncoding.encodeNotice(notice);
validateEncodedOutput(
bytes memory output = OutputEncoding.encodeNotice(notice);
validateOutput(
v,
encodedNotice,
output,
epochHash,
v.noticesEpochRootHash,
CanonicalMachine.EPOCH_NOTICE_LOG2_SIZE.uint64OfSize(),
Expand Down
15 changes: 13 additions & 2 deletions onchain/rollups/test/foundry/dapp/CartesiDApp.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -128,11 +128,22 @@ contract CartesiDAppTest is TestBase {
address destination,
bytes memory payload
) internal view {
logInput(number, destination, payload);
logVoucher(number, destination, 0, payload);
}

function logVoucher(
uint256 number,
address destination,
uint256 value,
bytes memory payload
) internal view {
bytes memory output = abi.encodePacked(destination, value, payload);
logInput(number, noticeSender, output);
}

function logNotice(uint256 number, bytes memory notice) internal view {
logInput(number, noticeSender, notice);
bytes memory output = abi.encodePacked(notice);
logInput(number, noticeSender, output);
}

// test notices
Expand Down
6 changes: 4 additions & 2 deletions onchain/rollups/test/foundry/dapp/helper/genProofLibraries.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,8 @@ function setupNoticeProof(positionIndex: number): OutputValidityProof {
inputIndexWithinEpoch: Number(v.inputIndexWithinEpoch),
outputIndexWithinInput: Number(v.outputIndexWithinInput),
outputHashesRootHash: v.outputHashesRootHash.data,
vouchersEpochRootHash: v.vouchersEpochRootHash.data,
// vouchersEpochRootHash: v.vouchersEpochRootHash.data,
vouchersEpochRootHash: v.noticesEpochRootHash.data,
noticesEpochRootHash: v.noticesEpochRootHash.data,
machineStateHash: v.machineStateHash.data,
outputHashInOutputHashesSiblings: outputHashInOutputHashesSiblings,
Expand Down Expand Up @@ -145,7 +146,8 @@ for (let i = 0; i < pairs; i++) {
let libraryName = `LibOutputProof${i}`;
let solidityCode = buildSolCodes(
noticeProofs[i],
voucherProofs[i],
// voucherProofs[i],
noticeProofs[i],
libraryName
);
let fileName = `${__dirname}/${libraryName}.sol`;
Expand Down

0 comments on commit d7156f5

Please sign in to comment.