Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(protocol): introduce risc0 proof #17877

Merged
merged 30 commits into from
Aug 6, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
37b71f2
feat(protocol): introduce risc0 proof
YoGhurt111 Aug 5, 2024
77fa510
forge fmt & update contract layout table
YoGhurt111 Aug 5, 2024
205db28
Update packages/protocol/contracts/verifiers/RiscZeroVerifier.sol
YoGhurt111 Aug 5, 2024
2884e53
Update packages/protocol/contracts/verifiers/RiscZeroVerifier.sol
YoGhurt111 Aug 5, 2024
dbbe731
changes based on comment
YoGhurt111 Aug 5, 2024
eb858a6
fix
YoGhurt111 Aug 5, 2024
e76ba7d
forge fmt & update contract layout table
YoGhurt111 Aug 5, 2024
4c1f9c8
changes based on comments
YoGhurt111 Aug 5, 2024
51c0a9b
Update _typos.toml
YoGhurt111 Aug 5, 2024
a4f7dc5
forge fmt & update contract layout table
YoGhurt111 Aug 5, 2024
1c3a696
Merge branch 'main' into feature/zk-protocol
YoGhurt111 Aug 5, 2024
605c55c
use low level calls instead of try-catch
YoGhurt111 Aug 5, 2024
31224ea
temp
YoGhurt111 Aug 5, 2024
0003e67
refactor(protocol): depend on risc0 contracts using npm (#17878)
dantaik Aug 5, 2024
990ece6
rm unused `pnpm.overrides`
YoGhurt111 Aug 5, 2024
f4353ae
Update RiscZeroVerifier.sol
dantaik Aug 6, 2024
07878a9
more
dantaik Aug 6, 2024
d453704
Merge branch 'main' into feature/zk-protocol
dantaik Aug 6, 2024
864f6af
Update pnpm-lock.yaml
dantaik Aug 6, 2024
9334d94
Merge branch 'main' into feature/zk-protocol
dantaik Aug 6, 2024
94a25bf
Update pnpm-lock.yaml
dantaik Aug 6, 2024
8f1a3d3
Update packages/protocol/script/DeployOnL1.s.sol
YoGhurt111 Aug 6, 2024
b4783eb
Update packages/protocol/script/DeployOnL1.s.sol
dantaik Aug 6, 2024
5bb1a8c
Merge branch 'main' into feature/zk-protocol
dantaik Aug 6, 2024
9481a8b
add overrrideable caching possiblity
Aug 6, 2024
bee2ed7
Update RiscZeroVerifier.sol
dantaik Aug 6, 2024
5790d0a
Update pnpm-lock.yaml
dantaik Aug 6, 2024
80ccf70
Merge branch 'main' into feature/zk-protocol
dantaik Aug 6, 2024
4a73905
forge fmt & update contract layout table
dantaik Aug 6, 2024
7497805
feat(taiko-client): introduce zk (#17831)
YoGhurt111 Aug 6, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion _typos.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,10 @@ extend-ignore-identifiers-re = [
"bafybeiegdqpwx3he5dvoxqklspdjekjepjcobfaakyficksratn73qbbyy",
"TGE",
"tge",
"baed"
"baed",
"Groth",
"groth",
"GROTH"
]

[files]
Expand Down
70 changes: 34 additions & 36 deletions packages/protocol/contract_layout.md
Original file line number Diff line number Diff line change
Expand Up @@ -444,24 +444,23 @@
| __gap | uint256[47] | 254 | 0 | 1504 | contracts/verifiers/SgxVerifier.sol:SgxVerifier |

## RiscZeroVerifier
| Name | Type | Slot | Offset | Bytes | Contract |
|-----------------|-----------------------------------|------|--------|-------|-----------------------------------------------------------|
| _initialized | uint8 | 0 | 0 | 1 | contracts/verifiers/RiscZeroVerifier.sol:RiscZeroVerifier |
| _initializing | bool | 0 | 1 | 1 | contracts/verifiers/RiscZeroVerifier.sol:RiscZeroVerifier |
| __gap | uint256[50] | 1 | 0 | 1600 | contracts/verifiers/RiscZeroVerifier.sol:RiscZeroVerifier |
| _owner | address | 51 | 0 | 20 | contracts/verifiers/RiscZeroVerifier.sol:RiscZeroVerifier |
| __gap | uint256[49] | 52 | 0 | 1568 | contracts/verifiers/RiscZeroVerifier.sol:RiscZeroVerifier |
| _pendingOwner | address | 101 | 0 | 20 | contracts/verifiers/RiscZeroVerifier.sol:RiscZeroVerifier |
| __gap | uint256[49] | 102 | 0 | 1568 | contracts/verifiers/RiscZeroVerifier.sol:RiscZeroVerifier |
| addressManager | address | 151 | 0 | 20 | contracts/verifiers/RiscZeroVerifier.sol:RiscZeroVerifier |
| __gap | uint256[49] | 152 | 0 | 1568 | contracts/verifiers/RiscZeroVerifier.sol:RiscZeroVerifier |
| __reentry | uint8 | 201 | 0 | 1 | contracts/verifiers/RiscZeroVerifier.sol:RiscZeroVerifier |
| __paused | uint8 | 201 | 1 | 1 | contracts/verifiers/RiscZeroVerifier.sol:RiscZeroVerifier |
| lastUnpausedAt | uint64 | 201 | 2 | 8 | contracts/verifiers/RiscZeroVerifier.sol:RiscZeroVerifier |
| __gap | uint256[49] | 202 | 0 | 1568 | contracts/verifiers/RiscZeroVerifier.sol:RiscZeroVerifier |
| receiptVerifier | contract IRiscZeroReceiptVerifier | 251 | 0 | 20 | contracts/verifiers/RiscZeroVerifier.sol:RiscZeroVerifier |
| isImageTrusted | mapping(bytes32 => bool) | 252 | 0 | 32 | contracts/verifiers/RiscZeroVerifier.sol:RiscZeroVerifier |
| __gap | uint256[48] | 253 | 0 | 1536 | contracts/verifiers/RiscZeroVerifier.sol:RiscZeroVerifier |
| Name | Type | Slot | Offset | Bytes | Contract |
|----------------|--------------------------|------|--------|-------|-----------------------------------------------------------|
| _initialized | uint8 | 0 | 0 | 1 | contracts/verifiers/RiscZeroVerifier.sol:RiscZeroVerifier |
| _initializing | bool | 0 | 1 | 1 | contracts/verifiers/RiscZeroVerifier.sol:RiscZeroVerifier |
| __gap | uint256[50] | 1 | 0 | 1600 | contracts/verifiers/RiscZeroVerifier.sol:RiscZeroVerifier |
| _owner | address | 51 | 0 | 20 | contracts/verifiers/RiscZeroVerifier.sol:RiscZeroVerifier |
| __gap | uint256[49] | 52 | 0 | 1568 | contracts/verifiers/RiscZeroVerifier.sol:RiscZeroVerifier |
| _pendingOwner | address | 101 | 0 | 20 | contracts/verifiers/RiscZeroVerifier.sol:RiscZeroVerifier |
| __gap | uint256[49] | 102 | 0 | 1568 | contracts/verifiers/RiscZeroVerifier.sol:RiscZeroVerifier |
| addressManager | address | 151 | 0 | 20 | contracts/verifiers/RiscZeroVerifier.sol:RiscZeroVerifier |
| __gap | uint256[49] | 152 | 0 | 1568 | contracts/verifiers/RiscZeroVerifier.sol:RiscZeroVerifier |
| __reentry | uint8 | 201 | 0 | 1 | contracts/verifiers/RiscZeroVerifier.sol:RiscZeroVerifier |
| __paused | uint8 | 201 | 1 | 1 | contracts/verifiers/RiscZeroVerifier.sol:RiscZeroVerifier |
| lastUnpausedAt | uint64 | 201 | 2 | 8 | contracts/verifiers/RiscZeroVerifier.sol:RiscZeroVerifier |
| __gap | uint256[49] | 202 | 0 | 1568 | contracts/verifiers/RiscZeroVerifier.sol:RiscZeroVerifier |
| isImageTrusted | mapping(bytes32 => bool) | 251 | 0 | 32 | contracts/verifiers/RiscZeroVerifier.sol:RiscZeroVerifier |
| __gap | uint256[49] | 252 | 0 | 1568 | contracts/verifiers/RiscZeroVerifier.sol:RiscZeroVerifier |

## QuotaManager
| Name | Type | Slot | Offset | Bytes | Contract |
Expand Down Expand Up @@ -683,24 +682,23 @@
| __gap | uint256[48] | 253 | 0 | 1536 | contracts/mainnet/MainnetProverSet.sol:MainnetProverSet |

## MainnetRiscZeroVerifier
| Name | Type | Slot | Offset | Bytes | Contract |
|-----------------|-----------------------------------|------|--------|-------|-----------------------------------------------------------------------|
| _initialized | uint8 | 0 | 0 | 1 | contracts/mainnet/MainnetRiscZeroVerifier.sol:MainnetRiscZeroVerifier |
| _initializing | bool | 0 | 1 | 1 | contracts/mainnet/MainnetRiscZeroVerifier.sol:MainnetRiscZeroVerifier |
| __gap | uint256[50] | 1 | 0 | 1600 | contracts/mainnet/MainnetRiscZeroVerifier.sol:MainnetRiscZeroVerifier |
| _owner | address | 51 | 0 | 20 | contracts/mainnet/MainnetRiscZeroVerifier.sol:MainnetRiscZeroVerifier |
| __gap | uint256[49] | 52 | 0 | 1568 | contracts/mainnet/MainnetRiscZeroVerifier.sol:MainnetRiscZeroVerifier |
| _pendingOwner | address | 101 | 0 | 20 | contracts/mainnet/MainnetRiscZeroVerifier.sol:MainnetRiscZeroVerifier |
| __gap | uint256[49] | 102 | 0 | 1568 | contracts/mainnet/MainnetRiscZeroVerifier.sol:MainnetRiscZeroVerifier |
| addressManager | address | 151 | 0 | 20 | contracts/mainnet/MainnetRiscZeroVerifier.sol:MainnetRiscZeroVerifier |
| __gap | uint256[49] | 152 | 0 | 1568 | contracts/mainnet/MainnetRiscZeroVerifier.sol:MainnetRiscZeroVerifier |
| __reentry | uint8 | 201 | 0 | 1 | contracts/mainnet/MainnetRiscZeroVerifier.sol:MainnetRiscZeroVerifier |
| __paused | uint8 | 201 | 1 | 1 | contracts/mainnet/MainnetRiscZeroVerifier.sol:MainnetRiscZeroVerifier |
| lastUnpausedAt | uint64 | 201 | 2 | 8 | contracts/mainnet/MainnetRiscZeroVerifier.sol:MainnetRiscZeroVerifier |
| __gap | uint256[49] | 202 | 0 | 1568 | contracts/mainnet/MainnetRiscZeroVerifier.sol:MainnetRiscZeroVerifier |
| receiptVerifier | contract IRiscZeroReceiptVerifier | 251 | 0 | 20 | contracts/mainnet/MainnetRiscZeroVerifier.sol:MainnetRiscZeroVerifier |
| isImageTrusted | mapping(bytes32 => bool) | 252 | 0 | 32 | contracts/mainnet/MainnetRiscZeroVerifier.sol:MainnetRiscZeroVerifier |
| __gap | uint256[48] | 253 | 0 | 1536 | contracts/mainnet/MainnetRiscZeroVerifier.sol:MainnetRiscZeroVerifier |
| Name | Type | Slot | Offset | Bytes | Contract |
|----------------|--------------------------|------|--------|-------|-----------------------------------------------------------------------|
| _initialized | uint8 | 0 | 0 | 1 | contracts/mainnet/MainnetRiscZeroVerifier.sol:MainnetRiscZeroVerifier |
| _initializing | bool | 0 | 1 | 1 | contracts/mainnet/MainnetRiscZeroVerifier.sol:MainnetRiscZeroVerifier |
| __gap | uint256[50] | 1 | 0 | 1600 | contracts/mainnet/MainnetRiscZeroVerifier.sol:MainnetRiscZeroVerifier |
| _owner | address | 51 | 0 | 20 | contracts/mainnet/MainnetRiscZeroVerifier.sol:MainnetRiscZeroVerifier |
| __gap | uint256[49] | 52 | 0 | 1568 | contracts/mainnet/MainnetRiscZeroVerifier.sol:MainnetRiscZeroVerifier |
| _pendingOwner | address | 101 | 0 | 20 | contracts/mainnet/MainnetRiscZeroVerifier.sol:MainnetRiscZeroVerifier |
| __gap | uint256[49] | 102 | 0 | 1568 | contracts/mainnet/MainnetRiscZeroVerifier.sol:MainnetRiscZeroVerifier |
| addressManager | address | 151 | 0 | 20 | contracts/mainnet/MainnetRiscZeroVerifier.sol:MainnetRiscZeroVerifier |
| __gap | uint256[49] | 152 | 0 | 1568 | contracts/mainnet/MainnetRiscZeroVerifier.sol:MainnetRiscZeroVerifier |
| __reentry | uint8 | 201 | 0 | 1 | contracts/mainnet/MainnetRiscZeroVerifier.sol:MainnetRiscZeroVerifier |
| __paused | uint8 | 201 | 1 | 1 | contracts/mainnet/MainnetRiscZeroVerifier.sol:MainnetRiscZeroVerifier |
| lastUnpausedAt | uint64 | 201 | 2 | 8 | contracts/mainnet/MainnetRiscZeroVerifier.sol:MainnetRiscZeroVerifier |
| __gap | uint256[49] | 202 | 0 | 1568 | contracts/mainnet/MainnetRiscZeroVerifier.sol:MainnetRiscZeroVerifier |
| isImageTrusted | mapping(bytes32 => bool) | 251 | 0 | 32 | contracts/mainnet/MainnetRiscZeroVerifier.sol:MainnetRiscZeroVerifier |
| __gap | uint256[49] | 252 | 0 | 1568 | contracts/mainnet/MainnetRiscZeroVerifier.sol:MainnetRiscZeroVerifier |

## MainnetRollupAddressManager
| Name | Type | Slot | Offset | Bytes | Contract |
Expand Down
1 change: 1 addition & 0 deletions packages/protocol/contracts/common/LibStrings.sol
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ library LibStrings {
bytes32 internal constant B_TIER_SGX = bytes32("tier_sgx");
bytes32 internal constant B_TIER_SGX2 = bytes32("tier_sgx2");
bytes32 internal constant B_TIER_SGX_ZKVM = bytes32("tier_sgx_zkvm");
bytes32 internal constant B_RISCZERO_GROTH16_VERIFIER = bytes32("risc0_groth16_verifier");
bytes32 internal constant B_WITHDRAWER = bytes32("withdrawer");
bytes32 internal constant H_RETURN_LIVENESS_BOND = keccak256("RETURN_LIVENESS_BOND");
bytes32 internal constant H_SIGNAL_ROOT = keccak256("SIGNAL_ROOT");
Expand Down

This file was deleted.

36 changes: 15 additions & 21 deletions packages/protocol/contracts/verifiers/RiscZeroVerifier.sol
Original file line number Diff line number Diff line change
@@ -1,46 +1,40 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.24;

import "@risc0/contracts/IRiscZeroVerifier.sol";
import "../common/EssentialContract.sol";
import "../common/LibStrings.sol";
import "../thirdparty/risczero/IRiscZeroReceiptVerifier.sol";
import "../L1/ITaikoL1.sol";
import "./IVerifier.sol";
import "./libs/LibPublicInput.sol";

/// @title RiscZeroVerifier
/// @custom:security-contact [email protected]
contract RiscZeroVerifier is EssentialContract, IVerifier {
/// @notice RISC Zero remote verifier contract address, e.g.:
/// https://sepolia.etherscan.io/address/0x3d24C84FC1A2B26f9229e58ddDf11A8dfba802d0
IRiscZeroReceiptVerifier public receiptVerifier;
// [32, 0, 0, 0] -- big-endian uint32(32) for hash bytes len
bytes private constant FIXED_JOURNAL_HEADER = hex"20000000";

/// @notice Trusted imageId mapping
mapping(bytes32 imageId => bool trusted) public isImageTrusted;

uint256[48] private __gap;
uint256[49] private __gap;

/// @dev Emitted when a trusted image is set / unset.
/// @param imageId The id of the image
/// @param trusted True if trusted, false otherwise
event ImageTrusted(bytes32 imageId, bool trusted);

/// @dev Emitted when a proof is verified
event ProofVerified(bytes32 metaHash, bytes32 publicInputHash);

error RISC_ZERO_INVALID_IMAGE_ID();
error RISC_ZERO_INVALID_PROOF();

/// @notice Initializes the contract with the provided address manager.
/// @param _owner The address of the owner.
/// @param _rollupAddressManager The address of the AddressManager.
/// @param _receiptVerifier The address of the risc zero receipt verifier contract.
function init(
address _owner,
address _rollupAddressManager,
address _receiptVerifier
)
external
initializer
{
function init(address _owner, address _rollupAddressManager) external initializer {
__Essential_init(_owner, _rollupAddressManager);
receiptVerifier = IRiscZeroReceiptVerifier(_receiptVerifier);
}

/// @notice Sets/unsets an the imageId as trusted entity
Expand All @@ -59,7 +53,6 @@ contract RiscZeroVerifier is EssentialContract, IVerifier {
TaikoData.TierProof calldata _proof
)
external
view
{
// Do not run proof verification to contest an existing proof
if (_ctx.isContesting) return;
Expand All @@ -71,21 +64,22 @@ contract RiscZeroVerifier is EssentialContract, IVerifier {
revert RISC_ZERO_INVALID_IMAGE_ID();
}

bytes32 hash = LibPublicInput.hashPublicInputs(
bytes32 publicInputHash = LibPublicInput.hashPublicInputs(
_tran, address(this), address(0), _ctx.prover, _ctx.metaHash, taikoChainId()
);

// journalDigest is the sha256 hash of the hashed public input
bytes32 journalDigest = sha256(bytes.concat(hash));
bytes32 journalDigest = sha256(bytes.concat(FIXED_JOURNAL_HEADER, publicInputHash));

// call risc0 verifier contract
(bool success,) = address(receiptVerifier).staticcall(
abi.encodeCall(IRiscZeroReceiptVerifier.verify, (seal, imageId, journalDigest))
(bool success,) = resolve(LibStrings.B_RISCZERO_GROTH16_VERIFIER, false).staticcall(
abi.encodeCall(IRiscZeroVerifier.verify, (seal, imageId, journalDigest))
YoGhurt111 marked this conversation as resolved.
Show resolved Hide resolved
);

if (!success) {
revert RISC_ZERO_INVALID_PROOF();
}

emit ProofVerified(_ctx.metaHash, publicInputHash);
}

function taikoChainId() internal view virtual returns (uint64) {
Expand Down
5 changes: 4 additions & 1 deletion packages/protocol/foundry.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,16 @@ solc_version = "0.8.24"
evm_version = "cancun"
remappings = [
"@openzeppelin/contracts-upgradeable/=node_modules/@openzeppelin/contracts-upgradeable/",
"openzeppelin/contracts-upgradeable/=node_modules/@openzeppelin/contracts-upgradeable/",
"@openzeppelin/contracts/=node_modules/@openzeppelin/contracts/",
"openzeppelin/contracts/=node_modules/@openzeppelin/contracts/",
"@risc0/contracts/=node_modules/risc0-ethereum/contracts/src/",
"@solady/=node_modules/solady/",
"@optimism/=node_modules/optimism/",
"@sp1-contracts/=node_modules/sp1-contracts/contracts/",
"forge-std/=node_modules/forge-std/",
"ds-test/=node_modules/ds-test/src/",
"p256-verifier/=node_modules/p256-verifier/",
"@p256-verifier/contracts/=node_modules/p256-verifier/src/",
]

# Do not change the block_gas_limit value, TaikoL2.t.sol depends on it.
Expand Down
1 change: 1 addition & 0 deletions packages/protocol/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@
"merkletreejs": "^0.3.11",
"optimism": "github:ethereum-optimism/optimism#v1.8.0",
"p256-verifier": "github:taikoxyz/p256-verifier#v0.1.0",
"risc0-ethereum": "github:risc0/risc0-ethereum#v1.0.0",
"solady": "github:Vectorized/solady#v0.0.231",
"sp1-contracts": "github:succinctlabs/sp1-contracts#v1.1.0"
}
Expand Down
26 changes: 20 additions & 6 deletions packages/protocol/script/DeployOnL1.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,13 @@
pragma solidity 0.8.24;

import "@openzeppelin/contracts/utils/Strings.sol";
import "@risc0/contracts/groth16/RiscZeroGroth16Verifier.sol";

// Actually this one is deployed already on mainnet, but we are now deploying our own (non via-ir)
// version. For mainnet, it is easier to go with one of:
// - https:/daimo-eth/p256-verifier
// - https:/rdubois-crypto/FreshCryptoLib
import "@p256-verifier/contracts/P256Verifier.sol";

import "../contracts/common/LibStrings.sol";
import "../contracts/tko/TaikoToken.sol";
Expand All @@ -27,12 +34,7 @@ import "../test/common/erc20/FreeMintERC20.sol";
import "../test/common/erc20/MayFailFreeMintERC20.sol";
import "../test/L1/TestTierProvider.sol";
import "../test/DeployCapability.sol";

// Actually this one is deployed already on mainnet, but we are now deploying our own (non via-ir)
// version. For mainnet, it is easier to go with one of:
// - https:/daimo-eth/p256-verifier
// - https:/rdubois-crypto/FreshCryptoLib
import { P256Verifier } from "p256-verifier/src/P256Verifier.sol";
import "../contracts/verifiers/RiscZeroVerifier.sol";

/// @title DeployOnL1
/// @notice This script deploys the core Taiko protocol smart contract on L1,
Expand Down Expand Up @@ -383,6 +385,18 @@ contract DeployOnL1 is DeployCapability {
ProverSet.init, (owner, vm.envAddress("PROVER_SET_ADMIN"), rollupAddressManager)
)
});

// Deploy r0 groth16 verifier
RiscZeroGroth16Verifier verifier =
new RiscZeroGroth16Verifier(ControlID.CONTROL_ROOT, ControlID.BN254_CONTROL_ID);
YoGhurt111 marked this conversation as resolved.
Show resolved Hide resolved
register(rollupAddressManager, "risc0_groth16_verifier", address(verifier));

deployProxy({
name: "risc0_verifier",
impl: address(new RiscZeroVerifier()),
data: abi.encodeCall(RiscZeroVerifier.init, (owner, rollupAddressManager)),
registerTo: rollupAddressManager
});
}

function deployTierProvider(string memory tierProviderName) private returns (address) {
Expand Down
5 changes: 3 additions & 2 deletions packages/protocol/script/DeploySP1Verifier.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,9 @@ contract DeploySP1Verifier is DeployCapability {
}

function run() external broadcast {
address sp1RemoteVerifierOrGateway = vm.envOr("SP1_REMOTE_VERIFIER_GATEWAY", address(0)); // address(0)
// is fine, we can set it later
// address sp1RemoteVerifierOrGateway = vm.envOr("SP1_REMOTE_VERIFIER_GATEWAY", address(0));
// // address(0)
// is fine, we can set it later
address owner = vm.envOr("SP1_VERIFIER_OWNER", msg.sender);

address sp1Verifier = address(new SP1Verifier());
Expand Down
2 changes: 1 addition & 1 deletion packages/protocol/test/TaikoTest.sol
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ abstract contract TaikoTest is Test, DeployCapability {
address internal Bob = vm.addr(0x2);
address internal Carol = vm.addr(0x3);
address internal David = vm.addr(0x4);
address internal Emma = randAddress();
address internal Emma = vm.addr(0x5);
address internal Frank = randAddress();
address internal Grace = randAddress();
address internal Henry = randAddress();
Expand Down
Loading
Loading