diff --git a/contracts/src/povw/IPovwAccounting.sol b/contracts/src/povw/IPovwAccounting.sol index feed445dd..4e36132f5 100644 --- a/contracts/src/povw/IPovwAccounting.sol +++ b/contracts/src/povw/IPovwAccounting.sol @@ -52,6 +52,11 @@ struct PendingEpoch { } interface IPovwAccounting { + /// @notice Error indicating that a provided signature is invalid. + /// @dev This error is used when the signature provided for a work log update does not + /// correctly authenticate the update. + error InvalidSignature(); + /// @notice Event emitted during the finalization of an epoch. /// @dev This event is emitted in some block after the end of the epoch, when the finalizeEpoch /// function is called. Note that this is no later than the first time that updateWorkLog @@ -94,6 +99,7 @@ interface IPovwAccounting { bytes32 updatedCommit, uint64 updateValue, address valueRecipient, + bytes calldata signature, bytes calldata seal ) external; diff --git a/contracts/src/povw/PovwAccounting.sol b/contracts/src/povw/PovwAccounting.sol index a111d3aa3..9443422f9 100644 --- a/contracts/src/povw/PovwAccounting.sol +++ b/contracts/src/povw/PovwAccounting.sol @@ -14,6 +14,8 @@ import {UUPSUpgradeable} from "@openzeppelin/contracts-upgradeable/proxy/utils/U import {Initializable} from "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; import {IZKC} from "zkc/interfaces/IZKC.sol"; import {IPovwAccounting, WorkLogUpdate, Journal, PendingEpoch} from "./IPovwAccounting.sol"; +import {IERC1271} from "@openzeppelin/contracts/interfaces/IERC1271.sol"; +import {WorkLogUpdateLibrary} from "./WorkLogUpdateLibrary.sol"; bytes32 constant EMPTY_LOG_ROOT = hex"b26927f749929e8484785e36e7ec93d5eeae4b58182f76f1e760263ab67f540c"; @@ -26,6 +28,7 @@ struct PendingEpochStorage { contract PovwAccounting is IPovwAccounting, Initializable, EIP712Upgradeable, OwnableUpgradeable, UUPSUpgradeable { using SafeCast for uint256; + using WorkLogUpdateLibrary for WorkLogUpdate; /// @dev The version of the contract, with respect to upgrades. uint64 public constant VERSION = 1; @@ -104,6 +107,7 @@ contract PovwAccounting is IPovwAccounting, Initializable, EIP712Upgradeable, Ow bytes32 updatedCommit, uint64 updateValue, address valueRecipient, + bytes calldata signature, bytes calldata seal ) public { uint64 currentEpoch = TOKEN.getCurrentEpoch().toUint64(); @@ -123,6 +127,8 @@ contract PovwAccounting is IPovwAccounting, Initializable, EIP712Upgradeable, Ow updateValue: updateValue, valueRecipient: valueRecipient }); + verifySignature(update, workLogId, signature); + Journal memory journal = Journal({update: update, eip712Domain: _domainSeparatorV4()}); VERIFIER.verify(seal, LOG_UPDATER_ID, sha256(abi.encode(journal))); @@ -150,4 +156,41 @@ contract PovwAccounting is IPovwAccounting, Initializable, EIP712Upgradeable, Ow } return commit; } + + function verifySignature(WorkLogUpdate memory update, address signer, bytes calldata signature) public view { + // Check if signer is a contract + uint256 codeSize; + assembly { + codeSize := extcodesize(signer) + } + bytes32 eip712Digest = update.eip712Digest(); + bytes32 hash = _hashTypedDataV4(eip712Digest); + if (codeSize > 0) { + // Signer is a contract, try IERC1271 + try IERC1271(signer).isValidSignature(hash, signature) returns (bytes4 magicValue) { + if (magicValue == 0x1626ba7e) { + return; // valid signature + } + } catch { + revert InvalidSignature(); + } + } else { + // Signer is likely an EOA, try ECDSA + bytes32 r; + bytes32 s; + uint8 v; + if (signature.length == 65) { + assembly { + r := calldataload(add(signature.offset, 0)) + s := calldataload(add(signature.offset, 32)) + v := byte(0, calldataload(add(signature.offset, 64))) + } + address recovered = ecrecover(hash, v, r, s); + if (recovered == signer) { + return; + } + } + revert InvalidSignature(); + } + } } diff --git a/contracts/src/povw/WorkLogUpdateLibrary.sol b/contracts/src/povw/WorkLogUpdateLibrary.sol new file mode 100644 index 000000000..2fc13997d --- /dev/null +++ b/contracts/src/povw/WorkLogUpdateLibrary.sol @@ -0,0 +1,30 @@ +// Copyright 2025 RISC Zero, Inc. +// +// Use of this source code is governed by the Business Source License +// as found in the LICENSE-BSL file. +pragma solidity ^0.8.26; + +import {WorkLogUpdate} from "./IPovwAccounting.sol"; + +library WorkLogUpdateLibrary { + string constant WORK_LOG_UPDATE_TYPE = + "WorkLogUpdate(address workLogId,bytes32 initialCommit,bytes32 updatedCommit,uint64 updateValue,address valueRecipient)"; + + bytes32 constant WORK_LOG_UPDATE_TYPEHASH = keccak256(abi.encodePacked(WORK_LOG_UPDATE_TYPE)); + + /// @notice Computes the EIP-712 digest for the given WorkLogUpdate. + /// @param update The WorkLogUpdate to compute the digest for. + /// @return The EIP-712 digest of the WorkLogUpdate. + function eip712Digest(WorkLogUpdate memory update) internal pure returns (bytes32) { + return keccak256( + abi.encode( + WORK_LOG_UPDATE_TYPEHASH, + update.workLogId, + update.initialCommit, + update.updatedCommit, + update.updateValue, + update.valueRecipient + ) + ); + } +} diff --git a/crates/boundless-cli/src/commands/povw/submit.rs b/crates/boundless-cli/src/commands/povw/submit.rs index 5035476b6..a64abb2c8 100644 --- a/crates/boundless-cli/src/commands/povw/submit.rs +++ b/crates/boundless-cli/src/commands/povw/submit.rs @@ -170,14 +170,14 @@ impl PovwSubmit { // Sign and prove the authorized work log update. tracing::info!("Proving work log update"); - let prove_info = prover + let (prove_info, signature) = prover .prove_update(receipt, work_log_signer) .await .context("Failed to prove authorized log update")?; tracing::info!("Sending work log update transaction"); let tx_result = povw_accounting - .update_work_log(&prove_info.receipt) + .update_work_log(&prove_info.receipt, &signature) .context("Failed to construct update transaction")? .send() .await diff --git a/crates/povw/elfs/boundless-povw-log-updater.bin b/crates/povw/elfs/boundless-povw-log-updater.bin index fbeb13d91..2a18639a4 100644 Binary files a/crates/povw/elfs/boundless-povw-log-updater.bin and b/crates/povw/elfs/boundless-povw-log-updater.bin differ diff --git a/crates/povw/elfs/boundless-povw-log-updater.iid b/crates/povw/elfs/boundless-povw-log-updater.iid index b2c4eab9c..0fe6a147e 100644 Binary files a/crates/povw/elfs/boundless-povw-log-updater.iid and b/crates/povw/elfs/boundless-povw-log-updater.iid differ diff --git a/crates/povw/log-updater/src/main.rs b/crates/povw/log-updater/src/main.rs index 3f8106281..718f0f221 100644 --- a/crates/povw/log-updater/src/main.rs +++ b/crates/povw/log-updater/src/main.rs @@ -26,14 +26,6 @@ fn main() { // Convert the input to the Solidity struct and verify the EIP-712 signature, using the work // log ID as the authenticating party. let update = WorkLogUpdate::from_log_builder_journal(input.update, input.value_recipient); - update - .verify_signature( - update.workLogId, - &input.signature, - input.contract_address, - input.chain_id, - ) - .expect("failed to verify signature on work log update"); // Write the journal, including the EIP-712 domain hash for the verifying contract. let journal = Journal { diff --git a/crates/povw/src/contracts/artifacts/IPovwAccounting.sol b/crates/povw/src/contracts/artifacts/IPovwAccounting.sol index feed445dd..4e36132f5 100644 --- a/crates/povw/src/contracts/artifacts/IPovwAccounting.sol +++ b/crates/povw/src/contracts/artifacts/IPovwAccounting.sol @@ -52,6 +52,11 @@ struct PendingEpoch { } interface IPovwAccounting { + /// @notice Error indicating that a provided signature is invalid. + /// @dev This error is used when the signature provided for a work log update does not + /// correctly authenticate the update. + error InvalidSignature(); + /// @notice Event emitted during the finalization of an epoch. /// @dev This event is emitted in some block after the end of the epoch, when the finalizeEpoch /// function is called. Note that this is no later than the first time that updateWorkLog @@ -94,6 +99,7 @@ interface IPovwAccounting { bytes32 updatedCommit, uint64 updateValue, address valueRecipient, + bytes calldata signature, bytes calldata seal ) external; diff --git a/crates/povw/src/contracts/bytecode.rs b/crates/povw/src/contracts/bytecode.rs index da1b41050..8782f4b2c 100644 --- a/crates/povw/src/contracts/bytecode.rs +++ b/crates/povw/src/contracts/bytecode.rs @@ -1,7 +1,7 @@ // Auto-generated bytecode module, do not edit manually alloy_sol_types::sol! { - #[sol(rpc, bytecode = "6101003461027857601f611a5e38819003918201601f19168301916001600160401b0383118484101761027c578084926060946040528339810103126102785780516001600160a01b0381169190828103610278576020820151916001600160a01b03831690818403610278576040015193306080521561023357156101ee5782156101a95760a05260e05260c0527ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a005460ff8160401c1661019a576002600160401b03196001600160401b03821601610131575b6040516117cd90816102918239608051818181610aef0152610bbe015260a051818181610daa0152610ff0015260c05181818161079b0152611079015260e0518181816103a801528181610931015281816109860152610e710152f35b6001600160401b0319166001600160401b039081177ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00556040519081527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d290602090a15f6100d4565b63f92ee8a960e01b5f5260045ffd5b60405162461bcd60e51b815260206004820152601b60248201527f6c6f675570646174657249642063616e6e6f74206265207a65726f00000000006044820152606490fd5b60405162461bcd60e51b815260206004820152601460248201527f746f6b656e2063616e6e6f74206265207a65726f0000000000000000000000006044820152606490fd5b60405162461bcd60e51b815260206004820152601760248201527f76657269666965722063616e6e6f74206265207a65726f0000000000000000006044820152606490fd5b5f80fd5b634e487b7160e01b5f52604160045260245ffdfe6080806040526004361015610012575f80fd5b5f905f3560e01c90816306fed50614610dd95750806308c84e7014610d945780634f1ef28614610b4357806352d1902d14610adc578063715018a614610a7357806382ae9ef71461096057806382bfefc81461091b57806384b0196e146107f35780638da5cb5b146107be57806399ed580114610783578063ad3cb1cc14610736578063c4d66de8146101a6578063c832627c1461017a578063f2fde38b1461014d578063f552501a146100ec5763ffa1ad74146100ce575f80fd5b346100e957806003193601126100e957602060405160018152f35b80fd5b50346100e957806003193601126100e957602060405161010b816111f9565b828152015260406001546001600160601b038116906001600160401b0360208451610135816111f9565b848152019160601c1681528251918252516020820152f35b50346100e95760203660031901126100e95761017761016a6111e3565b6101726113e7565b6112b9565b80f35b50346100e95760203660031901126100e957602061019e6101996111e3565b611274565b604051908152f35b50346100e95760203660031901126100e9576101c06111e3565b905f80516020611781833981519152549160ff8360401c1615926001600160401b0381168015908161072e575b6001149081610724575b15908161071b575b5061070c5767ffffffffffffffff1981166001175f805160206117818339815191525561023e9190846106e0575b506102366115ce565b6101726115ce565b6102466115ce565b60408051926102558285611214565b600e84526d506f76774163636f756e74696e6760901b602085015281519361027d8386611214565b60018552603160f81b60208601526102936115ce565b61029b6115ce565b8051906001600160401b0382116106cc5781906102c55f805160206116e18339815191525461141a565b601f8111610652575b50602090601f83116001146105d65786926105cb575b50508160011b915f199060031b1c1916175f805160206116e1833981519152555b83516001600160401b0381116105b75761032c5f805160206117218339815191525461141a565b601f8111610548575b50602094601f82116001146104cd5794849582939495926104c2575b50508160011b915f199060031b1c1916175f80516020611721833981519152555b5f805160206117418339815191528390555f805160206117a18339815191528390558151635cbeecf160e11b81526020816004817f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03165afa80156104b557849061047d575b6103e9915061132a565b82516103f4816111f9565b8481526001600160401b038216602090910152600180546001600160a01b03191660609290921b67ffffffffffffffff60601b16919091179055610436575080f35b5f80516020611781833981519152805460ff60401b1916905551600181527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d290602090a180f35b506020813d6020116104ad575b8161049760209383611214565b810103126104a9576103e990516103df565b5f80fd5b3d915061048a565b50505051903d90823e3d90fd5b015190505f80610351565b601f198216955f80516020611721833981519152865280862091865b88811061053057508360019596979810610518575b505050811b015f8051602061172183398151915255610372565b01515f1960f88460031b161c191690555f80806104fe565b919260206001819286850151815501940192016104e9565b5f8051602061172183398151915285527f5f9ce34815f8e11431c7bb75a8e6886a91478f7ffc1dbb0a98dc240fddd76b75601f830160051c810191602084106105ad575b601f0160051c01905b8181106105a25750610335565b858155600101610595565b909150819061058c565b634e487b7160e01b84526041600452602484fd5b015190505f806102e4565b5f805160206116e183398151915287528187209250601f198416875b81811061063a5750908460019594939210610622575b505050811b015f805160206116e183398151915255610305565b01515f1960f88460031b161c191690555f8080610608565b929360206001819287860151815501950193016105f2565b5f805160206116e183398151915287529091507f42ad5d3e1f2e6e70edcf6d991b8a3023d3fca8047a131592f9edb9fd9b89d57d601f840160051c810191602085106106c2575b90601f859493920160051c01905b8181106106b457506102ce565b8781558493506001016106a7565b9091508190610699565b634e487b7160e01b85526041600452602485fd5b68ffffffffffffffffff191668010000000000000001175f80516020611781833981519152555f61022d565b63f92ee8a960e01b8352600483fd5b9050155f6101ff565b303b1591506101f7565b8591506101ed565b50346100e957806003193601126100e9575061077f604051610759604082611214565b60058152640352e302e360dc1b6020820152604051918291602083526020830190611250565b0390f35b50346100e957806003193601126100e95760206040517f00000000000000000000000000000000000000000000000000000000000000008152f35b50346100e957806003193601126100e9575f80516020611701833981519152546040516001600160a01b039091168152602090f35b50346100e957806003193601126100e9575f80516020611741833981519152541580610905575b156108c85761086c9061082b611452565b90610834611521565b90602061087a604051936108488386611214565b8385525f368137604051968796600f60f81b885260e08589015260e0880190611250565b908682036040880152611250565b904660608601523060808601528260a086015284820360c08601528080855193848152019401925b8281106108b157505050500390f35b8351855286955093810193928101926001016108a2565b60405162461bcd60e51b81526020600482015260156024820152741152540dcc4c8e88155b9a5b9a5d1a585b1a5e9959605a1b6044820152606490fd5b505f805160206117a1833981519152541561081a565b50346100e957806003193601126100e9576040517f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03168152602090f35b50346100e957806003193601126100e957604051635cbeecf160e11b81526020816004817f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03165afa8015610a68578290610a34575b6109c7915061132a565b6001600160401b0360015460601c166001600160401b03821611156109ef576101779061135b565b60405162461bcd60e51b815260206004820152601b60248201527f70656e64696e672065706f636820686173206e6f7420656e64656400000000006044820152606490fd5b506020813d602011610a60575b81610a4e60209383611214565b810103126104a9576109c790516109bd565b3d9150610a41565b6040513d84823e3d90fd5b50346100e957806003193601126100e957610a8c6113e7565b5f8051602061170183398151915280546001600160a01b0319811690915581906001600160a01b03167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08280a380f35b50346100e957806003193601126100e9577f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03163003610b345760206040515f805160206117618339815191528152f35b63703e46dd60e11b8152600490fd5b5060403660031901126100e957610b586111e3565b602435906001600160401b038211610d905736602383011215610d905781600401359083610b8583611235565b93610b936040519586611214565b83855260208501933660248284010111610d9057806024602093018637850101526001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016308114908115610d6e575b50610d5f57610bf66113e7565b6040516352d1902d60e01b81526001600160a01b0382169390602081600481885afa869181610d27575b50610c3957634c9c8ce360e01b86526004859052602486fd5b93845f80516020611761833981519152879603610d155750823b15610d03575f8051602061176183398151915280546001600160a01b031916821790558491907fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b8380a2805115610ce857610cdc9382915190845af43d15610ce0573d91610cc083611235565b92610cce6040519485611214565b83523d85602085013e611682565b5080f35b606091611682565b5050505034610cf45780f35b63b398979f60e01b8152600490fd5b634c9c8ce360e01b8552600452602484fd5b632a87526960e21b8652600452602485fd5b9091506020813d602011610d57575b81610d4360209383611214565b81010312610d535751905f610c20565b8680fd5b3d9150610d36565b63703e46dd60e11b8452600484fd5b5f80516020611761833981519152546001600160a01b0316141590505f610be9565b8280fd5b50346100e957806003193601126100e9576040517f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03168152602090f35b9050346104a95760a03660031901126104a957610df46111e3565b90602435906044356001600160401b0381168091036104a9576064356001600160a01b038116908190036104a957608435946001600160401b0386116104a957366023870112156104a9578560040135936001600160401b0385116104a95736602486890101116104a957635cbeecf160e11b81526020816004817f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03165afa8015611181575f906111af575b610eb2915061132a565b946001600160401b0360015460601c16956001600160401b0381168097106111a0575b50610edf82611274565b916040519060a082018281106001600160401b0382111761118c5760405260018060a01b03169788825260208201938452604082019683885260205f6060850194898652608081019889528a610f336115f9565b610f3b611650565b60405190868201927f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f8452604083015260608201524660808201523060a082015260a08152610f8b60c082611214565b51902084604051610f9b816111f9565b848152019081526040805193516001600160a01b039081168786019081528c51928601929092529251606085015297516001600160401b031660808401528a51821660a08401525160c08084019190915282527f000000000000000000000000000000000000000000000000000000000000000016959061101d60e082611214565b604051918291518091835e8101838152039060025afa15611181575f5192803b156104a957602460845f94846040519788968795869463ab750e7560e01b865260606004870152826064870152018585013782820184018890527f000000000000000000000000000000000000000000000000000000000000000060248401526044830152601f01601f191681010301915afa80156111815761116c575b50868852876020526040882055600154836001600160601b038216016001600160601b038111611158576001600160601b03166bffffffffffffffffffffffff19919091161760015551925190516040805195865260208601949094529284015260608301526001600160a01b031660808201527fab9156dc7ba80367c8ed33a2855e4272488513cf903a3e85a684003e2df1ec489060a090a280f35b634e487b7160e01b89526011600452602489fd5b6111799198505f90611214565b5f965f6110bb565b6040513d5f823e3d90fd5b634e487b7160e01b5f52604160045260245ffd5b6111a99061135b565b5f610ed5565b506020813d6020116111db575b816111c960209383611214565b810103126104a957610eb29051610ea8565b3d91506111bc565b600435906001600160a01b03821682036104a957565b604081019081106001600160401b0382111761118c57604052565b90601f801991011681019081106001600160401b0382111761118c57604052565b6001600160401b03811161118c57601f01601f191660200190565b805180835260209291819084018484015e5f828201840152601f01601f1916010190565b6001600160a01b03165f9081526020819052604090205480156112945790565b507fb26927f749929e8484785e36e7ec93d5eeae4b58182f76f1e760263ab67f540c90565b6001600160a01b03168015611317575f8051602061170183398151915280546001600160a01b0319811683179091556001600160a01b03167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e05f80a3565b631e4fbdf760e01b5f525f60045260245ffd5b6001600160401b038111611344576001600160401b031690565b6306dfcc6560e41b5f52604060045260245260445ffd5b6001547f6debf9c0b8bd7ecda40db89a2641f61251d80a576b5c5e5f06de7f1c2a65850a60206001600160401b03604051936001600160601b038116855260601c1692a26040516113ab816111f9565b5f81526001600160401b038216602090910152600180546001600160a01b03191660609290921b67ffffffffffffffff60601b16919091179055565b5f80516020611701833981519152546001600160a01b0316330361140757565b63118cdaa760e01b5f523360045260245ffd5b90600182811c92168015611448575b602083101461143457565b634e487b7160e01b5f52602260045260245ffd5b91607f1691611429565b604051905f825f805160206116e183398151915254916114718361141a565b80835292600181169081156115025750600114611497575b61149592500383611214565b565b505f805160206116e18339815191525f90815290917f42ad5d3e1f2e6e70edcf6d991b8a3023d3fca8047a131592f9edb9fd9b89d57d5b8183106114e657505090602061149592820101611489565b60209193508060019154838589010152019101909184926114ce565b6020925061149594915060ff191682840152151560051b820101611489565b604051905f825f8051602061172183398151915254916115408361141a565b808352926001811690811561150257506001146115635761149592500383611214565b505f805160206117218339815191525f90815290917f5f9ce34815f8e11431c7bb75a8e6886a91478f7ffc1dbb0a98dc240fddd76b755b8183106115b257505090602061149592820101611489565b602091935080600191548385890101520191019091849261159a565b60ff5f805160206117818339815191525460401c16156115ea57565b631afcd79f60e31b5f5260045ffd5b611601611452565b8051908115611611576020012090565b50505f8051602061174183398151915254801561162b5790565b507fc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a47090565b611658611521565b8051908115611668576020012090565b50505f805160206117a183398151915254801561162b5790565b906116a6575080511561169757602081519101fd5b63d6bda27560e01b5f5260045ffd5b815115806116d7575b6116b7575090565b639996b31560e01b5f9081526001600160a01b0391909116600452602490fd5b50803b156116af56fea16a46d94261c7517cc8ff89f61c0ce93598e3c849801011dee649a6a557d1029016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c199300a16a46d94261c7517cc8ff89f61c0ce93598e3c849801011dee649a6a557d103a16a46d94261c7517cc8ff89f61c0ce93598e3c849801011dee649a6a557d100360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbcf0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00a16a46d94261c7517cc8ff89f61c0ce93598e3c849801011dee649a6a557d101a164736f6c634300081a000a")] + #[sol(rpc, bytecode = "6101003461027857601f611ddd38819003918201601f19168301916001600160401b0383118484101761027c578084926060946040528339810103126102785780516001600160a01b0381169190828103610278576020820151916001600160a01b03831690818403610278576040015193306080521561023357156101ee5782156101a95760a05260e05260c0527ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a005460ff8160401c1661019a576002600160401b03196001600160401b03821601610131575b604051611b4c90816102918239608051818181610f350152611004015260a051818181610a4701526111cb015260c0518181816108600152610ac7015260e05181818161046d0152818161092601528181610d840152610dd80152f35b6001600160401b0319166001600160401b039081177ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00556040519081527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d290602090a15f6100d4565b63f92ee8a960e01b5f5260045ffd5b60405162461bcd60e51b815260206004820152601b60248201527f6c6f675570646174657249642063616e6e6f74206265207a65726f00000000006044820152606490fd5b60405162461bcd60e51b815260206004820152601460248201527f746f6b656e2063616e6e6f74206265207a65726f0000000000000000000000006044820152606490fd5b60405162461bcd60e51b815260206004820152601760248201527f76657269666965722063616e6e6f74206265207a65726f0000000000000000006044820152606490fd5b5f80fd5b634e487b7160e01b5f52604160045260245ffdfe6080806040526004361015610012575f80fd5b5f905f3560e01c90816308c84e70146111b9575080634f1ef28614610f8957806352d1902d14610f23578063715018a614610ebc57806382ae9ef714610db357806382bfefc814610d6f57806384b0196e14610c4b5780638da5cb5b14610c17578063913062371461088357806399ed580114610848578063ad3cb1cc146107fb578063c4d66de81461026b578063c6e3e034146101b1578063c832627c14610185578063f2fde38b14610158578063f552501a146100f75763ffa1ad74146100d9575f80fd5b346100f457806003193601126100f457602060405160018152f35b80fd5b50346100f457806003193601126100f45760206040516101168161123f565b828152015260406001546001600160601b038116906001600160401b03602084516101408161123f565b848152019160601c1681528251918252516020820152f35b50346100f45760203660031901126100f4576101826101756111fa565b61017d611644565b6115d3565b80f35b50346100f45760203660031901126100f45760206101a96101a46111fa565b61158e565b604051908152f35b50346100f457366003190160e081126102675760a0136100f4576040516101d781611210565b6101df6111fa565b8152602435602082015260443560408201526064356001600160401b03811681036102635760608201526084356001600160a01b038116810361026357608082015260a4356001600160a01b03811681036102635760c435916001600160401b03831161025f576102576101829336906004016112ba565b929091611307565b8380fd5b8280fd5b5080fd5b50346100f45760203660031901126100f4576102856111fa565b905f80516020611b00833981519152549160ff8360401c1615926001600160401b038116801590816107f3575b60011490816107e9575b1590816107e0575b506107d15767ffffffffffffffff1981166001175f80516020611b00833981519152556103039190846107a5575b506102fb61194d565b61017d61194d565b61030b61194d565b604080519261031a828561125a565b600e84526d506f76774163636f756e74696e6760901b6020850152815193610342838661125a565b60018552603160f81b602086015261035861194d565b61036061194d565b8051906001600160401b03821161079157819061038a5f80516020611a6083398151915254611734565b601f8111610717575b50602090601f831160011461069b578692610690575b50508160011b915f199060031b1c1916175f80516020611a60833981519152555b83516001600160401b03811161067c576103f15f80516020611aa083398151915254611734565b601f811161060d575b50602094601f8211600114610592579484958293949592610587575b50508160011b915f199060031b1c1916175f80516020611aa0833981519152555b5f80516020611ac08339815191528390555f80516020611b208339815191528390558151635cbeecf160e11b81526020816004817f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03165afa801561057a578490610542575b6104ae9150611677565b82516104b98161123f565b8481526001600160401b038216602090910152600180546001600160a01b03191660609290921b67ffffffffffffffff60601b169190911790556104fb575080f35b5f80516020611b00833981519152805460ff60401b1916905551600181527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d290602090a180f35b506020813d602011610572575b8161055c6020938361125a565b8101031261056e576104ae90516104a4565b5f80fd5b3d915061054f565b50505051903d90823e3d90fd5b015190505f80610416565b601f198216955f80516020611aa0833981519152865280862091865b8881106105f5575083600195969798106105dd575b505050811b015f80516020611aa083398151915255610437565b01515f1960f88460031b161c191690555f80806105c3565b919260206001819286850151815501940192016105ae565b5f80516020611aa083398151915285527f5f9ce34815f8e11431c7bb75a8e6886a91478f7ffc1dbb0a98dc240fddd76b75601f830160051c81019160208410610672575b601f0160051c01905b81811061066757506103fa565b85815560010161065a565b9091508190610651565b634e487b7160e01b84526041600452602484fd5b015190505f806103a9565b5f80516020611a6083398151915287528187209250601f198416875b8181106106ff57509084600195949392106106e7575b505050811b015f80516020611a60833981519152556103ca565b01515f1960f88460031b161c191690555f80806106cd565b929360206001819287860151815501950193016106b7565b5f80516020611a6083398151915287529091507f42ad5d3e1f2e6e70edcf6d991b8a3023d3fca8047a131592f9edb9fd9b89d57d601f840160051c81019160208510610787575b90601f859493920160051c01905b8181106107795750610393565b87815584935060010161076c565b909150819061075e565b634e487b7160e01b85526041600452602485fd5b68ffffffffffffffffff191668010000000000000001175f80516020611b00833981519152555f6102f2565b63f92ee8a960e01b8352600483fd5b9050155f6102c4565b303b1591506102bc565b8591506102b2565b50346100f457806003193601126100f4575061084460405161081e60408261125a565b60058152640352e302e360dc1b6020820152604051918291602083526020830190611296565b0390f35b50346100f457806003193601126100f45760206040517f00000000000000000000000000000000000000000000000000000000000000008152f35b503461056e5760c036600319011261056e5761089d6111fa565b60243590604435916001600160401b03831680930361056e576064356001600160a01b038116939084900361056e576084356001600160401b03811161056e576108eb9036906004016112ba565b60a4959195356001600160401b03811161056e5761090d9036906004016112ba565b604051635cbeecf160e11b8152909690916020836004817f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03165afa8015610bc55787935f91610bdf575b5061096990611677565b976001600160401b0360015460601c16986001600160401b038116809a10610bd0575b5060205f6109998561158e565b966109dc604051916109aa83611210565b6001600160a01b0388168084528584019a8b52604084019d8e52606084018d8152608085019c8d52909f909884611307565b8a6109e56118e6565b846040516109f28161123f565b848152019081526040805193516001600160a01b039081168786019081528c51928601929092529251606085015297516001600160401b031660808401528a51821660a08401525160c08084019190915282527f0000000000000000000000000000000000000000000000000000000000000000169590610a7460e08261125a565b604051918291518091835e8101838152039060025afa15610bc5575f5192803b1561056e575f92610ac4926040519586948593849363ab750e7560e01b85526060600486015260648501916112e7565b907f00000000000000000000000000000000000000000000000000000000000000006024840152604483015203915afa8015610bc557610bb0575b50868852876020526040882055600154836001600160601b038216016001600160601b038111610b9c576001600160601b03166bffffffffffffffffffffffff19919091161760015551925190516040805195865260208601949094529284015260608301526001600160a01b031660808201527fab9156dc7ba80367c8ed33a2855e4272488513cf903a3e85a684003e2df1ec489060a090a280f35b634e487b7160e01b89526011600452602489fd5b610bbd9198505f9061125a565b5f965f610aff565b6040513d5f823e3d90fd5b610bd9906116a8565b5f61098c565b9350506020833d602011610c0f575b81610bfb6020938361125a565b8101031261056e576109698793519061095f565b3d9150610bee565b3461056e575f36600319011261056e575f80516020611a80833981519152546040516001600160a01b039091168152602090f35b3461056e575f36600319011261056e575f80516020611ac0833981519152541580610d59575b15610d1c57610cc0610c8161176c565b610c89611839565b6020610cce60405192610c9c838561125a565b5f84525f368137604051958695600f60f81b875260e08588015260e0870190611296565b908582036040870152611296565b4660608501523060808501525f60a085015283810360c08501528180845192838152019301915f5b828110610d0557505050500390f35b835185528695509381019392810192600101610cf6565b60405162461bcd60e51b81526020600482015260156024820152741152540dcc4c8e88155b9a5b9a5d1a585b1a5e9959605a1b6044820152606490fd5b505f80516020611b208339815191525415610c71565b3461056e575f36600319011261056e576040517f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03168152602090f35b3461056e575f36600319011261056e57604051635cbeecf160e11b81526020816004817f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03165afa8015610bc5575f90610e88575b610e199150611677565b6001600160401b0360015460601c166001600160401b0382161115610e4357610e41906116a8565b005b60405162461bcd60e51b815260206004820152601b60248201527f70656e64696e672065706f636820686173206e6f7420656e64656400000000006044820152606490fd5b506020813d602011610eb4575b81610ea26020938361125a565b8101031261056e57610e199051610e0f565b3d9150610e95565b3461056e575f36600319011261056e57610ed4611644565b5f80516020611a8083398151915280546001600160a01b031981169091555f906001600160a01b03167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08280a3005b3461056e575f36600319011261056e577f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03163003610f7a5760206040515f80516020611ae08339815191528152f35b63703e46dd60e11b5f5260045ffd5b604036600319011261056e57610f9d6111fa565b602435906001600160401b03821161056e573660238301121561056e57816004013590610fc98261127b565b91610fd7604051938461125a565b8083526020830193366024838301011161056e57815f926024602093018737840101526001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016308114908115611197575b50610f7a5761103c611644565b6040516352d1902d60e01b81526001600160a01b0382169390602081600481885afa5f9181611163575b5061107e5784634c9c8ce360e01b5f5260045260245ffd5b805f80516020611ae08339815191528692036111515750823b1561113f575f80516020611ae083398151915280546001600160a01b031916821790557fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b5f80a2825115611126575f8091610e41945190845af43d1561111e573d916111028361127b565b92611110604051948561125a565b83523d5f602085013e611a01565b606091611a01565b5050503461113057005b63b398979f60e01b5f5260045ffd5b634c9c8ce360e01b5f5260045260245ffd5b632a87526960e21b5f5260045260245ffd5b9091506020813d60201161118f575b8161117f6020938361125a565b8101031261056e57519086611066565b3d9150611172565b5f80516020611ae0833981519152546001600160a01b0316141590508461102f565b3461056e575f36600319011261056e577f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03168152602090f35b600435906001600160a01b038216820361056e57565b60a081019081106001600160401b0382111761122b57604052565b634e487b7160e01b5f52604160045260245ffd5b604081019081106001600160401b0382111761122b57604052565b90601f801991011681019081106001600160401b0382111761122b57604052565b6001600160401b03811161122b57601f01601f191660200190565b805180835260209291819084018484015e5f828201840152601f01601f1916010190565b9181601f8401121561056e578235916001600160401b03831161056e576020838186019501011161056e57565b908060209392818452848401375f828201840152601f01601f1916010190565b9290926042843b9160405161131d60a08261125a565b60768152756464726573732076616c7565526563697069656e742960501b608060208301927f576f726b4c6f67557064617465286164647265737320776f726b4c6f6749642c84527f6279746573333220696e697469616c436f6d6d69742c6279746573333220757060408201527f6461746564436f6d6d69742c75696e7436342075706461746556616c75652c616060820152015260405160766020820192835e5f6096820152607681526113d460968261125a565b5190209060018060a01b038151169060208101519060408101516001600160401b0360608301511691608060018060a01b039101511692604051946020860196875260408601526060850152608084015260a083015260c082015260c0815261143e60e08261125a565b5190206114496118e6565b906040519161190160f01b835260028301526022820152209015155f1461152657926020929161149d9460405195869485938493630b135d3f60e11b855260048501526040602485015260448401916112e7565b03916001600160a01b03165afa5f91816114e1575b506114c657638baa579f60e01b5f5260045ffd5b6001600160e01b031916630b135d3f60e11b146114df57565b565b9091506020813d60201161151e575b816114fd6020938361125a565b8101031261056e57516001600160e01b03198116810361056e57905f6114b2565b3d91506114f0565b9060411461153e575b638baa579f60e01b5f5260045ffd5b60805f918360209435604086830135920135851a604051938452868401526040830152606082015282805260015afa15610bc5575f516001600160a01b039081169116146114df5780808061152f565b6001600160a01b03165f9081526020819052604090205480156115ae5790565b507fb26927f749929e8484785e36e7ec93d5eeae4b58182f76f1e760263ab67f540c90565b6001600160a01b03168015611631575f80516020611a8083398151915280546001600160a01b0319811683179091556001600160a01b03167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e05f80a3565b631e4fbdf760e01b5f525f60045260245ffd5b5f80516020611a80833981519152546001600160a01b0316330361166457565b63118cdaa760e01b5f523360045260245ffd5b6001600160401b038111611691576001600160401b031690565b6306dfcc6560e41b5f52604060045260245260445ffd5b6001547f6debf9c0b8bd7ecda40db89a2641f61251d80a576b5c5e5f06de7f1c2a65850a60206001600160401b03604051936001600160601b038116855260601c1692a26040516116f88161123f565b5f81526001600160401b038216602090910152600180546001600160a01b03191660609290921b67ffffffffffffffff60601b16919091179055565b90600182811c92168015611762575b602083101461174e57565b634e487b7160e01b5f52602260045260245ffd5b91607f1691611743565b604051905f825f80516020611a60833981519152549161178b83611734565b808352926001811690811561181a57506001146117af575b6114df9250038361125a565b505f80516020611a608339815191525f90815290917f42ad5d3e1f2e6e70edcf6d991b8a3023d3fca8047a131592f9edb9fd9b89d57d5b8183106117fe5750509060206114df928201016117a3565b60209193508060019154838589010152019101909184926117e6565b602092506114df94915060ff191682840152151560051b8201016117a3565b604051905f825f80516020611aa0833981519152549161185883611734565b808352926001811690811561181a575060011461187b576114df9250038361125a565b505f80516020611aa08339815191525f90815290917f5f9ce34815f8e11431c7bb75a8e6886a91478f7ffc1dbb0a98dc240fddd76b755b8183106118ca5750509060206114df928201016117a3565b60209193508060019154838589010152019101909184926118b2565b6118ee611978565b6118f66119cf565b6040519060208201927f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f8452604083015260608201524660808201523060a082015260a0815261194760c08261125a565b51902090565b60ff5f80516020611b008339815191525460401c161561196957565b631afcd79f60e31b5f5260045ffd5b61198061176c565b8051908115611990576020012090565b50505f80516020611ac08339815191525480156119aa5790565b507fc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a47090565b6119d7611839565b80519081156119e7576020012090565b50505f80516020611b208339815191525480156119aa5790565b90611a255750805115611a1657602081519101fd5b63d6bda27560e01b5f5260045ffd5b81511580611a56575b611a36575090565b639996b31560e01b5f9081526001600160a01b0391909116600452602490fd5b50803b15611a2e56fea16a46d94261c7517cc8ff89f61c0ce93598e3c849801011dee649a6a557d1029016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c199300a16a46d94261c7517cc8ff89f61c0ce93598e3c849801011dee649a6a557d103a16a46d94261c7517cc8ff89f61c0ce93598e3c849801011dee649a6a557d100360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbcf0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00a16a46d94261c7517cc8ff89f61c0ce93598e3c849801011dee649a6a557d101a164736f6c634300081a000a")] contract PovwAccounting { constructor(address verifier, address zkc, bytes32 logUpdaterId) {} function initialize(address initialOwner) {} diff --git a/crates/povw/src/log_updater.rs b/crates/povw/src/log_updater.rs index 07ea5f305..62e5ab0d1 100644 --- a/crates/povw/src/log_updater.rs +++ b/crates/povw/src/log_updater.rs @@ -111,13 +111,6 @@ pub struct Input { #[builder(setter(into))] pub value_recipient: Address, - /// EIP-712 ECDSA signature using the private key associated with the work log ID. - /// - /// This signature is verified by the log updater guest to authorize the update. Authorization - /// is required to avoid third-parties posting conflicting updates to any given work log. - #[builder(setter(into))] - pub signature: Vec, - /// Address of the PoVW accounting contract, used to form the EIP-712 domain. #[borsh( deserialize_with = "borsh_deserialize_address", @@ -130,42 +123,6 @@ pub struct Input { pub chain_id: u64, } -impl InputBuilder { - #[cfg(feature = "signer")] - pub async fn sign_and_build( - &mut self, - signer: &impl alloy_signer::Signer, - ) -> anyhow::Result { - use anyhow::ensure; - use derive_builder::UninitializedFieldError; - - ensure!(self.signature.is_none(), "Cannot sign input, input already has a signature"); - - let update = self.update.clone().ok_or(UninitializedFieldError::new("update"))?; - let contract_address = - self.contract_address.ok_or(UninitializedFieldError::new("contract_address"))?; - let chain_id = self.chain_id.ok_or(UninitializedFieldError::new("chain_id"))?; - ensure!( - signer.address() == Address::from(update.work_log_id), - "Signer does not match work log ID: signer: {}, log: {:x}", - signer.address(), - update.work_log_id - ); - - // Get the value recipient or set it to be equal to the log ID. - let value_recipient = *self.value_recipient.get_or_insert(signer.address()); - - self.signature = WorkLogUpdate::from_log_builder_journal(update.clone(), value_recipient) - .sign(signer, contract_address, chain_id) - .await? - .as_bytes() - .to_vec() - .into(); - - self.build().map_err(Into::into) - } -} - impl Input { /// Create an [InputBuilder] to construct an [Input]. pub fn builder() -> InputBuilder { @@ -181,6 +138,28 @@ impl Input { pub fn decode(buffer: impl AsRef<[u8]>) -> anyhow::Result { borsh::from_slice(buffer.as_ref()).map_err(Into::into) } + + #[cfg(feature = "signer")] + pub async fn sign(&self, signer: &impl alloy_signer::Signer) -> anyhow::Result> { + use anyhow::ensure; + + let update = self.update.clone(); + ensure!( + signer.address() == Address::from(update.work_log_id), + "Signer does not match work log ID: signer: {}, log: {:x}", + signer.address(), + update.work_log_id + ); + + let signature = + WorkLogUpdate::from_log_builder_journal(update.clone(), self.value_recipient) + .sign(signer, self.contract_address, self.chain_id) + .await? + .as_bytes() + .to_vec(); + + Ok(signature) + } } fn borsh_deserialize_address( @@ -217,6 +196,7 @@ mod host { pub fn update_work_log( &self, receipt: &Receipt, + signature: impl AsRef<[u8]>, ) -> anyhow::Result>> { let journal = Journal::abi_decode(&receipt.journal.bytes) .context("Failed to decode journal from Log Updater receipt")?; @@ -228,6 +208,7 @@ mod host { journal.update.updatedCommit, journal.update.updateValue, journal.update.valueRecipient, + signature.as_ref().to_vec().into(), seal.into(), )) } @@ -330,7 +311,7 @@ pub mod prover { &self, log_builder_receipt: Receipt, signer: &impl alloy_signer::Signer, - ) -> anyhow::Result { + ) -> anyhow::Result<(ProveInfo, Vec)> { // Decode the LogBuilderJournal from the receipt let log_builder_journal = LogBuilderJournal::decode(&log_builder_receipt.journal.bytes) .context("failed to deserialize LogBuilderJournal from receipt")?; @@ -341,9 +322,10 @@ pub mod prover { .value_recipient(self.value_recipient.unwrap_or(signer.address())) .contract_address(self.contract_address) .chain_id(self.chain_id) - .sign_and_build(signer) - .await - .context("failed to create signed input")?; + .build() + .context("failed to create input")?; + + let signature = input.sign(signer).await?; // Build the executor environment with the log builder receipt as an assumption let env = ExecutorEnv::builder() @@ -368,7 +350,7 @@ pub mod prover { .context("failed to prove log update") })?; - Ok(prove_info) + Ok((prove_info, signature)) } } } diff --git a/crates/povw/tests/log-updater.rs b/crates/povw/tests/log-updater.rs index 5a449dccb..f89eb005f 100644 --- a/crates/povw/tests/log-updater.rs +++ b/crates/povw/tests/log-updater.rs @@ -39,10 +39,10 @@ async fn basic() -> anyhow::Result<()> { let input = Input::builder() .update(update.clone()) + .value_recipient(signer.address()) .contract_address(contract_address) .chain_id(chain_id) - .sign_and_build(&signer) - .await?; + .build()?; let journal = execute_log_updater_guest(&input)?; assert_eq!(journal.update.workLogId, signer.address()); @@ -76,8 +76,7 @@ async fn reject_zero_value_recipient() -> anyhow::Result<()> { .value_recipient(Address::ZERO) .contract_address(contract_address) .chain_id(chain_id) - .sign_and_build(&signer) - .await?; + .build()?; let err = execute_log_updater_guest(&input).unwrap_err(); println!("execute_log_updater_guest failed with: {err}"); @@ -88,6 +87,7 @@ async fn reject_zero_value_recipient() -> anyhow::Result<()> { #[tokio::test] async fn reject_wrong_signer() -> anyhow::Result<()> { + let ctx = test_ctx().await?; let signer = PrivateKeySigner::random(); let chain_id = 31337; let contract_address = address!("0x0000000000000000000000000000000000000f00"); @@ -103,51 +103,40 @@ async fn reject_wrong_signer() -> anyhow::Result<()> { let wrong_signer = PrivateKeySigner::random(); let signature = WorkLogUpdate::from_log_builder_journal(update.clone(), signer.address()) .sign(&wrong_signer, contract_address, chain_id) - .await?; + .await? + .as_bytes() + .to_vec(); let input = Input::builder() .update(update.clone()) .value_recipient(signer.address()) - .signature(signature.as_bytes().to_vec()) .contract_address(contract_address) .chain_id(chain_id) .build()?; - let err = execute_log_updater_guest(&input).unwrap_err(); - println!("execute_log_updater_guest failed with: {err}"); - assert!(err.to_string().contains("recovered signer does not match expected")); - - Ok(()) -} - -#[tokio::test] -async fn reject_wrong_chain_id() -> anyhow::Result<()> { - let signer = PrivateKeySigner::random(); - let chain_id = 31337; - let wrong_chain_id = 1; // Different chain ID - let contract_address = address!("0x0000000000000000000000000000000000000f00"); + let journal = execute_log_updater_guest(&input)?; - let update = LogBuilderJournal::builder() - .self_image_id(RISC0_POVW_LOG_BUILDER_ID) - .initial_commit(Digest::new(rand::random())) - .updated_commit(Digest::new(rand::random())) - .update_value(5) - .work_log_id(signer.address()) - .build()?; + // Guest execution succeeds, but contract should reject + let fake_receipt = risc0_zkvm::FakeReceipt::new(risc0_zkvm::ReceiptClaim::ok( + BOUNDLESS_POVW_LOG_UPDATER_ID, + journal.abi_encode(), + )); + let receipt: risc0_zkvm::Receipt = fake_receipt.try_into()?; - let signature = WorkLogUpdate::from_log_builder_journal(update.clone(), signer.address()) - .sign(&signer, contract_address, wrong_chain_id) - .await?; + let result = ctx + .povw_accounting + .updateWorkLog( + journal.update.workLogId, + journal.update.updatedCommit, + journal.update.updateValue, + journal.update.valueRecipient, + signature.into(), + encode_seal(&receipt)?.into(), + ) + .send() + .await; - let input = Input::builder() - .update(update.clone()) - .value_recipient(signer.address()) - .signature(signature.as_bytes().to_vec()) - .contract_address(contract_address) - .chain_id(chain_id) // Correct chain ID in input, but signature was for wrong one - .build()?; - let err = execute_log_updater_guest(&input).unwrap_err(); - println!("execute_log_updater_guest failed with: {err}"); - assert!(err.to_string().contains("recovered signer does not match expected")); + assert!(result.is_err(), "Contract should reject wrong signature"); + println!("Contract correctly rejected wrong signature: {:?}", result.unwrap_err()); Ok(()) } @@ -169,12 +158,13 @@ async fn reject_wrong_chain_id_contract() -> anyhow::Result<()> { // Sign with wrong chain ID but execute with that same wrong chain ID let signature = WorkLogUpdate::from_log_builder_journal(update.clone(), signer.address()) .sign(&signer, *ctx.povw_accounting.address(), wrong_chain_id) - .await?; + .await? + .as_bytes() + .to_vec(); let input = Input::builder() .update(update.clone()) .value_recipient(signer.address()) - .signature(signature.as_bytes().to_vec()) .contract_address(*ctx.povw_accounting.address()) .chain_id(wrong_chain_id) // Consistent but wrong chain ID .build()?; @@ -194,6 +184,7 @@ async fn reject_wrong_chain_id_contract() -> anyhow::Result<()> { journal.update.updatedCommit, journal.update.updateValue, journal.update.valueRecipient, + signature.into(), encode_seal(&receipt)?.into(), ) .send() @@ -207,6 +198,7 @@ async fn reject_wrong_chain_id_contract() -> anyhow::Result<()> { #[tokio::test] async fn reject_wrong_contract_address() -> anyhow::Result<()> { + let ctx = test_ctx().await?; let signer = PrivateKeySigner::random(); let chain_id = 31337; let contract_address = address!("0x0000000000000000000000000000000000000f00"); @@ -222,18 +214,40 @@ async fn reject_wrong_contract_address() -> anyhow::Result<()> { let signature = WorkLogUpdate::from_log_builder_journal(update.clone(), signer.address()) .sign(&signer, wrong_contract_address, chain_id) - .await?; + .await? + .as_bytes() + .to_vec(); let input = Input::builder() .update(update.clone()) .value_recipient(signer.address()) - .signature(signature.as_bytes().to_vec()) .contract_address(contract_address) // Correct contract address in input, but signature was for wrong one .chain_id(chain_id) .build()?; - let err = execute_log_updater_guest(&input).unwrap_err(); - println!("execute_log_updater_guest failed with: {err}"); - assert!(err.to_string().contains("recovered signer does not match expected")); + let journal = execute_log_updater_guest(&input)?; + + // Guest execution succeeds, but contract should reject + let fake_receipt = risc0_zkvm::FakeReceipt::new(risc0_zkvm::ReceiptClaim::ok( + BOUNDLESS_POVW_LOG_UPDATER_ID, + journal.abi_encode(), + )); + let receipt: risc0_zkvm::Receipt = fake_receipt.try_into()?; + + let result = ctx + .povw_accounting + .updateWorkLog( + journal.update.workLogId, + journal.update.updatedCommit, + journal.update.updateValue, + journal.update.valueRecipient, + signature.into(), + encode_seal(&receipt)?.into(), + ) + .send() + .await; + + assert!(result.is_err(), "Contract should reject wrong contract address"); + println!("Contract correctly rejected wrong contract address: {:?}", result.unwrap_err()); Ok(()) } @@ -270,8 +284,8 @@ async fn reject_invalid_initial_commit() -> anyhow::Result<()> { let err = result.unwrap_err(); println!("Contract correctly rejected invalid initial commit: {err}"); - // Check for verification failure selector 0x439cc0cd - assert!(err.to_string().contains("0x439cc0cd")); + // Check for verification failure selector 0x8baa579f + assert!(err.to_string().contains("0x8baa579f")); Ok(()) } @@ -298,14 +312,15 @@ async fn reject_duplicate_update() -> anyhow::Result<()> { let err = result.unwrap_err(); println!("Contract correctly rejected duplicate update: {err}"); - // Check for verification failure selector 0x439cc0cd - assert!(err.to_string().contains("0x439cc0cd")); + // Check for verification failure selector 0x8baa579f + assert!(err.to_string().contains("0x8baa579f")); Ok(()) } #[tokio::test] async fn reject_invalid_work_log_id() -> anyhow::Result<()> { + let ctx = test_ctx().await?; let signer = PrivateKeySigner::random(); let chain_id = 31337; let contract_address = address!("0x0000000000000000000000000000000000000f00"); @@ -323,18 +338,40 @@ async fn reject_invalid_work_log_id() -> anyhow::Result<()> { signer.address(), ) .sign(&signer, contract_address, chain_id) - .await?; + .await? + .as_bytes() + .to_vec(); let input = boundless_povw::log_updater::Input::builder() .update(update.clone()) .value_recipient(signer.address()) - .signature(signature.as_bytes().to_vec()) .contract_address(contract_address) .chain_id(chain_id) .build()?; - let err = execute_log_updater_guest(&input).unwrap_err(); - println!("execute_log_updater_guest failed with: {err}"); - assert!(err.to_string().contains("recovered signer does not match expected")); + let journal = execute_log_updater_guest(&input)?; + + // Guest execution succeeds, but contract should reject + let fake_receipt = risc0_zkvm::FakeReceipt::new(risc0_zkvm::ReceiptClaim::ok( + BOUNDLESS_POVW_LOG_UPDATER_ID, + journal.abi_encode(), + )); + let receipt: risc0_zkvm::Receipt = fake_receipt.try_into()?; + + let result = ctx + .povw_accounting + .updateWorkLog( + journal.update.workLogId, + journal.update.updatedCommit, + journal.update.updateValue, + journal.update.valueRecipient, + signature.into(), + encode_seal(&receipt)?.into(), + ) + .send() + .await; + + assert!(result.is_err(), "Contract should reject invalid work log ID"); + println!("Contract correctly rejected invalid work log ID: {:?}", result.unwrap_err()); Ok(()) } @@ -355,10 +392,11 @@ async fn reject_wrong_image_id() -> anyhow::Result<()> { // Execute guest to get valid journal let input = boundless_povw::log_updater::Input::builder() .update(update.clone()) + .value_recipient(signer.address()) .contract_address(*ctx.povw_accounting.address()) .chain_id(ctx.chain_id) - .sign_and_build(&signer) - .await?; + .build()?; + let signature = input.sign(&signer).await?; let journal = execute_log_updater_guest(&input)?; @@ -378,6 +416,7 @@ async fn reject_wrong_image_id() -> anyhow::Result<()> { journal.update.updatedCommit, journal.update.updateValue, journal.update.valueRecipient, + signature.into(), encode_seal(&receipt)?.into(), ) .send() @@ -628,10 +667,11 @@ async fn measure_log_update_gas() -> anyhow::Result<()> { async move |update: LogBuilderJournal| -> anyhow::Result { let input = Input::builder() .update(update.clone()) + .value_recipient(signer.address()) .contract_address(*ctx.povw_accounting.address()) .chain_id(ctx.chain_id) - .sign_and_build(&signer) - .await?; + .build()?; + let signature = input.sign(&signer).await?; let journal = execute_log_updater_guest(&input)?; let fake_receipt: Receipt = FakeReceipt::new(ReceiptClaim::ok( BOUNDLESS_POVW_LOG_UPDATER_ID, @@ -647,6 +687,7 @@ async fn measure_log_update_gas() -> anyhow::Result<()> { journal.update.updatedCommit, journal.update.updateValue, journal.update.valueRecipient, + signature.into(), encode_seal(&fake_receipt)?.into(), ) .send() @@ -685,14 +726,14 @@ async fn measure_log_update_gas() -> anyhow::Result<()> { .build()?; // First update: from the initial state. - assert!(measure_update_gas(first_update).await? < 80000); + assert!(measure_update_gas(first_update).await? < 90000); // First update: from the state of a fresh epoch. Finalizes the epoch. ctx.advance_epochs(U256::ONE).await?; - assert!(measure_update_gas(second_update).await? < 65000); + assert!(measure_update_gas(second_update).await? < 75000); // Second update within the same epoch. - assert!(measure_update_gas(third_update).await? < 60000); + assert!(measure_update_gas(third_update).await? < 70000); Ok(()) } diff --git a/crates/povw/tests/workflow.rs b/crates/povw/tests/workflow.rs index 20f7df293..06890d0c7 100644 --- a/crates/povw/tests/workflow.rs +++ b/crates/povw/tests/workflow.rs @@ -50,13 +50,13 @@ async fn test_workflow() -> anyhow::Result<()> { .verifier_ctx(VerifierContext::default().with_dev_mode(true)) .build()?; - let log_updater_prove_info = + let (log_updater_prove_info, signature) = log_updater_prover.prove_update(log_builder_receipt, &signer).await?; // Step 4: Post the proven log update to the smart contract let tx_receipt = ctx .povw_accounting - .update_work_log(&log_updater_prove_info.receipt)? + .update_work_log(&log_updater_prove_info.receipt, &signature)? .send() .await? .get_receipt() diff --git a/crates/test-utils/src/povw.rs b/crates/test-utils/src/povw.rs index 0b76b449d..f99eaa0e8 100644 --- a/crates/test-utils/src/povw.rs +++ b/crates/test-utils/src/povw.rs @@ -213,8 +213,8 @@ impl TestCtx { .value_recipient(value_recipient) .contract_address(*self.povw_accounting.address()) .chain_id(self.chain_id) - .sign_and_build(signer) - .await?; + .build()?; + let signature = input.sign(signer).await?; let journal = execute_log_updater_guest(&input)?; println!("Guest execution completed, journal: {journal:#?}"); @@ -230,6 +230,7 @@ impl TestCtx { journal.update.updatedCommit, journal.update.updateValue, journal.update.valueRecipient, + signature.into(), encode_seal(&fake_receipt)?.into(), ) .send()