diff --git a/.github/workflows/contracts_ci.yml b/.github/workflows/contracts_ci.yml new file mode 100644 index 000000000..c09da57e7 --- /dev/null +++ b/.github/workflows/contracts_ci.yml @@ -0,0 +1,27 @@ +name: Bolt-contracts CI + +on: [push, pull_request] + +jobs: + check: + name: Foundry project + runs-on: ubuntu-latest + timeout-minutes: 10 + + steps: + - uses: actions/checkout@v3 + with: + submodules: recursive + + - name: Install Foundry + uses: foundry-rs/foundry-toolchain@v1 + with: + version: nightly + + - name: Run tests + run: forge test --via-ir -vvv + working-directory: bolt-contracts + + - name: Run forge fmt + run: forge fmt --check + working-directory: bolt-contracts diff --git a/.gitignore b/.gitignore index da4d6e6a2..98cbb2478 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ *_dump.log +logs/ target/ .vscode .idea diff --git a/.gitmodules b/.gitmodules index 7fe3b3345..eda39c33e 100644 --- a/.gitmodules +++ b/.gitmodules @@ -4,6 +4,15 @@ [submodule "bolt-contracts/lib/openzeppelin-contracts"] path = bolt-contracts/lib/openzeppelin-contracts url = https://github.com/OpenZeppelin/openzeppelin-contracts -[submodule "bolt-contracts/lib/relic-sdk"] - path = bolt-contracts/lib/relic-sdk - url = https://github.com/Relic-Protocol/relic-sdk +[submodule "bolt-contracts/lib/core"] + path = bolt-contracts/lib/core + url = https://github.com/symbioticfi/core +[submodule "bolt-contracts/lib/eigenlayer-contracts"] + path = bolt-contracts/lib/eigenlayer-contracts + url = https://github.com/layr-labs/eigenlayer-contracts +[submodule "bolt-contracts/lib/openzeppelin-contracts-upgradeable"] + path = bolt-contracts/lib/openzeppelin-contracts-upgradeable + url = https://github.com/OpenZeppelin/openzeppelin-contracts-upgradeable +[submodule "bolt-contracts/lib/openzeppelin-foundry-upgrades"] + path = bolt-contracts/lib/openzeppelin-foundry-upgrades + url = https://github.com/OpenZeppelin/openzeppelin-foundry-upgrades diff --git a/bolt-contracts/.gas-snapshot b/bolt-contracts/.gas-snapshot new file mode 100644 index 000000000..f33a01fb9 --- /dev/null +++ b/bolt-contracts/.gas-snapshot @@ -0,0 +1,34 @@ +BoltChallengerTest:testCannotResolveChallengeBeforeExpiration() (gas: 434902) +BoltChallengerTest:testCommitmentDigestAndSignature() (gas: 4626) +BoltChallengerTest:testCommitmentSignature() (gas: 6761) +BoltChallengerTest:testOpenAlreadyExistingChallenge() (gas: 13688) +BoltChallengerTest:testOpenChallengeInvalidSignature() (gas: 45916) +BoltChallengerTest:testOpenChallengeSingleTx() (gas: 424427) +BoltChallengerTest:testOpenChallengeWithIncorrectBond() (gas: 24909) +BoltChallengerTest:testOpenChallengeWithLargebond() (gas: 24920) +BoltChallengerTest:testOpenChallengeWithSlotInTheFuture() (gas: 25008) +BoltChallengerTest:testProveAccountData() (gas: 355542) +BoltChallengerTest:testProveHeaderData() (gas: 46360) +BoltChallengerTest:testProveTransactionInclusion() (gas: 176543) +BoltChallengerTest:testResolveChallengeFullDefenseSingleTx() (gas: 562694) +BoltChallengerTest:testResolveChallengeFullDefenseStackedTxs() (gas: 939716) +BoltChallengerTest:testResolveExpiredChallenge() (gas: 426457) +BoltManagerEigenLayerTest:testDeregisterOperatorFromAVS() (gas: 834106) +BoltManagerEigenLayerTest:testGetOperatorStake() (gas: 998169) +BoltManagerEigenLayerTest:testNonExistentProposerStatus() (gas: 980157) +BoltManagerEigenLayerTest:testNonWhitelistedCollateral() (gas: 103778) +BoltManagerEigenLayerTest:testProposerStatus() (gas: 1007174) +BoltManagerEigenLayerTest:testProposersLookaheadStatus() (gas: 2302759) +BoltManagerEigenLayerTest:testWhitelistedCollaterals() (gas: 99260) +BoltManagerSymbioticTest:testGetNonExistentProposerStatus() (gas: 1265832) +BoltManagerSymbioticTest:testGetProposerStatus() (gas: 1508557) +BoltManagerSymbioticTest:testGetWhitelistedCollaterals() (gas: 17213) +BoltManagerSymbioticTest:testNonWhitelistedCollateral() (gas: 43806) +BoltManagerSymbioticTest:testProposersLookaheadStatus() (gas: 2582043) +BoltManagerSymbioticTest:testReadOperatorStake() (gas: 1541503) +BoltValidatorsTest:testUnsafeRegistration() (gas: 149361) +BoltValidatorsTest:testUnsafeRegistrationFailsIfAlreadyRegistered() (gas: 148862) +BoltValidatorsTest:testUnsafeRegistrationInvalidOperator() (gas: 22820) +BoltValidatorsTest:testUnsafeRegistrationWhenNotAllowed() (gas: 33183) +TransactionDecoderTest:testDecodeAllTestCases() (gas: 0) +TransactionDecoderTest:testDecodeGasUsage() (gas: 53281) \ No newline at end of file diff --git a/bolt-contracts/.gitignore b/bolt-contracts/.gitignore index ccb098322..319411769 100644 --- a/bolt-contracts/.gitignore +++ b/bolt-contracts/.gitignore @@ -1,3 +1,9 @@ cache/ out/ broadcast/ +logs/ + +.env + +node_modules/ +target/ diff --git a/bolt-contracts/README.md b/bolt-contracts/README.md index 0b75ab5c8..6c27065ad 100644 --- a/bolt-contracts/README.md +++ b/bolt-contracts/README.md @@ -1,62 +1,283 @@ # Bolt Contracts -## Registry -[`BoltRegistry.sol`](./src/contracts/BoltRegistry.sol) keeps track of registered proposers and operators. It allows an operator -to register by providing a list of validator indexes and depositing some collateral. It also exposes some view methods for off-chain actors to read. - -### Registration - -```js -function register( - uint64[] calldata validatorIndexes, - string calldata rpc, - bytes calldata extra -) external payable; - -``` -Besides validatorIndexes, `register` also registers an RPC endpoint and some optional other information in `extra`. - -### Exiting -The exit process is a 2-step process. The first step is triggering the exit, which will put the registrant into an `EXITING` status after which the registrant should be considered inactive. After the `EXIT_COOLDOWN` of 1 day, the exit can be confirmed and the deposit will be returned. - -```js -function startExit() external; - -function confirmExit(address payable recipient) external; -``` - -### View Methods -```js -function isActiveOperator(address _operator) external view returns (bool); - -function getOperatorStatus( - address _operator -) external view returns (Status); - -function getOperatorForValidator( - uint64 _validatorIndex -) external view returns (Registrant memory); -``` - -## Challenger -WIP - -## Deploying -```bash -# Example for Helder devnet. Set PRIVATE_KEY to your hex-encoded private key. -PRIVATE_KEY=$PRIVATE_KEY forge script script/DeployRegistry.s.sol --rpc-url https://rpc.helder-devnets.xyz --broadcast --legacy -``` - -## Registering -```bash -# Example for Helder devnet. Set PRIVATE_KEY to your hex-encoded private key. -export PRIVATE_KEY="0x..." -export RPC_ADDR="http://test.com" -export VALIDATOR_INDEXES="1,2,3,4" -forge script script/RegisterValidators.s.sol --rpc-url https://rpc.helder-devnets.xyz --broadcast --legacy -``` - -## Deployments -| Contract | Network | Address | -| -------- | ------- | ------- | -| `BoltRegistry.sol` | Helder (7014190335) | 0xdF11D829eeC4C192774F3Ec171D822f6Cb4C14d9 | \ No newline at end of file +## Table of Contents + +- [Overview](#overview) + - [Architecture](#architecture) +- [Admin Privileges](#admin-privileges) +- [Validator Registration: `BoltValidators`](#validator-registration-boltvalidators) +- [Bolt Network Entrypoint: `BoltManager`](#bolt-network-entrypoint-boltmanager) + - [Symbiotic Integration guide for Staking Pools](#symbiotic-integration-guide-for-staking-pools) + - [Symbiotic Integration guide for Operators](#symbiotic-integration-guide-for-operators) + - [Eigenlayer Integration guides](#eigenlayer-integration-guides) +- [Fault Proof Challenge and Slashing: `BoltChallenger`](#fault-proof-challenge-and-slashing-boltchallenger) +- [Testing](#testing) +- [Security Considerations](#security-considerations) +- [Conclusion](#conclusion) + +## Overview + +The Bolt smart contracts cover the following components: + +- Registration and delegation logic for validators to authenticate and opt-in to Bolt +- Operator registration and collateral deposits through flexible restaking protocol integrations (EigenLayer & Symbiotic) +- Fault proof challenges and resolution *without slashing* + +### Architecture +A high-level overview of architecture is depicted in the diagram below: + + + +**Notes** +- All contracts are upgradeable by implementing [ERC1967Proxy](https://docs.openzeppelin.com/contracts/4.x/api/proxy#erc1967). +- Storage layout safety is maintained with the use of [storage gaps](https://docs.openzeppelin.com/upgrades-plugins/1.x/writing-upgradeable#storage-gaps) and validated with the [OpenZeppelin Foundry Upgrades toolkit](https://github.com/OpenZeppelin/openzeppelin-foundry-upgrades). +- There is a single admin address operated by the Bolt team to facilitate upgrades and update system-wide parameters. + +## Admin Privileges + +The smart contracts are deployed with a single administrator account operated by the Bolt team. In this testnet deployment, all contracts are upgradeable +and multiple system-wide parameters can be changed by this administrator in the case of bugs, hacks, or other critical events. + +## System-wide Parameters: `BoltParameters` + +[`BoltParameters`](./src/contracts/BoltParameters.sol) is an upgradeable storage contract that stores system-wide parameters that the other +contracts can read from. An overview is given in the table below: + +| Parameter | Initial Value | Mutable after deployment | +| -------------------- | --------------- | ------------------------ | +| `EPOCH_DURATION` | 86400 (1 day) | No | +| `SLASHING_WINDOW` | 604800 (1 week) | No | +| `BLOCKHASH_EVM_LOOKBACK` | 256 | No | +| `ETH2_GENESIS_TIMESTAMP` | 1694786400 | No | +| `SLOT_TIME` | 12 | No | +| `JUSTIFICATION_DELAY` | 32 | Yes (by admin) | +| `MINIMUM_OPERATOR_STAKE` | 1 ether | Yes (by admin) | +| `MAX_CHALLENGE_DURATION` | 604800 (1 week) | Yes (by admin) | +| `CHALLENGE_BOND` | 1 ether | Yes (by admin) | +| `ALLOW_UNSAFE_REGISTRATION` | `true` | Yes (by admin) | + +The values of these parameters can also be found in [`config.holesky.json`](./config/config.holesky.json). + +## Validator Registration: `BoltValidators` + +The [`BoltValidators`](./src/contracts/BoltValidators.sol) contract is the only point of entry for +validators to signal their intent to participate in Bolt Protocol and authenticate with their BLS private key. + +The registration process includes the following steps: + +1. Validator signs a message with their BLS private key. This is required to prove that the + validator private key is under their control and that they are indeed its owner. +2. Validator calls the `registerValidator` function providing: + 1. Their BLS public key + 2. The BLS signature of the registration message + 3. The address of the authorized collateral provider + 4. The address of the authorized operator + +Until the Pectra hard-fork will be activated, the contract will also expose a `registerValidatorUnsafe` function +that will not check the BLS signature. This is gated by a feature flag that will be turned off post-Pectra and +will allow us to test the registration flow in a controlled environment. + +## Bolt Network Entrypoint: `BoltManager` + +The [`BoltManager`](./src/contracts/BoltManager.sol) contract is a crucial component of Bolt that +integrates with restaking ecosystems Symbiotic and Eigenlayer. It manages the registration and +coordination of validators, operators, and vaults within the Bolt network. + +Key features include: + +1. Retrieval of operator stake and proposer status from their pubkey +2. Integration with Symbiotic +3. Integration with Eigenlayer + +Specific functionalities about the restaking protocols are handled inside +the `IBoltMiddleware` contracts, such as `BoltSymbioticMiddleware` and `BoltEigenlayerMiddleware`. + +### Symbiotic Integration guide for Staking Pools + +As a staking pool, it is assumed that you are already in control of a Symbiotic Vault. +If not, please refer to the [Symbiotic docs](https://docs.symbiotic.fi/handbooks/Handbook%20for%20Vaults) +on how to spin up a Vault and start receiving stake from your node operators. + +Opting into Bolt works as any other Symbiotic middleware integration. Here are the steps: + +1. Make sure your vault collateral is whitelisted in `BoltSymbioticMiddleware` by calling `isCollateralWhitelisted`. +2. Register as a vault in `BoltSymbioticMiddleware` by calling `registerVault`. +3. Verify that your vault is active in `BoltSymbioticMiddleware` by calling `isVaultEnabled`. +4. Set the network limit for your vault in Symbiotic with `Vault.delegator().setNetworkLimit()`. +5. You can now start approving operators that opt in to your vault directly in Symbiotic. +6. When you assign shares to operators, they are able to provide commitments on behalf of your collateral. + +### Symbiotic Integration guide for Operators + +As an operator, you will need to opt-in to the Bolt Network and any Vault that trusts you to provide +commitments on their behalf. + +The opt-in process requires the following steps: + +1. register in Symbiotic with `OperatorRegistry.registerOperator()`. +2. opt-in to the Bolt network with `OperatorNetworkOptInService.optIn(networkAddress)`. +3. opt-in to any vault with `OperatorVaultOptInService.optIn(vaultAddress)`. +4. register in Bolt with `BoltSymbioticMiddleware.registerOperator(operatorAddress)`. +5. get approved by the vault. +6. start providing commitments with the stake provided by the vault. + +### EigenLayer Integration Guide for Node Operators and Solo Stakers + +> [!NOTE] +> Without loss of generality, we will assume the reader of this guide is a Node +> Operator (NO), since the same steps apply to solo stakers. + +As a Node Operator you will be an ["Operator"](https://docs.eigenlayer.xyz/eigenlayer/overview/key-terms) +in the Bolt AVS built on top of EigenLayer. This requires +running an Ethereum validator and the Bolt sidecar in order issue +preconfirmations. + +The Operator will be represented by an Ethereum address that needs +to follow the standard procedure outlined in the +[EigenLayer documentation](https://docs.eigenlayer.xyz/) to opt into EigenLayer. Let's go through the steps: + +1. As an Operator, you register into EigenLayer using [`DelegationManager.registerAsOperator`](https://github.com/Layr-Labs/eigenlayer-contracts/blob/mainnet/src/contracts/core/DelegationManager.sol#L107-L119). + +2. As an Ethereum validator offering precofirmations a NO needs some collateral in + order to be economically credible. In order to do that, some entities known as a "stakers" + need to deposit whitelisted Liquid Staking Tokens (LSTs) + into an appropriate "Strategy" associated to the LST via the + [`StrategyManager.depositIntoStrategy`](https://github.com/Layr-Labs/eigenlayer-contracts/blob/mainnet/src/contracts/core/StrategyManager.sol#L105-L110), + so that the Operator has a `min_amount` (TBD) of collateral associated to it. + Whitelisted LSTs are exposed by the `BoltEigenLayerMiddleware` contract + in the `getWhitelistedCollaterals` function. + Note that NOs and stakers can be two different entities + _but there is fully trusted relationship as stakers will be slashed if a NO misbehaves_. + +3. After the stakers have deposited their collateral into a strategy they need + to choose you as their operator. To do that, they need to call the function + [`DelegationManager.delegateTo`](https://github.com/Layr-Labs/eigenlayer-contracts/blob/mainnet/src/contracts/core/DelegationManager.sol#L154-L163). + +4. As an Operator you finally opt into the Bolt AVS by interacting with the `BoltEigenLayerMiddleware`. + This consists in calling the function `BoltEigenLayerMiddleware.registerOperatorToAVS`. + The payload is a signature whose digest consists of: + + 1. your operator address + 2. the `BoltEigenLayerMiddleware` contract address + 3. a salt + 4. an expiry 2. + + The contract will then forward the call to the [`AVSDirectory.registerOperatorToAVS`](https://github.com/Layr-Labs/eigenlayer-contracts/blob/mainnet/src/contracts/core/AVSDirectory.sol#L64-L108) + with the `msg.sender` set to the Bolt AVS contract. Upon successful verification of the signature, + the operator is considered `REGISTERED` in a mapping `avsOperatorStatus[msg.sender][operator]`. + +Lastly, a NO needs to interact with both the `BoltValidators` and `BoltEigenLayerMiddleware` +contract. This is needed for internal functioning of the AVS and to make RPCs aware that you are a +registered operator and so that they can forward you preconfirmation requests. + +The steps required are the following: + +1. Register all the validator public keys you want to use with Bolt via the `BoltValidators.registerValidator`. + If you own more than one validator public key, + you can use the more gas-efficient `BoltValidators.batchRegisterValidators` function. + The `authorizedOperator` argument must be the same Ethereum address used for + opting into EigenLayer as an Operator. + +2. Register the same Operator address in the `BoltEigenLayerMiddleware` contract by calling + the `BoltEigenLayerMiddleware.registerOperator` function. This formalizes your role within the Bolt network + and allows you to manage operations effectively, such as pausing or resuming + your service. + +3. Register the EigenLayer strategy you are using for restaking _if it has not been done by someone else already_. + This ensures that your restaked assets are correctly integrated with Bolt’s system. + +## Fault Proof Challenge: `BoltChallenger` + +The [`BoltChallenger`](./src/contracts/BoltChallenger.sol) contract is the component responsible +for handling fault attribution in the case of a validator failing to meet their commitments. + +In short, the challenger contract allows any user to challenge a validator's commitment by opening +a dispute with the following inputs: + +1. The signed commitment made by the validator (or a list of commitments on the same slot) +2. An ETH bond to cover the cost of the dispute and disincentivize frivolous challenges + +The entrypoint is the `openChallenge` function. Once a challenge is opened, a `ChallengeOpened` event +is emitted, and any arbitrator has a time window to submit a valid response to settle the dispute. + +### Dispute resolution + +The dispute resolution process is one-shot and requires the arbitrator to submit all necessary evidence +of the validator's correct behaviour within the challenge time window. + +The arbitrator is _anyone_ who can submit a valid response to the challenge. It doesn't have to be the +validator themselves. There is however one limitation: the time window for submitting a response must be +respected in the following way: + +- Start: the target block must be justified by LMD-GHOST: a minimum of 32 slots must have passed +- End: depending on the EVM block hash oracle: + - . If using the `BLOCKHASH` EVM opcode, the window is limited to 256 blocks (roughly 1 hour) + - . If using the [EIP-2935](https://eips.ethereum.org/EIPS/eip-2935) historical oracle, the window is limited to 8192 blocks (roughly 1 day) + +The inputs to the resolution process are as follows: + +1. The ID of the challenge to respond to: this is emitted in the `ChallengeOpened` event and is unique. +2. The [inclusion proofs](https://github.com/chainbound/bolt/blob/6c0f1b696cfe3de7e7e3830ac28c369c6ddf271e/bolt-contracts/src/interfaces/IBoltChallenger.sol#L39), consisting of the following components: + a. the block number of the block containing the committed transactions (we call it "inclusionBlock") + b. the RLP-encoded block header of the block **before** the one containing the committed transactions (we call it "previousBlock") + b. the RLP-encoded block header of the block containing the included transactions (aka "inclusionBlock") + c. the account merkle proofs of the sender of the committed transactions against the previousBlock's state root + d. the transaction merkle proofs of the included transactions against the inclusionBlock's transaction root + e. the transaction index in the block of each included transaction + +If the arbitrator submits a valid response that satisfies the requirements for the challenge, the +challenge is considered `DEFENDED` and the challenger's bond is slashed to cover the cost of the dispute +and to incentivize speedy resolution. + +If no arbitrators respond successfully within the challenge time window, the challenge is considered +`BREACHED` and anyone can call the `resolveExpiredChallenge()` method. The `BoltChallenger` will keep +track of this information for future reference. + + + +## Testing + +We use Forge, a fast and flexible Ethereum testing framework, for our smart contract tests. +Here's a guide to running the test suite for the Bolt contracts: + +1. Make sure you have Forge installed. If not, follow the [installation guide](https://book.getfoundry.sh/getting-started/installation). + +2. Navigate to the `bolt-contracts` directory + +3. Run all tests + + ``` + forge test + ``` + +4. Run tests with verbose output: + + ``` + forge test -vvv + ``` + +## Security Considerations + +While the Bolt Contracts have been designed with security best practices in mind, it's important +to note that they are still undergoing audits and should not be used in production environments without +thorough review and testing. As with any smart contract system, users should exercise caution and conduct +their own due diligence before interacting with these contracts. + +The following considerations should be taken into account before interacting with smart contracts: + +- Restaking is a complex process that involves trusting external systems and smart contracts. +- Validators should be aware of the potential for slashing if they fail to meet their commitments or engage in malicious behavior. +- Smart contracts are susceptible to bugs and vulnerabilities that could be exploited by attackers. diff --git a/bolt-contracts/config/holesky/deployments.json b/bolt-contracts/config/holesky/deployments.json new file mode 100644 index 000000000..2aa89b3ed --- /dev/null +++ b/bolt-contracts/config/holesky/deployments.json @@ -0,0 +1,31 @@ +{ + "bolt": { + "validators": "0x05275a4799cd1B07D81319390fC62Bc7BDbDf269" + }, + "symbiotic": { + "network": "0x90F79bf6EB2c4f870365E785982E1f101E93b906", + "operatorRegistry": "0xAdFC41729fF447974cE27DdFa358A0f2096c3F39", + "networkOptInService": "0xF5AFc9FA3Ca63a07E529DDbB6eae55C665cCa83E", + "vaultFactory": "0x18C659a269a7172eF78BBC19Fe47ad2237Be0590", + "networkRegistry": "0xac5acD8A105C8305fb980734a5AD920b5920106A", + "networkMiddlewareService": "0x683F470440964E353b389391CdDDf8df381C282f", + "middleware": "", + "supportedVaults": [ + "0x1df2fbfcD600ADd561013f44B2D055E2e974f605", + "0xf427d00c34609053d97167352061DD2F0F27F853" + ] + }, + "eigenLayer": { + "avsDirectory": "0x055733000064333CaDDbC92763c58BF0192fFeBf", + "delegationManager": "0xA44151489861Fe9e3055d95adC98FbD462B948e7", + "strategyManager": "0xdfB5f6CE42aAA7830E94ECFCcAd411beF4d4D5b6", + "middleware": "", + "supportedStrategies": [ + "0x7D704507b76571a51d9caE8AdDAbBFd0ba0e63d3", + "0x3A8fBdf9e77DFc25d09741f51d3E181b25d0c4E0", + "0x80528D6e9A2BAbFc766965E0E26d5aB08D9CFaF9", + "0x70EB4D3c164a6B4A5f908D4FBb5a9cAfFb66bAB6", + "0xaccc5A86732BE85b5012e8614AF237801636F8e5" + ] + } +} \ No newline at end of file diff --git a/bolt-contracts/config/holesky/operator.json b/bolt-contracts/config/holesky/operator.json new file mode 100644 index 000000000..0b6373c92 --- /dev/null +++ b/bolt-contracts/config/holesky/operator.json @@ -0,0 +1,5 @@ +{ + "rpc": "localhost:50051", + "salt": "0x000000000000000abc0000000000000000000000000000000000000000000000", + "expiry": null +} \ No newline at end of file diff --git a/bolt-contracts/config/holesky/parameters.json b/bolt-contracts/config/holesky/parameters.json new file mode 100644 index 000000000..0bdd76e4f --- /dev/null +++ b/bolt-contracts/config/holesky/parameters.json @@ -0,0 +1,12 @@ +{ + "epochDuration": 86400, + "slashingWindow": 604800, + "maxChallengeDuration": 604800, + "challengeBond": 1000000000000000000, + "blockhashEvmLookback": 256, + "justificationDelay": 32, + "eth2GenesisTimestamp": 1694786400, + "slotTime": 12, + "allowUnsafeRegistration": true, + "minimumOperatorStake": 1000000000000000000 +} \ No newline at end of file diff --git a/bolt-contracts/config/holesky/validators.json b/bolt-contracts/config/holesky/validators.json new file mode 100644 index 000000000..5f065521e --- /dev/null +++ b/bolt-contracts/config/holesky/validators.json @@ -0,0 +1,8 @@ +{ + "maxCommittedGasLimit": 10000000, + "authorizedOperator": "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266", + "pubkeys": [ + "0xad02df40e24e8534dc4c175163f7a034f99f2e81c43b7ea19904006cb05d001b5301d3444f1812b101803b7f72e340fa", + "0xa4c0a8b5dd70d0fe8f05580143bea4c40badb176c2b0c20001cddf897a11bb832137fa4050af1d0420a7dc27568697df" + ] +} \ No newline at end of file diff --git a/bolt-contracts/config/test/parameters.json b/bolt-contracts/config/test/parameters.json new file mode 100644 index 000000000..c9768df44 --- /dev/null +++ b/bolt-contracts/config/test/parameters.json @@ -0,0 +1,12 @@ +{ + "epochDuration": 86400, + "slashingWindow": 604800, + "maxChallengeDuration": 604800, + "challengeBond": 1000000000000000000, + "blockhashEvmLookback": 256, + "justificationDelay": 32, + "eth2GenesisTimestamp": 1606824023, + "slotTime": 12, + "allowUnsafeRegistration": true, + "minimumOperatorStake": 1000000000000000000 +} \ No newline at end of file diff --git a/bolt-contracts/docs/admin/avs.json b/bolt-contracts/docs/admin/avs.json new file mode 100644 index 000000000..8461c6510 --- /dev/null +++ b/bolt-contracts/docs/admin/avs.json @@ -0,0 +1,7 @@ +{ + "name": "Bolt Protocol", + "website": "https://boltprotocol.xyz", + "description": "Bolt Protocol enables proposers to make credible commitments about their blocks.", + "logo": "http://boltprotocol.xyz/wp-content/uploads/2024/07/Bolt-Logo.png", + "twitter": "https://twitter.com/boltprotocol_" +} \ No newline at end of file diff --git a/bolt-contracts/docs/admin/deploying.md b/bolt-contracts/docs/admin/deploying.md new file mode 100644 index 000000000..56de63f22 --- /dev/null +++ b/bolt-contracts/docs/admin/deploying.md @@ -0,0 +1,73 @@ +# Deployment Guide + +**This should only be done once with the `V1` contracts. For upgrades, refer to the [upgrading doc](./upgrading.md).** + +## Configuration + +There are 2 JSON configuration files: +- [`config/holesky/deployments.json`](../../config/holesky/deployments.json): contains deployment addresses of EigenLayer ([here](https://github.com/Layr-Labs/eigenlayer-contracts/blob/dev/README.md#deployments)) and Symbiotic ([here](https://docs.symbiotic.fi/deployments)). +- [`config/holesky/parameters.json`](../../config/holesky/parameters.json): contains the launch parameters for `BoltParameters`. + + + +## Deployment Guide +Make sure we have a full compilation for the Foundry Upgrades Toolkit: +```bash +forge clean && forge build +``` + +And have a local Anvil fork running to test and validate deployments: + +```bash +anvil --fork-url $HOLESKY_RPC +``` + +> [!IMPORTANT] +> Run everything on the local Anvil fork first! This requires just replacing the $HOLESKY_RPC with the $ANVIL_RPC. + +Also set your private keys as environment variables: + +```bash +export NETWORK_PRIVATE_KEY=0x... +export ADMIN_PRIVATE_KEY=0x... +``` + +### Pre-deployment + +Register a Symbiotic network for Bolt with the Symbiotic `NetworkRegistry`. The private key with which the script is run will determine the network address. This private key will also need to be used later. + +```bash +forge script script/holesky/admin/helpers/Symbiotic.s.sol --rpc-url $HOLESKY_RPC --private-key $NETWORK_PRIVATE_KEY --broadcast -vvvv --sig "run(string memory arg)" registerNetwork +``` + +Make sure `deployments.json` contains the correct address for the Symbiotic network. + +### Deployment + +Run the following script to deploy Bolt V1: +```bash +forge script script/holesky/admin/Deploy.s.sol --rpc-url $HOLESKY_RPC --private-key $ADMIN_PRIVATE_KEY --broadcast -vvvv +``` + +This will deploy all the contracts. The address corresponding to the private key will be the system admin. + +Now update `deployments.json` with the Symbiotic and EigenLayer middleware contracts, because we'll need to register it in the next step. Also update the `bolt` section with the correct addresses. + +### Post-deployment + +Register the deployed `SymbioticMiddleware` with the Symbiotic `NetworkMiddlewareService`. IMPORTANT: this script needs +to be run with the network private key! + +```bash +forge script script/holesky/admin/helpers/Symbiotic.s.sol --rpc-url $HOLESKY_RPC --private-key $NETWORK_PRIVATE_KEY --broadcast -vvvv --sig "run(string memory arg)" registerMiddleware +``` + +Also set the AVS metadata in the EigenLayer AVS Directory, needs to be run with the **admin private key** used at deployment. + +```bash +forge script script/holesky/admin/helpers/RegisterAVS.s.sol --rpc-url $HOLESKY_RPC --private-key $ADMIN_PRIVATE_KEY --broadcast -vvvv +``` + +> [!IMPORTANT] +> After the `deployments.json` file has been fully updated with the correct contract addresses, push it to Github. + diff --git a/bolt-contracts/docs/admin/upgrading.md b/bolt-contracts/docs/admin/upgrading.md new file mode 100644 index 000000000..65466d91b --- /dev/null +++ b/bolt-contracts/docs/admin/upgrading.md @@ -0,0 +1,16 @@ +# Upgrading Guide + +When upgrading contracts, always keep the old implementation of the contracts around and increment the version number. +For example, when upgrading `BoltManagerV1`, copy it into a new file called `BoltManagerV2` and make your changes. + +This is needed to reference check the new contracts with the old contracts so that the OpenZeppelin Upgrades library can +validate the safety of the upgrade. You MUST add this reference when upgrading a contract: + +```solidity +Options memory opts; +opts.referenceContract = "BoltManagerV1.sol"; +bytes memory initManager = abi.encodeCall(BoltManagerV2.initialize, (params)); +Upgrades.upgradeProxy(proxy, "BoltManagerV2.sol", initManager, opts); +``` + +Before an upgrade, update the [`Upgrade.s.sol`](../script/holesky/Upgrade.s.sol) script to include the correct contracts, references and configurations. \ No newline at end of file diff --git a/bolt-contracts/docs/erd.png b/bolt-contracts/docs/erd.png new file mode 100644 index 000000000..b89c9deeb Binary files /dev/null and b/bolt-contracts/docs/erd.png differ diff --git a/bolt-contracts/foundry.toml b/bolt-contracts/foundry.toml index 25b918f9c..b19612e4a 100644 --- a/bolt-contracts/foundry.toml +++ b/bolt-contracts/foundry.toml @@ -1,6 +1,73 @@ [profile.default] +solc = "0.8.25" +via_ir = true src = "src" out = "out" libs = ["lib"] +fs_permissions = [{ access = "read-write", path = "./" }] +gas_reports = ["*"] +# Required options for the OpenZeppelin Foundry Upgrades toolkit: https://github.com/OpenZeppelin/openzeppelin-foundry-upgrades +ffi = true +ast = true +build_info = true +extra_output = ["storageLayout"] + +# silence some warnings during compilation +# https://book.getfoundry.sh/reference/config/solidity-compiler#ignored_error_codes +ignored_error_codes = [3628, 1878, 5574] +ignored_warnings_from = [ + "lib/openzeppelin-contracts/contracts", + "lib/core", + "lib/eigenlayer-contracts", +] + +remappings = [ + # Bolt remappings + "@relic/=lib/relic-sdk/packages/contracts", + "@symbiotic/=lib/core/src/", + "@eigenlayer/=lib/eigenlayer-contracts/", + "@openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/", + "@openzeppelin/contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/contracts/", + "@openzeppelin-foundry-upgrades/=lib/openzeppelin-foundry-upgrades/", + + # Symbiotic remappings contexts + "lib/core/:forge-std/=lib/core/lib/forge-std/src/", + "lib/core/:@openzeppelin/contracts/=lib/core/lib/openzeppelin-contracts/contracts/", + "lib/core/:@openzeppelin/contracts-upgradeable/=lib/core/lib/openzeppelin-contracts-upgradeable/contracts/", + + # Eigenlayer remappings contexts + "lib/eigenlayer-contracts/:@openzeppelin-upgrades/=lib/eigenlayer-contracts/lib/openzeppelin-contracts-upgradeable/", + "lib/eigenlayer-contracts/:@openzeppelin/=lib/eigenlayer-contracts/lib/openzeppelin-contracts/", + "lib/eigenlayer-contracts/:@openzeppelin-v4.9.0/=lib/eigenlayer-contracts/lib/openzeppelin-contracts-v4.9.0/", + "lib/eigenlayer-contracts/:@openzeppelin-upgrades-v4.9.0/=lib/eigenlayer-contracts/lib/openzeppelin-contracts-upgradeable-v4.9.0/", + "lib/eigenlayer-contracts/:ds-test/=lib/eigenlayer-contracts/lib/ds-test/src/", + "lib/eigenlayer-contracts/:forge-std/=lib/eigenlayer-contracts/lib/forge-std/src/", + "lib/eigenlayer-contracts/lib/openzeppelin-contracts/:@openzeppelin/contracts/=lib/eigenlayer-contracts/lib/openzeppelin-contracts/contracts/", + + # OpenZeppelin remappings contexts + "lib/openzeppelin-contracts/:@openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/", +] + + +[rpc_endpoints] +mainnet = "${ETH_RPC_URL}" +holesky = "${ETH_RPC_URL_HOLESKY}" + +[fmt] +bracket_spacing = false +int_types = "long" +line_length = 120 +multiline_func_header = "params_first" +number_underscore = "thousands" +quote_style = "double" +tab_width = 4 + +[fuzz] +runs = 4096 +max_test_rejects = 262144 + +# [etherscan] +# mainnet = { key = "${ETHERSCAN_API_KEY_MAINNET}" } +# holesky = { key = "${ETHERSCAN_API_KEY_HOLESKY}" } # See more config options https://github.com/foundry-rs/foundry/blob/master/crates/config/README.md#all-options diff --git a/bolt-contracts/go.mod b/bolt-contracts/go.mod new file mode 100644 index 000000000..f5b65da25 --- /dev/null +++ b/bolt-contracts/go.mod @@ -0,0 +1,5 @@ +module bolt-contracts + +go 1.22.7 + +require github.com/supranational/blst v0.3.11 diff --git a/bolt-contracts/go.sum b/bolt-contracts/go.sum new file mode 100644 index 000000000..6f41ee08a --- /dev/null +++ b/bolt-contracts/go.sum @@ -0,0 +1,2 @@ +github.com/supranational/blst v0.3.11 h1:LyU6FolezeWAhvQk0k6O/d49jqgO52MSDDfYgbeoEm4= +github.com/supranational/blst v0.3.11/go.mod h1:jZJtfjgudtNl4en1tzwPIV3KjUnQUvG3/j+w+fVonLw= diff --git a/bolt-contracts/lib/core b/bolt-contracts/lib/core new file mode 160000 index 000000000..76bf70945 --- /dev/null +++ b/bolt-contracts/lib/core @@ -0,0 +1 @@ +Subproject commit 76bf709458410b2682f7bc20c6e3a90845bf4b51 diff --git a/bolt-contracts/lib/eigenlayer-contracts b/bolt-contracts/lib/eigenlayer-contracts new file mode 160000 index 000000000..00fc4b95e --- /dev/null +++ b/bolt-contracts/lib/eigenlayer-contracts @@ -0,0 +1 @@ +Subproject commit 00fc4b95e9c1a5c4f370e41f56d01052d186da07 diff --git a/bolt-contracts/lib/forge-std b/bolt-contracts/lib/forge-std index bb4ceea94..978ac6fad 160000 --- a/bolt-contracts/lib/forge-std +++ b/bolt-contracts/lib/forge-std @@ -1 +1 @@ -Subproject commit bb4ceea94d6f10eeb5b41dc2391c6c8bf8e734ef +Subproject commit 978ac6fadb62f5f0b723c996f64be52eddba6801 diff --git a/bolt-contracts/lib/openzeppelin-contracts-upgradeable b/bolt-contracts/lib/openzeppelin-contracts-upgradeable new file mode 160000 index 000000000..723f8cab0 --- /dev/null +++ b/bolt-contracts/lib/openzeppelin-contracts-upgradeable @@ -0,0 +1 @@ +Subproject commit 723f8cab09cdae1aca9ec9cc1cfa040c2d4b06c1 diff --git a/bolt-contracts/lib/openzeppelin-foundry-upgrades b/bolt-contracts/lib/openzeppelin-foundry-upgrades new file mode 160000 index 000000000..16e0ae21e --- /dev/null +++ b/bolt-contracts/lib/openzeppelin-foundry-upgrades @@ -0,0 +1 @@ +Subproject commit 16e0ae21e0e39049f619f2396fa28c57fad07368 diff --git a/bolt-contracts/lib/relic-sdk b/bolt-contracts/lib/relic-sdk deleted file mode 160000 index 8d6c88e44..000000000 --- a/bolt-contracts/lib/relic-sdk +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 8d6c88e4437eade19a08aca6457d6bd14b06e454 diff --git a/bolt-contracts/main.go b/bolt-contracts/main.go new file mode 100644 index 000000000..527d6f3d5 --- /dev/null +++ b/bolt-contracts/main.go @@ -0,0 +1,41 @@ +package main + +import ( + "encoding/hex" + "fmt" + "os" + "strings" + + blst "github.com/supranational/blst/bindings/go" +) + +type blsPublicKey = blst.P1Affine + +func main() { + if len(os.Args) != 2 { + fmt.Println("Usage: pubkey_to_g1 ") + os.Exit(1) + } + + pubkey := strings.TrimPrefix(os.Args[1], "0x") + + pubkeyBytes, err := hex.DecodeString(pubkey) + if err != nil { + fmt.Println("Failed to decode pubkey:", err) + os.Exit(1) + } + + if len(pubkeyBytes) != 48 { + fmt.Println("Invalid pubkey length") + os.Exit(1) + } + + G1 := new(blsPublicKey).Uncompress(pubkeyBytes) + + serialized := G1.Serialize() + + x := serialized[0:48] + y := serialized[48:] + + fmt.Printf("0x%x,0x%x\n", x, y) +} diff --git a/bolt-contracts/script/DeployRegistry.s.sol b/bolt-contracts/script/DeployRegistry.s.sol deleted file mode 100644 index 28a31ffe8..000000000 --- a/bolt-contracts/script/DeployRegistry.s.sol +++ /dev/null @@ -1,21 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.13; - -import {Script, console} from "forge-std/Script.sol"; - -import {BoltRegistry} from "../src/contracts/BoltRegistry.sol"; -import {BoltChallenger} from "../src/contracts/BoltChallenger.sol"; - -contract DeployRegistry is Script { - uint256 public signerKey; - - function run() public { - signerKey = vm.envUint("PRIVATE_KEY"); - vm.startBroadcast(signerKey); - - BoltRegistry registry = new BoltRegistry(10 ether); - console.log("BoltRegistry deployed at", address(registry)); - - vm.stopBroadcast(); - } -} diff --git a/bolt-contracts/script/ReadRegistry.s.sol b/bolt-contracts/script/ReadRegistry.s.sol deleted file mode 100644 index 0e26d204e..000000000 --- a/bolt-contracts/script/ReadRegistry.s.sol +++ /dev/null @@ -1,36 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.13; - -import {Script, console} from "forge-std/Script.sol"; - -import {BoltRegistry, IBoltRegistry} from "../src/contracts/BoltRegistry.sol"; - -contract ReadRegistry is Script { - address public registryAddress = 0xdF11D829eeC4C192774F3Ec171D822f6Cb4C14d9; - - function run() public view { - console.log("Bolt registry address:", registryAddress); - BoltRegistry registry = BoltRegistry(registryAddress); - - console.log( - "Bolt registry minimum collateral:", - registry.MINIMUM_COLLATERAL() - ); - - for (uint64 i = 0; i < 2000; i++) { - try registry.getOperatorForValidator(i) returns ( - IBoltRegistry.Registrant memory operator - ) { - console.log( - "Operator for validator found", - i, - ":", - operator.operator - ); - console.log("Operator RPC:", operator.metadata.rpc); - } catch { - // console.log("No operator for validator", i); - } - } - } -} diff --git a/bolt-contracts/script/RegisterValidators.s.sol b/bolt-contracts/script/RegisterValidators.s.sol deleted file mode 100644 index e4554fd88..000000000 --- a/bolt-contracts/script/RegisterValidators.s.sol +++ /dev/null @@ -1,111 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.13; - -import {Script, console} from "forge-std/Script.sol"; - -import {BoltRegistry} from "../src/contracts/BoltRegistry.sol"; - -contract RegisterValidators is Script { - uint256 public signerKey; - uint64[] public validatorIndexes; - address public registryAddress = 0xdF11D829eeC4C192774F3Ec171D822f6Cb4C14d9; - - function run() public { - signerKey = vm.envUint("PRIVATE_KEY"); - string memory rpc = vm.envString("RPC_ADDR"); - vm.startBroadcast(signerKey); - - string memory validatorIndexesEnv = vm.envString("VALIDATOR_INDEXES"); - uint256[] memory indexes = StringToUintArrayLib.fromStr(validatorIndexesEnv); - for (uint256 i = 0; i < indexes.length; i++) { - validatorIndexes.push(uint64(indexes[i])); - } - - console.log("Bolt registry address:", registryAddress); - BoltRegistry registry = BoltRegistry(registryAddress); - - console.log( - "Bolt registry minimum collateral:", - registry.MINIMUM_COLLATERAL() - ); - - address sender = vm.addr(signerKey); - - console.log("Sender address:", sender); - console.log("Sender balance:", sender.balance); - - if (sender.balance < registry.MINIMUM_COLLATERAL()) { - revert("Insufficient balance"); - } - - // Register with minimal collateral - registry.register{value: registry.MINIMUM_COLLATERAL()}( - validatorIndexes, - rpc, - "" - ); - - vm.stopBroadcast(); - } -} - -library StringToUintArrayLib { - // Maximum number of validators parsed in a single function call - uint256 constant MAX_VALIDATORS = 256; - - function fromStr(string memory s) internal pure returns (uint256[] memory) { - bytes memory strBytes = bytes(s); - uint256[] memory vec = new uint256[](MAX_VALIDATORS); // Initial allocation, will resize later - uint256 vecIndex = 0; - uint256 tempNum; - bool parsingRange = false; - uint256 rangeStart; - - for (uint256 i = 0; i < strBytes.length; i++) { - if (strBytes[i] == ',') { - if (parsingRange) { - // Handle end of range - for (uint256 j = rangeStart; j <= tempNum; j++) { - vec[vecIndex] = j; - vecIndex++; - } - parsingRange = false; - } else { - // Handle single number - vec[vecIndex] = tempNum; - vecIndex++; - } - tempNum = 0; - } else if (strBytes[i] == '.') { - if (i + 1 < strBytes.length && strBytes[i + 1] == '.') { - // Handle start of range - parsingRange = true; - rangeStart = tempNum; - tempNum = 0; - i++; // Skip next dot - } - } else if (strBytes[i] >= '0' && strBytes[i] <= '9') { - tempNum = tempNum * 10 + (uint8(strBytes[i]) - 48); // Convert ASCII to integer - } - } - - // Handle the last part after the final comma (or single number/range end) - if (parsingRange) { - for (uint256 j = rangeStart; j <= tempNum; j++) { - vec[vecIndex] = j; - vecIndex++; - } - } else { - vec[vecIndex] = tempNum; - vecIndex++; - } - - // Resize array to actual size - uint256[] memory result = new uint256[](vecIndex); - for (uint256 i = 0; i < vecIndex; i++) { - result[i] = vec[i]; - } - - return result; - } -} \ No newline at end of file diff --git a/bolt-contracts/script/holesky/admin/Deploy.s.sol b/bolt-contracts/script/holesky/admin/Deploy.s.sol new file mode 100644 index 000000000..554791237 --- /dev/null +++ b/bolt-contracts/script/holesky/admin/Deploy.s.sol @@ -0,0 +1,174 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.25; + +import {Script, console} from "forge-std/Script.sol"; + +import {ERC1967Proxy} from "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol"; +import {Upgrades, Options} from "@openzeppelin-foundry-upgrades/src/Upgrades.sol"; +import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; + +import {IVault} from "@symbiotic/interfaces/vault/IVault.sol"; +import {IStrategy} from "@eigenlayer/src/contracts/interfaces/IStrategy.sol"; + +import {BoltParametersV1} from "../../../src/contracts/BoltParametersV1.sol"; +import {BoltValidatorsV1} from "../../../src/contracts/BoltValidatorsV1.sol"; +import {BoltManagerV1} from "../../../src/contracts/BoltManagerV1.sol"; +import {BoltEigenLayerMiddlewareV1} from "../../../src/contracts/BoltEigenLayerMiddlewareV1.sol"; +import {BoltSymbioticMiddlewareV1} from "../../../src/contracts/BoltSymbioticMiddlewareV1.sol"; +import {BoltConfig} from "../../../src/lib/Config.sol"; + +/// @notice Script to deploy the Bolt contracts. +contract DeployBolt is Script { + function run() public { + // The admin address will be authorized to call the adminOnly functions + // on the contract implementations, as well as upgrade the contracts. + address admin = msg.sender; + console.log("Deploying with admin", admin); + + BoltConfig.Parameters memory config = readParameters(); + BoltConfig.Deployments memory deployments = readDeployments(); + + vm.startBroadcast(admin); + + // TODO: Fix safe deploy, currently failing with `ASTDereferencerError` from openzeppelin + Options memory opts; + opts.unsafeSkipAllChecks = true; + + bytes memory initParameters = abi.encodeCall( + BoltParametersV1.initialize, + ( + admin, + config.epochDuration, + config.slashingWindow, + config.maxChallengeDuration, + config.allowUnsafeRegistration, + config.challengeBond, + config.blockhashEvmLookback, + config.justificationDelay, + config.eth2GenesisTimestamp, + config.slotTime, + config.minimumOperatorStake + ) + ); + address parametersProxy = Upgrades.deployUUPSProxy("BoltParametersV1.sol", initParameters, opts); + console.log("BoltParametersV1 proxy deployed at", parametersProxy); + + // Generate the `initialize` call data for the contract. + bytes memory initValidators = abi.encodeCall(BoltValidatorsV1.initialize, (admin, parametersProxy)); + // Deploy the UUPSProxy through the `Upgrades` library, with the correct `initialize` call data. + address validatorsProxy = Upgrades.deployUUPSProxy("BoltValidatorsV1.sol", initValidators, opts); + console.log("BoltValidatorsV1 proxy deployed at", validatorsProxy); + + bytes memory initManager = abi.encodeCall(BoltManagerV1.initialize, (admin, parametersProxy, validatorsProxy)); + address managerProxy = Upgrades.deployUUPSProxy("BoltManagerV1.sol", initManager, opts); + console.log("BoltManagerV1 proxy deployed at", managerProxy); + + bytes memory initEigenLayerMiddleware = abi.encodeCall( + BoltEigenLayerMiddlewareV1.initialize, + ( + admin, + parametersProxy, + managerProxy, + deployments.eigenLayerAVSDirectory, + deployments.eigenLayerDelegationManager, + deployments.eigenLayerStrategyManager + ) + ); + address eigenLayerMiddlewareProxy = + Upgrades.deployUUPSProxy("BoltEigenLayerMiddlewareV1.sol", initEigenLayerMiddleware, opts); + console.log("BoltEigenLayerMiddlewareV1 proxy deployed at", eigenLayerMiddlewareProxy); + + bytes memory initSymbioticMiddleware = abi.encodeCall( + BoltSymbioticMiddlewareV1.initialize, + ( + admin, + parametersProxy, + managerProxy, + deployments.symbioticNetwork, + deployments.symbioticOperatorRegistry, + deployments.symbioticOperatorNetOptIn, + deployments.symbioticVaultFactory + ) + ); + address symbioticMiddlewareProxy = + Upgrades.deployUUPSProxy("BoltSymbioticMiddlewareV1.sol", initSymbioticMiddleware, opts); + console.log("BoltSymbioticMiddlewareV1 proxy deployed at", address(symbioticMiddlewareProxy)); + + console.log("Core contracts deployed succesfully, whitelisting middleware contracts in BoltManager..."); + console.log("EigenLayer middleware:", address(eigenLayerMiddlewareProxy)); + console.log("Symbiotic middleware:", address(symbioticMiddlewareProxy)); + BoltManagerV1(managerProxy).addRestakingProtocol(address(eigenLayerMiddlewareProxy)); + BoltManagerV1(managerProxy).addRestakingProtocol(address(symbioticMiddlewareProxy)); + + console.log("Whitelisted middleware contracts in BoltManager"); + console.log("Registering supported Symbiotic Vaults..."); + + for (uint256 i = 0; i < deployments.supportedVaults.length; i++) { + IVault vault = IVault(deployments.supportedVaults[i]); + console.log("Registering vault with collateral: %s (address: %s)", vault.collateral(), address(vault)); + BoltSymbioticMiddlewareV1(symbioticMiddlewareProxy).registerVault(address(deployments.supportedVaults[i])); + } + + console.log("Registering supported EigenLayer Strategies..."); + + for (uint256 i = 0; i < deployments.supportedStrategies.length; i++) { + IStrategy strategy = IStrategy(deployments.supportedStrategies[i]); + console.log( + "Registering strategy with collateral: %s (address: %s)", + address(strategy.underlyingToken()), + address(strategy) + ); + BoltEigenLayerMiddlewareV1(eigenLayerMiddlewareProxy).registerStrategy(address(strategy)); + } + + vm.stopBroadcast(); + } + + function readParameters() public view returns (BoltConfig.Parameters memory) { + string memory root = vm.projectRoot(); + string memory path = string.concat(root, "/config/holesky/parameters.json"); + string memory json = vm.readFile(path); + + uint48 epochDuration = uint48(vm.parseJsonUint(json, ".epochDuration")); + uint48 slashingWindow = uint48(vm.parseJsonUint(json, ".slashingWindow")); + uint48 maxChallengeDuration = uint48(vm.parseJsonUint(json, ".maxChallengeDuration")); + bool allowUnsafeRegistration = vm.parseJsonBool(json, ".allowUnsafeRegistration"); + uint256 challengeBond = vm.parseJsonUint(json, ".challengeBond"); + uint256 blockhashEvmLookback = vm.parseJsonUint(json, ".blockhashEvmLookback"); + uint256 justificationDelay = vm.parseJsonUint(json, ".justificationDelay"); + uint256 eth2GenesisTimestamp = vm.parseJsonUint(json, ".eth2GenesisTimestamp"); + uint256 slotTime = vm.parseJsonUint(json, ".slotTime"); + uint256 minimumOperatorStake = vm.parseJsonUint(json, ".minimumOperatorStake"); + + return BoltConfig.Parameters({ + epochDuration: epochDuration, + slashingWindow: slashingWindow, + maxChallengeDuration: maxChallengeDuration, + challengeBond: challengeBond, + blockhashEvmLookback: blockhashEvmLookback, + justificationDelay: justificationDelay, + eth2GenesisTimestamp: eth2GenesisTimestamp, + slotTime: slotTime, + allowUnsafeRegistration: allowUnsafeRegistration, + minimumOperatorStake: minimumOperatorStake + }); + } + + function readDeployments() public view returns (BoltConfig.Deployments memory) { + string memory root = vm.projectRoot(); + string memory path = string.concat(root, "/config/holesky/deployments.json"); + string memory json = vm.readFile(path); + + return BoltConfig.Deployments({ + symbioticNetwork: vm.parseJsonAddress(json, ".symbiotic.network"), + symbioticOperatorRegistry: vm.parseJsonAddress(json, ".symbiotic.operatorRegistry"), + symbioticOperatorNetOptIn: vm.parseJsonAddress(json, ".symbiotic.networkOptInService"), + symbioticVaultFactory: vm.parseJsonAddress(json, ".symbiotic.vaultFactory"), + supportedVaults: vm.parseJsonAddressArray(json, ".symbiotic.supportedVaults"), + eigenLayerAVSDirectory: vm.parseJsonAddress(json, ".eigenLayer.avsDirectory"), + eigenLayerDelegationManager: vm.parseJsonAddress(json, ".eigenLayer.delegationManager"), + eigenLayerStrategyManager: vm.parseJsonAddress(json, ".eigenLayer.strategyManager"), + supportedStrategies: vm.parseJsonAddressArray(json, ".eigenLayer.supportedStrategies") + }); + } +} diff --git a/bolt-contracts/script/holesky/admin/Upgrade.s.sol b/bolt-contracts/script/holesky/admin/Upgrade.s.sol new file mode 100644 index 000000000..6ca5b0518 --- /dev/null +++ b/bolt-contracts/script/holesky/admin/Upgrade.s.sol @@ -0,0 +1,22 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.25; + +import {Script, console} from "forge-std/Script.sol"; + +import {ERC1967Proxy} from "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol"; +import {Upgrades} from "@openzeppelin-foundry-upgrades/src/Upgrades.sol"; + +import {BoltParametersV1} from "../../../src/contracts/BoltParametersV1.sol"; +import {BoltValidatorsV1} from "../../../src/contracts/BoltValidatorsV1.sol"; +import {BoltManagerV1} from "../../../src/contracts/BoltManagerV1.sol"; +import {BoltEigenLayerMiddlewareV1} from "../../../src/contracts/BoltEigenLayerMiddlewareV1.sol"; +import {BoltSymbioticMiddlewareV1} from "../../../src/contracts/BoltSymbioticMiddlewareV1.sol"; +import {BoltConfig} from "../../../src/lib/Config.sol"; + +contract UpgradeBolt is Script { + function run() public { + // TODO: Validate upgrades with Upgrades.validateUpgrade + + // TODO: Upgrade contracts with Upgrades.upgradeProxy + } +} diff --git a/bolt-contracts/script/holesky/admin/helpers/RegisterAVS.s.sol b/bolt-contracts/script/holesky/admin/helpers/RegisterAVS.s.sol new file mode 100644 index 000000000..f4a3117d4 --- /dev/null +++ b/bolt-contracts/script/holesky/admin/helpers/RegisterAVS.s.sol @@ -0,0 +1,31 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.25; + +import {Script, console} from "forge-std/Script.sol"; + +import {BoltEigenLayerMiddlewareV1} from "../../../../src/contracts/BoltEigenLayerMiddlewareV1.sol"; + +contract RegisterAVS is Script { + function run() public { + address admin = msg.sender; + console.log("Running with admin address:", admin); + + BoltEigenLayerMiddlewareV1 middleware = BoltEigenLayerMiddlewareV1(readMiddleware()); + + string memory avsURI = "https://boltprotocol.xyz/avs.json"; + console.log("Setting AVS metadata URI to:", avsURI); + + vm.startBroadcast(admin); + + middleware.updateAVSMetadataURI(avsURI); + vm.stopBroadcast(); + } + + function readMiddleware() public view returns (address) { + string memory root = vm.projectRoot(); + string memory path = string.concat(root, "/config/holesky/deployments.json"); + string memory json = vm.readFile(path); + + return vm.parseJsonAddress(json, ".eigenLayer.middleware"); + } +} diff --git a/bolt-contracts/script/holesky/admin/helpers/Symbiotic.s.sol b/bolt-contracts/script/holesky/admin/helpers/Symbiotic.s.sol new file mode 100644 index 000000000..c899a9e79 --- /dev/null +++ b/bolt-contracts/script/holesky/admin/helpers/Symbiotic.s.sol @@ -0,0 +1,66 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.25; + +import {Script, console} from "forge-std/Script.sol"; + +import {INetworkRegistry} from "@symbiotic/interfaces/INetworkRegistry.sol"; +import {INetworkMiddlewareService} from "@symbiotic/interfaces/service/INetworkMiddlewareService.sol"; + +/// forge script script/holesky/Symbiotic.s.sol --rpc-url $RPC_HOLESKY --private-key $NETWORK_PRIVATE_KEY --broadcast -vvvv --sig "run(string memory arg)" registerNetwork +/// forge script script/holesky/Symbiotic.s.sol --rpc-url $RPC_HOLESKY --private-key $NETWORK_PRIVATE_KEY --broadcast -vvvv --sig "run(string memory arg)" registerMiddleware +contract SymbioticHelper is Script { + function run( + string memory arg + ) public { + address networkAdmin = msg.sender; + console.log("Running with network admin", networkAdmin); + + vm.startBroadcast(networkAdmin); + + if (keccak256(abi.encode(arg)) == keccak256(abi.encode("registerNetwork"))) { + INetworkRegistry networkRegistry = INetworkRegistry(readNetworkRegistry()); + + console.log("Registering network with NetworkRegistry (%s)", address(networkRegistry)); + + networkRegistry.registerNetwork(); + } else if (keccak256(abi.encode(arg)) == keccak256(abi.encode("registerMiddleware"))) { + INetworkMiddlewareService middlewareService = INetworkMiddlewareService(readMiddlewareService()); + + address middleware = readMiddleware(); + + console.log( + "Registering network middleware (%s) with MiddlewareService (%s)", + middleware, + address(middlewareService) + ); + + middlewareService.setMiddleware(middleware); + } + + vm.stopBroadcast(); + } + + function readNetworkRegistry() public view returns (address) { + string memory root = vm.projectRoot(); + string memory path = string.concat(root, "/config/holesky/deployments.json"); + string memory json = vm.readFile(path); + + return vm.parseJsonAddress(json, ".symbiotic.networkRegistry"); + } + + function readMiddlewareService() public view returns (address) { + string memory root = vm.projectRoot(); + string memory path = string.concat(root, "/config/holesky/deployments.json"); + string memory json = vm.readFile(path); + + return vm.parseJsonAddress(json, ".symbiotic.networkMiddlewareService"); + } + + function readMiddleware() public view returns (address) { + string memory root = vm.projectRoot(); + string memory path = string.concat(root, "/config/holesky/deployments.json"); + string memory json = vm.readFile(path); + + return vm.parseJsonAddress(json, ".symbiotic.middleware"); + } +} diff --git a/bolt-contracts/script/holesky/validators/RegisterEigenLayerOperator.s.sol b/bolt-contracts/script/holesky/validators/RegisterEigenLayerOperator.s.sol new file mode 100644 index 000000000..3acbe2d81 --- /dev/null +++ b/bolt-contracts/script/holesky/validators/RegisterEigenLayerOperator.s.sol @@ -0,0 +1,94 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.25; + +import {Script, console} from "forge-std/Script.sol"; + +import {IAVSDirectory} from "@eigenlayer/src/contracts/interfaces/IAVSDirectory.sol"; +import {ISignatureUtils} from "@eigenlayer/src/contracts/interfaces/ISignatureUtils.sol"; + +import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; +import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol"; + +import {BoltEigenLayerMiddlewareV1} from "../../../src/contracts/BoltEigenLayerMiddlewareV1.sol"; +import {IBoltMiddlewareV1} from "../../../src/interfaces/IBoltMiddlewareV1.sol"; + +contract RegisterEigenLayerOperator is Script { + struct OperatorConfig { + string rpc; + bytes32 salt; + uint256 expiry; + } + + function run() public { + uint256 operatorSk = vm.envUint("OPERATOR_SK"); + + address operator = vm.addr(operatorSk); + + BoltEigenLayerMiddlewareV1 middleware = _readMiddleware(); + IAVSDirectory avsDirectory = _readAvsDirectory(); + OperatorConfig memory config = _readConfig("config/holesky/operator.json"); + + console.log("Registering EigenLayer operator"); + console.log("Operator address:", operator); + console.log("Operator RPC:", config.rpc); + + bytes32 digest = avsDirectory.calculateOperatorAVSRegistrationDigestHash({ + operator: operator, + avs: address(middleware), + salt: config.salt, + expiry: config.expiry + }); + + (uint8 v, bytes32 r, bytes32 s) = vm.sign(operatorSk, digest); + bytes memory rawSignature = abi.encodePacked(r, s, v); + + ISignatureUtils.SignatureWithSaltAndExpiry memory operatorSignature = + ISignatureUtils.SignatureWithSaltAndExpiry(rawSignature, config.salt, config.expiry); + + vm.startBroadcast(operatorSk); + + middleware.registerOperator(config.rpc, operatorSignature); + console.log("Successfully registered EigenLayer operator"); + + vm.stopBroadcast(); + } + + function _readMiddleware() public view returns (BoltEigenLayerMiddlewareV1) { + string memory root = vm.projectRoot(); + string memory path = string.concat(root, "/config/holesky/deployments.json"); + string memory json = vm.readFile(path); + + return BoltEigenLayerMiddlewareV1(vm.parseJsonAddress(json, ".eigenLayer.middleware")); + } + + function _readAvsDirectory() public view returns (IAVSDirectory) { + string memory root = vm.projectRoot(); + string memory path = string.concat(root, "/config/holesky/deployments.json"); + string memory json = vm.readFile(path); + + return IAVSDirectory(vm.parseJsonAddress(json, ".eigenLayer.avsDirectory")); + } + + function _readConfig( + string memory path + ) public view returns (OperatorConfig memory) { + string memory json = vm.readFile(path); + + bytes32 salt = bytes32(0); + uint256 expiry = UINT256_MAX; + + try vm.parseJsonBytes32(json, ".salt") returns (bytes32 val) { + salt = val; + } catch { + console.log("No salt found in config, using 0"); + } + + try vm.parseJsonUint(json, ".expiry") returns (uint256 val) { + expiry = val; + } catch { + console.log("No expiry found in config, using UINT256_MAX"); + } + + return OperatorConfig({rpc: vm.parseJsonString(json, ".rpc"), salt: salt, expiry: expiry}); + } +} diff --git a/bolt-contracts/script/holesky/validators/RegisterSymbioticOperator.s.sol b/bolt-contracts/script/holesky/validators/RegisterSymbioticOperator.s.sol new file mode 100644 index 000000000..0089432c0 --- /dev/null +++ b/bolt-contracts/script/holesky/validators/RegisterSymbioticOperator.s.sol @@ -0,0 +1,59 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.25; + +import {Script, console} from "forge-std/Script.sol"; + +import {BoltSymbioticMiddlewareV1} from "../../../src/contracts/BoltSymbioticMiddlewareV1.sol"; +import {IOptInService} from "@symbiotic/interfaces/service/IOptInService.sol"; + +contract RegisterSymbioticOperator is Script { + struct Config { + string rpc; + BoltSymbioticMiddlewareV1 symbioticMiddleware; + IOptInService symbioticNetworkOptInService; + address symbioticNetwork; + } + + function run() public { + uint256 operatorSk = vm.envUint("OPERATOR_SK"); + + address operator = vm.addr(operatorSk); + + Config memory config = _readConfig(); + + // First, make sure the operator is opted into the network + if (!config.symbioticNetworkOptInService.isOptedIn(operator, config.symbioticNetwork)) { + console.log("Operator is not opted into the network yet. Opting in..."); + vm.startBroadcast(operatorSk); + config.symbioticNetworkOptInService.optIn(config.symbioticNetwork); + vm.stopBroadcast(); + console.log("Operator successfully opted into the network"); + } + + console.log("Registering Symbiotic operator"); + console.log("Operator address:", operator); + console.log("Operator RPC:", config.rpc); + + vm.startBroadcast(operatorSk); + config.symbioticMiddleware.registerOperator(config.rpc); + console.log("Successfully registered Symbiotic operator"); + + vm.stopBroadcast(); + } + + function _readConfig() public view returns (Config memory) { + string memory root = vm.projectRoot(); + string memory path = string.concat(root, "/config/holesky/deployments.json"); + string memory json = vm.readFile(path); + + string memory operatorPath = string.concat(root, "/config/holesky/operator.json"); + string memory operatorJson = vm.readFile(operatorPath); + + return Config({ + rpc: vm.parseJsonString(operatorJson, ".rpc"), + symbioticNetwork: vm.parseJsonAddress(json, ".symbiotic.network"), + symbioticMiddleware: BoltSymbioticMiddlewareV1(vm.parseJsonAddress(json, ".symbiotic.middleware")), + symbioticNetworkOptInService: IOptInService(vm.parseJsonAddress(json, ".symbiotic.networkOptInService")) + }); + } +} diff --git a/bolt-contracts/script/holesky/validators/RegisterValidators.s.sol b/bolt-contracts/script/holesky/validators/RegisterValidators.s.sol new file mode 100644 index 000000000..52fd1d0b3 --- /dev/null +++ b/bolt-contracts/script/holesky/validators/RegisterValidators.s.sol @@ -0,0 +1,100 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.25; + +import {IBoltValidatorsV1} from "../../../src/interfaces/IBoltValidatorsV1.sol"; +import {BLS12381} from "../../../src/lib/bls/BLS12381.sol"; + +import {Script, console} from "forge-std/Script.sol"; + +/// @notice Script to register Ethereum validators to Bolt +/// @dev this script reads from the config file in /config/holesky/register_validators.json +contract RegisterValidators is Script { + using BLS12381 for BLS12381.G1Point; + + struct RegisterValidatorsConfig { + uint128 maxCommittedGasLimit; + address authorizedOperator; + BLS12381.G1Point[] pubkeys; + } + + function run() public { + address controller = msg.sender; + + console.log("Registering validators to Bolt"); + console.log("Controller address: ", controller); + + IBoltValidatorsV1 validators = _readValidators(); + RegisterValidatorsConfig memory config = _parseConfig(); + + vm.startBroadcast(controller); + validators.batchRegisterValidatorsUnsafe(config.pubkeys, config.maxCommittedGasLimit, config.authorizedOperator); + vm.stopBroadcast(); + + console.log("Validators registered successfully"); + } + + function _readValidators() public view returns (IBoltValidatorsV1) { + string memory root = vm.projectRoot(); + string memory path = string.concat(root, "/config/holesky/deployments.json"); + string memory json = vm.readFile(path); + + return IBoltValidatorsV1(vm.parseJsonAddress(json, ".bolt.validators")); + } + + function _parseConfig() public returns (RegisterValidatorsConfig memory config) { + string memory root = vm.projectRoot(); + string memory path = string.concat(root, "/config/holesky/validators.json"); + string memory json = vm.readFile(path); + + config.authorizedOperator = vm.parseJsonAddress(json, ".authorizedOperator"); + config.maxCommittedGasLimit = uint128(vm.parseJsonUint(json, ".maxCommittedGasLimit")); + + string[] memory pubkeysRaw = vm.parseJsonStringArray(json, ".pubkeys"); + BLS12381.G1Point[] memory pubkeys = new BLS12381.G1Point[](pubkeysRaw.length); + + for (uint256 i = 0; i < pubkeysRaw.length; i++) { + string memory pubkey = pubkeysRaw[i]; + + string[] memory convertCmd = new string[](2); + convertCmd[0] = "./script/pubkey_to_g1_wrapper.sh"; + convertCmd[1] = pubkey; + + bytes memory output = vm.ffi(convertCmd); + string memory outputStr = string(output); + string[] memory array = vm.split(outputStr, ","); + + uint256[2] memory x = _bytesToParts(vm.parseBytes(array[0])); + uint256[2] memory y = _bytesToParts(vm.parseBytes(array[1])); + + pubkeys[i] = BLS12381.G1Point(x, y); + + console.log("Registering pubkey:", vm.toString(abi.encodePacked(pubkeys[i].compress()))); + } + + config.pubkeys = pubkeys; + } + + function _bytesToParts( + bytes memory data + ) public pure returns (uint256[2] memory out) { + require(data.length == 48, "Invalid data length"); + + uint256 value1; + uint256 value2; + + // Load the first 32 bytes into value1 + assembly { + value1 := mload(add(data, 32)) + } + value1 = value1 >> 128; // Clear unwanted upper bits + + // Load the next 16 bytes into value2 + assembly { + value2 := mload(add(data, 48)) + } + // value2 = value2 >> 128; + + out[0] = value1; + out[1] = value2; + } +} diff --git a/bolt-contracts/script/pubkey_to_g1-darwin-amd64 b/bolt-contracts/script/pubkey_to_g1-darwin-amd64 new file mode 100755 index 000000000..d0a8b990e Binary files /dev/null and b/bolt-contracts/script/pubkey_to_g1-darwin-amd64 differ diff --git a/bolt-contracts/script/pubkey_to_g1-darwin-arm64 b/bolt-contracts/script/pubkey_to_g1-darwin-arm64 new file mode 100755 index 000000000..fd51c95cc Binary files /dev/null and b/bolt-contracts/script/pubkey_to_g1-darwin-arm64 differ diff --git a/bolt-contracts/script/pubkey_to_g1-linux-amd64 b/bolt-contracts/script/pubkey_to_g1-linux-amd64 new file mode 100755 index 000000000..62679828f Binary files /dev/null and b/bolt-contracts/script/pubkey_to_g1-linux-amd64 differ diff --git a/bolt-contracts/script/pubkey_to_g1-linux-arm64 b/bolt-contracts/script/pubkey_to_g1-linux-arm64 new file mode 100755 index 000000000..234db8fb6 Binary files /dev/null and b/bolt-contracts/script/pubkey_to_g1-linux-arm64 differ diff --git a/bolt-contracts/script/pubkey_to_g1_wrapper.sh b/bolt-contracts/script/pubkey_to_g1_wrapper.sh new file mode 100755 index 000000000..260304707 --- /dev/null +++ b/bolt-contracts/script/pubkey_to_g1_wrapper.sh @@ -0,0 +1,32 @@ +#!/bin/bash + +# Notes on cross-compiling the binaries: +# +# Linux for arm64: +# - sudo apt install gcc-aarch64-linux-gnu g++-aarch64-linux-gnu +# - GOOS=linux GOARCH=arm64 CC=aarch64-linux-gnu-gcc CGO_ENABLED=1 go build -o pubkey_to_g1-linux-arm64 +# +# MacOS for amd64: +# - GOOS=darwin GOARCH=amd64 CGO_ENABLED=1 go build -o pubkey_to_g1-darwin-amd64 + + +arch=$(uname -i 2>/dev/null) || arch=$(uname -p) +if [[ $OSTYPE == linux* ]]; then + if [[ $arch == x86_64 ]]; then + ./script/pubkey_to_g1-linux-amd64 "$1" + elif [[ $arch == aarch64 ]]; then + ./script/pubkey_to_g1-linux-arm64 "$1" + else + exit 1 + fi +elif [[ $OSTYPE == darwin* ]]; then + if [[ $arch == arm* ]]; then + ./script/pubkey_to_g1-darwin-arm64 "$1" + elif [[ $arch == x86_64 ]]; then + ./script/pubkey_to_g1-darwin-amd64 "$1" + else + exit 1 + fi +else + exit 1 +fi \ No newline at end of file diff --git a/bolt-contracts/src/contracts/BoltChallenger.sol b/bolt-contracts/src/contracts/BoltChallenger.sol deleted file mode 100644 index d66784cc9..000000000 --- a/bolt-contracts/src/contracts/BoltChallenger.sol +++ /dev/null @@ -1,413 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.13; - -import {IProver} from "relic-sdk/packages/contracts/interfaces/IProver.sol"; -import {IReliquary} from "relic-sdk/packages/contracts/interfaces/IReliquary.sol"; -import {Facts, Fact, FactSignature} from "relic-sdk/packages/contracts/lib/Facts.sol"; -import {FactSigs} from "relic-sdk/packages/contracts/lib/FactSigs.sol"; -import {CoreTypes} from "relic-sdk/packages/contracts/lib/CoreTypes.sol"; - -import {ECDSA} from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; - -import {IBoltRegistry} from "../interfaces/IBoltRegistry.sol"; -import {IBoltChallenger} from "../interfaces/IBoltChallenger.sol"; - -import {SSZ} from "../lib/SSZ.sol"; -import {SSZContainers} from "../lib/SSZContainers.sol"; -import {BeaconChainUtils} from "../lib/BeaconChainUtils.sol"; - -contract BoltChallenger is IBoltChallenger { - /// @notice The max duration of a challenge, after which it is considered resolved - /// even if no one has provided a valid proof for it. - uint256 public constant MAX_CHALLENGE_DURATION = 7 days; - - /// @notice The bond required to open a challenge. This is to avoid spamming - /// and DOS attacks on proposers. If a challenge is successful, the bond is - /// returned to the challenger, otherwise it is sent to the based proposer. - uint256 public constant CHALLENGE_BOND = 1 ether; - - /// @notice The max number of slots that can pass after which a challenge cannot - /// be opened anymore. This corresponds to about 1 day. - /// @dev This is a limitation of the `BEACON_ROOTS` contract (see EIP-4788 for more info). - uint256 internal constant CHALLENGE_RETROACTIVE_TARGET_SLOT_WINDOW = 8190; - - /// @notice The address of the BoltRegistry contract - IBoltRegistry public immutable boltRegistry; - - /// @notice The address of the Relic Reliquary contract - IReliquary public immutable reliquary; - - /// @notice The address of the block header prover contract - IProver public immutable blockHeaderProver; - - /// @notice The address of the account info prover contract - IProver public immutable accountInfoProver; - - // Struct to hold all challenge details in stoage - struct Challenge { - // The address of the based proposer being challenged - address basedProposer; - // The signed commitment that the proposer supposedly failed to honor - SignedCommitment signedCommitment; - // The address of the challenger - address challenger; - // The beacon root object of the target slot's block header. - // This is directly fetched from the on-chain BEACON_ROOTS oracle. - bytes32 targetSlotBeaconRoot; - // The status of the challenge - ChallengeStatus status; - // The timestamp at which the challenge was opened - uint256 openTimestamp; - } - - /// @notice The struct to hold the inclusion commitment, including the proposer's signature - /// @dev there can be different kinds of commitments, this is just an example - struct SignedCommitment { - uint256 slot; - uint256 nonce; - uint256 gasUsed; - bytes32 transactionHash; - bytes signedRawTransaction; - bytes signature; - } - - /// @notice The mapping of challenges, indexed by the unique ID of their inclusion commitment - mapping(bytes32 => Challenge) public challenges; - - /// @notice Constructor - /// @param _boltRegistry The address of the BoltRegistry contract - /// @param _reliquary The address of the Relic Reliquary contract - /// @param _blockHeaderProver The address of the Relic block header prover contract - constructor( - address _boltRegistry, - address _reliquary, - address _blockHeaderProver, - address _accountInfoProver - ) { - boltRegistry = IBoltRegistry(_boltRegistry); - reliquary = IReliquary(_reliquary); - - // Check if the provided provers are valid - // TODO: readd this for mainnet deployment (currently disabled for testing) - // reliquary.checkProver(reliquary.provers(_blockHeaderProver)); - // reliquary.checkProver(reliquary.provers(_accountInfoProver)); - - blockHeaderProver = IProver(_blockHeaderProver); - accountInfoProver = IProver(_accountInfoProver); - } - - /// @notice Challenge a proposer if it hasn't honored a preconfirmation. - /// @notice A challenge requires a bond to be transferred to this contract to avoid spamming. - /// @param _basedProposer The address of the proposer to challenge - /// @param _signedCommitment The signed commitment that the proposer is getting challenged for - function challengeProposer( - address _basedProposer, - SignedCommitment calldata _signedCommitment - ) public payable { - // First sanity checks - if (_basedProposer == address(0) || _signedCommitment.slot == 0) { - revert InvalidChallenge(); - } - - // Check if there is a sufficient bond attached to the transaction - if (msg.value < CHALLENGE_BOND) { - revert InsufficientBond(); - } else if (msg.value > CHALLENGE_BOND) { - // Refund the excess bond - payable(msg.sender).transfer(msg.value - CHALLENGE_BOND); - } - - // Check if the target slot is not too far in the past - if ( - BeaconChainUtils._getSlotFromTimestamp(block.timestamp) - - _signedCommitment.slot > - CHALLENGE_RETROACTIVE_TARGET_SLOT_WINDOW - ) { - // Challenges cannot be opened for slots that are too far in the past, because we rely - // on the BEACON_ROOTS ring buffer to be available for the challenge to be resolved. - revert TargetSlotTooFarInThePast(); - } - - // Check if the proposer is an active based proposer - if (!boltRegistry.isActiveOperator(_basedProposer)) { - revert InvalidProposerAddress(); - } - - bytes32 commitmentID = _getCommitmentID(_signedCommitment); - - // Check if a challenge already exists for the given commitment - // Challenge duplicates are not allowed - if (challenges[commitmentID].basedProposer != address(0)) { - revert ChallengeAlreadyExists(); - } - - // Check if the signed commitment was made by the challenged based proposer - if ( - _recoverCommitmentSigner( - commitmentID, - _signedCommitment.signature - ) != _basedProposer - ) { - revert InvalidCommitmentSigner(); - } - - // Note: we don't check if the based proposer was actually scheduled for proposal at their - // target slot. Proposers are expected to not preconfirm if they are not the scheduled proposer, - // as they would be penalized for it. - - // Get the beacon block root for the target slot. We store it in the Challenge so that - // it can be used even after 8192 slots have passed (the limit of the BEACON_ROOTS contract) - bytes32 beaconBlockRoot = BeaconChainUtils._getBeaconBlockRoot( - _signedCommitment.slot - ); - - // ==== Create a new challenge ==== - - challenges[commitmentID] = Challenge({ - basedProposer: _basedProposer, - challenger: msg.sender, - signedCommitment: _signedCommitment, - targetSlotBeaconRoot: beaconBlockRoot, - status: ChallengeStatus.Pending, - openTimestamp: block.timestamp - }); - - emit NewChallenge(_basedProposer, commitmentID, _signedCommitment.slot); - } - - /// @notice Resolve a challenge by providing a valid proof for the preconfirmation. - /// @param _challengeID The unique ID of the challenge to resolve - /// @param _blockHeaderProof The proof of the block header of the target slot - /// @param _accountDataProof The proof of the account data of the preconfirmed sender - /// @param _transactionIndex The index of the transaction in the block - /// @param _inclusionProof The Merkle proof of the transaction's inclusion in the block - /// @dev anyone can call this function on a pending challenge, but only the challenged based proposer - /// @dev will be able to provide a valid proof to counter it. If the challenge expires or the proof is invalid, - /// @dev the challenger will be rewarded with the bond + a portion of the slashed amount. - function resolveChallenge( - bytes32 _challengeID, - bytes calldata _blockHeaderProof, - bytes calldata _accountDataProof, - uint256 _transactionIndex, - bytes32[] calldata _inclusionProof - ) public { - Challenge memory challenge = challenges[_challengeID]; - - // Check if the challenge exists - if (challenge.basedProposer == address(0)) { - revert ChallengeNotFound(); - } - - // Check if the challenge is still pending - if (challenge.status != ChallengeStatus.Pending) { - revert ChallengeAlreadyResolved(); - } - - // Check if the challenge has expired. - // This means that the validator failed to honor the commitment and will get slashed. - if ( - block.timestamp - challenge.openTimestamp > MAX_CHALLENGE_DURATION - ) { - // Part of the slashed amount will also be returned to the challenger as a reward. - // This is the reason we don't have access control in this function. - // TODO: slash the based proposer. - _onChallengeSuccess(_challengeID); - return; - } - - // From here on, we assume the function was called by the based proposer - if (msg.sender != challenge.basedProposer) { - revert Unauthorized(); - } - - // Derive the block header data of the target block from the block header proof - CoreTypes.BlockHeaderData - memory verifiedHeader = _deriveBlockHeaderInfo(_blockHeaderProof); - - // Derive the preconfirmed sender's account data from the account data proof - CoreTypes.AccountData memory verifiedAccount = _deriveAccountData( - _accountDataProof, - verifiedHeader.Number - ); - - // Check that the nonce of the preconfirmed sender is valid (not too low) - // at the time of the based proposer's slot. - if (verifiedAccount.Nonce > challenge.signedCommitment.nonce) { - // consider the challenge unsuccessful: the user sent a transaction before - // the proposer could include it, as such it is not at fault. - _onChallengeFailure(_challengeID); - return; - } - - // Check that the balance of the preconfirmed sender is enough to cover the base fee - // of the block. - if ( - verifiedAccount.Balance < - challenge.signedCommitment.gasUsed * verifiedHeader.BaseFee - ) { - // consider the challenge unsuccessful: the user doesn't have enough balance to cover the gas - // thus invalidating the preconfirmation: the proposer is not at fault. - _onChallengeFailure(_challengeID); - return; - } - - // TODO: we could use the beacon root oracle to check that the based proposer proposed a block - // at the target slot or if it was reorged. This could be useful to differentiate between a - // safety vs liveness fault. - - // Check if the block header timestamp is UP TO the challenge's target slot. - // It can be earlier, in case the transaction was included before the based proposer's slot. - if ( - verifiedHeader.Time > - BeaconChainUtils._getTimestampFromSlot( - challenge.signedCommitment.slot - ) - ) { - // The block header timestamp is after the target slot, so the proposer didn't - // honor the preconfirmation and the challenge is successful. - // TODO: slash the based proposer - _onChallengeSuccess(_challengeID); - return; - } - - bool isValid = _verifyInclusionProof( - verifiedHeader.TxHash, - _transactionIndex, - _inclusionProof, - challenge.signedCommitment.signedRawTransaction - ); - - if (!isValid) { - // The challenge was successful: the proposer failed to honor the preconfirmation - // TODO: slash the based proposer - _onChallengeSuccess(_challengeID); - } else { - // The challenge was unsuccessful: the proposer honored the preconfirmation - _onChallengeFailure(_challengeID); - } - } - - /// @notice Handle the success of a challenge - /// @param _challengeID The unique ID of the challenge - function _onChallengeSuccess(bytes32 _challengeID) internal { - Challenge storage challenge = challenges[_challengeID]; - challenge.status = ChallengeStatus.Resolved; - payable(challenge.challenger).transfer(CHALLENGE_BOND); - emit ChallengeResolved(_challengeID, ChallengeResult.Success); - } - - /// @notice Handle the failure of a challenge - /// @param _challengeID The unique ID of the challenge - function _onChallengeFailure(bytes32 _challengeID) internal { - Challenge storage challenge = challenges[_challengeID]; - challenge.status = ChallengeStatus.Resolved; - payable(challenge.basedProposer).transfer(CHALLENGE_BOND); - emit ChallengeResolved(_challengeID, ChallengeResult.Failure); - } - - /// @notice Fetch trustlessly valid block header data - /// @param _proof The ABI-encoded proof of the block header - /// @return header The block header data - function _deriveBlockHeaderInfo( - bytes calldata _proof - ) internal returns (CoreTypes.BlockHeaderData memory header) { - // TODO: handle fee for proving. make payable? - - Fact memory fact = blockHeaderProver.prove(_proof, false); - header = abi.decode(fact.data, (CoreTypes.BlockHeaderData)); - - if ( - FactSignature.unwrap(fact.sig) != - FactSignature.unwrap(FactSigs.blockHeaderSig(header.Number)) - ) { - revert UnexpectedFactSignature(); - } - } - - /// @notice Fetch trustlessly valid account data at a given block number - /// @param _proof The ABI-encoded proof of the account data - /// @param _blockNumber The block number for which the account data is being proven - /// @return account The account data - function _deriveAccountData( - bytes calldata _proof, - uint256 _blockNumber - ) internal returns (CoreTypes.AccountData memory account) { - // TODO: handle fee for proving. make payable? - - Fact memory fact = accountInfoProver.prove(_proof, false); - account = abi.decode(fact.data, (CoreTypes.AccountData)); - - // verify that the account data proof was provided for the correct block - if ( - FactSignature.unwrap(fact.sig) != - FactSignature.unwrap(FactSigs.accountFactSig(_blockNumber)) - ) { - revert UnexpectedFactSignature(); - } - } - - /// @notice Verify the inclusion proof of a transaction in a block - /// @param _transactionsRoot The transactions root of the block - /// @param _transactionIndex The index of the transaction in the block - /// @param _inclusionProof The Merkle proof of the transaction's inclusion in the block - /// @param _signedRawTransaction The signed raw transaction being proven - /// @return isValid true if the proof is valid, false otherwise - function _verifyInclusionProof( - bytes32 _transactionsRoot, - uint256 _transactionIndex, - bytes32[] calldata _inclusionProof, - bytes memory _signedRawTransaction - ) internal view returns (bool isValid) { - // Check if the transactions root matches the signed commitment - - // The genelized index is the index of the merkle tree generated by the merkleization - // process of a SSZ list of transactions. Since this list is dynamic and can be of maximum - // length of 2^21 = 2_097_152, the merkleization process fills the tree with empty hashes, - // therefore this number is an offset from where transactions hash tree root starts. - // To read more, check out https://github.com/ethereum/consensus-specs/blob/dev/ssz/simple-serialize.md#merkleization - uint256 generalizedIndex = 2_097_152 + _transactionIndex; - - bytes32 leaf = SSZContainers._transactionHashTreeRoot( - _signedRawTransaction - ); - - isValid = SSZ._verifyProof( - _inclusionProof, - _transactionsRoot, - leaf, - generalizedIndex - ); - } - - /// @notice Recover the signer of a commitment - /// @param _commitmentSignature The signature of the commitment - /// @param _commitmentHash The keccak hash of an unsigned message - function _recoverCommitmentSigner( - bytes32 _commitmentHash, - bytes calldata _commitmentSignature - ) internal pure returns (address) { - (address signer, ECDSA.RecoverError err, ) = ECDSA.tryRecover( - _commitmentHash, - _commitmentSignature - ); - if (err != ECDSA.RecoverError.NoError || signer == address(0)) { - revert InvalidCommitmentSignature(); - } - - return signer; - } - - /// @notice Hashes the inclusion commitment to a unique ID to index the challenge - function _getCommitmentID( - SignedCommitment memory _commitment - ) internal pure returns (bytes32) { - return - keccak256( - abi.encodePacked( - _commitment.slot, - _commitment.transactionHash, - _commitment.signedRawTransaction - ) - ); - } -} diff --git a/bolt-contracts/src/contracts/BoltChallengerV1.sol b/bolt-contracts/src/contracts/BoltChallengerV1.sol new file mode 100644 index 000000000..66594566d --- /dev/null +++ b/bolt-contracts/src/contracts/BoltChallengerV1.sol @@ -0,0 +1,577 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.25; + +import {OwnableUpgradeable} from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol"; +import {UUPSUpgradeable} from "@openzeppelin/contracts/proxy/utils/UUPSUpgradeable.sol"; + +import {EnumerableSet} from "@openzeppelin/contracts/utils/structs/EnumerableSet.sol"; +import {ECDSA} from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; +import {Time} from "@openzeppelin/contracts/utils/types/Time.sol"; + +import {SecureMerkleTrie} from "../lib/trie/SecureMerkleTrie.sol"; +import {MerkleTrie} from "../lib/trie/MerkleTrie.sol"; +import {RLPReader} from "../lib/rlp/RLPReader.sol"; +import {RLPWriter} from "../lib/rlp/RLPWriter.sol"; +import {TransactionDecoder} from "../lib/TransactionDecoder.sol"; +import {IBoltChallengerV1} from "../interfaces/IBoltChallengerV1.sol"; +import {IBoltParametersV1} from "../interfaces/IBoltParametersV1.sol"; + +/// @title Bolt Challenger +/// @notice Contract for managing (creating & resolving) challenges for Bolt inclusion commitments. +/// @dev This contract is upgradeable using the UUPSProxy pattern. Storage layout remains fixed across upgrades +/// with the use of storage gaps. +/// See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps +/// To validate the storage layout, use the Openzeppelin Foundry Upgrades toolkit. +/// You can also validate manually with forge: forge inspect storage-layout --pretty +contract BoltChallengerV1 is IBoltChallengerV1, OwnableUpgradeable, UUPSUpgradeable { + using RLPReader for bytes; + using RLPReader for RLPReader.RLPItem; + using TransactionDecoder for bytes; + using TransactionDecoder for TransactionDecoder.Transaction; + using EnumerableSet for EnumerableSet.Bytes32Set; + + // ========= STORAGE ========= + + /// @notice Bolt Parameters contract. + IBoltParametersV1 public parameters; + + /// @notice The set of existing unique challenge IDs. + EnumerableSet.Bytes32Set internal challengeIDs; + + /// @notice The mapping of challenge IDs to their respective challenges. + mapping(bytes32 => Challenge) internal challenges; + + // --> Storage layout marker: 4 slots + + /** + * @dev This empty reserved space is put in place to allow future versions to add new + * variables without shifting down storage in the inheritance chain. + * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps + * This can be validated with the Openzeppelin Foundry Upgrades toolkit. + * + * Total storage slots: 50 + */ + uint256[46] private __gap; + + // ========= INITIALIZER ========= + + /// @notice Initializer + /// @param _owner Address of the owner of the contract + /// @param _parameters Address of the Bolt Parameters contract + function initialize(address _owner, address _parameters) public initializer { + __Ownable_init(_owner); + + parameters = IBoltParametersV1(_parameters); + } + + function _authorizeUpgrade( + address newImplementation + ) internal override onlyOwner {} + + // ========= VIEW FUNCTIONS ========= + + /// @notice Get all existing challenges. + /// @dev Should be used in view contexts only to avoid unnecessary gas costs. + /// @return allChallenges The array of all existing challenges. + function getAllChallenges() public view returns (Challenge[] memory) { + Challenge[] memory allChallenges = new Challenge[](challengeIDs.length()); + + for (uint256 i = 0; i < challengeIDs.length(); i++) { + allChallenges[i] = challenges[challengeIDs.at(i)]; + } + + return allChallenges; + } + + /// @notice Get all currently non-resolved challenges. + /// @dev Should be used in view contexts only to avoid unnecessary gas costs. + /// @return openChallenges The array of all currently non-resolved challenges. + function getOpenChallenges() public view returns (Challenge[] memory) { + uint256 openCount = 0; + for (uint256 i = 0; i < challengeIDs.length(); i++) { + if (challenges[challengeIDs.at(i)].status == ChallengeStatus.Open) { + openCount++; + } + } + + Challenge[] memory openChallenges = new Challenge[](openCount); + + uint256 j = 0; + for (uint256 i = 0; i < challengeIDs.length(); i++) { + Challenge memory challenge = challenges[challengeIDs.at(i)]; + if (challenge.status == ChallengeStatus.Open) { + openChallenges[j] = challenge; + j++; + } + } + + return openChallenges; + } + + /// @notice Get a challenge by its ID. + /// @param challengeID The ID of the challenge to get. + /// @return challenge The challenge with the given ID. + function getChallengeByID( + bytes32 challengeID + ) public view returns (Challenge memory) { + if (!challengeIDs.contains(challengeID)) { + revert ChallengeDoesNotExist(); + } + + return challenges[challengeID]; + } + + // ========= CHALLENGE CREATION ========= + + /// @notice Open a challenge against a bundle of committed transactions. + /// @dev The challenge bond must be paid in order to open a challenge. + /// @param commitments The signed commitments to open a challenge for. + function openChallenge( + SignedCommitment[] calldata commitments + ) public payable { + if (commitments.length == 0) { + revert EmptyCommitments(); + } + + // Check that the attached bond amount is correct + if (msg.value != parameters.CHALLENGE_BOND()) { + revert IncorrectChallengeBond(); + } + + // Compute the unique challenge ID, based on the signatures of the provided commitments + bytes32 challengeID = _computeChallengeID(commitments); + + // Check that a challenge for this commitment bundle does not already exist + if (challengeIDs.contains(challengeID)) { + revert ChallengeAlreadyExists(); + } + + uint256 targetSlot = commitments[0].slot; + if (targetSlot > _getCurrentSlot() - parameters.JUSTIFICATION_DELAY()) { + // We cannot open challenges for slots that are not finalized by Ethereum consensus yet. + // This is admittedly a bit strict, since 32-slot deep reorgs are very unlikely. + revert BlockIsNotFinalized(); + } + + // Check that all commitments are for the same slot and signed by the same sender + // and store the parsed transaction data for each commitment + TransactionData[] memory transactionsData = new TransactionData[](commitments.length); + (address txSender, address commitmentSigner, TransactionData memory firstTransactionData) = + _recoverCommitmentData(commitments[0]); + + transactionsData[0] = firstTransactionData; + + for (uint256 i = 1; i < commitments.length; i++) { + (address otherTxSender, address otherCommitmentSigner, TransactionData memory otherTransactionData) = + _recoverCommitmentData(commitments[i]); + + transactionsData[i] = otherTransactionData; + + // check that all commitments are for the same slot + if (commitments[i].slot != targetSlot) { + revert UnexpectedMixedSlots(); + } + + // check that all commitments are signed by the same sender + if (otherTxSender != txSender) { + revert UnexpectedMixedSenders(); + } + + // check that all commitments are signed by the same signer (aka "operator") + if (otherCommitmentSigner != commitmentSigner) { + revert UnexpectedMixedSigners(); + } + + // check that the nonces are strictly sequentially increasing in the bundle + if (otherTransactionData.nonce != transactionsData[i - 1].nonce + 1) { + revert UnexpectedNonceOrder(); + } + } + + // Add the challenge to the set of challenges + challengeIDs.add(challengeID); + challenges[challengeID] = Challenge({ + id: challengeID, + openedAt: Time.timestamp(), + status: ChallengeStatus.Open, + targetSlot: targetSlot, + challenger: msg.sender, + commitmentSigner: commitmentSigner, + commitmentReceiver: txSender, + committedTxs: transactionsData + }); + emit ChallengeOpened(challengeID, msg.sender, commitmentSigner); + } + + // ========= CHALLENGE RESOLUTION ========= + + /// @notice Resolve a challenge by providing proofs of the inclusion of the committed transactions. + /// @dev Challenges are DEFENDED if the resolver successfully defends the inclusion of the transactions. + /// In the event of no valid defense in the challenge time window, the challenge is considered BREACHED + /// and anyone can call `resolveExpiredChallenge()` to settle the challenge. + /// @param challengeID The ID of the challenge to resolve. + /// @param proof The proof data to resolve the challenge. + function resolveOpenChallenge(bytes32 challengeID, Proof calldata proof) public { + // Check that the challenge exists + if (!challengeIDs.contains(challengeID)) { + revert ChallengeDoesNotExist(); + } + + // The visibility of the BLOCKHASH opcode is limited to the 256 most recent blocks. + // For simplicity we restrict this to 256 slots even though 256 blocks would be more accurate. + if (challenges[challengeID].targetSlot < _getCurrentSlot() - parameters.BLOCKHASH_EVM_LOOKBACK()) { + revert BlockIsTooOld(); + } + + // Check that the previous block is within the EVM lookback window for block hashes. + // Clearly, if the previous block is available, the inclusion one will be too. + uint256 previousBlockNumber = proof.inclusionBlockNumber - 1; + if ( + previousBlockNumber > block.number + || previousBlockNumber < block.number - parameters.BLOCKHASH_EVM_LOOKBACK() + ) { + revert InvalidBlockNumber(); + } + + // Get the trusted block hash for the block number in which the transactions were included. + bytes32 trustedPreviousBlockHash = blockhash(proof.inclusionBlockNumber); + + // Finally resolve the challenge with the trusted block hash and the provided proofs + _resolve(challengeID, trustedPreviousBlockHash, proof); + } + + /// @notice Resolve a challenge that has expired without being resolved. + /// @dev This will result in the challenge being considered breached, without need to provide + /// additional proofs of inclusion, as the time window has elapsed. + /// @param challengeID The ID of the challenge to resolve. + function resolveExpiredChallenge( + bytes32 challengeID + ) public { + if (!challengeIDs.contains(challengeID)) { + revert ChallengeDoesNotExist(); + } + + Challenge storage challenge = challenges[challengeID]; + + if (challenge.status != ChallengeStatus.Open) { + revert ChallengeAlreadyResolved(); + } + + if (challenge.openedAt + parameters.MAX_CHALLENGE_DURATION() >= Time.timestamp()) { + revert ChallengeNotExpired(); + } + + // If the challenge has expired without being resolved, it is considered breached. + _settleChallengeResolution(ChallengeStatus.Breached, challenge); + } + + /// @notice Resolve a challenge by providing proofs of the inclusion of the committed transactions. + /// @dev Challenges are DEFENDED if the resolver successfully defends the inclusion of the transactions. + /// In the event of no valid defense in the challenge time window, the challenge is considered BREACHED. + /// @param challengeID The ID of the challenge to resolve. + /// @param trustedPreviousBlockHash The block hash of the block before the inclusion block of the committed txs. + /// @param proof The proof data to resolve the challenge. See `IBoltChallenger.Proof` struct for more details. + function _resolve(bytes32 challengeID, bytes32 trustedPreviousBlockHash, Proof calldata proof) internal { + if (!challengeIDs.contains(challengeID)) { + revert ChallengeDoesNotExist(); + } + + Challenge storage challenge = challenges[challengeID]; + + if (challenge.status != ChallengeStatus.Open) { + revert ChallengeAlreadyResolved(); + } + + if (challenge.openedAt + parameters.MAX_CHALLENGE_DURATION() < Time.timestamp()) { + // If the challenge has expired without being resolved, it is considered breached. + // This should be handled by calling the `resolveExpiredChallenge()` function instead. + revert ChallengeExpired(); + } + + // Check the integrity of the proof data + uint256 committedTxsCount = challenge.committedTxs.length; + if (proof.txMerkleProofs.length != committedTxsCount || proof.txIndexesInBlock.length != committedTxsCount) { + revert InvalidProofsLength(); + } + + // Check the integrity of the trusted block hash + bytes32 previousBlockHash = keccak256(proof.previousBlockHeaderRLP); + if (previousBlockHash != trustedPreviousBlockHash) { + revert InvalidBlockHash(); + } + + // Decode the RLP-encoded block header of the previous block to the inclusion block. + // + // The previous block's state root is necessary to verify the account had the correct balance and + // nonce at the top of the inclusion block (before any transactions were applied). + BlockHeaderData memory previousBlockHeader = _decodeBlockHeaderRLP(proof.previousBlockHeaderRLP); + + // Decode the RLP-encoded block header of the inclusion block. + // + // The inclusion block is necessary to extract the transaction root and verify the inclusion of the + // committed transactions. By checking against the previous block's parent hash we can ensure this + // is the correct block trusting a single block hash. + BlockHeaderData memory inclusionBlockHeader = _decodeBlockHeaderRLP(proof.inclusionBlockHeaderRLP); + + // Check that the inclusion block is a child of the previous block + if (inclusionBlockHeader.parentHash != previousBlockHash) { + revert InvalidParentBlockHash(); + } + + // Decode the account fields by checking the account proof against the state root of the previous block header. + // The key in the account trie is the account pubkey (address) that sent the committed transactions. + (bool accountExists, bytes memory accountRLP) = SecureMerkleTrie.get( + abi.encodePacked(challenge.commitmentReceiver), proof.accountMerkleProof, previousBlockHeader.stateRoot + ); + + if (!accountExists) { + revert AccountDoesNotExist(); + } + + // Extract the nonce and balance of the account from the RLP-encoded data + AccountData memory account = _decodeAccountRLP(accountRLP); + + // Loop through each committed transaction and verify its inclusion in the block + // along with the sender's balance and nonce (starting from the account state at the top of the block). + for (uint256 i = 0; i < committedTxsCount; i++) { + TransactionData memory committedTx = challenge.committedTxs[i]; + + if (account.nonce > committedTx.nonce) { + // The tx sender (aka "challenge.commitmentReceiver") has sent a transaction with a higher nonce + // than the committed transaction, before the proposer could include it. Consider the challenge + // defended, as the proposer is not at fault. + _settleChallengeResolution(ChallengeStatus.Defended, challenge); + return; + } + + if (account.balance < inclusionBlockHeader.baseFee * committedTx.gasLimit) { + // The tx sender account doesn't have enough balance to pay for the worst-case baseFee of the committed + // transaction. Consider the challenge defended, as the proposer is not at fault. + _settleChallengeResolution(ChallengeStatus.Defended, challenge); + return; + } + + // Over/Underflow is checked in the previous if statements. + // + // Note: This is the same logic applied by the Bolt Sidecar's off-chain checks + // before deciding to sign a new commitment for a particular account. + account.balance -= inclusionBlockHeader.baseFee * committedTx.gasLimit; + account.nonce++; + + // The key in the transaction trie is the RLP-encoded index of the transaction in the block + bytes memory txLeaf = RLPWriter.writeUint(proof.txIndexesInBlock[i]); + + // Verify transaction inclusion proof + // + // The transactions trie is built with raw leaves, without hashing them first + // (This denotes why we use `MerkleTrie.get()` as opposed to `SecureMerkleTrie.get()`). + (bool txExists, bytes memory txRLP) = + MerkleTrie.get(txLeaf, proof.txMerkleProofs[i], inclusionBlockHeader.txRoot); + + if (!txExists) { + revert TransactionNotIncluded(); + } + + // Check if the committed transaction hash matches the hash of the included transaction + if (committedTx.txHash != keccak256(txRLP)) { + revert WrongTransactionHashProof(); + } + } + + // If all checks pass, the challenge is considered DEFENDED as the proposer provided valid proofs. + _settleChallengeResolution(ChallengeStatus.Defended, challenge); + } + + // ========= HELPERS ========= + + /// @notice Settle the resolution of a challenge based on the outcome. + /// @dev The outcome must be either DEFENDED or BREACHED. + /// @param outcome The outcome of the challenge resolution. + /// @param challenge The challenge to settle the resolution for. + function _settleChallengeResolution(ChallengeStatus outcome, Challenge storage challenge) internal { + if (outcome == ChallengeStatus.Defended) { + // If the challenge is considered DEFENDED, the proposer has provided valid proofs. + // The bond will be shared between the resolver and commitment signer. + challenge.status = ChallengeStatus.Defended; + _transferHalfBond(msg.sender); + _transferHalfBond(challenge.commitmentSigner); + emit ChallengeDefended(challenge.id); + } else if (outcome == ChallengeStatus.Breached) { + // If the challenge is considered BREACHED, the proposer has failed to provide valid proofs. + // The bond will be transferred back to the challenger in full. + challenge.status = ChallengeStatus.Breached; + _transferFullBond(challenge.challenger); + emit ChallengeBreached(challenge.id); + } + + // Remove the challenge from the set of challenges + delete challenges[challenge.id]; + challengeIDs.remove(challenge.id); + } + + /// @notice Recover the commitment data from a signed commitment. + /// @param commitment The signed commitment to recover the data from. + /// @return txSender The sender of the committed transaction. + /// @return commitmentSigner The signer of the commitment. + /// @return transactionData The decoded transaction data of the committed transaction. + function _recoverCommitmentData( + SignedCommitment calldata commitment + ) internal pure returns (address txSender, address commitmentSigner, TransactionData memory transactionData) { + commitmentSigner = ECDSA.recover(_computeCommitmentID(commitment), commitment.signature); + TransactionDecoder.Transaction memory decodedTx = commitment.signedTx.decodeEnveloped(); + txSender = decodedTx.recoverSender(); + transactionData = TransactionData({ + txHash: keccak256(commitment.signedTx), + nonce: decodedTx.nonce, + gasLimit: decodedTx.gasLimit + }); + } + + /// @notice Compute the challenge ID for a given set of signed commitments. + /// @dev Formula: `keccak( keccak(signature_1) || keccak(signature_2) || ... )` + /// @param commitments The signed commitments to compute the ID for. + /// @return challengeID The computed challenge ID. + function _computeChallengeID( + SignedCommitment[] calldata commitments + ) internal pure returns (bytes32) { + bytes32[] memory signatures = new bytes32[](commitments.length); + for (uint256 i = 0; i < commitments.length; i++) { + signatures[i] = keccak256(commitments[i].signature); + } + + return keccak256(abi.encodePacked(signatures)); + } + + /// @notice Compute the commitment ID for a given signed commitment. + /// @param commitment The signed commitment to compute the ID for. + /// @return commitmentID The computed commitment ID. + function _computeCommitmentID( + SignedCommitment calldata commitment + ) internal pure returns (bytes32) { + return keccak256(abi.encodePacked(keccak256(commitment.signedTx), _toLittleEndian(commitment.slot))); + } + + /// @notice Helper to convert a u64 to a little-endian bytes + /// @param x The u64 to convert + /// @return b The little-endian bytes + function _toLittleEndian( + uint64 x + ) internal pure returns (bytes memory) { + bytes memory b = new bytes(8); + for (uint256 i = 0; i < 8; i++) { + b[i] = bytes1(uint8(x >> (8 * i))); + } + return b; + } + + /// @notice Decode the block header fields from an RLP-encoded block header. + /// @param headerRLP The RLP-encoded block header to decode + function _decodeBlockHeaderRLP( + bytes calldata headerRLP + ) internal pure returns (BlockHeaderData memory blockHeader) { + RLPReader.RLPItem[] memory headerFields = headerRLP.toRLPItem().readList(); + + blockHeader.parentHash = headerFields[0].readBytes32(); + blockHeader.stateRoot = headerFields[3].readBytes32(); + blockHeader.txRoot = headerFields[4].readBytes32(); + blockHeader.blockNumber = headerFields[8].readUint256(); + blockHeader.timestamp = headerFields[11].readUint256(); + blockHeader.baseFee = headerFields[15].readUint256(); + } + + /// @notice Decode the account fields from an RLP-encoded account. + /// @param accountRLP The RLP-encoded account to decode + /// @return account The decoded account data. + function _decodeAccountRLP( + bytes memory accountRLP + ) internal pure returns (AccountData memory account) { + RLPReader.RLPItem[] memory accountFields = accountRLP.toRLPItem().readList(); + + account.nonce = accountFields[0].readUint256(); + account.balance = accountFields[1].readUint256(); + } + + /// @notice Transfer the full challenge bond to a recipient. + /// @param recipient The address to transfer the bond to. + function _transferFullBond( + address recipient + ) internal { + (bool success,) = payable(recipient).call{value: parameters.CHALLENGE_BOND()}(""); + if (!success) { + revert BondTransferFailed(); + } + } + + /// @notice Transfer half of the challenge bond to a recipient. + /// @param recipient The address to transfer half of the bond to. + function _transferHalfBond( + address recipient + ) internal { + (bool success,) = payable(recipient).call{value: parameters.CHALLENGE_BOND() / 2}(""); + if (!success) { + revert BondTransferFailed(); + } + } + + /// @notice Get the slot number from a given timestamp + /// @param _timestamp The timestamp + /// @return The slot number + function _getSlotFromTimestamp( + uint256 _timestamp + ) internal view returns (uint256) { + return (_timestamp - parameters.ETH2_GENESIS_TIMESTAMP()) / parameters.SLOT_TIME(); + } + + /// @notice Get the timestamp from a given slot + /// @param _slot The slot number + /// @return The timestamp + function _getTimestampFromSlot( + uint256 _slot + ) internal view returns (uint256) { + return parameters.ETH2_GENESIS_TIMESTAMP() + _slot * parameters.SLOT_TIME(); + } + + /// @notice Get the beacon block root for a given slot + /// @param _slot The slot number + /// @return The beacon block root + function _getBeaconBlockRootAtSlot( + uint256 _slot + ) internal view returns (bytes32) { + uint256 slotTimestamp = parameters.ETH2_GENESIS_TIMESTAMP() + _slot * parameters.SLOT_TIME(); + return _getBeaconBlockRootAtTimestamp(slotTimestamp); + } + + function _getBeaconBlockRootAtTimestamp( + uint256 _timestamp + ) internal view returns (bytes32) { + (bool success, bytes memory data) = parameters.BEACON_ROOTS_CONTRACT().staticcall(abi.encode(_timestamp)); + + if (!success || data.length == 0) { + revert BeaconRootNotFound(); + } + + return abi.decode(data, (bytes32)); + } + + /// @notice Get the latest beacon block root + /// @return The beacon block root + function _getLatestBeaconBlockRoot() internal view returns (bytes32) { + uint256 latestSlot = _getSlotFromTimestamp(block.timestamp); + return _getBeaconBlockRootAtSlot(latestSlot); + } + + /// @notice Get the current slot + /// @return The current slot + function _getCurrentSlot() internal view returns (uint256) { + return _getSlotFromTimestamp(block.timestamp); + } + + /// @notice Check if a timestamp is within the EIP-4788 window + /// @param _timestamp The timestamp + /// @return True if the timestamp is within the EIP-4788 window, false otherwise + function _isWithinEIP4788Window( + uint256 _timestamp + ) internal view returns (bool) { + return _getSlotFromTimestamp(_timestamp) <= _getCurrentSlot() + parameters.EIP4788_WINDOW(); + } +} diff --git a/bolt-contracts/src/contracts/BoltEigenLayerMiddlewareV1.sol b/bolt-contracts/src/contracts/BoltEigenLayerMiddlewareV1.sol new file mode 100644 index 000000000..750501777 --- /dev/null +++ b/bolt-contracts/src/contracts/BoltEigenLayerMiddlewareV1.sol @@ -0,0 +1,354 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.25; + +import {Time} from "@openzeppelin/contracts/utils/types/Time.sol"; +import {EnumerableMap} from "@openzeppelin/contracts/utils/structs/EnumerableMap.sol"; +import {EnumerableSet} from "@openzeppelin/contracts/utils/structs/EnumerableSet.sol"; +import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; +import {OwnableUpgradeable} from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol"; +import {UUPSUpgradeable} from "@openzeppelin/contracts/proxy/utils/UUPSUpgradeable.sol"; + +import {MapWithTimeData} from "../lib/MapWithTimeData.sol"; +import {IBoltParametersV1} from "../interfaces/IBoltParametersV1.sol"; +import {IBoltValidatorsV1} from "../interfaces/IBoltValidatorsV1.sol"; +import {IBoltMiddlewareV1} from "../interfaces/IBoltMiddlewareV1.sol"; +import {IBoltManagerV1} from "../interfaces/IBoltManagerV1.sol"; + +import {IStrategyManager} from "@eigenlayer/src/contracts/interfaces/IStrategyManager.sol"; +import {IAVSDirectory} from "@eigenlayer/src/contracts/interfaces/IAVSDirectory.sol"; +import {IDelegationManager} from "@eigenlayer/src/contracts/interfaces/IDelegationManager.sol"; +import {ISignatureUtils} from "@eigenlayer/src/contracts/interfaces/ISignatureUtils.sol"; +import {IStrategy} from "@eigenlayer/src/contracts/interfaces/IStrategy.sol"; +import {AVSDirectoryStorage} from "@eigenlayer/src/contracts/core/AVSDirectoryStorage.sol"; +import {DelegationManagerStorage} from "@eigenlayer/src/contracts/core/DelegationManagerStorage.sol"; +import {StrategyManagerStorage} from "@eigenlayer/src/contracts/core/StrategyManagerStorage.sol"; + +/// @title Bolt Manager +/// @notice This contract is responsible for interfacing with the EigenLayer restaking protocol. +/// @dev This contract is upgradeable using the UUPSProxy pattern. Storage layout remains fixed across upgrades +/// with the use of storage gaps. +/// See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps +/// To validate the storage layout, use the Openzeppelin Foundry Upgrades toolkit. +/// You can also validate manually with forge: forge inspect storage-layout --pretty +contract BoltEigenLayerMiddlewareV1 is IBoltMiddlewareV1, OwnableUpgradeable, UUPSUpgradeable { + using EnumerableSet for EnumerableSet.AddressSet; + using EnumerableMap for EnumerableMap.AddressToUintMap; + using MapWithTimeData for EnumerableMap.AddressToUintMap; + + // ========= STORAGE ========= + + /// @notice Start timestamp of the first epoch. + uint48 public START_TIMESTAMP; + + /// @notice Bolt Parameters contract. + IBoltParametersV1 public parameters; + + /// @notice Validators registry, where validators are registered via their + /// BLS pubkey and are assigned a sequence number. + IBoltManagerV1 public manager; + + /// @notice Set of EigenLayer protocol strategies that are used in Bolt Protocol. + EnumerableMap.AddressToUintMap private strategies; + + /// @notice Address of the EigenLayer AVS Directory contract. + IAVSDirectory public AVS_DIRECTORY; + + /// @notice Address of the EigenLayer Delegation Manager contract. + DelegationManagerStorage public DELEGATION_MANAGER; + + /// @notice Address of the EigenLayer Strategy Manager contract. + StrategyManagerStorage public STRATEGY_MANAGER; + + /// @notice Name hash of the restaking protocol for identifying the instance of `IBoltMiddleware`. + bytes32 public NAME_HASH; + + // --> Storage layout marker: 9 slots + + /** + * @dev This empty reserved space is put in place to allow future versions to add new + * variables without shifting down storage in the inheritance chain. + * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps + * This can be validated with the Openzeppelin Foundry Upgrades toolkit. + * + * Total storage slots: 50 + */ + uint256[41] private __gap; + + // ========= ERRORS ========= + + error StrategyNotAllowed(); + error OperatorAlreadyRegisteredToAVS(); + + // ========= INITIALIZER & PROXY FUNCTIONALITY ========= // + + /// @notice Constructor for the BoltEigenLayerMiddleware contract. + /// @param _parameters The address of the Bolt Parameters contract. + /// @param _manager The address of the Bolt Manager contract. + /// @param _eigenlayerAVSDirectory The address of the EigenLayer AVS Directory contract. + /// @param _eigenlayerDelegationManager The address of the EigenLayer Delegation Manager contract. + /// @param _eigenlayerStrategyManager The address of the EigenLayer Strategy Manager. + function initialize( + address _owner, + address _parameters, + address _manager, + address _eigenlayerAVSDirectory, + address _eigenlayerDelegationManager, + address _eigenlayerStrategyManager + ) public initializer { + __Ownable_init(_owner); + parameters = IBoltParametersV1(_parameters); + manager = IBoltManagerV1(_manager); + START_TIMESTAMP = Time.timestamp(); + + AVS_DIRECTORY = IAVSDirectory(_eigenlayerAVSDirectory); + DELEGATION_MANAGER = DelegationManagerStorage(_eigenlayerDelegationManager); + STRATEGY_MANAGER = StrategyManagerStorage(_eigenlayerStrategyManager); + NAME_HASH = keccak256("EIGENLAYER"); + } + + function _authorizeUpgrade( + address newImplementation + ) internal override onlyOwner {} + + // ========= VIEW FUNCTIONS ========= + + /// @notice Get the start timestamp of an epoch. + function getEpochStartTs( + uint48 epoch + ) public view returns (uint48 timestamp) { + return START_TIMESTAMP + epoch * parameters.EPOCH_DURATION(); + } + + /// @notice Get the epoch at a given timestamp. + function getEpochAtTs( + uint48 timestamp + ) public view returns (uint48 epoch) { + return (timestamp - START_TIMESTAMP) / parameters.EPOCH_DURATION(); + } + + /// @notice Get the current epoch. + function getCurrentEpoch() public view returns (uint48 epoch) { + return getEpochAtTs(Time.timestamp()); + } + + function getWhitelistedStrategies() public view returns (address[] memory) { + return strategies.keys(); + } + + // ========= ADMIN FUNCTIONS ========= + /// @notice Register a strategy to work in Bolt Protocol. + /// @param strategy The EigenLayer strategy address + function registerStrategy( + address strategy + ) public onlyOwner { + if (strategies.contains(strategy)) { + revert AlreadyRegistered(); + } + + if (!STRATEGY_MANAGER.strategyIsWhitelistedForDeposit(IStrategy(strategy))) { + revert StrategyNotAllowed(); + } + + strategies.add(strategy); + strategies.enable(strategy); + } + + /// @notice Deregister a strategy from working in Bolt Protocol. + /// @param strategy The EigenLayer strategy address. + function deregisterStrategy( + address strategy + ) public onlyOwner { + if (!strategies.contains(strategy)) { + revert NotRegistered(); + } + + strategies.remove(strategy); + } + + // ========= EIGENLAYER MIDDLEWARE LOGIC ========= + + /// @notice Allow an operator to signal opt-in to Bolt Protocol. + /// @dev This requires calling the EigenLayer AVS Directory contract to register the operator. + /// EigenLayer internally contains a mapping from `msg.sender` (our AVS contract) to the operator. + /// The msg.sender of this call will be the operator address. + function registerOperator( + string calldata rpc, + ISignatureUtils.SignatureWithSaltAndExpiry calldata operatorSignature + ) public { + if (manager.isOperator(msg.sender)) { + revert AlreadyRegistered(); + } + + if (!DELEGATION_MANAGER.isOperator(msg.sender)) { + revert NotOperator(); + } + + // Register the operator to the AVS directory for this AVS + AVS_DIRECTORY.registerOperatorToAVS(msg.sender, operatorSignature); + + // Register the operator in the manager + manager.registerOperator(msg.sender, rpc); + } + + /// @notice Deregister an EigenLayer operator from working in Bolt Protocol. + /// @dev This requires calling the EigenLayer AVS Directory contract to deregister the operator. + /// EigenLayer internally contains a mapping from `msg.sender` (our AVS contract) to the operator. + function deregisterOperator() public { + if (!manager.isOperator(msg.sender)) { + revert NotRegistered(); + } + + AVS_DIRECTORY.deregisterOperatorFromAVS(msg.sender); + + manager.deregisterOperator(msg.sender); + } + + /// @notice Allow an operator to signal indefinite opt-out from Bolt Protocol. + /// @dev Pausing activity does not prevent the operator from being slashable for + /// the current network epoch until the end of the slashing window. + function pauseOperator() public { + manager.pauseOperator(msg.sender); + } + + /// @notice Allow a disabled operator to signal opt-in to Bolt Protocol. + function unpauseOperator() public { + manager.unpauseOperator(msg.sender); + } + + /// @notice Allow a strategy to signal indefinite opt-out from Bolt Protocol. + function pauseStrategy() public { + if (!strategies.contains(msg.sender)) { + revert NotRegistered(); + } + + strategies.disable(msg.sender); + } + + /// @notice Allow a disabled strategy to signal opt-in to Bolt Protocol. + function unpauseStrategy() public { + if (!strategies.contains(msg.sender)) { + revert NotRegistered(); + } + + strategies.enable(msg.sender); + } + + /// @notice Check if a strategy is currently enabled to work in Bolt Protocol. + /// @param strategy The strategy address to check the enabled status for. + /// @return True if the strategy is enabled, false otherwise. + function isStrategyEnabled( + address strategy + ) public view returns (bool) { + (uint48 enabledTime, uint48 disabledTime) = strategies.getTimes(strategy); + return enabledTime != 0 && disabledTime == 0; + } + + /// @notice Get the collaterals and amounts staked by an operator across the supported strategies. + /// + /// @param operator The operator address to get the collaterals and amounts staked for. + /// @return collaterals The collaterals staked by the operator. + /// @dev Assumes that the operator is registered and enabled. + function getOperatorCollaterals( + address operator + ) public view returns (address[] memory, uint256[] memory) { + address[] memory collateralTokens = new address[](strategies.length()); + uint256[] memory amounts = new uint256[](strategies.length()); + + uint48 epochStartTs = getEpochStartTs(getEpochAtTs(Time.timestamp())); + + IStrategy[] memory strategyImpls = new IStrategy[](strategies.length()); + + for (uint256 i = 0; i < strategies.length(); ++i) { + (address strategy, uint48 enabledTime, uint48 disabledTime) = strategies.atWithTimes(i); + + if (!_wasEnabledAt(enabledTime, disabledTime, epochStartTs)) { + continue; + } + + IStrategy strategyImpl = IStrategy(strategy); + + address collateral = address(strategyImpl.underlyingToken()); + collateralTokens[i] = collateral; + + strategyImpls[i] = strategyImpl; + } + + // NOTE: order is preserved, which is why we can use the same index for both arrays below + uint256[] memory shares = DELEGATION_MANAGER.getOperatorShares(operator, strategyImpls); + + for (uint256 i = 0; i < strategyImpls.length; ++i) { + amounts[i] = strategyImpls[i].sharesToUnderlyingView(shares[i]); + } + + return (collateralTokens, amounts); + } + + /// @notice Get the amount of tokens delegated to an operator across the allowed strategies. + /// @param operator The operator address to get the stake for. + /// @param collateral The collateral address to get the stake for. + /// @return amount The amount of tokens delegated to the operator of the specified collateral. + function getOperatorStake(address operator, address collateral) public view returns (uint256 amount) { + uint48 timestamp = Time.timestamp(); + return getOperatorStakeAt(operator, collateral, timestamp); + } + + /// @notice Get the stake of an operator in EigenLayer protocol at a given timestamp. + /// @param operator The operator address to check the stake for. + /// @param collateral The collateral address to check the stake for. + /// @param timestamp The timestamp to check the stake at. + /// @return amount The stake of the operator at the given timestamp, in collateral token. + function getOperatorStakeAt( + address operator, + address collateral, + uint48 timestamp + ) public view returns (uint256 amount) { + if (timestamp > Time.timestamp() || timestamp < START_TIMESTAMP) { + revert InvalidQuery(); + } + + uint48 epochStartTs = getEpochStartTs(getEpochAtTs(timestamp)); + + // NOTE: Can this be done more gas-efficiently? + IStrategy[] memory strategyMem = new IStrategy[](1); + + for (uint256 i = 0; i < strategies.length(); i++) { + (address strategy, uint48 enabledTime, uint48 disabledTime) = strategies.atWithTimes(i); + + if (collateral != address(IStrategy(strategy).underlyingToken())) { + continue; + } + + if (!_wasEnabledAt(enabledTime, disabledTime, epochStartTs)) { + continue; + } + + strategyMem[0] = IStrategy(strategy); + // NOTE: order is preserved i.e., shares[i] corresponds to strategies[i] + uint256[] memory shares = DELEGATION_MANAGER.getOperatorShares(operator, strategyMem); + amount += IStrategy(strategy).sharesToUnderlyingView(shares[0]); + } + + return amount; + } + + // ========= EIGENLAYER AVS FUNCTIONS ========= + + /// @notice emits an `AVSMetadataURIUpdated` event indicating the information has updated. + /// @param metadataURI The URI for metadata associated with an avs + function updateAVSMetadataURI( + string calldata metadataURI + ) public onlyOwner { + AVS_DIRECTORY.updateAVSMetadataURI(metadataURI); + } + + // ========= HELPER FUNCTIONS ========= + + /// @notice Check if a map entry was active at a given timestamp. + /// @param enabledTime The enabled time of the map entry. + /// @param disabledTime The disabled time of the map entry. + /// @param timestamp The timestamp to check the map entry status at. + /// @return True if the map entry was active at the given timestamp, false otherwise. + function _wasEnabledAt(uint48 enabledTime, uint48 disabledTime, uint48 timestamp) private pure returns (bool) { + return enabledTime != 0 && enabledTime <= timestamp && (disabledTime == 0 || disabledTime >= timestamp); + } +} diff --git a/bolt-contracts/src/contracts/BoltManagerV1.sol b/bolt-contracts/src/contracts/BoltManagerV1.sol new file mode 100644 index 000000000..8f4ac86bb --- /dev/null +++ b/bolt-contracts/src/contracts/BoltManagerV1.sol @@ -0,0 +1,287 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.25; + +import {OwnableUpgradeable} from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol"; +import {UUPSUpgradeable} from "@openzeppelin/contracts/proxy/utils/UUPSUpgradeable.sol"; +import {Time} from "@openzeppelin/contracts/utils/types/Time.sol"; +import {EnumerableSet} from "@openzeppelin/contracts/utils/structs/EnumerableSet.sol"; +import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; + +import {OperatorMapWithTime} from "../lib/OperatorMapWithTime.sol"; +import {EnumerableMap} from "../lib/EnumerableMap.sol"; +import {IBoltValidatorsV1} from "../interfaces/IBoltValidatorsV1.sol"; +import {IBoltParametersV1} from "../interfaces/IBoltParametersV1.sol"; +import {IBoltMiddlewareV1} from "../interfaces/IBoltMiddlewareV1.sol"; +import {IBoltManagerV1} from "../interfaces/IBoltManagerV1.sol"; + +/// @title Bolt Manager +/// @notice The Bolt Manager contract is responsible for managing operators & restaking middlewares, and is the +/// entrypoint contract for all Bolt-related queries for off-chain consumers. +/// @dev This contract is upgradeable using the UUPSProxy pattern. Storage layout remains fixed across upgrades +/// with the use of storage gaps. +/// See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps +/// To validate the storage layout, use the Openzeppelin Foundry Upgrades toolkit. +/// You can also validate manually with forge: forge inspect storage-layout --pretty +contract BoltManagerV1 is IBoltManagerV1, OwnableUpgradeable, UUPSUpgradeable { + using EnumerableSet for EnumerableSet.AddressSet; + using EnumerableMap for EnumerableMap.OperatorMap; + using OperatorMapWithTime for EnumerableMap.OperatorMap; + + // ========= STORAGE ========= + /// @notice Start timestamp of the first epoch. + uint48 public START_TIMESTAMP; + + /// @notice Bolt Parameters contract. + IBoltParametersV1 public parameters; + + /// @notice Validators registry, where validators are registered via their + /// BLS pubkey and are assigned a sequence number. + IBoltValidatorsV1 public validators; + + /// @notice Set of operator addresses that have opted in to Bolt Protocol. + EnumerableMap.OperatorMap private operators; + + /// @notice Set of restaking protocols supported. Each address corresponds to the + /// associated Bolt Middleware contract. + EnumerableSet.AddressSet private restakingProtocols; + + // --> Storage layout marker: 7 slots + + /** + * @dev This empty reserved space is put in place to allow future versions to add new + * variables without shifting down storage in the inheritance chain. + * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps + * This can be validated with the Openzeppelin Foundry Upgrades toolkit. + * + * Total storage slots: 50 + */ + uint256[43] private __gap; + + modifier onlyMiddleware() { + if (!restakingProtocols.contains(msg.sender)) { + revert UnauthorizedMiddleware(); + } + _; + } + + // ========= INITIALIZER & PROXY FUNCTIONALITY ========== // + + /// @notice The initializer for the BoltManager contract. + /// @param _validators The address of the validators registry. + function initialize(address _owner, address _parameters, address _validators) public initializer { + __Ownable_init(_owner); + + parameters = IBoltParametersV1(_parameters); + validators = IBoltValidatorsV1(_validators); + + START_TIMESTAMP = Time.timestamp(); + } + + function _authorizeUpgrade( + address newImplementation + ) internal override onlyOwner {} + + // ========= VIEW FUNCTIONS ========= + + function getEpochStartTs( + uint48 epoch + ) public view returns (uint48 timestamp) { + return START_TIMESTAMP + epoch * parameters.EPOCH_DURATION(); + } + + /// @notice Get the epoch at a given timestamp. + function getEpochAtTs( + uint48 timestamp + ) public view returns (uint48 epoch) { + return (timestamp - START_TIMESTAMP) / parameters.EPOCH_DURATION(); + } + + /// @notice Get the current epoch. + function getCurrentEpoch() public view returns (uint48 epoch) { + return getEpochAtTs(Time.timestamp()); + } + + /// @notice Check if an operator address is authorized to work for a validator, + /// given the validator's pubkey hash. This function performs a lookup in the + /// validators registry to check if they explicitly authorized the operator. + /// @param operator The operator address to check the authorization for. + /// @param pubkeyHash The pubkey hash of the validator to check the authorization for. + /// @return True if the operator is authorized, false otherwise. + function isOperatorAuthorizedForValidator(address operator, bytes32 pubkeyHash) public view returns (bool) { + if (operator == address(0) || pubkeyHash == bytes32(0)) { + revert InvalidQuery(); + } + + return validators.getValidatorByPubkeyHash(pubkeyHash).authorizedOperator == operator; + } + + /// @notice Returns the addresses of the middleware contracts of restaking protocols supported by Bolt. + function getSupportedRestakingProtocols() public view returns (address[] memory middlewares) { + return restakingProtocols.values(); + } + + /// @notice Returns whether an operator is registered with Bolt. + function isOperator( + address operator + ) public view returns (bool) { + return operators.contains(operator); + } + + /// @notice Get the status of multiple proposers, given their pubkey hashes. + /// @param pubkeyHashes The pubkey hashes of the proposers to get the status for. + /// @return statuses The statuses of the proposers, including their operator and active stake. + function getProposerStatuses( + bytes32[] calldata pubkeyHashes + ) public view returns (IBoltValidatorsV1.ProposerStatus[] memory statuses) { + statuses = new IBoltValidatorsV1.ProposerStatus[](pubkeyHashes.length); + for (uint256 i = 0; i < pubkeyHashes.length; ++i) { + statuses[i] = getProposerStatus(pubkeyHashes[i]); + } + } + + /// @notice Get the status of a proposer, given their pubkey hash. + /// @param pubkeyHash The pubkey hash of the proposer to get the status for. + /// @return status The status of the proposer, including their operator and active stake. + function getProposerStatus( + bytes32 pubkeyHash + ) public view returns (IBoltValidatorsV1.ProposerStatus memory status) { + if (pubkeyHash == bytes32(0)) { + revert InvalidQuery(); + } + + uint48 epochStartTs = getEpochStartTs(getEpochAtTs(Time.timestamp())); + // NOTE: this will revert when the proposer does not exist. + IBoltValidatorsV1.Validator memory validator = validators.getValidatorByPubkeyHash(pubkeyHash); + + Operator memory operator = operators.get(validator.authorizedOperator); + + status.pubkeyHash = pubkeyHash; + status.operator = validator.authorizedOperator; + status.operatorRPC = operator.rpc; + + (uint48 enabledTime, uint48 disabledTime) = operators.getTimes(validator.authorizedOperator); + if (!_wasEnabledAt(enabledTime, disabledTime, epochStartTs)) { + return status; + } + + (status.collaterals, status.amounts) = + IBoltMiddlewareV1(operator.middleware).getOperatorCollaterals(validator.authorizedOperator); + + // NOTE: check if the sum of the collaterals covers the minimum operator stake required. + + uint256 totalOperatorStake = 0; + for (uint256 i = 0; i < status.amounts.length; ++i) { + totalOperatorStake += status.amounts[i]; + } + + if (totalOperatorStake < parameters.MINIMUM_OPERATOR_STAKE()) { + status.active = false; + } else { + status.active = true; + } + + return status; + } + + /// @notice Get the amount staked by an operator for a given collateral asset. + function getOperatorStake(address operator, address collateral) public view returns (uint256) { + Operator memory operatorData = operators.get(operator); + + return IBoltMiddlewareV1(operatorData.middleware).getOperatorStake(operator, collateral); + } + + /// @notice Get the total amount staked of a given collateral asset. + function getTotalStake( + address collateral + ) public view returns (uint256 amount) { + // Loop over all of the operators, get their middleware, and retrieve their staked amount. + for (uint256 i = 0; i < operators.length(); ++i) { + (address operator, IBoltManagerV1.Operator memory operatorData) = operators.at(i); + amount += IBoltMiddlewareV1(operatorData.middleware).getOperatorStake(operator, collateral); + } + + return amount; + } + + // ========= OPERATOR FUNCTIONS ====== // + + /// @notice Registers an operator with Bolt. Only callable by a supported middleware contract. + function registerOperator(address operatorAddr, string calldata rpc) external onlyMiddleware { + if (operators.contains(operatorAddr)) { + revert OperatorAlreadyRegistered(); + } + + // Create an already enabled operator + Operator memory operator = Operator(rpc, msg.sender, Time.timestamp()); + + operators.set(operatorAddr, operator); + } + + /// @notice De-registers an operator from Bolt. Only callable by a supported middleware contract. + function deregisterOperator( + address operator + ) public onlyMiddleware { + operators.remove(operator); + } + + /// @notice Allow an operator to signal indefinite opt-out from Bolt Protocol. + /// @dev Pausing activity does not prevent the operator from being slashable for + /// the current network epoch until the end of the slashing window. + function pauseOperator( + address operator + ) external onlyMiddleware { + // SAFETY: This will revert if the operator key is not present. + operators.disable(operator); + } + + /// @notice Allow a disabled operator to signal opt-in to Bolt Protocol. + function unpauseOperator( + address operator + ) external onlyMiddleware { + // SAFETY: This will revert if the operator key is not present. + operators.enable(operator); + } + + /// @notice Check if an operator is currently enabled to work in Bolt Protocol. + /// @param operator The operator address to check the enabled status for. + /// @return True if the operator is enabled, false otherwise. + function isOperatorEnabled( + address operator + ) public view returns (bool) { + if (!operators.contains(operator)) { + revert OperatorNotRegistered(); + } + + (uint48 enabledTime, uint48 disabledTime) = operators.getTimes(operator); + return enabledTime != 0 && disabledTime == 0; + } + + // ========= ADMIN FUNCTIONS ========= // + + /// @notice Add a restaking protocol into Bolt + /// @param protocolMiddleware The address of the restaking protocol Bolt middleware + function addRestakingProtocol( + address protocolMiddleware + ) public onlyOwner { + restakingProtocols.add(protocolMiddleware); + } + + /// @notice Remove a restaking protocol from Bolt + /// @param protocolMiddleware The address of the restaking protocol Bolt middleware + function removeRestakingProtocol( + address protocolMiddleware + ) public onlyOwner { + restakingProtocols.remove(protocolMiddleware); + } + + // ========= HELPER FUNCTIONS ========= + + /// @notice Check if a map entry was active at a given timestamp. + /// @param enabledTime The enabled time of the map entry. + /// @param disabledTime The disabled time of the map entry. + /// @param timestamp The timestamp to check the map entry status at. + /// @return True if the map entry was active at the given timestamp, false otherwise. + function _wasEnabledAt(uint48 enabledTime, uint48 disabledTime, uint48 timestamp) private pure returns (bool) { + return enabledTime != 0 && enabledTime <= timestamp && (disabledTime == 0 || disabledTime >= timestamp); + } +} diff --git a/bolt-contracts/src/contracts/BoltParametersV1.sol b/bolt-contracts/src/contracts/BoltParametersV1.sol new file mode 100644 index 000000000..034b03b2e --- /dev/null +++ b/bolt-contracts/src/contracts/BoltParametersV1.sol @@ -0,0 +1,148 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.25; + +import {OwnableUpgradeable} from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol"; +import {UUPSUpgradeable} from "@openzeppelin/contracts/proxy/utils/UUPSUpgradeable.sol"; + +/// @title Bolt Parameters +/// @notice The BoltParameters contract contains all the parameters for the Bolt protocol. +/// @dev This contract is upgradeable using the UUPSProxy pattern. Storage layout remains fixed across upgrades +/// with the use of storage gaps. +/// See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps +/// To validate the storage layout, use the Openzeppelin Foundry Upgrades toolkit. +/// You can also validate manually with forge: forge inspect storage-layout --pretty +contract BoltParametersV1 is OwnableUpgradeable, UUPSUpgradeable { + // =========== CONSTANTS ========= // + /// @dev See EIP-4788 for more info + address internal constant BEACON_ROOTS_CONTRACT = 0x000F3df6D732807Ef1319fB7B8bB8522d0Beac02; + + /// @notice The EIP-4788 time window in slots + uint256 internal constant EIP4788_WINDOW = 8191; + // =========== STORAGE =========== // + + // --> Storage layout marker: 0 bits + + /// @notice Duration of an epoch in seconds. + uint48 public EPOCH_DURATION; + + /// @notice Duration of the slashing window in seconds. + uint48 public SLASHING_WINDOW; + + /// @notice Whether to allow unsafe registration of validators + /// @dev Until the BLS12_381 precompile is live, we need to allow unsafe registration + /// which means we don't check the BLS signature of the validator pubkey. + bool public ALLOW_UNSAFE_REGISTRATION; + // --> Storage layout marker: 48 + 48 + 8 = 104 bits + + /// @notice The maximum duration of a challenge before it is automatically considered valid. + uint48 public MAX_CHALLENGE_DURATION; + + /// @notice The challenge bond required to open a challenge. + uint256 public CHALLENGE_BOND; + + /// @notice The maximum number of blocks to look back for block hashes in the EVM. + uint256 public BLOCKHASH_EVM_LOOKBACK; + + /// @notice The number of slots to wait before considering a block justified by LMD-GHOST. + uint256 public JUSTIFICATION_DELAY; + + /// @notice The timestamp of the eth2 genesis block. + uint256 public ETH2_GENESIS_TIMESTAMP; + + /// @notice The duration of a slot in seconds. + uint256 public SLOT_TIME; + + /// @notice The minimum stake required for an operator to be considered active in wei. + uint256 public MINIMUM_OPERATOR_STAKE; + // --> Storage layout marker: 7 words + + /** + * @dev This empty reserved space is put in place to allow future versions to add new + * variables without shifting down storage in the inheritance chain. + * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps + * This can be validated with the Openzeppelin Foundry Upgrades toolkit. + * + * Total storage slots: 50 + */ + uint256[43] private __gap; + + /// @notice Error emitted when a beacon block root is not found + error BeaconRootNotFound(); + + // ============== INITIALIZER ============== // + + /// @notice The initializer for the BoltManager contract. + /// @param _epochDuration The epoch duration. + function initialize( + address _owner, + uint48 _epochDuration, + uint48 _slashingWindow, + uint48 _maxChallengeDuration, + bool _allowUnsafeRegistration, + uint256 _challengeBond, + uint256 _blockhashEvmLookback, + uint256 _justificationDelay, + uint256 _eth2GenesisTimestamp, + uint256 _slotTime, + uint256 _minimumOperatorStake + ) public initializer { + __Ownable_init(_owner); + + EPOCH_DURATION = _epochDuration; + SLASHING_WINDOW = _slashingWindow; + ALLOW_UNSAFE_REGISTRATION = _allowUnsafeRegistration; + MAX_CHALLENGE_DURATION = _maxChallengeDuration; + CHALLENGE_BOND = _challengeBond; + BLOCKHASH_EVM_LOOKBACK = _blockhashEvmLookback; + JUSTIFICATION_DELAY = _justificationDelay; + ETH2_GENESIS_TIMESTAMP = _eth2GenesisTimestamp; + SLOT_TIME = _slotTime; + MINIMUM_OPERATOR_STAKE = _minimumOperatorStake; + } + + function _authorizeUpgrade( + address newImplementation + ) internal override onlyOwner {} + + // ========= ADMIN METHODS ========= // + + /// @notice Enable or disable the use of the BLS precompile + /// @param allowUnsafeRegistration Whether to allow unsafe registration of validators + function setAllowUnsafeRegistration( + bool allowUnsafeRegistration + ) public onlyOwner { + ALLOW_UNSAFE_REGISTRATION = allowUnsafeRegistration; + } + + /// @notice Set the max challenge duration. + /// @param maxChallengeDuration The maximum duration of a challenge before it is automatically considered valid. + function setMaxChallengeDuration( + uint48 maxChallengeDuration + ) public onlyOwner { + MAX_CHALLENGE_DURATION = maxChallengeDuration; + } + + /// @notice Set the required challenge bond. + /// @param challengeBond The challenge bond required to open a challenge. + function setChallengeBond( + uint256 challengeBond + ) public onlyOwner { + CHALLENGE_BOND = challengeBond; + } + + /// @notice Set the minimum operator stake. + /// @param minimumOperatorStake The minimum stake required for an operator to be considered active in wei. + function setMinimumOperatorStake( + uint256 minimumOperatorStake + ) public onlyOwner { + MINIMUM_OPERATOR_STAKE = minimumOperatorStake; + } + + /// @notice Set the justification delay. + /// @param justificationDelay The number of slots to wait before considering a block final. + function setJustificationDelay( + uint256 justificationDelay + ) public onlyOwner { + JUSTIFICATION_DELAY = justificationDelay; + } +} diff --git a/bolt-contracts/src/contracts/BoltRegistry.sol b/bolt-contracts/src/contracts/BoltRegistry.sol deleted file mode 100644 index c064eda8d..000000000 --- a/bolt-contracts/src/contracts/BoltRegistry.sol +++ /dev/null @@ -1,166 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.13; - -import {IBoltRegistry} from "../interfaces/IBoltRegistry.sol"; - -contract BoltRegistry is IBoltRegistry { - // Cooldown period after which a based proposer can complete the exit process - uint256 public constant EXIT_COOLDOWN = 1 days; - - // Minimum collateral per operator - uint256 public immutable MINIMUM_COLLATERAL; - - // Mapping to hold the registrants - mapping(address => Registrant) public registrants; - - // Array to hold operator addresses - address[] public operators; - - // Mapping that holds the relationship between validator index and operator address - mapping(uint64 => address) public delegations; - - /// @notice Constructor which can set the minimum collateral required to register - constructor(uint256 _minimumCollateral) { - MINIMUM_COLLATERAL = _minimumCollateral; - } - - /// @notice Allows a based proposer to opt-in to the protocol - function register( - uint64[] calldata validatorIndexes, - string calldata rpc, - bytes calldata extra - ) external payable { - if (msg.value < MINIMUM_COLLATERAL) { - revert InsufficientCollateral(); - } - - if (registrants[msg.sender].operator != address(0)) { - revert AlreadyOptedIn(); - } - - MetaData memory metadata = MetaData(rpc, extra); - - registrants[msg.sender] = Registrant( - msg.sender, - validatorIndexes, - block.timestamp, - 0, - msg.value, - Status.ACTIVE, - metadata - ); - - operators.push(msg.sender); - - // Set the delegations - for (uint256 i = 0; i < validatorIndexes.length; i++) { - delegations[validatorIndexes[i]] = msg.sender; - } - - emit Registered(msg.sender, validatorIndexes, metadata); - } - - /// @notice Allows a based proposer to exit out of the protocol. - /// @dev Requires a second transaction after the cooldown period to complete the exit process. - function startExit() external { - Registrant storage registrant = registrants[msg.sender]; - - if (registrant.operator != msg.sender) { - revert BasedProposerDoesNotExist(); - } - - if (registrant.status == Status.EXITING) { - revert InvalidStatusChange(); - } - - registrant.exitInitiatedAt = block.timestamp; - - emit StatusChange(msg.sender, Status.EXITING); - } - - /// @notice Completes the exit process for a based proposer - /// and sends the funds back to the `recipient` address. - function confirmExit(address payable recipient) external { - Registrant storage registrant = registrants[msg.sender]; - - if (registrant.operator != msg.sender) { - revert BasedProposerDoesNotExist(); - } - if (registrant.exitInitiatedAt == 0) { - revert InvalidStatusChange(); - } - if (registrant.status == Status.INACTIVE) { - revert InvalidStatusChange(); - } - - if (block.timestamp < registrant.exitInitiatedAt + EXIT_COOLDOWN) { - revert CooldownNotElapsed(); - } - - // Remove operator from the operators array - for (uint256 i = 0; i < operators.length; i++) { - if (operators[i] == msg.sender) { - operators[i] = operators[operators.length - 1]; - operators.pop(); - break; - } - } - - delete registrants[msg.sender]; - - for (uint256 i = 0; i < registrant.validatorIndexes.length; i++) { - delete delegations[registrant.validatorIndexes[i]]; - } - - recipient.transfer(registrant.balance); - - emit StatusChange(msg.sender, Status.INACTIVE); - } - - /// @notice Check if an address is a based proposer opted into the protocol - /// @param _operator The address to check - /// @return True if the address is an active based proposer, false otherwise - function isActiveOperator(address _operator) public view returns (bool) { - return registrants[_operator].status == Status.ACTIVE; - } - - /// @notice Get the status of a based proposer - /// @param _operator The address of the operator - /// @return The status of the based proposer - function getOperatorStatus( - address _operator - ) external view returns (Status) { - // Will return INACTIVE if the operator is not registered - return registrants[_operator].status; - } - - function getOperatorForValidator( - uint64 _validatorIndex - ) external view returns (Registrant memory) { - if (delegations[_validatorIndex] != address(0)) { - return registrants[delegations[_validatorIndex]]; - } - - revert NotFound(); - } - - function getAllRegistrants() external view returns (Registrant[] memory) { - uint256 activeCount = 0; - for (uint256 i = 0; i < operators.length; i++) { - if (isActiveOperator(operators[i])) { - activeCount++; - } - } - - Registrant[] memory _registrants = new Registrant[](activeCount); - uint256 index = 0; - for (uint256 i = 0; i < operators.length; i++) { - if (isActiveOperator(operators[i])) { - _registrants[index] = registrants[operators[i]]; - index++; - } - } - - return _registrants; - } -} diff --git a/bolt-contracts/src/contracts/BoltSymbioticMiddlewareV1.sol b/bolt-contracts/src/contracts/BoltSymbioticMiddlewareV1.sol new file mode 100644 index 000000000..a844b78c4 --- /dev/null +++ b/bolt-contracts/src/contracts/BoltSymbioticMiddlewareV1.sol @@ -0,0 +1,415 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.25; + +import {Time} from "@openzeppelin/contracts/utils/types/Time.sol"; +import {EnumerableMap} from "@openzeppelin/contracts/utils/structs/EnumerableMap.sol"; +import {EnumerableSet} from "@openzeppelin/contracts/utils/structs/EnumerableSet.sol"; +import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; +import {OwnableUpgradeable} from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol"; +import {UUPSUpgradeable} from "@openzeppelin/contracts/proxy/utils/UUPSUpgradeable.sol"; + +import {IBaseDelegator} from "@symbiotic/interfaces/delegator/IBaseDelegator.sol"; +import {Subnetwork} from "@symbiotic/contracts/libraries/Subnetwork.sol"; +import {IVault} from "@symbiotic/interfaces/vault/IVault.sol"; +import {IRegistry} from "@symbiotic/interfaces/common/IRegistry.sol"; +import {IOptInService} from "@symbiotic/interfaces/service/IOptInService.sol"; +import {ISlasher} from "@symbiotic/interfaces/slasher/ISlasher.sol"; +import {IVetoSlasher} from "@symbiotic/interfaces/slasher/IVetoSlasher.sol"; +import {IEntity} from "@symbiotic/interfaces/common/IEntity.sol"; + +import {MapWithTimeData} from "../lib/MapWithTimeData.sol"; +import {IBoltValidatorsV1} from "../interfaces/IBoltValidatorsV1.sol"; +import {IBoltParametersV1} from "../interfaces/IBoltParametersV1.sol"; +import {IBoltMiddlewareV1} from "../interfaces/IBoltMiddlewareV1.sol"; +import {IBoltManagerV1} from "../interfaces/IBoltManagerV1.sol"; + +/// @title Bolt Symbiotic Middleware +/// @notice This contract is responsible for interfacing with the Symbiotic restaking protocol. +/// @dev This contract is upgradeable using the UUPSProxy pattern. Storage layout remains fixed across upgrades +/// with the use of storage gaps. +/// See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps +/// To validate the storage layout, use the Openzeppelin Foundry Upgrades toolkit. +/// You can also validate manually with forge: forge inspect storage-layout --pretty +contract BoltSymbioticMiddlewareV1 is IBoltMiddlewareV1, OwnableUpgradeable, UUPSUpgradeable { + using EnumerableSet for EnumerableSet.AddressSet; + using EnumerableMap for EnumerableMap.AddressToUintMap; + using MapWithTimeData for EnumerableMap.AddressToUintMap; + using Subnetwork for address; + + // ========== CONSTANTS ============ // + /// @notice Slasher that can instantly slash operators without veto. + uint256 public INSTANT_SLASHER_TYPE = 0; + + /// @notice Slasher that can request a veto before actually slashing operators. + uint256 public VETO_SLASHER_TYPE = 1; + + // ========= STORAGE ========= // + + /// @notice Start timestamp of the first epoch. + uint48 public START_TIMESTAMP; + + /// @notice Bolt Parameters contract. + IBoltParametersV1 public parameters; + + /// @notice Validators registry, where validators are registered via their + /// BLS pubkey and are assigned a sequence number. + IBoltManagerV1 public manager; + + /// @notice Set of Symbiotic protocol vaults that are used in Bolt Protocol. + EnumerableMap.AddressToUintMap private vaults; + + /// @notice Address of the Bolt network in Symbiotic Protocol. + address public BOLT_SYMBIOTIC_NETWORK; + + /// @notice Address of the Symbiotic Operator Registry contract. + address public OPERATOR_REGISTRY; + + /// @notice Address of the Symbiotic Vault Factory contract. + address public VAULT_FACTORY; + + /// @notice Address of the Symbiotic Operator Network Opt-In contract. + address public OPERATOR_NET_OPTIN; + + bytes32 public NAME_HASH; + + // --> Storage layout marker: 12 slots + + /** + * @dev This empty reserved space is put in place to allow future versions to add new + * variables without shifting down storage in the inheritance chain. + * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps + * This can be validated with the Openzeppelin Foundry Upgrades toolkit. + * + * Total storage slots: 50 + */ + uint256[38] private __gap; + + // ========= ERRORS ========= + + error NotVault(); + error SlashAmountTooHigh(); + error UnknownSlasherType(); + + // ========= CONSTRUCTOR ========= + + /// @notice Constructor for the BoltSymbioticMiddleware contract. + /// @param _parameters The address of the Bolt Parameters contract. + /// @param _manager The address of the Bolt Manager contract. + /// @param _symbioticNetwork The address of the Symbiotic network. + /// @param _symbioticOperatorRegistry The address of the Symbiotic operator registry. + /// @param _symbioticOperatorNetOptIn The address of the Symbiotic operator network opt-in contract. + /// @param _symbioticVaultFactory The address of the Symbiotic vault registry. + function initialize( + address _owner, + address _parameters, + address _manager, + address _symbioticNetwork, + address _symbioticOperatorRegistry, + address _symbioticOperatorNetOptIn, + address _symbioticVaultFactory + ) public initializer { + __Ownable_init(_owner); + parameters = IBoltParametersV1(_parameters); + manager = IBoltManagerV1(_manager); + START_TIMESTAMP = Time.timestamp(); + + BOLT_SYMBIOTIC_NETWORK = _symbioticNetwork; + OPERATOR_REGISTRY = _symbioticOperatorRegistry; + OPERATOR_NET_OPTIN = _symbioticOperatorNetOptIn; + VAULT_FACTORY = _symbioticVaultFactory; + NAME_HASH = keccak256("SYMBIOTIC"); + } + + function _authorizeUpgrade( + address newImplementation + ) internal override onlyOwner {} + + // ========= VIEW FUNCTIONS ========= + + /// @notice Get the start timestamp of an epoch. + function getEpochStartTs( + uint48 epoch + ) public view returns (uint48 timestamp) { + return START_TIMESTAMP + epoch * parameters.EPOCH_DURATION(); + } + + /// @notice Get the epoch at a given timestamp. + function getEpochAtTs( + uint48 timestamp + ) public view returns (uint48 epoch) { + return (timestamp - START_TIMESTAMP) / parameters.EPOCH_DURATION(); + } + + /// @notice Get the current epoch. + function getCurrentEpoch() public view returns (uint48 epoch) { + return getEpochAtTs(Time.timestamp()); + } + + /// @notice Get the whitelisted vaults. + function getWhitelistedVaults() public view returns (address[] memory) { + return vaults.keys(); + } + + // =========== ADMIN FUNCTIONS ============ // + + /// @notice Allow a vault to signal opt-in to Bolt Protocol. + /// @param vault The vault address to signal opt-in for. + function registerVault( + address vault + ) public onlyOwner { + if (vaults.contains(vault)) { + revert AlreadyRegistered(); + } + + if (!IRegistry(VAULT_FACTORY).isEntity(vault)) { + revert NotVault(); + } + + // TODO: check slashing conditions and veto duration + + vaults.add(vault); + vaults.enable(vault); + } + + /// @notice Deregister a vault from working in Bolt Protocol. + /// @param vault The vault address to deregister. + function deregisterVault( + address vault + ) public onlyOwner { + if (!vaults.contains(vault)) { + revert NotRegistered(); + } + + vaults.remove(vault); + } + + // ========= SYMBIOTIC MIDDLEWARE LOGIC ========= + + /// @notice Allow an operator to signal opt-in to Bolt Protocol. + /// msg.sender must be an operator in the Symbiotic network. + function registerOperator( + string calldata rpc + ) public { + if (manager.isOperator(msg.sender)) { + revert AlreadyRegistered(); + } + + if (!IRegistry(OPERATOR_REGISTRY).isEntity(msg.sender)) { + revert NotOperator(); + } + + if (!IOptInService(OPERATOR_NET_OPTIN).isOptedIn(msg.sender, BOLT_SYMBIOTIC_NETWORK)) { + revert OperatorNotOptedIn(); + } + + manager.registerOperator(msg.sender, rpc); + } + + /// @notice Deregister a Symbiotic operator from working in Bolt Protocol. + /// @dev This does NOT deregister the operator from the Symbiotic network. + function deregisterOperator() public { + if (!manager.isOperator(msg.sender)) { + revert NotRegistered(); + } + + manager.deregisterOperator(msg.sender); + } + + /// @notice Allow an operator to signal indefinite opt-out from Bolt Protocol. + /// @dev Pausing activity does not prevent the operator from being slashable for + /// the current network epoch until the end of the slashing window. + function pauseOperator() public { + manager.pauseOperator(msg.sender); + } + + /// @notice Allow a disabled operator to signal opt-in to Bolt Protocol. + function unpauseOperator() public { + manager.unpauseOperator(msg.sender); + } + + /// @notice Allow a vault to signal indefinite opt-out from Bolt Protocol. + function pauseVault() public { + if (!vaults.contains(msg.sender)) { + revert NotRegistered(); + } + + vaults.disable(msg.sender); + } + + /// @notice Allow a disabled vault to signal opt-in to Bolt Protocol. + function unpauseVault() public { + if (!vaults.contains(msg.sender)) { + revert NotRegistered(); + } + + vaults.enable(msg.sender); + } + + /// @notice Check if a vault is currently enabled to work in Bolt Protocol. + /// @param vault The vault address to check the enabled status for. + /// @return True if the vault is enabled, false otherwise. + function isVaultEnabled( + address vault + ) public view returns (bool) { + (uint48 enabledTime, uint48 disabledTime) = vaults.getTimes(vault); + return enabledTime != 0 && disabledTime == 0; + } + + /// @notice Get the collaterals and amounts staked by an operator across the supported strategies. + /// + /// @param operator The operator address to get the collaterals and amounts staked for. + /// @return collaterals The collaterals staked by the operator. + /// @dev Assumes that the operator is registered and enabled. + function getOperatorCollaterals( + address operator + ) public view returns (address[] memory, uint256[] memory) { + address[] memory collateralTokens = new address[](vaults.length()); + uint256[] memory amounts = new uint256[](vaults.length()); + + uint48 epochStartTs = getEpochStartTs(getEpochAtTs(Time.timestamp())); + + for (uint256 i = 0; i < vaults.length(); ++i) { + (address vault, uint48 enabledTime, uint48 disabledTime) = vaults.atWithTimes(i); + + if (!_wasEnabledAt(enabledTime, disabledTime, epochStartTs)) { + continue; + } + + address collateral = IVault(vault).collateral(); + collateralTokens[i] = collateral; + + // in order to have stake in a network, the operator needs to be opted in to that vault. + // this authorization is fully handled in the Vault, we just need to read the stake. + amounts[i] = IBaseDelegator(IVault(vault).delegator()).stakeAt( + // The stake for each subnetwork is stored in the vault's delegator contract. + // stakeAt returns the stake of "operator" at "timestamp" for "network" (or subnetwork) + // bytes(0) is for hints, which we don't currently use. + BOLT_SYMBIOTIC_NETWORK.subnetwork(0), + operator, + epochStartTs, + new bytes(0) + ); + } + + return (collateralTokens, amounts); + } + + /// @notice Get the stake of an operator in Symbiotic protocol at the current timestamp. + /// @param operator The operator address to check the stake for. + /// @param collateral The collateral address to check the stake for. + /// @return amount The stake of the operator at the current timestamp, in collateral token. + function getOperatorStake(address operator, address collateral) public view returns (uint256 amount) { + uint48 timestamp = Time.timestamp(); + return getOperatorStakeAt(operator, collateral, timestamp); + } + + /// @notice Get the stake of an operator in Symbiotic protocol at a given timestamp. + /// @param operator The operator address to check the stake for. + /// @param collateral The collateral address to check the stake for. + /// @param timestamp The timestamp to check the stake at. + /// @return amount The stake of the operator at the given timestamp, in collateral token. + function getOperatorStakeAt( + address operator, + address collateral, + uint48 timestamp + ) public view returns (uint256 amount) { + if (timestamp > Time.timestamp() || timestamp < START_TIMESTAMP) { + revert InvalidQuery(); + } + + uint48 epochStartTs = getEpochStartTs(getEpochAtTs(timestamp)); + + for (uint256 i = 0; i < vaults.length(); ++i) { + (address vault, uint48 enabledTime, uint48 disabledTime) = vaults.atWithTimes(i); + + if (collateral != IVault(vault).collateral()) { + continue; + } + + if (!_wasEnabledAt(enabledTime, disabledTime, epochStartTs)) { + continue; + } + + // in order to have stake in a network, the operator needs to be opted in to that vault. + // this authorization is fully handled in the Vault, we just need to read the stake. + amount += IBaseDelegator(IVault(vault).delegator()).stakeAt( + // The stake for each subnetwork is stored in the vault's delegator contract. + // stakeAt returns the stake of "operator" at "timestamp" for "network" (or subnetwork) + // bytes(0) is for hints, which we don't currently use. + BOLT_SYMBIOTIC_NETWORK.subnetwork(0), + operator, + epochStartTs, + new bytes(0) + ); + } + + return amount; + } + + /// @notice Slash a given operator for a given amount of collateral. + /// @param timestamp The timestamp of the slash event. + /// @param operator The operator address to slash. + /// @param collateral The collateral address to slash. + /// @param amount The amount of collateral to slash. + function slash(uint48 timestamp, address operator, address collateral, uint256 amount) public onlyOwner { + // TODO: remove onlyOwner modifier and gate the slashing logic behind the BoltChallenger + // fault proof mechanism to allow for permissionless slashing. + + uint48 epochStartTs = getEpochStartTs(getEpochAtTs(timestamp)); + + for (uint256 i = 0; i < vaults.length(); ++i) { + (address vault, uint48 enabledTime, uint48 disabledTime) = vaults.atWithTimes(i); + + if (!_wasEnabledAt(enabledTime, disabledTime, epochStartTs)) { + continue; + } + + if (collateral != IVault(vault).collateral()) { + continue; + } + + uint256 operatorStake = getOperatorStakeAt(operator, collateral, epochStartTs); + + if (amount > operatorStake) { + revert SlashAmountTooHigh(); + } + + uint256 vaultStake = IBaseDelegator(IVault(vault).delegator()).stakeAt( + BOLT_SYMBIOTIC_NETWORK.subnetwork(0), operator, epochStartTs, new bytes(0) + ); + + // Slash the vault pro-rata. + _slashVault(epochStartTs, vault, operator, (amount * vaultStake) / operatorStake); + } + } + + // ========= HELPER FUNCTIONS ========= + + /// @notice Check if a map entry was active at a given timestamp. + /// @param enabledTime The enabled time of the map entry. + /// @param disabledTime The disabled time of the map entry. + /// @param timestamp The timestamp to check the map entry status at. + /// @return True if the map entry was active at the given timestamp, false otherwise. + function _wasEnabledAt(uint48 enabledTime, uint48 disabledTime, uint48 timestamp) private pure returns (bool) { + return enabledTime != 0 && enabledTime <= timestamp && (disabledTime == 0 || disabledTime >= timestamp); + } + + /// @notice Slash an operator for a given amount of collateral. + /// @param timestamp The timestamp of the slash event. + /// @param operator The operator address to slash. + /// @param amount The amount of collateral to slash. + function _slashVault(uint48 timestamp, address vault, address operator, uint256 amount) private { + address slasher = IVault(vault).slasher(); + uint256 slasherType = IEntity(slasher).TYPE(); + + if (slasherType == INSTANT_SLASHER_TYPE) { + ISlasher(slasher).slash(BOLT_SYMBIOTIC_NETWORK.subnetwork(0), operator, amount, timestamp, new bytes(0)); + } else if (slasherType == VETO_SLASHER_TYPE) { + IVetoSlasher(slasher).requestSlash( + BOLT_SYMBIOTIC_NETWORK.subnetwork(0), operator, amount, timestamp, new bytes(0) + ); + } else { + revert UnknownSlasherType(); + } + } +} diff --git a/bolt-contracts/src/contracts/BoltValidatorsV1.sol b/bolt-contracts/src/contracts/BoltValidatorsV1.sol new file mode 100644 index 000000000..aa67dd5df --- /dev/null +++ b/bolt-contracts/src/contracts/BoltValidatorsV1.sol @@ -0,0 +1,303 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.25; + +import {OwnableUpgradeable} from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol"; +import {UUPSUpgradeable} from "@openzeppelin/contracts/proxy/utils/UUPSUpgradeable.sol"; + +import {BLS12381} from "../lib/bls/BLS12381.sol"; +import {BLSSignatureVerifier} from "../lib/bls/BLSSignatureVerifier.sol"; +import {IBoltValidatorsV1} from "../interfaces/IBoltValidatorsV1.sol"; +import {IBoltParametersV1} from "../interfaces/IBoltParametersV1.sol"; + +/// @title Bolt Validators +/// @notice This contract is responsible for registering validators and managing their configuration +/// @dev This contract is upgradeable using the UUPSProxy pattern. Storage layout remains fixed across upgrades +/// with the use of storage gaps. +/// See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps +/// To validate the storage layout, use the Openzeppelin Foundry Upgrades toolkit. +/// You can also validate manually with forge: forge inspect storage-layout --pretty +contract BoltValidatorsV1 is IBoltValidatorsV1, BLSSignatureVerifier, OwnableUpgradeable, UUPSUpgradeable { + using BLS12381 for BLS12381.G1Point; + + // ========= STORAGE ========= + + /// @notice Bolt Parameters contract. + IBoltParametersV1 public parameters; + + /// @notice Validators (aka Blockspace providers) + /// @dev For our purpose, validators are blockspace providers for commitments. + /// They are identified by their BLS pubkey hash. + /// + /// Validators can be separate from their Collateral Provider, such as in the + /// case of non-custodial staking pools. Validators can also delegate commitment + /// power to an Operator to make commitments on their behalf. + mapping(bytes32 => Validator) public VALIDATORS; + + /// @notice Mapping from validator sequence number to validator pubkey hash + /// @dev This is used internally to easily query the pubkey hash of a validator. + mapping(uint64 => bytes32) private sequenceNumberToPubkeyHash; + + /// @notice counter of the next index to be assigned to a validator. + /// @dev This incremental index is only used to identify validators in the registry. + /// It is not related to the `validatorIndex` assigned by the Beacon Chain. + uint64 internal nextValidatorSequenceNumber; + + // --> Storage layout marker: 4 slots + + /** + * @dev This empty reserved space is put in place to allow future versions to add new + * variables without shifting down storage in the inheritance chain. + * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps + * This can be validated with the Openzeppelin Foundry Upgrades toolkit. + * + * Total storage slots: 50 + */ + uint256[46] private __gap; + + // ========= EVENTS ========= + + /// @notice Emitted when a validator is registered + /// @param pubkeyHash BLS public key hash of the validator + /// @param validator Validator struct + event ValidatorRegistered(bytes32 indexed pubkeyHash, Validator validator); + + // ========= INITIALIZER ========= + + /// @notice Initializer + /// @param _owner Address of the owner of the contract + /// @param _parameters Address of the Bolt Parameters contract + function initialize(address _owner, address _parameters) public initializer { + __Ownable_init(_owner); + + parameters = IBoltParametersV1(_parameters); + } + + function _authorizeUpgrade( + address newImplementation + ) internal override onlyOwner {} + + // ========= VIEW FUNCTIONS ========= + + /// @notice Get all validators + /// @dev This function should be used with caution as it can return a large amount of data. + /// @return Validator[] memory Array of validator structs + function getAllValidators() public view returns (Validator[] memory) { + uint256 validatorCount = nextValidatorSequenceNumber; + Validator[] memory validators = new Validator[](validatorCount); + for (uint256 i = 0; i < validatorCount; i++) { + bytes32 pubkeyHash = sequenceNumberToPubkeyHash[uint64(i)]; + validators[i] = VALIDATORS[pubkeyHash]; + } + return validators; + } + + /// @notice Get a validator by its BLS public key + /// @param pubkey BLS public key of the validator + /// @return Validator memory Validator struct + function getValidatorByPubkey( + BLS12381.G1Point calldata pubkey + ) public view returns (Validator memory) { + return getValidatorByPubkeyHash(_pubkeyHash(pubkey)); + } + + /// @notice Get a validator by its BLS public key hash + /// @param pubkeyHash BLS public key hash of the validator + /// @return Validator memory Validator struct + function getValidatorByPubkeyHash( + bytes32 pubkeyHash + ) public view returns (Validator memory) { + Validator memory validator = VALIDATORS[pubkeyHash]; + if (!validator.exists) { + revert ValidatorDoesNotExist(); + } + return validator; + } + + /// @notice Get a validator by its sequence number + /// @param sequenceNumber Sequence number of the validator + /// @return Validator memory Validator struct + function getValidatorBySequenceNumber( + uint64 sequenceNumber + ) public view returns (Validator memory) { + bytes32 pubkeyHash = sequenceNumberToPubkeyHash[sequenceNumber]; + return VALIDATORS[pubkeyHash]; + } + + // ========= REGISTRATION LOGIC ========= + + /// @notice Register a single Validator and authorize a Collateral Provider and Operator for it + /// @dev This function allows anyone to register a single Validator. We do not perform any checks. + /// @param pubkey BLS public key for the Validator to be registered + /// @param maxCommittedGasLimit The maximum gas that the Validator can commit for preconfirmations + /// @param authorizedOperator The address of the authorized operator + function registerValidatorUnsafe( + BLS12381.G1Point calldata pubkey, + uint128 maxCommittedGasLimit, + address authorizedOperator + ) public { + if (!parameters.ALLOW_UNSAFE_REGISTRATION()) { + revert UnsafeRegistrationNotAllowed(); + } + + _registerValidator(pubkey, nextValidatorSequenceNumber, maxCommittedGasLimit, authorizedOperator); + } + + /// @notice Register a single Validator and authorize a Collateral Provider and Operator for it + /// @dev This function allows anyone to register a single Validator. We perform an important check: + /// The owner of the Validator (controller) must have signed the message with its BLS private key. + /// + /// Message format: `chainId || controller || sequenceNumber` + /// @param pubkey BLS public key for the Validator to be registered + /// @param signature BLS signature of the registration message for the Validator + /// @param maxCommittedGasLimit The maximum gas that the Validator can commit for preconfirmations + /// @param authorizedOperator The address of the authorized operator + function registerValidator( + BLS12381.G1Point calldata pubkey, + BLS12381.G2Point calldata signature, + uint128 maxCommittedGasLimit, + address authorizedOperator + ) public { + bytes memory message = abi.encodePacked(block.chainid, msg.sender, nextValidatorSequenceNumber); + if (!_verifySignature(message, signature, pubkey)) { + revert InvalidBLSSignature(); + } + + _registerValidator(pubkey, nextValidatorSequenceNumber, maxCommittedGasLimit, authorizedOperator); + } + + /// @notice Register a batch of Validators and authorize a Collateral Provider and Operator for them + /// @dev This function allows anyone to register a list of Validators. + /// @param pubkeys List of BLS public keys for the Validators to be registered + /// @param signature BLS aggregated signature of the registration message for this batch of Validators + /// @param maxCommittedGasLimit The maximum gas that the Validator can commit for preconfirmations + /// @param authorizedOperator The address of the authorized operator + function batchRegisterValidators( + BLS12381.G1Point[] calldata pubkeys, + BLS12381.G2Point calldata signature, + uint128 maxCommittedGasLimit, + address authorizedOperator + ) public { + uint256 validatorsCount = pubkeys.length; + uint64[] memory expectedValidatorSequenceNumbers = new uint64[](validatorsCount); + for (uint256 i = 0; i < validatorsCount; i++) { + expectedValidatorSequenceNumbers[i] = nextValidatorSequenceNumber + uint64(i); + } + + // Reconstruct the unique message for which we expect an aggregated signature. + // We need the msg.sender to prevent a front-running attack by an EOA that may + // try to register the same validators + bytes memory message = abi.encodePacked(block.chainid, msg.sender, expectedValidatorSequenceNumbers); + + // Aggregate the pubkeys into a single pubkey to verify the aggregated signature once + BLS12381.G1Point memory aggPubkey = _aggregatePubkeys(pubkeys); + + if (!_verifySignature(message, signature, aggPubkey)) { + revert InvalidBLSSignature(); + } + + // Register the validators and authorize the Collateral Provider and Operator for them + for (uint256 i = 0; i < validatorsCount; i++) { + _registerValidator( + pubkeys[i], expectedValidatorSequenceNumbers[i], maxCommittedGasLimit, authorizedOperator + ); + } + } + + /// @notice Register a batch of Validators and authorize a Collateral Provider and Operator for them + /// @dev This function allows anyone to register a list of Validators. + /// @param pubkeys List of BLS public keys for the Validators to be registered + /// @param maxCommittedGasLimit The maximum gas that the Validator can commit for preconfirmations + /// @param authorizedOperator The address of the authorized operator + function batchRegisterValidatorsUnsafe( + BLS12381.G1Point[] calldata pubkeys, + uint128 maxCommittedGasLimit, + address authorizedOperator + ) public { + if (!parameters.ALLOW_UNSAFE_REGISTRATION()) { + revert UnsafeRegistrationNotAllowed(); + } + + uint256 validatorsCount = pubkeys.length; + uint64[] memory expectedValidatorSequenceNumbers = new uint64[](validatorsCount); + for (uint256 i = 0; i < validatorsCount; i++) { + expectedValidatorSequenceNumbers[i] = nextValidatorSequenceNumber + uint64(i); + } + + // Register the validators and authorize the Collateral Provider and Operator for them + for (uint256 i = 0; i < validatorsCount; i++) { + _registerValidator( + pubkeys[i], expectedValidatorSequenceNumbers[i], maxCommittedGasLimit, authorizedOperator + ); + } + } + + // ========= UPDATE FUNCTIONS ========= + + /// @notice Update the maximum gas limit that a validator can commit for preconfirmations + /// @dev Only the `controller` of the validator can update this value. + /// @param pubkeyHash The hash of the BLS public key of the validator + /// @param maxCommittedGasLimit The new maximum gas limit + function updateMaxCommittedGasLimit(bytes32 pubkeyHash, uint128 maxCommittedGasLimit) public { + Validator storage validator = VALIDATORS[pubkeyHash]; + + if (!validator.exists) { + revert ValidatorDoesNotExist(); + } + + if (msg.sender != validator.controller) { + revert UnauthorizedCaller(); + } + + validator.maxCommittedGasLimit = maxCommittedGasLimit; + } + + // ========= HELPERS ========= + + /// @notice Internal helper to add a validator to the registry + /// @param pubkey BLS public key of the validator + /// @param sequenceNumber Sequence number of the validator + /// @param maxCommittedGasLimit The maximum gas that the Validator can commit for preconfirmations + /// @param authorizedOperator Address of the authorized operator + function _registerValidator( + BLS12381.G1Point calldata pubkey, + uint64 sequenceNumber, + uint128 maxCommittedGasLimit, + address authorizedOperator + ) internal { + if (authorizedOperator == address(0)) { + revert InvalidAuthorizedOperator(); + } + + bytes32 pubKeyHash = _pubkeyHash(pubkey); + + // check if the validator already exists + if (VALIDATORS[pubKeyHash].exists) { + revert ValidatorAlreadyExists(); + } + + Validator memory newValidator = Validator({ + sequenceNumber: sequenceNumber, + maxCommittedGasLimit: maxCommittedGasLimit, + authorizedOperator: authorizedOperator, + controller: msg.sender, + exists: true + }); + + // register the validator + VALIDATORS[pubKeyHash] = newValidator; + emit ValidatorRegistered(pubKeyHash, newValidator); + + sequenceNumberToPubkeyHash[sequenceNumber] = pubKeyHash; + nextValidatorSequenceNumber += 1; + } + + /// @notice Compute the hash of a BLS public key + /// @param pubkey BLS public key + /// @return Hash of the public key in compressed form + function _pubkeyHash( + BLS12381.G1Point memory pubkey + ) internal pure returns (bytes32) { + uint256[2] memory compressedPubKey = pubkey.compress(); + return keccak256(abi.encodePacked(compressedPubKey)); + } +} diff --git a/bolt-contracts/src/interfaces/IBoltChallenger.sol b/bolt-contracts/src/interfaces/IBoltChallenger.sol deleted file mode 100644 index aba98cc89..000000000 --- a/bolt-contracts/src/interfaces/IBoltChallenger.sol +++ /dev/null @@ -1,40 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.13; - -interface IBoltChallenger { - enum ChallengeStatus { - // The challenge is open and waiting for a resolution - Pending, - // The challenge has been resolved - Resolved - } - - enum ChallengeResult { - // The challenge was successful: the proposer failed to honor the preconfirmation - Success, - // The challenge was unsuccessful: the proposer honored the preconfirmation - Failure - } - - // Bolt challenge errors - error ChallengeAlreadyExists(); - error InsufficientBond(); - error InvalidProposerAddress(); - error Unauthorized(); - error InvalidChallenge(); - error ChallengeNotFound(); - error ChallengeAlreadyResolved(); - error TargetSlotTooFarInThePast(); - error InvalidCommitmentSignature(); - error InvalidCommitmentSigner(); - - // Relic related errors - error UnexpectedFactSignature(); - error WrongBlockHeader(); - - /// @notice Event emitted when a new challenge is opened - event NewChallenge(address indexed basedProposer, bytes32 indexed commitmentID, uint256 targetSlot); - - /// @notice Event emitted when a challenge is resolved - event ChallengeResolved(bytes32 indexed challengeID, ChallengeResult result); -} diff --git a/bolt-contracts/src/interfaces/IBoltChallengerV1.sol b/bolt-contracts/src/interfaces/IBoltChallengerV1.sol new file mode 100644 index 000000000..e24f3e188 --- /dev/null +++ b/bolt-contracts/src/interfaces/IBoltChallengerV1.sol @@ -0,0 +1,113 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.25; + +interface IBoltChallengerV1 { + enum ChallengeStatus { + Open, + Defended, + Breached + } + + struct Challenge { + bytes32 id; + uint48 openedAt; + ChallengeStatus status; + uint256 targetSlot; + address challenger; + address commitmentSigner; + address commitmentReceiver; + TransactionData[] committedTxs; + } + + struct SignedCommitment { + uint64 slot; + bytes signature; + bytes signedTx; + } + + struct TransactionData { + bytes32 txHash; + uint256 nonce; + uint256 gasLimit; + } + + struct BlockHeaderData { + bytes32 parentHash; + bytes32 stateRoot; + bytes32 txRoot; + uint256 blockNumber; + uint256 timestamp; + uint256 baseFee; + } + + struct AccountData { + uint256 nonce; + uint256 balance; + } + + struct Proof { + // block number where the transactions are included + uint256 inclusionBlockNumber; + // RLP-encoded block header of the previous block of the inclusion block + // (for clarity: `previousBlockHeader.number == inclusionBlockNumber - 1`) + bytes previousBlockHeaderRLP; + // RLP-encoded block header where the committed transactions are included + bytes inclusionBlockHeaderRLP; + // merkle inclusion proof of the account in the state trie of the previous block + // (checked against the previousBlockHeader.stateRoot) + bytes accountMerkleProof; + // merkle inclusion proof of the transactions in the transaction trie of the inclusion block + // (checked against the inclusionBlockHeader.txRoot). The order of the proofs should match + // the order of the committed transactions in the challenge: `Challenge.committedTxs`. + bytes[] txMerkleProofs; + // indexes of the committed transactions in the block. The order of the indexes should match + // the order of the committed transactions in the challenge: `Challenge.committedTxs`. + uint256[] txIndexesInBlock; + } + + error SlotInTheFuture(); + error BlockIsNotFinalized(); + error IncorrectChallengeBond(); + error ChallengeAlreadyExists(); + error ChallengeAlreadyResolved(); + error ChallengeDoesNotExist(); + error BlockIsTooOld(); + error InvalidBlockHash(); + error InvalidParentBlockHash(); + error AccountDoesNotExist(); + error TransactionNotIncluded(); + error WrongTransactionHashProof(); + error InvalidBlockNumber(); + error BondTransferFailed(); + error ChallengeNotExpired(); + error ChallengeExpired(); + error EmptyCommitments(); + error UnexpectedMixedSenders(); + error UnexpectedMixedSlots(); + error UnexpectedMixedSigners(); + error UnexpectedNonceOrder(); + error InvalidProofsLength(); + error BeaconRootNotFound(); + + event ChallengeOpened(bytes32 indexed challengeId, address indexed challenger, address indexed commitmentSigner); + event ChallengeDefended(bytes32 indexed challengeId); + event ChallengeBreached(bytes32 indexed challengeId); + + function getAllChallenges() external view returns (Challenge[] memory); + + function getOpenChallenges() external view returns (Challenge[] memory); + + function getChallengeByID( + bytes32 challengeID + ) external view returns (Challenge memory); + + function openChallenge( + SignedCommitment[] calldata commitments + ) external payable; + + function resolveExpiredChallenge( + bytes32 challengeID + ) external; + + function resolveOpenChallenge(bytes32 challengeID, Proof calldata proof) external; +} diff --git a/bolt-contracts/src/interfaces/IBoltManagerV1.sol b/bolt-contracts/src/interfaces/IBoltManagerV1.sol new file mode 100644 index 000000000..cebb303fa --- /dev/null +++ b/bolt-contracts/src/interfaces/IBoltManagerV1.sol @@ -0,0 +1,50 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.25; + +import {IBoltValidatorsV1} from "./IBoltValidatorsV1.sol"; + +interface IBoltManagerV1 { + error InvalidQuery(); + error OperatorAlreadyRegistered(); + error OperatorNotRegistered(); + error UnauthorizedMiddleware(); + error InactiveOperator(); + + struct Operator { + string rpc; + address middleware; + uint256 timestamp; + } + + function registerOperator(address operator, string calldata rpc) external; + + function deregisterOperator( + address operator + ) external; + + function pauseOperator( + address operator + ) external; + + function unpauseOperator( + address operator + ) external; + + function isOperator( + address operator + ) external view returns (bool); + + function validators() external view returns (IBoltValidatorsV1); + + function getProposerStatus( + bytes32 pubkeyHash + ) external view returns (IBoltValidatorsV1.ProposerStatus memory status); + + function getProposerStatuses( + bytes32[] calldata pubkeyHashes + ) external view returns (IBoltValidatorsV1.ProposerStatus[] memory statuses); + + function isOperatorAuthorizedForValidator(address operator, bytes32 pubkeyHash) external view returns (bool); + + function getSupportedRestakingProtocols() external view returns (address[] memory middlewares); +} diff --git a/bolt-contracts/src/interfaces/IBoltMiddlewareV1.sol b/bolt-contracts/src/interfaces/IBoltMiddlewareV1.sol new file mode 100644 index 000000000..f48885ffa --- /dev/null +++ b/bolt-contracts/src/interfaces/IBoltMiddlewareV1.sol @@ -0,0 +1,38 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.25; + +import {BLS12381} from "../lib/bls/BLS12381.sol"; +import {IBoltValidatorsV1} from "./IBoltValidatorsV1.sol"; + +interface IBoltMiddlewareV1 { + error InvalidQuery(); + error AlreadyRegistered(); + error NotRegistered(); + error OperatorNotOptedIn(); + error NotOperator(); + error NotAllowed(); + + function NAME_HASH() external view returns (bytes32); + + function getEpochStartTs( + uint48 epoch + ) external view returns (uint48); + + function getEpochAtTs( + uint48 timestamp + ) external view returns (uint48); + + function getCurrentEpoch() external view returns (uint48); + + function getOperatorStake(address operator, address collateral) external view returns (uint256); + + function getOperatorCollaterals( + address operator + ) external view returns (address[] memory, uint256[] memory); + + function getOperatorStakeAt( + address operator, + address collateral, + uint48 timestamp + ) external view returns (uint256); +} diff --git a/bolt-contracts/src/interfaces/IBoltParametersV1.sol b/bolt-contracts/src/interfaces/IBoltParametersV1.sol new file mode 100644 index 000000000..f9a3c7a73 --- /dev/null +++ b/bolt-contracts/src/interfaces/IBoltParametersV1.sol @@ -0,0 +1,17 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.25; + +interface IBoltParametersV1 { + function EPOCH_DURATION() external view returns (uint48); + function SLASHING_WINDOW() external view returns (uint48); + function ALLOW_UNSAFE_REGISTRATION() external view returns (bool); + function MAX_CHALLENGE_DURATION() external view returns (uint48); + function CHALLENGE_BOND() external view returns (uint256); + function BLOCKHASH_EVM_LOOKBACK() external view returns (uint256); + function JUSTIFICATION_DELAY() external view returns (uint256); + function EIP4788_WINDOW() external view returns (uint256); + function SLOT_TIME() external view returns (uint256); + function ETH2_GENESIS_TIMESTAMP() external view returns (uint256); + function BEACON_ROOTS_CONTRACT() external view returns (address); + function MINIMUM_OPERATOR_STAKE() external view returns (uint256); +} diff --git a/bolt-contracts/src/interfaces/IBoltRegistry.sol b/bolt-contracts/src/interfaces/IBoltRegistry.sol deleted file mode 100644 index 283f8f505..000000000 --- a/bolt-contracts/src/interfaces/IBoltRegistry.sol +++ /dev/null @@ -1,69 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.13; - -interface IBoltRegistry { - /// @notice Struct to hold opted-in proposer information - struct Registrant { - // The address of the operator - address operator; - // The validator indexes this registrant is responsible for - uint64[] validatorIndexes; - uint256 enteredAt; - uint256 exitInitiatedAt; - uint256 balance; - Status status; - MetaData metadata; - } - - struct MetaData { - string rpc; - bytes extra; - } - - /// @notice Enum to hold the status of the based proposers - enum Status { - // Default INACTIVE - INACTIVE, - ACTIVE, - FROZEN, - EXITING - } - - // Error messages - error AlreadyOptedIn(); - error InsufficientCollateral(); - error BasedProposerDoesNotExist(); - error InvalidStatusChange(); - error CooldownNotElapsed(); - error Unauthorized(); - error NotFound(); - - /// @notice Event to log the status change of a based proposer - event StatusChange(address indexed operator, Status status); - - event Registered( - address indexed operator, - uint64[] validatorIndexes, - MetaData metadata - ); - - function register( - uint64[] calldata validatorIndexes, - string calldata rpc, - bytes calldata extra - ) external payable; - - function isActiveOperator(address _operator) external view returns (bool); - - function getOperatorStatus( - address _operator - ) external view returns (Status); - - function getOperatorForValidator( - uint64 _validatorIndex - ) external view returns (Registrant memory); - - function startExit() external; - - function confirmExit(address payable recipient) external; -} diff --git a/bolt-contracts/src/interfaces/IBoltValidatorsV1.sol b/bolt-contracts/src/interfaces/IBoltValidatorsV1.sol new file mode 100644 index 000000000..af5e32a00 --- /dev/null +++ b/bolt-contracts/src/interfaces/IBoltValidatorsV1.sol @@ -0,0 +1,87 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.25; + +import {BLS12381} from "../lib/bls/BLS12381.sol"; + +interface IBoltValidatorsV1 { + /// @notice Validator info + struct Validator { + // whether the validator exists in the registry + bool exists; + // the incremental sequence number assigned to the validator + uint64 sequenceNumber; + // the maximum amount of gas that the validator can consume with preconfirmations + // in a single slot. Operators must respect this limit when making commitments. + uint128 maxCommittedGasLimit; + // the entity authorized to make commitments on behalf of the validator + address authorizedOperator; + // the EOA that registered the validator and can update its configuration + address controller; + } + + /// @notice Proposer status info. + struct ProposerStatus { + // The pubkey hash of the validator. + bytes32 pubkeyHash; + // Whether the corresponding operator is active based on collateral requirements. + bool active; + // The operator address that is authorized to make & sign commitments on behalf of the validator. + address operator; + // The operator RPC endpoint. + string operatorRPC; + // The addresses of the collateral tokens. + address[] collaterals; + // The corresponding amounts of the collateral tokens. + uint256[] amounts; + } + + error InvalidBLSSignature(); + error InvalidAuthorizedCollateralProvider(); + error InvalidAuthorizedOperator(); + error ValidatorAlreadyExists(); + error ValidatorDoesNotExist(); + error UnsafeRegistrationNotAllowed(); + error UnauthorizedCaller(); + + function getAllValidators() external view returns (Validator[] memory); + + function getValidatorByPubkey( + BLS12381.G1Point calldata pubkey + ) external view returns (Validator memory); + + function getValidatorByPubkeyHash( + bytes32 pubkeyHash + ) external view returns (Validator memory); + + function getValidatorBySequenceNumber( + uint64 sequenceNumber + ) external view returns (Validator memory); + + function registerValidatorUnsafe( + BLS12381.G1Point calldata pubkey, + uint128 maxCommittedGasLimit, + address authorizedOperator + ) external; + + function registerValidator( + BLS12381.G1Point calldata pubkey, + BLS12381.G2Point calldata signature, + uint128 maxCommittedGasLimit, + address authorizedOperator + ) external; + + function batchRegisterValidators( + BLS12381.G1Point[] calldata pubkeys, + BLS12381.G2Point calldata signature, + uint128 maxCommittedGasLimit, + address authorizedOperator + ) external; + + function batchRegisterValidatorsUnsafe( + BLS12381.G1Point[] calldata pubkeys, + uint128 maxCommittedGasLimit, + address authorizedOperator + ) external; + + function updateMaxCommittedGasLimit(bytes32 pubkeyHash, uint128 maxCommittedGasLimit) external; +} diff --git a/bolt-contracts/src/lib/BeaconChainUtils.sol b/bolt-contracts/src/lib/BeaconChainUtils.sol deleted file mode 100644 index 3664e3ce5..000000000 --- a/bolt-contracts/src/lib/BeaconChainUtils.sol +++ /dev/null @@ -1,46 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.13; - -library BeaconChainUtils { - /// @notice The address of the BeaconRoots contract - /// @dev See EIP-4788 for more info - address internal constant BEACON_ROOTS_CONTRACT = 0x000F3df6D732807Ef1319fB7B8bB8522d0Beac02; - - /// @notice The duration of a slot in seconds - uint256 internal constant SLOT_TIME = 12; - - /// @notice The timestamp of the genesis of the eth2 chain - uint256 internal constant ETH2_GENESIS_TIMESTAMP = 1606824023; - - /// @notice Error emitted when a beacon block root is not found - error BeaconRootNotFound(); - - /// @notice Get the slot number from a given timestamp - /// @param _timestamp The timestamp - /// @return The slot number - function _getSlotFromTimestamp(uint256 _timestamp) internal pure returns (uint256) { - return (_timestamp - ETH2_GENESIS_TIMESTAMP) / SLOT_TIME; - } - - /// @notice Get the timestamp from a given slot - /// @param _slot The slot number - /// @return The timestamp - function _getTimestampFromSlot(uint256 _slot) internal pure returns (uint256) { - return ETH2_GENESIS_TIMESTAMP + _slot * SLOT_TIME; - } - - /// @notice Get the beacon block root for a given slot - /// @param _slot The slot number - /// @return The beacon block root - function _getBeaconBlockRoot(uint256 _slot) internal view returns (bytes32) { - uint256 slotTimestamp = ETH2_GENESIS_TIMESTAMP + _slot * SLOT_TIME; - - (bool success, bytes memory data) = BEACON_ROOTS_CONTRACT.staticcall(abi.encode(slotTimestamp)); - - if (!success || data.length == 0) { - revert BeaconRootNotFound(); - } - - return abi.decode(data, (bytes32)); - } -} diff --git a/bolt-contracts/src/lib/BytesUtils.sol b/bolt-contracts/src/lib/BytesUtils.sol new file mode 100644 index 000000000..036640ad7 --- /dev/null +++ b/bolt-contracts/src/lib/BytesUtils.sol @@ -0,0 +1,192 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.25; + +// Credits: Optimism contributors. +// Ref: https://github.com/ethereum-optimism/optimism/blob/05deae54595b0e6bdd33580de81cb9ad194898bc/packages/contracts-bedrock/src/libraries/Bytes.sol + +/** + * @title BytesUtils + */ +library BytesUtils { + /** + * + * Internal Functions * + * + */ + function slice(bytes memory _bytes, uint256 _start, uint256 _length) internal pure returns (bytes memory) { + unchecked { + require(_length + 31 >= _length, "slice_overflow"); + require(_start + _length >= _start, "slice_overflow"); + require(_bytes.length >= _start + _length, "slice_outOfBounds"); + + bytes memory tempBytes; + + assembly { + switch iszero(_length) + case 0 { + // Get a location of some free memory and store it in tempBytes as + // Solidity does for memory variables. + tempBytes := mload(0x40) + + // The first word of the slice result is potentially a partial + // word read from the original array. To read it, we calculate + // the length of that partial word and start copying that many + // bytes into the array. The first word we copy will start with + // data we don't care about, but the last `lengthmod` bytes will + // land at the beginning of the contents of the new array. When + // we're done copying, we overwrite the full first word with + // the actual length of the slice. + let lengthmod := and(_length, 31) + + // The multiplication in the next line is necessary + // because when slicing multiples of 32 bytes (lengthmod == 0) + // the following copy loop was copying the origin's length + // and then ending prematurely not copying everything it should. + let mc := add(add(tempBytes, lengthmod), mul(0x20, iszero(lengthmod))) + let end := add(mc, _length) + + for { + // The multiplication in the next line has the same exact purpose + // as the one above. + let cc := add(add(add(_bytes, lengthmod), mul(0x20, iszero(lengthmod))), _start) + } lt(mc, end) { + mc := add(mc, 0x20) + cc := add(cc, 0x20) + } { mstore(mc, mload(cc)) } + + mstore(tempBytes, _length) + + //update free-memory pointer + //allocating the array padded to 32 bytes like the compiler does now + mstore(0x40, and(add(mc, 31), not(31))) + } + //if we want a zero-length slice let's just return a zero-length array + default { + tempBytes := mload(0x40) + + //zero out the 32 bytes slice we are about to return + //we need to do it because Solidity does not garbage collect + mstore(tempBytes, 0) + + mstore(0x40, add(tempBytes, 0x20)) + } + } + + return tempBytes; + } + } + + function slice(bytes memory _bytes, uint256 _start) internal pure returns (bytes memory) { + unchecked { + if (_bytes.length - _start == 0) { + return bytes(""); + } + + return slice(_bytes, _start, _bytes.length - _start); + } + } + + function toBytes32PadLeft( + bytes memory _bytes + ) internal pure returns (bytes32) { + unchecked { + bytes32 ret; + uint256 len = _bytes.length <= 32 ? _bytes.length : 32; + assembly { + ret := shr(mul(sub(32, len), 8), mload(add(_bytes, 32))) + } + return ret; + } + } + + function toBytes32( + bytes memory _bytes + ) internal pure returns (bytes32) { + unchecked { + if (_bytes.length < 32) { + bytes32 ret; + assembly { + ret := mload(add(_bytes, 32)) + } + return ret; + } + + return abi.decode(_bytes, (bytes32)); // will truncate if input length > 32 bytes + } + } + + function toUint256( + bytes memory _bytes + ) internal pure returns (uint256) { + return uint256(toBytes32(_bytes)); + } + + function toUint24(bytes memory _bytes, uint256 _start) internal pure returns (uint24) { + require(_start + 3 >= _start, "toUint24_overflow"); + require(_bytes.length >= _start + 3, "toUint24_outOfBounds"); + uint24 tempUint; + + assembly { + tempUint := mload(add(add(_bytes, 0x3), _start)) + } + + return tempUint; + } + + function toUint8(bytes memory _bytes, uint256 _start) internal pure returns (uint8) { + require(_start + 1 >= _start, "toUint8_overflow"); + require(_bytes.length >= _start + 1, "toUint8_outOfBounds"); + uint8 tempUint; + + assembly { + tempUint := mload(add(add(_bytes, 0x1), _start)) + } + + return tempUint; + } + + function toAddress(bytes memory _bytes, uint256 _start) internal pure returns (address) { + require(_start + 20 >= _start, "toAddress_overflow"); + require(_bytes.length >= _start + 20, "toAddress_outOfBounds"); + address tempAddress; + + assembly { + tempAddress := div(mload(add(add(_bytes, 0x20), _start)), 0x1000000000000000000000000) + } + + return tempAddress; + } + + function toNibbles( + bytes memory _bytes + ) internal pure returns (bytes memory) { + unchecked { + bytes memory nibbles = new bytes(_bytes.length * 2); + + for (uint256 i = 0; i < _bytes.length; i++) { + nibbles[i * 2] = _bytes[i] >> 4; + nibbles[i * 2 + 1] = bytes1(uint8(_bytes[i]) % 16); + } + + return nibbles; + } + } + + function fromNibbles( + bytes memory _bytes + ) internal pure returns (bytes memory) { + unchecked { + bytes memory ret = new bytes(_bytes.length / 2); + + for (uint256 i = 0; i < ret.length; i++) { + ret[i] = (_bytes[i * 2] << 4) | (_bytes[i * 2 + 1]); + } + + return ret; + } + } + + function equal(bytes memory _bytes, bytes memory _other) internal pure returns (bool) { + return keccak256(_bytes) == keccak256(_other); + } +} diff --git a/bolt-contracts/src/lib/Config.sol b/bolt-contracts/src/lib/Config.sol new file mode 100644 index 000000000..a65c80e61 --- /dev/null +++ b/bolt-contracts/src/lib/Config.sol @@ -0,0 +1,34 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.25; + +library BoltConfig { + struct Parameters { + uint48 epochDuration; + uint48 slashingWindow; + uint48 maxChallengeDuration; + uint256 challengeBond; + uint256 blockhashEvmLookback; + uint256 justificationDelay; + uint256 eth2GenesisTimestamp; + uint256 slotTime; + bool allowUnsafeRegistration; + uint256 minimumOperatorStake; + } + + struct Deployments { + address symbioticNetwork; + address symbioticOperatorRegistry; + address symbioticOperatorNetOptIn; + address symbioticVaultFactory; + address[] supportedVaults; + address eigenLayerAVSDirectory; + address eigenLayerDelegationManager; + address eigenLayerStrategyManager; + address[] supportedStrategies; + } + + struct SymbioticDeployments { + address symbioticNetworkRegistry; + address symbioticNetworkMiddlewareService; + } +} diff --git a/bolt-contracts/src/lib/EnumerableMap.sol b/bolt-contracts/src/lib/EnumerableMap.sol new file mode 100644 index 000000000..af366efe5 --- /dev/null +++ b/bolt-contracts/src/lib/EnumerableMap.sol @@ -0,0 +1,67 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.25; + +import {EnumerableSet} from "@openzeppelin/contracts/utils/structs/EnumerableSet.sol"; + +import {IBoltManagerV1} from "../interfaces/IBoltManagerV1.sol"; + +library EnumerableMap { + using EnumerableSet for EnumerableSet.Bytes32Set; + + error KeyNotFound(); + + struct OperatorMap { + // Storage of keys + EnumerableSet.Bytes32Set _keys; + mapping(bytes32 key => IBoltManagerV1.Operator) _values; + } + + function set(OperatorMap storage self, address key, IBoltManagerV1.Operator memory value) internal returns (bool) { + bytes32 keyBytes = bytes32(uint256(uint160(key))); + self._values[keyBytes] = value; + return self._keys.add(keyBytes); + } + + function remove(OperatorMap storage self, address key) internal returns (bool) { + bytes32 keyBytes = bytes32(uint256(uint160(key))); + delete self._values[keyBytes]; + return self._keys.remove(keyBytes); + } + + function contains(OperatorMap storage self, address key) internal view returns (bool) { + return self._keys.contains(bytes32(uint256(uint160(key)))); + } + + function length( + OperatorMap storage self + ) internal view returns (uint256) { + return self._keys.length(); + } + + function at( + OperatorMap storage self, + uint256 index + ) internal view returns (address, IBoltManagerV1.Operator memory) { + bytes32 key = self._keys.at(index); + return (address(uint160(uint256(key))), self._values[key]); + } + + function get(OperatorMap storage self, address key) internal view returns (IBoltManagerV1.Operator memory) { + if (!contains(self, key)) { + revert KeyNotFound(); + } + + return self._values[bytes32(uint256(uint160(key)))]; + } + + function keys( + OperatorMap storage self + ) internal view returns (address[] memory) { + address[] memory result = new address[](self._keys.length()); + for (uint256 i = 0; i < self._keys.length(); i++) { + result[i] = address(uint160(uint256(self._keys.at(i)))); + } + + return result; + } +} diff --git a/bolt-contracts/src/lib/MapWithTimeData.sol b/bolt-contracts/src/lib/MapWithTimeData.sol new file mode 100644 index 000000000..fa0a10afe --- /dev/null +++ b/bolt-contracts/src/lib/MapWithTimeData.sol @@ -0,0 +1,67 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.25; + +// Credits: Symbiotic contributors. +// Ref: https://github.com/symbioticfi/cosmos-sdk/blob/c25b6d5f320eb8ea4189584fa04d28c47362c2a7/middleware/src/libraries/MapWithTimeData.sol + +import {Checkpoints} from "@openzeppelin/contracts/utils/structs/Checkpoints.sol"; +import {Time} from "@openzeppelin/contracts/utils/types/Time.sol"; +import {EnumerableMap} from "@openzeppelin/contracts/utils/structs/EnumerableMap.sol"; + +library MapWithTimeData { + using EnumerableMap for EnumerableMap.AddressToUintMap; + + error AlreadyAdded(); + error NotEnabled(); + error AlreadyEnabled(); + + uint256 private constant ENABLED_TIME_MASK = 0xFFFFFFFFFFFFFFFFFFFFFFFF; + uint256 private constant DISABLED_TIME_MASK = 0xFFFFFFFFFFFFFFFFFFFFFFFF << 48; + + function add(EnumerableMap.AddressToUintMap storage self, address addr) internal { + if (!self.set(addr, uint256(0))) { + revert AlreadyAdded(); + } + } + + function disable(EnumerableMap.AddressToUintMap storage self, address addr) internal { + uint256 value = self.get(addr); + + if (uint48(value) == 0 || uint48(value >> 48) != 0) { + revert NotEnabled(); + } + + value |= uint256(Time.timestamp()) << 48; + self.set(addr, value); + } + + function enable(EnumerableMap.AddressToUintMap storage self, address addr) internal { + uint256 value = self.get(addr); + + if (uint48(value) != 0 && uint48(value >> 48) == 0) { + revert AlreadyEnabled(); + } + + value = uint256(Time.timestamp()); + self.set(addr, value); + } + + function atWithTimes( + EnumerableMap.AddressToUintMap storage self, + uint256 idx + ) internal view returns (address key, uint48 enabledTime, uint48 disabledTime) { + uint256 value; + (key, value) = self.at(idx); + enabledTime = uint48(value); + disabledTime = uint48(value >> 48); + } + + function getTimes( + EnumerableMap.AddressToUintMap storage self, + address addr + ) internal view returns (uint48 enabledTime, uint48 disabledTime) { + uint256 value = self.get(addr); + enabledTime = uint48(value); + disabledTime = uint48(value >> 48); + } +} diff --git a/bolt-contracts/src/lib/OperatorMapWithTime.sol b/bolt-contracts/src/lib/OperatorMapWithTime.sol new file mode 100644 index 000000000..0b5f7ab78 --- /dev/null +++ b/bolt-contracts/src/lib/OperatorMapWithTime.sol @@ -0,0 +1,71 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.25; + +import {Checkpoints} from "@openzeppelin/contracts/utils/structs/Checkpoints.sol"; +import {Time} from "@openzeppelin/contracts/utils/types/Time.sol"; + +import {IBoltManagerV1} from "../interfaces/IBoltManagerV1.sol"; +import {EnumerableMap} from "./EnumerableMap.sol"; + +library OperatorMapWithTime { + using EnumerableMap for EnumerableMap.OperatorMap; + + error AlreadyAdded(); + error NotEnabled(); + error AlreadyEnabled(); + + uint256 private constant ENABLED_TIME_MASK = 0xFFFFFFFFFFFFFFFFFFFFFFFF; + uint256 private constant DISABLED_TIME_MASK = 0xFFFFFFFFFFFFFFFFFFFFFFFF << 48; + + function add(EnumerableMap.OperatorMap storage self, address addr) internal { + if (!self.set(addr, IBoltManagerV1.Operator("", address(0), 0))) { + revert AlreadyAdded(); + } + } + + function disable(EnumerableMap.OperatorMap storage self, address addr) internal { + IBoltManagerV1.Operator memory operator = self.get(addr); + uint256 value = operator.timestamp; + + if (uint48(value) == 0 || uint48(value >> 48) != 0) { + revert NotEnabled(); + } + + value |= uint256(Time.timestamp()) << 48; + operator.timestamp = value; + self.set(addr, operator); + } + + function enable(EnumerableMap.OperatorMap storage self, address addr) internal { + IBoltManagerV1.Operator memory operator = self.get(addr); + uint256 value = operator.timestamp; + + if (uint48(value) != 0 && uint48(value >> 48) == 0) { + revert AlreadyEnabled(); + } + + value = uint256(Time.timestamp()); + operator.timestamp = value; + self.set(addr, operator); + } + + function atWithTimes( + EnumerableMap.OperatorMap storage self, + uint256 idx + ) internal view returns (address key, uint48 enabledTime, uint48 disabledTime) { + IBoltManagerV1.Operator memory value; + (key, value) = self.at(idx); + uint256 timestamp = value.timestamp; + enabledTime = uint48(timestamp); + disabledTime = uint48(timestamp >> 48); + } + + function getTimes( + EnumerableMap.OperatorMap storage self, + address addr + ) internal view returns (uint48 enabledTime, uint48 disabledTime) { + IBoltManagerV1.Operator memory value = self.get(addr); + enabledTime = uint48(value.timestamp); + disabledTime = uint48(value.timestamp >> 48); + } +} diff --git a/bolt-contracts/src/lib/TransactionDecoder.sol b/bolt-contracts/src/lib/TransactionDecoder.sol new file mode 100644 index 000000000..3bd7cfc66 --- /dev/null +++ b/bolt-contracts/src/lib/TransactionDecoder.sol @@ -0,0 +1,451 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.25; + +import {ECDSA} from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; + +import {RLPReader} from "./rlp/RLPReader.sol"; +import {RLPWriter} from "./rlp/RLPWriter.sol"; +import {BytesUtils} from "./BytesUtils.sol"; + +/// @title TransactionDecoder +/// @notice A library to decode Ethereum transactions in EIP-2718 format +/// into a structured transaction object, and to recover the sender. +library TransactionDecoder { + using BytesUtils for bytes; + using RLPReader for bytes; + using RLPReader for RLPReader.RLPItem; + + /// @notice The type of transaction + enum TxType { + Legacy, + Eip2930, + Eip1559, + Eip4844 + } + + /// @notice A decoded transaction object + struct Transaction { + TxType txType; + uint64 chainId; + // As Solidity doesn't have NULL values, we use this flag to differentiate between + // an explicit chainId of 0 and a missing chainID. The difference is important for + // encoding unsigned eip-155 legacy transactions. + bool isChainIdSet; + uint256 nonce; + uint256 gasPrice; + uint256 maxPriorityFeePerGas; + uint256 maxFeePerGas; + uint256 gasLimit; + address to; + uint256 value; + bytes data; + bytes[] accessList; + uint256 maxFeePerBlobGas; + bytes32[] blobVersionedHashes; + bytes sig; + uint64 legacyV; + } + + error NoSignature(); + error InvalidYParity(); + error UnsupportedTxType(); + error InvalidFieldCount(); + error InvalidSignatureLength(); + + /// @notice Decode a raw transaction into a transaction object + /// @param raw The raw transaction bytes + /// @return transaction The decoded transaction object + function decodeEnveloped( + bytes memory raw + ) internal pure returns (Transaction memory transaction) { + bytes1 prefix = raw[0]; + + if (prefix >= 0x7F) { + return _decodeLegacy(raw); + } else if (prefix == 0x01) { + return _decodeEip2930(raw); + } else if (prefix == 0x02) { + return _decodeEip1559(raw); + } else if (prefix == 0x03) { + return _decodeEip4844(raw); + } else { + revert UnsupportedTxType(); + } + } + + /// @notice Recover the sender of a transaction + /// @param transaction The transaction object + /// @return sender The address of the sender + function recoverSender( + Transaction memory transaction + ) internal pure returns (address) { + return ECDSA.recover(preimage(transaction), signature(transaction)); + } + + /// @notice Compute the preimage of a transaction object + /// @dev This is the hash of the transaction that is signed by the sender to obtain the signature + /// @param transaction The transaction object + /// @return preimg The preimage hash of the transaction + function preimage( + Transaction memory transaction + ) internal pure returns (bytes32 preimg) { + preimg = keccak256(unsigned(transaction)); + } + + /// @notice Compute the unsigned transaction object + /// @dev This is the transaction object without the signature + /// @param transaction The transaction object + /// @return unsignedTx The unsigned transaction object + function unsigned( + Transaction memory transaction + ) internal pure returns (bytes memory unsignedTx) { + if (transaction.txType == TxType.Legacy) { + unsignedTx = _unsignedLegacy(transaction); + } else if (transaction.txType == TxType.Eip2930) { + unsignedTx = _unsignedEip2930(transaction); + } else if (transaction.txType == TxType.Eip1559) { + unsignedTx = _unsignedEip1559(transaction); + } else if (transaction.txType == TxType.Eip4844) { + unsignedTx = _unsignedEip4844(transaction); + } else { + revert UnsupportedTxType(); + } + } + + /// @notice Return the hex-encoded signature of a transaction object + /// @param transaction The transaction object + /// @return sig The hex-encoded signature + function signature( + Transaction memory transaction + ) internal pure returns (bytes memory sig) { + if (transaction.sig.length == 0) { + revert NoSignature(); + } else if (transaction.sig.length != 65) { + revert InvalidSignatureLength(); + } else { + sig = transaction.sig; + } + } + + /// @notice Helper to decode a legacy (type 0) transaction + /// @param raw The raw transaction bytes + /// @return transaction The decoded transaction object + function _decodeLegacy( + bytes memory raw + ) private pure returns (Transaction memory transaction) { + transaction.txType = TxType.Legacy; + + // Legacy transactions don't have a type prefix, so we can decode directly + RLPReader.RLPItem[] memory fields = raw.toRLPItem().readList(); + + if (fields.length != 9 && fields.length != 6) { + revert InvalidFieldCount(); + } + + transaction.nonce = fields[0].readUint256(); + transaction.gasPrice = fields[1].readUint256(); + transaction.gasLimit = fields[2].readUint256(); + transaction.to = fields[3].readAddress(); + transaction.value = fields[4].readUint256(); + transaction.data = fields[5].readBytes(); + + // Legacy unsigned transaction + if (fields.length == 6) { + return transaction; + } + + // rlp expects signature values in (v, r, s) order + uint64 v = uint64(fields[6].readUint256()); + uint256 r = fields[7].readUint256(); + uint256 s = fields[8].readUint256(); + + if (r == 0 && s == 0) { + // EIP-155 unsigned transaction + transaction.chainId = v; + transaction.isChainIdSet = true; + } else { + if (v >= 35) { + // Compute the EIP-155 chain ID (or 0 for legacy) + transaction.chainId = (v - 35) / 2; + transaction.legacyV = v; + transaction.isChainIdSet = true; + } + + // Compute the signature + uint8 parityV = uint8(((v ^ 1) % 2) + 27); + transaction.sig = abi.encodePacked(bytes32(r), bytes32(s), parityV); + } + } + + /// @notice Helper to decode an EIP-2930 (type 1) transaction + /// @param raw The raw transaction bytes + /// @return transaction The decoded transaction object + function _decodeEip2930( + bytes memory raw + ) private pure returns (Transaction memory transaction) { + transaction.txType = TxType.Eip2930; + + // Skip the first byte (transaction type) + bytes memory rlpData = raw.slice(1, raw.length - 1); + RLPReader.RLPItem[] memory fields = rlpData.toRLPItem().readList(); + + if (fields.length != 8 && fields.length != 11) { + revert InvalidFieldCount(); + } + + transaction.chainId = uint64(fields[0].readUint256()); + transaction.nonce = fields[1].readUint256(); + transaction.gasPrice = fields[2].readUint256(); + transaction.gasLimit = fields[3].readUint256(); + transaction.to = fields[4].readAddress(); + transaction.value = fields[5].readUint256(); + transaction.data = fields[6].readBytes(); + + RLPReader.RLPItem[] memory accessListItems = fields[7].readList(); + transaction.accessList = new bytes[](accessListItems.length); + for (uint256 i = 0; i < accessListItems.length; i++) { + transaction.accessList[i] = accessListItems[i].readRawBytes(); + } + + // EIP-2930 Unsigned transaction + if (fields.length == 8) { + return transaction; + } + + uint8 v = uint8(fields[8].readUint256()) + 27; + bytes32 r = fields[9].readBytes32(); + bytes32 s = fields[10].readBytes32(); + + // compute the signature + transaction.sig = abi.encodePacked(r, s, v); + } + + /// @notice Helper to decode an EIP-1559 (type 2) transaction + /// @param raw The raw transaction bytes + /// @return transaction The decoded transaction object + function _decodeEip1559( + bytes memory raw + ) private pure returns (Transaction memory transaction) { + transaction.txType = TxType.Eip1559; + + // Skip the first byte (transaction type) + bytes memory rlpData = raw.slice(1, raw.length - 1); + RLPReader.RLPItem[] memory fields = rlpData.toRLPItem().readList(); + + if (fields.length != 9 && fields.length != 12) { + revert InvalidFieldCount(); + } + + transaction.chainId = uint64(fields[0].readUint256()); + transaction.nonce = fields[1].readUint256(); + transaction.maxPriorityFeePerGas = fields[2].readUint256(); + transaction.maxFeePerGas = fields[3].readUint256(); + transaction.gasLimit = fields[4].readUint256(); + transaction.to = fields[5].readAddress(); + transaction.value = fields[6].readUint256(); + transaction.data = fields[7].readBytes(); + + RLPReader.RLPItem[] memory accessListItems = fields[8].readList(); + transaction.accessList = new bytes[](accessListItems.length); + for (uint256 i = 0; i < accessListItems.length; i++) { + transaction.accessList[i] = accessListItems[i].readRawBytes(); + } + + if (fields.length == 9) { + // EIP-1559 Unsigned transaction + return transaction; + } + + uint8 v = uint8(fields[9].readUint256()) + 27; + bytes32 r = fields[10].readBytes32(); + bytes32 s = fields[11].readBytes32(); + + // compute the signature + transaction.sig = abi.encodePacked(r, s, v); + } + + /// @notice Helper to decode an EIP-4844 (type 3) transaction + /// @param raw The raw transaction bytes + /// @return transaction The decoded transaction object + function _decodeEip4844( + bytes memory raw + ) private pure returns (Transaction memory transaction) { + transaction.txType = TxType.Eip4844; + + // Skip the first byte (transaction type) + bytes memory rlpData = raw.slice(1, raw.length - 1); + RLPReader.RLPItem[] memory fields = rlpData.toRLPItem().readList(); + + if (fields.length != 11 && fields.length != 14) { + revert InvalidFieldCount(); + } + + transaction.chainId = uint64(fields[0].readUint256()); + transaction.nonce = fields[1].readUint256(); + transaction.maxPriorityFeePerGas = fields[2].readUint256(); + transaction.maxFeePerGas = fields[3].readUint256(); + transaction.gasLimit = fields[4].readUint256(); + transaction.to = fields[5].readAddress(); + transaction.value = fields[6].readUint256(); + transaction.data = fields[7].readBytes(); + + RLPReader.RLPItem[] memory accessListItems = fields[8].readList(); + transaction.accessList = new bytes[](accessListItems.length); + for (uint256 i = 0; i < accessListItems.length; i++) { + transaction.accessList[i] = accessListItems[i].readRawBytes(); + } + + transaction.maxFeePerBlobGas = fields[9].readUint256(); + + RLPReader.RLPItem[] memory blobVersionedHashesItems = fields[10].readList(); + transaction.blobVersionedHashes = new bytes32[](blobVersionedHashesItems.length); + for (uint256 i = 0; i < blobVersionedHashesItems.length; i++) { + transaction.blobVersionedHashes[i] = blobVersionedHashesItems[i].readBytes32(); + } + + if (fields.length == 11) { + // Unsigned transaction + return transaction; + } + + uint8 v = uint8(fields[11].readUint256()) + 27; + bytes32 r = fields[12].readBytes32(); + bytes32 s = fields[13].readBytes32(); + + // compute the signature + transaction.sig = abi.encodePacked(r, s, v); + } + + /// @notice Helper to RLP-encode an unsigned legacy transaction + /// @param transaction The transaction object + /// @return unsignedTx The unsigned transaction bytes + function _unsignedLegacy( + Transaction memory transaction + ) private pure returns (bytes memory unsignedTx) { + uint64 chainId = 0; + if (transaction.chainId != 0) { + // A chainId was provided: if non-zero, we'll use EIP-155 + chainId = transaction.chainId; + } else if (transaction.sig.length != 0) { + // No explicit chainId, but EIP-155 have a derived implicit chainId + // based on the V value of the signature + if (transaction.legacyV >= 35) { + chainId = (transaction.legacyV - 35) / 2; + } + } + + uint256 fieldsCount = 6 + (transaction.isChainIdSet ? 3 : 0); + bytes[] memory fields = new bytes[](fieldsCount); + + fields[0] = RLPWriter.writeUint(transaction.nonce); + fields[1] = RLPWriter.writeUint(transaction.gasPrice); + fields[2] = RLPWriter.writeUint(transaction.gasLimit); + fields[3] = RLPWriter.writeAddress(transaction.to); + fields[4] = RLPWriter.writeUint(transaction.value); + fields[5] = RLPWriter.writeBytes(transaction.data); + + if (transaction.isChainIdSet) { + if (transaction.chainId == 0) { + // Edge case: chainId is 0, but we still need to encode it + // as a single empty byte. + fields[6] = abi.encodePacked(bytes1(0)); + } else { + fields[6] = RLPWriter.writeUint(chainId); + } + + fields[7] = RLPWriter.writeBytes(new bytes(0)); + fields[8] = RLPWriter.writeBytes(new bytes(0)); + } + + unsignedTx = RLPWriter.writeList(fields); + } + + /// @notice Helper to RLP-encode an unsigned EIP-2930 transaction + /// @param transaction The transaction object + /// @return unsignedTx The unsigned transaction bytes + function _unsignedEip2930( + Transaction memory transaction + ) private pure returns (bytes memory unsignedTx) { + bytes[] memory fields = new bytes[](8); + + fields[0] = RLPWriter.writeUint(transaction.chainId); + fields[1] = RLPWriter.writeUint(transaction.nonce); + fields[2] = RLPWriter.writeUint(transaction.gasPrice); + fields[3] = RLPWriter.writeUint(transaction.gasLimit); + fields[4] = RLPWriter.writeAddress(transaction.to); + fields[5] = RLPWriter.writeUint(transaction.value); + fields[6] = RLPWriter.writeBytes(transaction.data); + + bytes[] memory accessList = new bytes[](transaction.accessList.length); + for (uint256 i = 0; i < transaction.accessList.length; i++) { + accessList[i] = transaction.accessList[i]; + } + fields[7] = RLPWriter.writeList(accessList); + + // EIP-2718 envelope + unsignedTx = abi.encodePacked(uint8(TxType.Eip2930), RLPWriter.writeList(fields)); + } + + /// @notice Helper to RLP-encode an unsigned EIP-1559 transaction + /// @param transaction The transaction object + /// @return unsignedTx The unsigned transaction bytes + function _unsignedEip1559( + Transaction memory transaction + ) private pure returns (bytes memory unsignedTx) { + bytes[] memory fields = new bytes[](9); + + fields[0] = RLPWriter.writeUint(transaction.chainId); + fields[1] = RLPWriter.writeUint(transaction.nonce); + fields[2] = RLPWriter.writeUint(transaction.maxPriorityFeePerGas); + fields[3] = RLPWriter.writeUint(transaction.maxFeePerGas); + fields[4] = RLPWriter.writeUint(transaction.gasLimit); + fields[5] = RLPWriter.writeAddress(transaction.to); + fields[6] = RLPWriter.writeUint(transaction.value); + fields[7] = RLPWriter.writeBytes(transaction.data); + + bytes[] memory accessList = new bytes[](transaction.accessList.length); + for (uint256 i = 0; i < transaction.accessList.length; i++) { + accessList[i] = transaction.accessList[i]; + } + fields[8] = RLPWriter.writeList(accessList); + + // EIP-2718 envelope + unsignedTx = abi.encodePacked(uint8(TxType.Eip1559), RLPWriter.writeList(fields)); + } + + /// @notice Helper to RLP-encode an unsigned EIP-4844 transaction + /// @param transaction The transaction object + /// @return unsignedTx The unsigned transaction bytes + function _unsignedEip4844( + Transaction memory transaction + ) private pure returns (bytes memory unsignedTx) { + bytes[] memory fields = new bytes[](11); + + fields[0] = RLPWriter.writeUint(transaction.chainId); + fields[1] = RLPWriter.writeUint(transaction.nonce); + fields[2] = RLPWriter.writeUint(transaction.maxPriorityFeePerGas); + fields[3] = RLPWriter.writeUint(transaction.maxFeePerGas); + fields[4] = RLPWriter.writeUint(transaction.gasLimit); + fields[5] = RLPWriter.writeAddress(transaction.to); + fields[6] = RLPWriter.writeUint(transaction.value); + fields[7] = RLPWriter.writeBytes(transaction.data); + + bytes[] memory accessList = new bytes[](transaction.accessList.length); + for (uint256 i = 0; i < transaction.accessList.length; i++) { + accessList[i] = transaction.accessList[i]; + } + fields[8] = RLPWriter.writeList(accessList); + + fields[9] = RLPWriter.writeUint(transaction.maxFeePerBlobGas); + + bytes[] memory blobVersionedHashes = new bytes[](transaction.blobVersionedHashes.length); + for (uint256 i = 0; i < transaction.blobVersionedHashes.length; i++) { + // Decode bytes32 as uint256 (RLPWriter doesn't support bytes32 but they are equivalent) + blobVersionedHashes[i] = RLPWriter.writeUint(uint256(transaction.blobVersionedHashes[i])); + } + fields[10] = RLPWriter.writeList(blobVersionedHashes); + + // EIP-2718 envelope + unsignedTx = abi.encodePacked(uint8(TxType.Eip4844), RLPWriter.writeList(fields)); + } +} diff --git a/bolt-contracts/src/lib/bls/BLS12381.sol b/bolt-contracts/src/lib/bls/BLS12381.sol new file mode 100644 index 000000000..635302213 --- /dev/null +++ b/bolt-contracts/src/lib/bls/BLS12381.sol @@ -0,0 +1,428 @@ +// ====================================================== +// Code below is copied from: +// https://github.com/NethermindEth/Taiko-Preconf-AVS/blob/caf9fbbde0dd84947af5a7b26610ffd38525d932/SmartContracts/src/libraries/BLS12381.sol +// +// If/when a license will be added to the original code, it will be added here as well. +// ====================================================== + +// SPDX-License-Identifier: UNLICENSED +// Functions in this library have been adapted from: +// https://github.com/ethyla/bls12-381-hash-to-curve/blob/main/src/HashToCurve.sol +pragma solidity 0.8.25; + +library BLS12381 { + using BLS12381 for *; + + struct FieldPoint2 { + uint256[2] u; + uint256[2] u_I; + } + + struct G1Point { + uint256[2] x; + uint256[2] y; + } + + struct G2Point { + uint256[2] x; + uint256[2] x_I; + uint256[2] y; + uint256[2] y_I; + } + + /// @dev Referenced from https://eips.ethereum.org/EIPS/eip-2537#curve-parameters + function baseFieldModulus() internal pure returns (uint256[2] memory) { + return [ + 0x000000000000000000000000000000001a0111ea397fe69a4b1ba7b6434bacd7, + 0x64774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaab + ]; + } + + /// @dev Referenced from https://eips.ethereum.org/EIPS/eip-2537#curve-parameters + function generatorG1() internal pure returns (G1Point memory) { + return G1Point({ + x: [ + 0x0000000000000000000000000000000017f1d3a73197d7942695638c4fa9ac0f, + 0xc3688c4f9774b905a14e3a3f171bac586c55e83ff97a1aeffb3af00adb22c6bb + ], + y: [ + 0x0000000000000000000000000000000008b3f481e3aaa0f1a09e30ed741d8ae4, + 0xfcf5e095d5d00af600db18cb2c04b3edd03cc744a2888ae40caa232946c5e7e1 + ] + }); + } + + /** + * @notice Negates a G1 point, by reflecting it over the x-axis + * @dev Assumes that the Y coordinate is always less than the field modulus + * @param point The G1 point to negate + */ + function negate( + G1Point memory point + ) internal pure returns (G1Point memory) { + uint256[2] memory fieldModulus = baseFieldModulus(); + uint256[2] memory yNeg; + + // Perform word-wise elementary subtraction + if (fieldModulus[1] < point.y[1]) { + yNeg[1] = type(uint256).max - (point.y[1] - fieldModulus[1]); + fieldModulus[0] -= 1; // borrow + } else { + yNeg[1] = fieldModulus[1] - point.y[1]; + } + yNeg[0] = fieldModulus[0] - point.y[0]; + + return G1Point({x: point.x, y: yNeg}); + } + + /** + * @notice Transforms a sequence of bytes into a G2 point + * @dev Based on https://datatracker.ietf.org/doc/html/rfc9380 + * @param message The message to hash + * @param dst The domain separation tag + */ + function hashToCurveG2(bytes memory message, bytes memory dst) internal view returns (G2Point memory r) { + // 1. u = hash_to_field(msg, 2) + FieldPoint2[2] memory u = hashToFieldFp2(message, dst); + // 2. Q0 = map_to_curve(u[0]) + G2Point memory q0 = u[0].mapToG2(); + // 3. Q1 = map_to_curve(u[1]) + G2Point memory q1 = u[1].mapToG2(); + // 4. R = Q0 + Q1 + r = q0.plus(q1); + // 5. P = clear_cofactor(R) + // Not needed as map fp to g1 already does it + } + + /** + * @notice Transforms a sequence of bytes into an element in the FP2 field + * @dev Based on https://datatracker.ietf.org/doc/html/rfc9380 + * @param message The message to hash + * @param dst The domain separation tag + */ + function hashToFieldFp2(bytes memory message, bytes memory dst) internal view returns (FieldPoint2[2] memory) { + // 1. len_in_bytes = count * m * L + // so always 2 * 2 * 64 = 256 + uint16 lenInBytes = 256; + // 2. uniform_bytes = expand_message(msg, DST, len_in_bytes) + uint256[] memory pseudoRandomBytes = _expandMsgXmd(message, dst, lenInBytes); + FieldPoint2[2] memory u; + // No loop here saves 800 gas hardcoding offset an additional 300 + // 3. for i in (0, ..., count - 1): + // 4. for j in (0, ..., m - 1): + // 5. elm_offset = L * (j + i * m) + // 6. tv = substr(uniform_bytes, elm_offset, HTF_L) + // uint8 HTF_L = 64; + // bytes memory tv = new bytes(64); + // 7. e_j = OS2IP(tv) mod p + // 8. u_i = (e_0, ..., e_(m - 1)) + // tv = bytes.concat(pseudo_random_bytes[0], pseudo_random_bytes[1]); + u[0].u = _modfield(pseudoRandomBytes[0], pseudoRandomBytes[1]); + u[0].u_I = _modfield(pseudoRandomBytes[2], pseudoRandomBytes[3]); + u[1].u = _modfield(pseudoRandomBytes[4], pseudoRandomBytes[5]); + u[1].u_I = _modfield(pseudoRandomBytes[6], pseudoRandomBytes[7]); + // 9. return (u_0, ..., u_(count - 1)) + return u; + } + + /** + * @notice Returns a G1Point in the compressed form + * @dev Based on https://github.com/zcash/librustzcash/blob/6e0364cd42a2b3d2b958a54771ef51a8db79dd29/pairing/src/bls12_381/README.md#serialization + * @param point The G1 point to compress + */ + function compress( + G1Point memory point + ) internal pure returns (uint256[2] memory) { + uint256[2] memory r = point.x; + + // Set the first MSB + r[0] = r[0] | (1 << 127); + + // Second MSB is left to be 0 since we are assuming that no infinity points are involved + + // Set the third MSF If point.y is lexicographically larger than the y in negated point + if (_greaterThan(point.y, point.negate().y)) { + r[0] = r[0] | (1 << 125); + } + + return r; + } + + //================== + // Precompile calls + //================== + + /** + * @notice Adds two G2 points using the precompile at 0x0e + */ + function plus(G2Point memory point1, G2Point memory point2) internal view returns (G2Point memory) { + G2Point memory r; + + uint256[16] memory input = [ + point1.x[0], + point1.x[1], + point1.x_I[0], + point1.x_I[1], + point1.y[0], + point1.y[1], + point1.y_I[0], + point1.y_I[1], + point2.x[0], + point2.x[1], + point2.x_I[0], + point2.x_I[1], + point2.y[0], + point2.y[1], + point2.y_I[0], + point2.y_I[1] + ]; + + // ABI for G2 addition precompile + // G2 addition call expects 512 bytes as an input that is interpreted as byte concatenation of two G2 points (256 bytes each). Output is an encoding of addition operation result - single G2 point (256 bytes). + assembly { + let success := + staticcall( + sub(gas(), 2000), + /// gas should be 800 + 0x0e, // address of BLS12_G2ADD + input, //input offset + 512, // input size + r, // output offset + 256 // output size + ) + if iszero(success) { revert(0, 0) } + } + + return r; + } + + /** + * @notice Maps an element of the FP2 field to a G2 point using the precompile at 0x13 + */ + function mapToG2( + FieldPoint2 memory fp2 + ) internal view returns (G2Point memory) { + G2Point memory r; + + uint256[4] memory input = [fp2.u[0], fp2.u[1], fp2.u_I[0], fp2.u_I[1]]; + + // ABI for mapping Fp2 element to G2 point precompile + // Field-to-curve call expects 128 bytes an an input that is interpreted as a an element of the quadratic extension field. Output of this call is 256 bytes and is G2 point following respective encoding rules. + assembly { + let success := + staticcall( + sub(gas(), 2000), + /// gas should be 75000 + 0x13, // address of BLS12_MAP_FP2_TO_G2 + input, //input offset + 128, // input size + r, // output offset + 256 // output size + ) + if iszero(success) { revert(0, 0) } + } + + return r; + } + + /** + * @notice Pairing check using the precompile at 0x11 + */ + function pairing( + G1Point memory a1, + G2Point memory b1, + G1Point memory a2, + G2Point memory b2 + ) internal view returns (bool) { + bool r; + + uint256[24] memory input = [ + a1.x[0], + a1.x[1], + a1.y[0], + a1.y[1], + b1.x[0], + b1.x[1], + b1.x_I[0], + b1.x_I[1], + b1.y[0], + b1.y[1], + b1.y_I[0], + b1.y_I[1], + a2.x[0], + a2.x[1], + a2.y[0], + a2.y[1], + b2.x[0], + b2.x[1], + b2.x_I[0], + b2.x_I[1], + b2.y[0], + b2.y[1], + b2.y_I[0], + b2.y_I[1] + ]; + + // ABI for pairing precompile + // Pairing expects 384 (G1Point = 128 bytes, G2Point = 256 bytes) * k bytes as input. + // In this case, since two pairs of points are being passed, the input size is 384 * 2 = 768 bytes. + assembly { + let success := + staticcall( + sub(gas(), 2000), + /// gas should be 151000 + 0x11, // address of BLS12_PAIRING + input, //input offset + 768, // input size + r, // output offset + 32 // output size + ) + if iszero(success) { revert(0, 0) } + } + + return r; + } + + //========= + // Helpers + //========= + + function _expandMsgXmd( + bytes memory message, + bytes memory dst, + uint16 lenInBytes + ) internal pure returns (uint256[] memory) { + // 1. ell = ceil(len_in_bytes / b_in_bytes) + // b_in_bytes seems to be 32 for sha256 + // ceil the division + uint256 ell = (lenInBytes - 1) / 32 + 1; + + // 2. ABORT if ell > 255 or len_in_bytes > 65535 or len(DST) > 255 + require(ell <= 255, "len_in_bytes too large for sha256"); + // Not really needed because of parameter type + // require(lenInBytes <= 65535, "len_in_bytes too large"); + // no length normalizing via hashing + require(dst.length <= 255, "dst too long"); + + bytes memory dstPrime = bytes.concat(dst, bytes1(uint8(dst.length))); + + // 4. Z_pad = I2OSP(0, s_in_bytes) + // this should be sha256 blocksize so 64 bytes + bytes memory zPad = + hex"00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"; + + // 5. l_i_b_str = I2OSP(len_in_bytes, 2) + // length in byte string? + bytes2 libStr = bytes2(lenInBytes); + + // 6. msg_prime = Z_pad || msg || l_i_b_str || I2OSP(0, 1) || DST_prime + bytes memory msgPrime = bytes.concat(zPad, message, libStr, hex"00", dstPrime); + + uint256 b_0; + uint256[] memory b = new uint256[](ell); + + // 7. b_0 = H(msg_prime) + b_0 = uint256(sha256(msgPrime)); + + // 8. b_1 = H(b_0 || I2OSP(1, 1) || DST_prime) + b[0] = uint256(sha256(bytes.concat(bytes32(b_0), hex"01", dstPrime))); + + // 9. for i in (2, ..., ell): + for (uint8 i = 2; i <= ell; i++) { + // 10. b_i = H(strxor(b_0, b_(i - 1)) || I2OSP(i, 1) || DST_prime) + bytes memory tmp = abi.encodePacked(b_0 ^ b[i - 2], i, dstPrime); + b[i - 1] = uint256(sha256(tmp)); + } + // 11. uniform_bytes = b_1 || ... || b_ell + // 12. return substr(uniform_bytes, 0, len_in_bytes) + // Here we don't need the uniform_bytes because b is already properly formed + return b; + } + + function _modfield(uint256 _b1, uint256 _b2) internal view returns (uint256[2] memory r) { + assembly { + let bl := 0x40 + let ml := 0x40 + + let freemem := mload(0x40) // Free memory pointer is always stored at 0x40 + + // arg[0] = base.length @ +0 + mstore(freemem, bl) + // arg[1] = exp.length @ +0x20 + mstore(add(freemem, 0x20), 0x20) + // arg[2] = mod.length @ +0x40 + mstore(add(freemem, 0x40), ml) + + // arg[3] = base.bits @ + 0x60 + // places the first 32 bytes of _b1 and the last 32 bytes of _b2 + mstore(add(freemem, 0x60), _b1) + mstore(add(freemem, 0x80), _b2) + + // arg[4] = exp.bits @ +0x60+base.length + // exponent always 1 + mstore(add(freemem, 0xa0), 1) + + // arg[5] = mod.bits @ +96+base.length+exp.length + // this field_modulus as hex 4002409555221667393417789825735904156556882819939007885332058136124031650490837864442687629129015664037894272559787 + // we add the 0 prefix so that the result will be exactly 64 bytes + // saves 300 gas per call instead of sending it along every time + // places the first 32 bytes and the last 32 bytes of the field modulus + mstore(add(freemem, 0xc0), 0x000000000000000000000000000000001a0111ea397fe69a4b1ba7b6434bacd7) + mstore(add(freemem, 0xe0), 0x64774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaab) + + // Invoke contract 0x5, put return value right after mod.length, @ 0x60 + let success := + staticcall( + sub(gas(), 1350), // gas + 0x5, // mpdexp precompile + freemem, //input offset + 0x100, // input size = 0x60+base.length+exp.length+mod.length + add(freemem, 0x60), // output offset + ml // output size + ) + if iszero(success) { revert(0, 0) } + + // point to mod length, result was placed immediately after + r := add(freemem, 0x60) + //adjust freemem pointer + mstore(0x40, add(add(freemem, 0x60), ml)) + } + } + + /** + * @notice Returns true if `a` is lexicographically greater than `b` + * @dev It makes the comparison bit-wise. + * This functions also assumes that the passed values are 48-byte long BLS pub keys that have + * 16 functional bytes in the first word, and 32 bytes in the second. + */ + function _greaterThan(uint256[2] memory a, uint256[2] memory b) internal pure returns (bool) { + uint256 wordA; + uint256 wordB; + uint256 mask; + + // Only compare the unequal words + if (a[0] == b[0]) { + wordA = a[1]; + wordB = b[1]; + mask = 1 << 255; + } else { + wordA = a[0]; + wordB = b[0]; + mask = 1 << 127; // Only check for lower 16 bytes in the first word + } + + // We may safely set the control value to be less than 256 since it is guaranteed that the + // the loop returns if the first words are different. + for (uint256 i; i < 256; ++i) { + uint256 x = wordA & mask; + uint256 y = wordB & mask; + + if (x == 0 && y != 0) return false; + if (x != 0 && y == 0) return true; + + mask = mask >> 1; + } + + return false; + } +} diff --git a/bolt-contracts/src/lib/bls/BLSSignatureVerifier.sol b/bolt-contracts/src/lib/bls/BLSSignatureVerifier.sol new file mode 100644 index 000000000..43344e733 --- /dev/null +++ b/bolt-contracts/src/lib/bls/BLSSignatureVerifier.sol @@ -0,0 +1,64 @@ +// ====================================================== +// Code below is copied from: +// https://github.com/NethermindEth/Taiko-Preconf-AVS/blob/caf9fbbde0dd84947af5a7b26610ffd38525d932/SmartContracts/src/libraries/BLS12381.sol +// +// If/when a license will be added to the original code, it will be added here as well. +// ====================================================== + +// SPDX-License-Identifier: UNLICENSED +pragma solidity 0.8.25; + +import {BLS12381} from "./BLS12381.sol"; + +contract BLSSignatureVerifier { + using BLS12381 for *; + + /// @dev The domain separation tag for the BLS signature + function dst() internal pure returns (bytes memory) { + // Todo: This must be set based on the recommendations of RFC9380 + return hex""; + } + + /** + * @notice Returns `true` if the BLS signature on the message matches against the public key + * @param message The message bytes + * @param sig The BLS signature + * @param pubkey The BLS public key of the expected signer + */ + function _verifySignature( + bytes memory message, + BLS12381.G2Point memory sig, + BLS12381.G1Point memory pubkey + ) internal view returns (bool) { + // Hash the message bytes into a G2 point + BLS12381.G2Point memory msgG2 = message.hashToCurveG2(dst()); + + // Return the pairing check result + return BLS12381.pairing(BLS12381.generatorG1().negate(), sig, pubkey, msgG2); + } + + /** + * @notice Aggregate a list of BLS public keys into a single BLS public key + * @param pubkeys The list of BLS public keys to aggregate + * @return The aggregated BLS public key + */ + function _aggregatePubkeys( + BLS12381.G1Point[] calldata pubkeys + ) internal pure returns (BLS12381.G1Point memory) { + // TODO: implement + test. + + // Simply adding pubkeys will result in a rogue key vulnerability. + // + // https://xn--2-umb.com/22/bls-signatures/#rogue-key-attack + // https://github.com/chronicleprotocol/scribe/blob/main/docs/Schnorr.md#key-aggregation-for-multisignatures + + uint256[2] memory aggPubkeyZero = [uint256(0), uint256(0)]; + BLS12381.G1Point memory aggPubkey = BLS12381.G1Point(aggPubkeyZero, aggPubkeyZero); + + // unimplemented!() + // silence compiler warnings + pubkeys; + + return aggPubkey; + } +} diff --git a/bolt-contracts/src/lib/rlp/RLPReader.sol b/bolt-contracts/src/lib/rlp/RLPReader.sol new file mode 100644 index 000000000..a8620e488 --- /dev/null +++ b/bolt-contracts/src/lib/rlp/RLPReader.sol @@ -0,0 +1,418 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.25; + +// Credits: Optimism contributors. +// Ref: https://github.com/ethereum-optimism/optimism/blob/05deae54595b0e6bdd33580de81cb9ad194898bc/packages/contracts-bedrock/src/libraries/rlp/RLPReader.sol + +/** + * @title RLPReader + * @dev Adapted from "RLPReader" by Hamdi Allam (hamdi.allam97@gmail.com). + */ +library RLPReader { + /** + * + * Constants * + * + */ + uint256 internal constant MAX_LIST_LENGTH = 32; + + /** + * + * Enums * + * + */ + enum RLPItemType { + DATA_ITEM, + LIST_ITEM + } + + /** + * + * Structs * + * + */ + struct RLPItem { + uint256 length; + uint256 ptr; + } + + /** + * + * Internal Functions * + * + */ + + /** + * Converts bytes to a reference to memory position and length. + * @param _in Input bytes to convert. + * @return Output memory reference. + */ + function toRLPItem( + bytes memory _in + ) internal pure returns (RLPItem memory) { + uint256 ptr; + assembly { + ptr := add(_in, 32) + } + + return RLPItem({length: _in.length, ptr: ptr}); + } + + /** + * Reads an RLP list value into a list of RLP items. + * @param _in RLP list value. + * @return Decoded RLP list items. + */ + function readList( + RLPItem memory _in + ) internal pure returns (RLPItem[] memory) { + (uint256 listOffset,, RLPItemType itemType) = _decodeLength(_in); + + require(itemType == RLPItemType.LIST_ITEM, "Invalid RLP list value."); + + // Solidity in-memory arrays can't be increased in size, but *can* be decreased in size by + // writing to the length. Since we can't know the number of RLP items without looping over + // the entire input, we'd have to loop twice to accurately size this array. It's easier to + // simply set a reasonable maximum list length and decrease the size before we finish. + RLPItem[] memory out = new RLPItem[](MAX_LIST_LENGTH); + + uint256 itemCount = 0; + uint256 offset = listOffset; + while (offset < _in.length) { + require(itemCount < MAX_LIST_LENGTH, "Provided RLP list exceeds max list length."); + + (uint256 itemOffset, uint256 itemLength,) = + _decodeLength(RLPItem({length: _in.length - offset, ptr: _in.ptr + offset})); + + out[itemCount] = RLPItem({length: itemLength + itemOffset, ptr: _in.ptr + offset}); + + itemCount += 1; + offset += itemOffset + itemLength; + } + + // Decrease the array size to match the actual item count. + assembly { + mstore(out, itemCount) + } + + return out; + } + + /** + * Reads an RLP list value into a list of RLP items. + * @param _in RLP list value. + * @return Decoded RLP list items. + */ + function readList( + bytes memory _in + ) internal pure returns (RLPItem[] memory) { + return readList(toRLPItem(_in)); + } + + /** + * Reads an RLP bytes value into bytes. + * @param _in RLP bytes value. + * @return Decoded bytes. + */ + function readBytes( + RLPItem memory _in + ) internal pure returns (bytes memory) { + (uint256 itemOffset, uint256 itemLength, RLPItemType itemType) = _decodeLength(_in); + + require(itemType == RLPItemType.DATA_ITEM, "Invalid RLP bytes value."); + + return _copy(_in.ptr, itemOffset, itemLength); + } + + /** + * Reads an RLP bytes value into bytes. + * @param _in RLP bytes value. + * @return Decoded bytes. + */ + function readBytes( + bytes memory _in + ) internal pure returns (bytes memory) { + return readBytes(toRLPItem(_in)); + } + + /** + * Reads an RLP string value into a string. + * @param _in RLP string value. + * @return Decoded string. + */ + function readString( + RLPItem memory _in + ) internal pure returns (string memory) { + return string(readBytes(_in)); + } + + /** + * Reads an RLP string value into a string. + * @param _in RLP string value. + * @return Decoded string. + */ + function readString( + bytes memory _in + ) internal pure returns (string memory) { + return readString(toRLPItem(_in)); + } + + /** + * Reads an RLP bytes32 value into a bytes32. + * @param _in RLP bytes32 value. + * @return Decoded bytes32. + */ + function readBytes32( + RLPItem memory _in + ) internal pure returns (bytes32) { + require(_in.length <= 33, "Invalid RLP bytes32 value."); + + (uint256 itemOffset, uint256 itemLength, RLPItemType itemType) = _decodeLength(_in); + + require(itemType == RLPItemType.DATA_ITEM, "Invalid RLP bytes32 value."); + + uint256 ptr = _in.ptr + itemOffset; + bytes32 out; + assembly { + out := mload(ptr) + + // Shift the bytes over to match the item size. + if lt(itemLength, 32) { out := div(out, exp(256, sub(32, itemLength))) } + } + + return out; + } + + /** + * Reads an RLP bytes32 value into a bytes32. + * @param _in RLP bytes32 value. + * @return Decoded bytes32. + */ + function readBytes32( + bytes memory _in + ) internal pure returns (bytes32) { + return readBytes32(toRLPItem(_in)); + } + + /** + * Reads an RLP uint256 value into a uint256. + * @param _in RLP uint256 value. + * @return Decoded uint256. + */ + function readUint256( + RLPItem memory _in + ) internal pure returns (uint256) { + return uint256(readBytes32(_in)); + } + + /** + * Reads an RLP uint256 value into a uint256. + * @param _in RLP uint256 value. + * @return Decoded uint256. + */ + function readUint256( + bytes memory _in + ) internal pure returns (uint256) { + return readUint256(toRLPItem(_in)); + } + + /** + * Reads an RLP bool value into a bool. + * @param _in RLP bool value. + * @return Decoded bool. + */ + function readBool( + RLPItem memory _in + ) internal pure returns (bool) { + require(_in.length == 1, "Invalid RLP boolean value."); + + uint256 ptr = _in.ptr; + uint256 out; + assembly { + out := byte(0, mload(ptr)) + } + + require(out == 0 || out == 1, "RLPReader: Invalid RLP boolean value, must be 0 or 1"); + + return out != 0; + } + + /** + * Reads an RLP bool value into a bool. + * @param _in RLP bool value. + * @return Decoded bool. + */ + function readBool( + bytes memory _in + ) internal pure returns (bool) { + return readBool(toRLPItem(_in)); + } + + /** + * Reads an RLP address value into a address. + * @param _in RLP address value. + * @return Decoded address. + */ + function readAddress( + RLPItem memory _in + ) internal pure returns (address) { + if (_in.length == 1) { + return address(0); + } + + require(_in.length == 21, "Invalid RLP address value."); + + return address(uint160(readUint256(_in))); + } + + /** + * Reads an RLP address value into a address. + * @param _in RLP address value. + * @return Decoded address. + */ + function readAddress( + bytes memory _in + ) internal pure returns (address) { + return readAddress(toRLPItem(_in)); + } + + /** + * Reads the raw bytes of an RLP item. + * @param _in RLP item to read. + * @return Raw RLP bytes. + */ + function readRawBytes( + RLPItem memory _in + ) internal pure returns (bytes memory) { + return _copy(_in); + } + + /** + * + * Private Functions * + * + */ + + /** + * Decodes the length of an RLP item. + * @param _in RLP item to decode. + * @return Offset of the encoded data. + * @return Length of the encoded data. + * @return RLP item type (LIST_ITEM or DATA_ITEM). + */ + function _decodeLength( + RLPItem memory _in + ) private pure returns (uint256, uint256, RLPItemType) { + unchecked { + require(_in.length > 0, "RLP item cannot be null."); + + uint256 ptr = _in.ptr; + uint256 prefix; + assembly { + prefix := byte(0, mload(ptr)) + } + + if (prefix <= 0x7f) { + // Single byte. + + return (0, 1, RLPItemType.DATA_ITEM); + } else if (prefix <= 0xb7) { + // Short string. + + uint256 strLen = prefix - 0x80; + + require(_in.length > strLen, "Invalid RLP short string."); + + return (1, strLen, RLPItemType.DATA_ITEM); + } else if (prefix <= 0xbf) { + // Long string. + uint256 lenOfStrLen = prefix - 0xb7; + + require(_in.length > lenOfStrLen, "Invalid RLP long string length."); + + uint256 strLen; + assembly { + // Pick out the string length. + strLen := div(mload(add(ptr, 1)), exp(256, sub(32, lenOfStrLen))) + } + + require(_in.length > lenOfStrLen + strLen, "Invalid RLP long string."); + + return (1 + lenOfStrLen, strLen, RLPItemType.DATA_ITEM); + } else if (prefix <= 0xf7) { + // Short list. + uint256 listLen = prefix - 0xc0; + + require(_in.length > listLen, "Invalid RLP short list."); + + return (1, listLen, RLPItemType.LIST_ITEM); + } else { + // Long list. + uint256 lenOfListLen = prefix - 0xf7; + + require(_in.length > lenOfListLen, "Invalid RLP long list length."); + + uint256 listLen; + assembly { + // Pick out the list length. + listLen := div(mload(add(ptr, 1)), exp(256, sub(32, lenOfListLen))) + } + + require(_in.length > lenOfListLen + listLen, "Invalid RLP long list."); + + return (1 + lenOfListLen, listLen, RLPItemType.LIST_ITEM); + } + } + } + + /** + * Copies the bytes from a memory location. + * @param _src Pointer to the location to read from. + * @param _offset Offset to start reading from. + * @param _length Number of bytes to read. + * @return Copied bytes. + */ + function _copy(uint256 _src, uint256 _offset, uint256 _length) private pure returns (bytes memory) { + unchecked { + bytes memory out = new bytes(_length); + if (out.length == 0) { + return out; + } + + uint256 src = _src + _offset; + uint256 dest; + assembly { + dest := add(out, 32) + } + + // Copy over as many complete words as we can. + for (uint256 i = 0; i < _length / 32; i++) { + assembly { + mstore(dest, mload(src)) + } + + src += 32; + dest += 32; + } + + // Pick out the remaining bytes. + uint256 mask = 256 ** (32 - (_length % 32)) - 1; + assembly { + mstore(dest, or(and(mload(src), not(mask)), and(mload(dest), mask))) + } + + return out; + } + } + + /** + * Copies an RLP item into bytes. + * @param _in RLP item to copy. + * @return Copied bytes. + */ + function _copy( + RLPItem memory _in + ) private pure returns (bytes memory) { + return _copy(_in.ptr, 0, _in.length); + } +} diff --git a/bolt-contracts/src/lib/rlp/RLPWriter.sol b/bolt-contracts/src/lib/rlp/RLPWriter.sol new file mode 100644 index 000000000..fad2365a9 --- /dev/null +++ b/bolt-contracts/src/lib/rlp/RLPWriter.sol @@ -0,0 +1,225 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.25; +pragma experimental ABIEncoderV2; + +// Credits: Optimism contributors. +// Ref: https://github.com/ethereum-optimism/optimism/blob/05deae54595b0e6bdd33580de81cb9ad194898bc/packages/contracts-bedrock/src/libraries/rlp/RLPWriter.sol + +/** + * @title RLPWriter + * @author Bakaoh (with modifications) + */ +library RLPWriter { + /** + * + * Internal Functions * + * + */ + + /** + * RLP encodes a byte string. + * @param _in The byte string to encode. + * @return _out The RLP encoded string in bytes. + */ + function writeBytes( + bytes memory _in + ) internal pure returns (bytes memory _out) { + bytes memory encoded; + + if (_in.length == 1 && uint8(_in[0]) < 128) { + encoded = _in; + } else { + encoded = abi.encodePacked(_writeLength(_in.length, 128), _in); + } + + return encoded; + } + + /** + * RLP encodes a list of RLP encoded byte byte strings. + * @param _in The list of RLP encoded byte strings. + * @return _out The RLP encoded list of items in bytes. + */ + function writeList( + bytes[] memory _in + ) internal pure returns (bytes memory _out) { + bytes memory list = _flatten(_in); + return abi.encodePacked(_writeLength(list.length, 192), list); + } + + /** + * RLP encodes a string. + * @param _in The string to encode. + * @return _out The RLP encoded string in bytes. + */ + function writeString( + string memory _in + ) internal pure returns (bytes memory _out) { + return writeBytes(bytes(_in)); + } + + /** + * RLP encodes an address. + * @param _in The address to encode. + * @return _out The RLP encoded address in bytes. + */ + function writeAddress( + address _in + ) internal pure returns (bytes memory _out) { + return writeBytes(abi.encodePacked(_in)); + } + + /** + * RLP encodes a uint. + * @param _in The uint256 to encode. + * @return _out The RLP encoded uint256 in bytes. + */ + function writeUint( + uint256 _in + ) internal pure returns (bytes memory _out) { + return writeBytes(_toBinary(_in)); + } + + /** + * RLP encodes a bool. + * @param _in The bool to encode. + * @return _out The RLP encoded bool in bytes. + */ + function writeBool( + bool _in + ) internal pure returns (bytes memory _out) { + bytes memory encoded = new bytes(1); + encoded[0] = (_in ? bytes1(0x01) : bytes1(0x80)); + return encoded; + } + + /** + * + * Private Functions * + * + */ + + /** + * Encode the first byte, followed by the `len` in binary form if `length` is more than 55. + * @param _len The length of the string or the payload. + * @param _offset 128 if item is string, 192 if item is list. + * @return _encoded RLP encoded bytes. + */ + function _writeLength(uint256 _len, uint256 _offset) private pure returns (bytes memory _encoded) { + bytes memory encoded; + + if (_len < 56) { + encoded = new bytes(1); + encoded[0] = bytes1(uint8(_len) + uint8(_offset)); + } else { + uint256 lenLen; + uint256 i = 1; + while (_len / i != 0) { + lenLen++; + i *= 256; + } + + encoded = new bytes(lenLen + 1); + encoded[0] = bytes1(uint8(lenLen) + uint8(_offset) + 55); + for (i = 1; i <= lenLen; i++) { + encoded[i] = bytes1(uint8((_len / (256 ** (lenLen - i))) % 256)); + } + } + + return encoded; + } + + /** + * Encode integer in big endian binary form with no leading zeroes. + * @notice TODO: This should be optimized with assembly to save gas costs. + * @param _x The integer to encode. + * @return _binary RLP encoded bytes. + */ + function _toBinary( + uint256 _x + ) private pure returns (bytes memory _binary) { + bytes memory b = abi.encodePacked(_x); + + uint256 i = 0; + for (; i < 32; i++) { + if (b[i] != 0) { + break; + } + } + + bytes memory res = new bytes(32 - i); + for (uint256 j = 0; j < res.length; j++) { + res[j] = b[i++]; + } + + return res; + } + + /** + * Copies a piece of memory to another location. + * @notice From: https://github.com/Arachnid/solidity-stringutils/blob/master/src/strings.sol. + * @param _dest Destination location. + * @param _src Source location. + * @param _len Length of memory to copy. + */ + function _memcpy(uint256 _dest, uint256 _src, uint256 _len) private pure { + uint256 dest = _dest; + uint256 src = _src; + uint256 len = _len; + + for (; len >= 32; len -= 32) { + assembly { + mstore(dest, mload(src)) + } + dest += 32; + src += 32; + } + + uint256 mask = 256 ** (32 - len) - 1; + assembly { + let srcpart := and(mload(src), not(mask)) + let destpart := and(mload(dest), mask) + mstore(dest, or(destpart, srcpart)) + } + } + + /** + * Flattens a list of byte strings into one byte string. + * @notice From: https://github.com/sammayo/solidity-rlp-encoder/blob/master/RLPEncode.sol. + * @param _list List of byte strings to flatten. + * @return _flattened The flattened byte string. + */ + function _flatten( + bytes[] memory _list + ) private pure returns (bytes memory _flattened) { + if (_list.length == 0) { + return new bytes(0); + } + + uint256 len; + uint256 i = 0; + for (; i < _list.length; i++) { + len += _list[i].length; + } + + bytes memory flattened = new bytes(len); + uint256 flattenedPtr; + assembly { + flattenedPtr := add(flattened, 0x20) + } + + for (i = 0; i < _list.length; i++) { + bytes memory item = _list[i]; + + uint256 listPtr; + assembly { + listPtr := add(item, 0x20) + } + + _memcpy(flattenedPtr, listPtr, item.length); + flattenedPtr += _list[i].length; + } + + return flattened; + } +} diff --git a/bolt-contracts/src/lib/SSZ.sol b/bolt-contracts/src/lib/ssz/SSZ.sol similarity index 93% rename from bolt-contracts/src/lib/SSZ.sol rename to bolt-contracts/src/lib/ssz/SSZ.sol index 057fbc4f9..754864277 100644 --- a/bolt-contracts/src/lib/SSZ.sol +++ b/bolt-contracts/src/lib/ssz/SSZ.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -pragma solidity ^0.8.13; +pragma solidity 0.8.25; /// @title SSZ library /// @dev inspired by https://github.com/succinctlabs/telepathy-contracts/blob/main/src/libraries/SimpleSerialize.sol @@ -15,11 +15,12 @@ library SSZ { /// @notice Modified version of `verify` from `MerkleProofLib` to support generalized indices and sha256 precompile. /// @dev Returns whether `leaf` exists in the Merkle tree with `root`, given `proof`. /// @dev from https://github.com/madlabman/eip-4788-proof/blob/master/src/SSZ.sol - function _verifyProof(bytes32[] calldata proof, bytes32 root, bytes32 leaf, uint256 index) - internal - view - returns (bool isValid) - { + function _verifyProof( + bytes32[] calldata proof, + bytes32 root, + bytes32 leaf, + uint256 index + ) internal view returns (bool isValid) { /// @solidity memory-safe-assembly assembly { if proof.length { @@ -107,7 +108,9 @@ library SSZ { /// @notice Converts a value to a little-endian byte array /// @dev from https://github.com/succinctlabs/telepathy-contracts/blob/main/src/libraries/SimpleSerialize.sol - function _toLittleEndian(uint256 v) internal pure returns (bytes32) { + function _toLittleEndian( + uint256 v + ) internal pure returns (bytes32) { v = ((v & 0xFF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00) >> 8) | ((v & 0x00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF) << 8); v = ((v & 0xFFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000) >> 16) @@ -121,14 +124,18 @@ library SSZ { } /// @notice Converts a boolean to a little-endian byte array - function _toLittleEndian(bool v) internal pure returns (bytes32) { + function _toLittleEndian( + bool v + ) internal pure returns (bytes32) { return bytes32(v ? 1 << 248 : 0); } /// @notice Log base 2 of a number /// @dev From solady FixedPointMath /// @dev Equivalent to computing the index of the most significant bit (MSB) of `x`. - function _log2(uint256 x) internal pure returns (uint256 r) { + function _log2( + uint256 x + ) internal pure returns (uint256 r) { /// @solidity memory-safe-assembly assembly { if iszero(x) { diff --git a/bolt-contracts/src/lib/SSZContainers.sol b/bolt-contracts/src/lib/ssz/SSZContainers.sol similarity index 90% rename from bolt-contracts/src/lib/SSZContainers.sol rename to bolt-contracts/src/lib/ssz/SSZContainers.sol index d87742965..7d4302c7f 100644 --- a/bolt-contracts/src/lib/SSZContainers.sol +++ b/bolt-contracts/src/lib/ssz/SSZContainers.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -pragma solidity ^0.8.13; +pragma solidity 0.8.25; import {SSZ} from "./SSZ.sol"; @@ -28,7 +28,9 @@ library SSZContainers { } /// @notice Computes the hash tree root of a validator SSZ container - function _validatorHashTreeRoot(Validator memory validator) internal view returns (bytes32) { + function _validatorHashTreeRoot( + Validator memory validator + ) internal view returns (bytes32) { bytes32 pubkeyRoot; uint256 _sha256 = SSZ.SHA256_PRECOMPILE; @@ -57,7 +59,9 @@ library SSZContainers { } /// @notice Computes the hash tree root of a beacon block header SSZ container - function _beaconHeaderHashTreeRoot(BeaconBlockHeader memory header) internal view returns (bytes32) { + function _beaconHeaderHashTreeRoot( + BeaconBlockHeader memory header + ) internal view returns (bytes32) { bytes32[] memory nodes = new bytes32[](8); nodes[0] = SSZ._toLittleEndian(header.slot); nodes[1] = SSZ._toLittleEndian(header.proposerIndex); @@ -72,7 +76,9 @@ library SSZContainers { } /// @notice Computes the hash tree root of an RLP-encoded signed transaction (raw bytes) - function _transactionHashTreeRoot(bytes memory transaction) internal view returns (bytes32) { + function _transactionHashTreeRoot( + bytes memory transaction + ) internal view returns (bytes32) { uint256 chunkCount = (transaction.length + 31) / 32; bytes32[] memory nodes = new bytes32[](chunkCount); diff --git a/bolt-contracts/src/lib/trie/MerkleTrie.sol b/bolt-contracts/src/lib/trie/MerkleTrie.sol new file mode 100644 index 000000000..d861e5093 --- /dev/null +++ b/bolt-contracts/src/lib/trie/MerkleTrie.sol @@ -0,0 +1,761 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.25; + +// Credits: Optimism contributors. +// Ref: https://github.com/ethereum-optimism/optimism/blob/05deae54595b0e6bdd33580de81cb9ad194898bc/packages/contracts-bedrock/src/libraries/trie/MerkleTrie.sol + +/* Library Imports */ +import {BytesUtils} from "../BytesUtils.sol"; +import {RLPReader} from "../rlp/RLPReader.sol"; +import {RLPWriter} from "../rlp/RLPWriter.sol"; + +/** + * @title MerkleTrie + */ +library MerkleTrie { + /** + * + * Data Structures * + * + */ + enum NodeType { + BranchNode, + ExtensionNode, + LeafNode + } + + struct TrieNode { + bytes encoded; + RLPReader.RLPItem[] decoded; + } + + /** + * + * Contract Constants * + * + */ + + // TREE_RADIX determines the number of elements per branch node. + uint256 constant TREE_RADIX = 16; + // Branch nodes have TREE_RADIX elements plus an additional `value` slot. + uint256 constant BRANCH_NODE_LENGTH = TREE_RADIX + 1; + // Leaf nodes and extension nodes always have two elements, a `path` and a `value`. + uint256 constant LEAF_OR_EXTENSION_NODE_LENGTH = 2; + + // Prefixes are prepended to the `path` within a leaf or extension node and + // allow us to differentiate between the two node types. `ODD` or `EVEN` is + // determined by the number of nibbles within the unprefixed `path`. If the + // number of nibbles if even, we need to insert an extra padding nibble so + // the resulting prefixed `path` has an even number of nibbles. + uint8 constant PREFIX_EXTENSION_EVEN = 0; + uint8 constant PREFIX_EXTENSION_ODD = 1; + uint8 constant PREFIX_LEAF_EVEN = 2; + uint8 constant PREFIX_LEAF_ODD = 3; + + // Just a utility constant. RLP represents `NULL` as 0x80. + bytes1 constant RLP_NULL = bytes1(0x80); + bytes constant RLP_NULL_BYTES = hex"80"; + bytes32 internal constant KECCAK256_RLP_NULL_BYTES = keccak256(RLP_NULL_BYTES); + + /** + * + * Internal Functions * + * + */ + + /** + * @notice Verifies a proof that a given key/value pair is present in the + * Merkle trie. + * @param _key Key of the node to search for, as a hex string. + * @param _value Value of the node to search for, as a hex string. + * @param _proof Merkle trie inclusion proof for the desired node. Unlike + * traditional Merkle trees, this proof is executed top-down and consists + * of a list of RLP-encoded nodes that make a path down to the target node. + * @param _root Known root of the Merkle trie. Used to verify that the + * included proof is correctly constructed. + * @return _verified `true` if the k/v pair exists in the trie, `false` otherwise. + */ + function verifyInclusionProof( + bytes memory _key, + bytes memory _value, + bytes memory _proof, + bytes32 _root + ) internal pure returns (bool _verified) { + (bool exists, bytes memory value) = get(_key, _proof, _root); + + return (exists && BytesUtils.equal(_value, value)); + } + + /** + * @notice Verifies a proof that a given key is *not* present in + * the Merkle trie. + * @param _key Key of the node to search for, as a hex string. + * @param _proof Merkle trie inclusion proof for the node *nearest* the + * target node. + * @param _root Known root of the Merkle trie. Used to verify that the + * included proof is correctly constructed. + * @return _verified `true` if the key is absent in the trie, `false` otherwise. + */ + function verifyExclusionProof( + bytes memory _key, + bytes memory _proof, + bytes32 _root + ) internal pure returns (bool _verified) { + (bool exists,) = get(_key, _proof, _root); + + return exists == false; + } + + /** + * @notice Updates a Merkle trie and returns a new root hash. + * @param _key Key of the node to update, as a hex string. + * @param _value Value of the node to update, as a hex string. + * @param _proof Merkle trie inclusion proof for the node *nearest* the + * target node. If the key exists, we can simply update the value. + * Otherwise, we need to modify the trie to handle the new k/v pair. + * @param _root Known root of the Merkle trie. Used to verify that the + * included proof is correctly constructed. + * @return _updatedRoot Root hash of the newly constructed trie. + */ + function update( + bytes memory _key, + bytes memory _value, + bytes memory _proof, + bytes32 _root + ) internal pure returns (bytes32 _updatedRoot) { + // Special case when inserting the very first node. + if (_root == KECCAK256_RLP_NULL_BYTES) { + return getSingleNodeRootHash(_key, _value); + } + + TrieNode[] memory proof = _parseProof(_proof); + (uint256 pathLength, bytes memory keyRemainder,) = _walkNodePath(proof, _key, _root); + TrieNode[] memory newPath = _getNewPath(proof, pathLength, keyRemainder, _value); + + return _getUpdatedTrieRoot(newPath, _key); + } + + /** + * @notice Retrieves the value associated with a given key. + * @param _key Key to search for, as hex bytes. + * @param _proof Merkle trie inclusion proof for the key. + * @param _root Known root of the Merkle trie. + * @return _exists Whether or not the key exists. + * @return _value Value of the key if it exists. + */ + function get( + bytes memory _key, + bytes memory _proof, + bytes32 _root + ) internal pure returns (bool _exists, bytes memory _value) { + TrieNode[] memory proof = _parseProof(_proof); + (uint256 pathLength, bytes memory keyRemainder, bool isFinalNode) = _walkNodePath(proof, _key, _root); + + bool exists = keyRemainder.length == 0; + + require(exists || isFinalNode, "Provided proof is invalid."); + + bytes memory value = exists ? _getNodeValue(proof[pathLength - 1]) : bytes(""); + + return (exists, value); + } + + /** + * Computes the root hash for a trie with a single node. + * @param _key Key for the single node. + * @param _value Value for the single node. + * @return _updatedRoot Hash of the trie. + */ + function getSingleNodeRootHash( + bytes memory _key, + bytes memory _value + ) internal pure returns (bytes32 _updatedRoot) { + return keccak256(_makeLeafNode(BytesUtils.toNibbles(_key), _value).encoded); + } + + /** + * + * Private Functions * + * + */ + + /** + * @notice Walks through a proof using a provided key. + * @param _proof Inclusion proof to walk through. + * @param _key Key to use for the walk. + * @param _root Known root of the trie. + * @return _pathLength Length of the final path + * @return _keyRemainder Portion of the key remaining after the walk. + * @return _isFinalNode Whether or not we've hit a dead end. + */ + function _walkNodePath( + TrieNode[] memory _proof, + bytes memory _key, + bytes32 _root + ) private pure returns (uint256 _pathLength, bytes memory _keyRemainder, bool _isFinalNode) { + uint256 pathLength = 0; + bytes memory key = BytesUtils.toNibbles(_key); + + bytes32 currentNodeID = _root; + uint256 currentKeyIndex = 0; + uint256 currentKeyIncrement = 0; + TrieNode memory currentNode; + + // Proof is top-down, so we start at the first element (root). + for (uint256 i = 0; i < _proof.length; i++) { + currentNode = _proof[i]; + currentKeyIndex += currentKeyIncrement; + + // Keep track of the proof elements we actually need. + // It's expensive to resize arrays, so this simply reduces gas costs. + pathLength += 1; + + if (currentKeyIndex == 0) { + // First proof element is always the root node. + require(keccak256(currentNode.encoded) == currentNodeID, "Invalid root hash"); + } else if (currentNode.encoded.length >= 32) { + // Nodes 32 bytes or larger are hashed inside branch nodes. + require(keccak256(currentNode.encoded) == currentNodeID, "Invalid large internal hash"); + } else { + // Nodes smaller than 31 bytes aren't hashed. + require(BytesUtils.toBytes32(currentNode.encoded) == currentNodeID, "Invalid internal node hash"); + } + + if (currentNode.decoded.length == BRANCH_NODE_LENGTH) { + if (currentKeyIndex == key.length) { + // We've hit the end of the key, meaning the value should be within this branch node. + break; + } else { + // We're not at the end of the key yet. + // Figure out what the next node ID should be and continue. + uint8 branchKey = uint8(key[currentKeyIndex]); + RLPReader.RLPItem memory nextNode = currentNode.decoded[branchKey]; + currentNodeID = _getNodeID(nextNode); + currentKeyIncrement = 1; + continue; + } + } else if (currentNode.decoded.length == LEAF_OR_EXTENSION_NODE_LENGTH) { + bytes memory path = _getNodePath(currentNode); + uint8 prefix = uint8(path[0]); + uint8 offset = 2 - prefix % 2; + bytes memory pathRemainder = BytesUtils.slice(path, offset); + bytes memory keyRemainder = BytesUtils.slice(key, currentKeyIndex); + uint256 sharedNibbleLength = _getSharedNibbleLength(pathRemainder, keyRemainder); + + if (prefix == PREFIX_LEAF_EVEN || prefix == PREFIX_LEAF_ODD) { + if (pathRemainder.length == sharedNibbleLength && keyRemainder.length == sharedNibbleLength) { + // The key within this leaf matches our key exactly. + // Increment the key index to reflect that we have no remainder. + currentKeyIndex += sharedNibbleLength; + } + + // We've hit a leaf node, so our next node should be NULL. + currentNodeID = bytes32(RLP_NULL); + break; + } else if (prefix == PREFIX_EXTENSION_EVEN || prefix == PREFIX_EXTENSION_ODD) { + if (sharedNibbleLength == 0) { + // Our extension node doesn't share any part of our key. + // We've hit the end of this path, updates will need to modify this extension. + currentNodeID = bytes32(RLP_NULL); + break; + } else { + // Our extension shares some nibbles. + // Carry on to the next node. + currentNodeID = _getNodeID(currentNode.decoded[1]); + currentKeyIncrement = sharedNibbleLength; + continue; + } + } else { + revert("Received a node with an unknown prefix"); + } + } else { + revert("Received an unparseable node."); + } + } + + // If our node ID is NULL, then we're at a dead end. + bool isFinalNode = currentNodeID == bytes32(RLP_NULL); + return (pathLength, BytesUtils.slice(key, currentKeyIndex), isFinalNode); + } + + /** + * @notice Creates new nodes to support a k/v pair insertion into a given + * Merkle trie path. + * @param _path Path to the node nearest the k/v pair. + * @param _pathLength Length of the path. Necessary because the provided + * path may include additional nodes (e.g., it comes directly from a proof) + * and we can't resize in-memory arrays without costly duplication. + * @param _keyRemainder Portion of the initial key that must be inserted + * into the trie. + * @param _value Value to insert at the given key. + * @return _newPath A new path with the inserted k/v pair and extra supporting nodes. + */ + function _getNewPath( + TrieNode[] memory _path, + uint256 _pathLength, + bytes memory _keyRemainder, + bytes memory _value + ) private pure returns (TrieNode[] memory _newPath) { + bytes memory keyRemainder = _keyRemainder; + + // Most of our logic depends on the status of the last node in the path. + TrieNode memory lastNode = _path[_pathLength - 1]; + NodeType lastNodeType = _getNodeType(lastNode); + + // Create an array for newly created nodes. + // We need up to three new nodes, depending on the contents of the last node. + // Since array resizing is expensive, we'll keep track of the size manually. + // We're using an explicit `totalNewNodes += 1` after insertions for clarity. + TrieNode[] memory newNodes = new TrieNode[](3); + uint256 totalNewNodes = 0; + + if (keyRemainder.length == 0 && lastNodeType == NodeType.LeafNode) { + // We've found a leaf node with the given key. + // Simply need to update the value of the node to match. + newNodes[totalNewNodes] = _makeLeafNode(_getNodeKey(lastNode), _value); + totalNewNodes += 1; + } else if (lastNodeType == NodeType.BranchNode) { + if (keyRemainder.length == 0) { + // We've found a branch node with the given key. + // Simply need to update the value of the node to match. + newNodes[totalNewNodes] = _editBranchValue(lastNode, _value); + totalNewNodes += 1; + } else { + // We've found a branch node, but it doesn't contain our key. + // Reinsert the old branch for now. + newNodes[totalNewNodes] = lastNode; + totalNewNodes += 1; + // Create a new leaf node, slicing our remainder since the first byte points + // to our branch node. + newNodes[totalNewNodes] = _makeLeafNode(BytesUtils.slice(keyRemainder, 1), _value); + totalNewNodes += 1; + } + } else { + // Our last node is either an extension node or a leaf node with a different key. + bytes memory lastNodeKey = _getNodeKey(lastNode); + uint256 sharedNibbleLength = _getSharedNibbleLength(lastNodeKey, keyRemainder); + + if (sharedNibbleLength != 0) { + // We've got some shared nibbles between the last node and our key remainder. + // We'll need to insert an extension node that covers these shared nibbles. + bytes memory nextNodeKey = BytesUtils.slice(lastNodeKey, 0, sharedNibbleLength); + newNodes[totalNewNodes] = _makeExtensionNode(nextNodeKey, _getNodeHash(_value)); + totalNewNodes += 1; + + // Cut down the keys since we've just covered these shared nibbles. + lastNodeKey = BytesUtils.slice(lastNodeKey, sharedNibbleLength); + keyRemainder = BytesUtils.slice(keyRemainder, sharedNibbleLength); + } + + // Create an empty branch to fill in. + TrieNode memory newBranch = _makeEmptyBranchNode(); + + if (lastNodeKey.length == 0) { + // Key remainder was larger than the key for our last node. + // The value within our last node is therefore going to be shifted into + // a branch value slot. + newBranch = _editBranchValue(newBranch, _getNodeValue(lastNode)); + } else { + // Last node key was larger than the key remainder. + // We're going to modify some index of our branch. + uint8 branchKey = uint8(lastNodeKey[0]); + // Move on to the next nibble. + lastNodeKey = BytesUtils.slice(lastNodeKey, 1); + + if (lastNodeType == NodeType.LeafNode) { + // We're dealing with a leaf node. + // We'll modify the key and insert the old leaf node into the branch index. + TrieNode memory modifiedLastNode = _makeLeafNode(lastNodeKey, _getNodeValue(lastNode)); + newBranch = _editBranchIndex(newBranch, branchKey, _getNodeHash(modifiedLastNode.encoded)); + } else if (lastNodeKey.length != 0) { + // We're dealing with a shrinking extension node. + // We need to modify the node to decrease the size of the key. + TrieNode memory modifiedLastNode = _makeExtensionNode(lastNodeKey, _getNodeValue(lastNode)); + newBranch = _editBranchIndex(newBranch, branchKey, _getNodeHash(modifiedLastNode.encoded)); + } else { + // We're dealing with an unnecessary extension node. + // We're going to delete the node entirely. + // Simply insert its current value into the branch index. + newBranch = _editBranchIndex(newBranch, branchKey, _getNodeValue(lastNode)); + } + } + + if (keyRemainder.length == 0) { + // We've got nothing left in the key remainder. + // Simply insert the value into the branch value slot. + newBranch = _editBranchValue(newBranch, _value); + // Push the branch into the list of new nodes. + newNodes[totalNewNodes] = newBranch; + totalNewNodes += 1; + } else { + // We've got some key remainder to work with. + // We'll be inserting a leaf node into the trie. + // First, move on to the next nibble. + keyRemainder = BytesUtils.slice(keyRemainder, 1); + // Push the branch into the list of new nodes. + newNodes[totalNewNodes] = newBranch; + totalNewNodes += 1; + // Push a new leaf node for our k/v pair. + newNodes[totalNewNodes] = _makeLeafNode(keyRemainder, _value); + totalNewNodes += 1; + } + } + + // Finally, join the old path with our newly created nodes. + // Since we're overwriting the last node in the path, we use `_pathLength - 1`. + return _joinNodeArrays(_path, _pathLength - 1, newNodes, totalNewNodes); + } + + /** + * @notice Computes the trie root from a given path. + * @param _nodes Path to some k/v pair. + * @param _key Key for the k/v pair. + * @return _updatedRoot Root hash for the updated trie. + */ + function _getUpdatedTrieRoot( + TrieNode[] memory _nodes, + bytes memory _key + ) private pure returns (bytes32 _updatedRoot) { + bytes memory key = BytesUtils.toNibbles(_key); + + // Some variables to keep track of during iteration. + TrieNode memory currentNode; + NodeType currentNodeType; + bytes memory previousNodeHash; + + // Run through the path backwards to rebuild our root hash. + for (uint256 i = _nodes.length; i > 0; i--) { + // Pick out the current node. + currentNode = _nodes[i - 1]; + currentNodeType = _getNodeType(currentNode); + + if (currentNodeType == NodeType.LeafNode) { + // Leaf nodes are already correctly encoded. + // Shift the key over to account for the nodes key. + bytes memory nodeKey = _getNodeKey(currentNode); + key = BytesUtils.slice(key, 0, key.length - nodeKey.length); + } else if (currentNodeType == NodeType.ExtensionNode) { + // Shift the key over to account for the nodes key. + bytes memory nodeKey = _getNodeKey(currentNode); + key = BytesUtils.slice(key, 0, key.length - nodeKey.length); + + // If this node is the last element in the path, it'll be correctly encoded + // and we can skip this part. + if (previousNodeHash.length > 0) { + // Re-encode the node based on the previous node. + currentNode = _makeExtensionNode(nodeKey, previousNodeHash); + } + } else if (currentNodeType == NodeType.BranchNode) { + // If this node is the last element in the path, it'll be correctly encoded + // and we can skip this part. + if (previousNodeHash.length > 0) { + // Re-encode the node based on the previous node. + uint8 branchKey = uint8(key[key.length - 1]); + key = BytesUtils.slice(key, 0, key.length - 1); + currentNode = _editBranchIndex(currentNode, branchKey, previousNodeHash); + } + } + + // Compute the node hash for the next iteration. + previousNodeHash = _getNodeHash(currentNode.encoded); + } + + // Current node should be the root at this point. + // Simply return the hash of its encoding. + return keccak256(currentNode.encoded); + } + + /** + * @notice Parses an RLP-encoded proof into something more useful. + * @param _proof RLP-encoded proof to parse. + * @return _parsed Proof parsed into easily accessible structs. + */ + function _parseProof( + bytes memory _proof + ) private pure returns (TrieNode[] memory _parsed) { + RLPReader.RLPItem[] memory nodes = RLPReader.readList(_proof); + TrieNode[] memory proof = new TrieNode[](nodes.length); + + for (uint256 i = 0; i < nodes.length; i++) { + bytes memory encoded = RLPReader.readBytes(nodes[i]); + proof[i] = TrieNode({encoded: encoded, decoded: RLPReader.readList(encoded)}); + } + + return proof; + } + + /** + * @notice Picks out the ID for a node. Node ID is referred to as the + * "hash" within the specification, but nodes < 32 bytes are not actually + * hashed. + * @param _node Node to pull an ID for. + * @return _nodeID ID for the node, depending on the size of its contents. + */ + function _getNodeID( + RLPReader.RLPItem memory _node + ) private pure returns (bytes32 _nodeID) { + bytes memory nodeID; + + if (_node.length < 32) { + // Nodes smaller than 32 bytes are RLP encoded. + nodeID = RLPReader.readRawBytes(_node); + } else { + // Nodes 32 bytes or larger are hashed. + nodeID = RLPReader.readBytes(_node); + } + + return BytesUtils.toBytes32(nodeID); + } + + /** + * @notice Gets the path for a leaf or extension node. + * @param _node Node to get a path for. + * @return _path Node path, converted to an array of nibbles. + */ + function _getNodePath( + TrieNode memory _node + ) private pure returns (bytes memory _path) { + return BytesUtils.toNibbles(RLPReader.readBytes(_node.decoded[0])); + } + + /** + * @notice Gets the key for a leaf or extension node. Keys are essentially + * just paths without any prefix. + * @param _node Node to get a key for. + * @return _key Node key, converted to an array of nibbles. + */ + function _getNodeKey( + TrieNode memory _node + ) private pure returns (bytes memory _key) { + return _removeHexPrefix(_getNodePath(_node)); + } + + /** + * @notice Gets the path for a node. + * @param _node Node to get a value for. + * @return _value Node value, as hex bytes. + */ + function _getNodeValue( + TrieNode memory _node + ) private pure returns (bytes memory _value) { + return RLPReader.readBytes(_node.decoded[_node.decoded.length - 1]); + } + + /** + * @notice Computes the node hash for an encoded node. Nodes < 32 bytes + * are not hashed, all others are keccak256 hashed. + * @param _encoded Encoded node to hash. + * @return _hash Hash of the encoded node. Simply the input if < 32 bytes. + */ + function _getNodeHash( + bytes memory _encoded + ) private pure returns (bytes memory _hash) { + if (_encoded.length < 32) { + return _encoded; + } else { + return abi.encodePacked(keccak256(_encoded)); + } + } + + /** + * @notice Determines the type for a given node. + * @param _node Node to determine a type for. + * @return _type Type of the node; BranchNode/ExtensionNode/LeafNode. + */ + function _getNodeType( + TrieNode memory _node + ) private pure returns (NodeType _type) { + if (_node.decoded.length == BRANCH_NODE_LENGTH) { + return NodeType.BranchNode; + } else if (_node.decoded.length == LEAF_OR_EXTENSION_NODE_LENGTH) { + bytes memory path = _getNodePath(_node); + uint8 prefix = uint8(path[0]); + + if (prefix == PREFIX_LEAF_EVEN || prefix == PREFIX_LEAF_ODD) { + return NodeType.LeafNode; + } else if (prefix == PREFIX_EXTENSION_EVEN || prefix == PREFIX_EXTENSION_ODD) { + return NodeType.ExtensionNode; + } + } + + revert("Invalid node type"); + } + + /** + * @notice Utility; determines the number of nibbles shared between two + * nibble arrays. + * @param _a First nibble array. + * @param _b Second nibble array. + * @return _shared Number of shared nibbles. + */ + function _getSharedNibbleLength(bytes memory _a, bytes memory _b) private pure returns (uint256 _shared) { + uint256 i = 0; + while (_a.length > i && _b.length > i && _a[i] == _b[i]) { + i++; + } + return i; + } + + /** + * @notice Utility; converts an RLP-encoded node into our nice struct. + * @param _raw RLP-encoded node to convert. + * @return _node Node as a TrieNode struct. + */ + function _makeNode( + bytes[] memory _raw + ) private pure returns (TrieNode memory _node) { + bytes memory encoded = RLPWriter.writeList(_raw); + + return TrieNode({encoded: encoded, decoded: RLPReader.readList(encoded)}); + } + + /** + * @notice Utility; converts an RLP-decoded node into our nice struct. + * @param _items RLP-decoded node to convert. + * @return _node Node as a TrieNode struct. + */ + function _makeNode( + RLPReader.RLPItem[] memory _items + ) private pure returns (TrieNode memory _node) { + bytes[] memory raw = new bytes[](_items.length); + for (uint256 i = 0; i < _items.length; i++) { + raw[i] = RLPReader.readRawBytes(_items[i]); + } + return _makeNode(raw); + } + + /** + * @notice Creates a new extension node. + * @param _key Key for the extension node, unprefixed. + * @param _value Value for the extension node. + * @return _node New extension node with the given k/v pair. + */ + function _makeExtensionNode(bytes memory _key, bytes memory _value) private pure returns (TrieNode memory _node) { + bytes[] memory raw = new bytes[](2); + bytes memory key = _addHexPrefix(_key, false); + raw[0] = RLPWriter.writeBytes(BytesUtils.fromNibbles(key)); + raw[1] = RLPWriter.writeBytes(_value); + return _makeNode(raw); + } + + /** + * @notice Creates a new leaf node. + * @dev This function is essentially identical to `_makeExtensionNode`. + * Although we could route both to a single method with a flag, it's + * more gas efficient to keep them separate and duplicate the logic. + * @param _key Key for the leaf node, unprefixed. + * @param _value Value for the leaf node. + * @return _node New leaf node with the given k/v pair. + */ + function _makeLeafNode(bytes memory _key, bytes memory _value) private pure returns (TrieNode memory _node) { + bytes[] memory raw = new bytes[](2); + bytes memory key = _addHexPrefix(_key, true); + raw[0] = RLPWriter.writeBytes(BytesUtils.fromNibbles(key)); + raw[1] = RLPWriter.writeBytes(_value); + return _makeNode(raw); + } + + /** + * @notice Creates an empty branch node. + * @return _node Empty branch node as a TrieNode struct. + */ + function _makeEmptyBranchNode() private pure returns (TrieNode memory _node) { + bytes[] memory raw = new bytes[](BRANCH_NODE_LENGTH); + for (uint256 i = 0; i < raw.length; i++) { + raw[i] = RLP_NULL_BYTES; + } + return _makeNode(raw); + } + + /** + * @notice Modifies the value slot for a given branch. + * @param _branch Branch node to modify. + * @param _value Value to insert into the branch. + * @return _updatedNode Modified branch node. + */ + function _editBranchValue( + TrieNode memory _branch, + bytes memory _value + ) private pure returns (TrieNode memory _updatedNode) { + bytes memory encoded = RLPWriter.writeBytes(_value); + _branch.decoded[_branch.decoded.length - 1] = RLPReader.toRLPItem(encoded); + return _makeNode(_branch.decoded); + } + + /** + * @notice Modifies a slot at an index for a given branch. + * @param _branch Branch node to modify. + * @param _index Slot index to modify. + * @param _value Value to insert into the slot. + * @return _updatedNode Modified branch node. + */ + function _editBranchIndex( + TrieNode memory _branch, + uint8 _index, + bytes memory _value + ) private pure returns (TrieNode memory _updatedNode) { + bytes memory encoded = _value.length < 32 ? _value : RLPWriter.writeBytes(_value); + _branch.decoded[_index] = RLPReader.toRLPItem(encoded); + return _makeNode(_branch.decoded); + } + + /** + * @notice Utility; adds a prefix to a key. + * @param _key Key to prefix. + * @param _isLeaf Whether or not the key belongs to a leaf. + * @return _prefixedKey Prefixed key. + */ + function _addHexPrefix(bytes memory _key, bool _isLeaf) private pure returns (bytes memory _prefixedKey) { + uint8 prefix = _isLeaf ? uint8(0x02) : uint8(0x00); + uint8 offset = uint8(_key.length % 2); + bytes memory prefixed = new bytes(2 - offset); + prefixed[0] = bytes1(prefix + offset); + return abi.encodePacked(prefixed, _key); + } + + /** + * @notice Utility; removes a prefix from a path. + * @param _path Path to remove the prefix from. + * @return _unprefixedKey Unprefixed key. + */ + function _removeHexPrefix( + bytes memory _path + ) private pure returns (bytes memory _unprefixedKey) { + if (uint8(_path[0]) % 2 == 0) { + return BytesUtils.slice(_path, 2); + } else { + return BytesUtils.slice(_path, 1); + } + } + + /** + * @notice Utility; combines two node arrays. Array lengths are required + * because the actual lengths may be longer than the filled lengths. + * Array resizing is extremely costly and should be avoided. + * @param _a First array to join. + * @param _aLength Length of the first array. + * @param _b Second array to join. + * @param _bLength Length of the second array. + * @return _joined Combined node array. + */ + function _joinNodeArrays( + TrieNode[] memory _a, + uint256 _aLength, + TrieNode[] memory _b, + uint256 _bLength + ) private pure returns (TrieNode[] memory _joined) { + TrieNode[] memory ret = new TrieNode[](_aLength + _bLength); + + // Copy elements from the first array. + for (uint256 i = 0; i < _aLength; i++) { + ret[i] = _a[i]; + } + + // Copy elements from the second array. + for (uint256 i = 0; i < _bLength; i++) { + ret[i + _aLength] = _b[i]; + } + + return ret; + } +} diff --git a/bolt-contracts/src/lib/trie/SecureMerkleTrie.sol b/bolt-contracts/src/lib/trie/SecureMerkleTrie.sol new file mode 100644 index 000000000..d25b90aa9 --- /dev/null +++ b/bolt-contracts/src/lib/trie/SecureMerkleTrie.sol @@ -0,0 +1,130 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.25; +pragma experimental ABIEncoderV2; + +// Credits: Optimism contributors. +// Ref: https://github.com/ethereum-optimism/optimism/blob/05deae54595b0e6bdd33580de81cb9ad194898bc/packages/contracts-bedrock/src/libraries/trie/SecureMerkleTrie.sol + +/* Library Imports */ +import {MerkleTrie} from "./MerkleTrie.sol"; + +/** + * @title SecureMerkleTrie + */ +library SecureMerkleTrie { + /** + * + * Internal Functions * + * + */ + + /** + * @notice Verifies a proof that a given key/value pair is present in the + * Merkle trie. + * @param _key Key of the node to search for, as a hex string. + * @param _value Value of the node to search for, as a hex string. + * @param _proof Merkle trie inclusion proof for the desired node. Unlike + * traditional Merkle trees, this proof is executed top-down and consists + * of a list of RLP-encoded nodes that make a path down to the target node. + * @param _root Known root of the Merkle trie. Used to verify that the + * included proof is correctly constructed. + * @return _verified `true` if the k/v pair exists in the trie, `false` otherwise. + */ + function verifyInclusionProof( + bytes memory _key, + bytes memory _value, + bytes memory _proof, + bytes32 _root + ) internal pure returns (bool _verified) { + bytes memory key = _getSecureKey(_key); + return MerkleTrie.verifyInclusionProof(key, _value, _proof, _root); + } + + /** + * @notice Verifies a proof that a given key is *not* present in + * the Merkle trie. + * @param _key Key of the node to search for, as a hex string. + * @param _proof Merkle trie inclusion proof for the node *nearest* the + * target node. + * @param _root Known root of the Merkle trie. Used to verify that the + * included proof is correctly constructed. + * @return _verified `true` if the key is not present in the trie, `false` otherwise. + */ + function verifyExclusionProof( + bytes memory _key, + bytes memory _proof, + bytes32 _root + ) internal pure returns (bool _verified) { + bytes memory key = _getSecureKey(_key); + return MerkleTrie.verifyExclusionProof(key, _proof, _root); + } + + /** + * @notice Updates a Merkle trie and returns a new root hash. + * @param _key Key of the node to update, as a hex string. + * @param _value Value of the node to update, as a hex string. + * @param _proof Merkle trie inclusion proof for the node *nearest* the + * target node. If the key exists, we can simply update the value. + * Otherwise, we need to modify the trie to handle the new k/v pair. + * @param _root Known root of the Merkle trie. Used to verify that the + * included proof is correctly constructed. + * @return _updatedRoot Root hash of the newly constructed trie. + */ + function update( + bytes memory _key, + bytes memory _value, + bytes memory _proof, + bytes32 _root + ) internal pure returns (bytes32 _updatedRoot) { + bytes memory key = _getSecureKey(_key); + return MerkleTrie.update(key, _value, _proof, _root); + } + + /** + * @notice Retrieves the value associated with a given key. + * @param _key Key to search for, as hex bytes. + * @param _proof Merkle trie inclusion proof for the key. + * @param _root Known root of the Merkle trie. + * @return _exists Whether or not the key exists. + * @return _value Value of the key if it exists. + */ + function get( + bytes memory _key, + bytes memory _proof, + bytes32 _root + ) internal pure returns (bool _exists, bytes memory _value) { + bytes memory key = _getSecureKey(_key); + return MerkleTrie.get(key, _proof, _root); + } + + /** + * Computes the root hash for a trie with a single node. + * @param _key Key for the single node. + * @param _value Value for the single node. + * @return _updatedRoot Hash of the trie. + */ + function getSingleNodeRootHash( + bytes memory _key, + bytes memory _value + ) internal pure returns (bytes32 _updatedRoot) { + bytes memory key = _getSecureKey(_key); + return MerkleTrie.getSingleNodeRootHash(key, _value); + } + + /** + * + * Private Functions * + * + */ + + /** + * Computes the secure counterpart to a key. + * @param _key Key to get a secure key from. + * @return _secureKey Secure version of the key. + */ + function _getSecureKey( + bytes memory _key + ) private pure returns (bytes memory _secureKey) { + return abi.encodePacked(keccak256(_key)); + } +} diff --git a/bolt-contracts/test/BoltChallenger.t.sol b/bolt-contracts/test/BoltChallenger.t.sol index 6a8718eef..b8dd716bc 100644 --- a/bolt-contracts/test/BoltChallenger.t.sol +++ b/bolt-contracts/test/BoltChallenger.t.sol @@ -1,114 +1,600 @@ -// // SPDX-License-Identifier: MIT -// pragma solidity ^0.8.13; - -// import {Test, console} from "forge-std/Test.sol"; -// import {BoltRegistry} from "../src/contracts/BoltRegistry.sol"; -// import {BoltChallenger} from "../src/contracts/BoltChallenger.sol"; -// import {IBoltChallenger} from "../src/interfaces/IBoltChallenger.sol"; -// import {BeaconChainUtils} from "../src/lib/BeaconChainUtils.sol"; - -// contract BoltChallengerTest is Test { -// BoltRegistry public registry; -// BoltChallenger public challenger; - -// // Relic protocol contracts -// address relicReliquary = 0x5E4DE6Bb8c6824f29c44Bd3473d44da120387d08; -// address relicBlockHeaderProver = 0x9f9A1eb0CF9340538297c853915DCc06Eb6D72c4; -// address relicAccountInfoProver = 0xf74105AE736Ca0C4B171a2EC4F1D4B0b6EBB99ae; -// address beaconRootsContract = 0x000F3df6D732807Ef1319fB7B8bB8522d0Beac02; - -// address public alice; -// address public bob; - -// uint256 public alicePk; -// uint256 public bobPk; - -// function setUp() public { -// // Set up mainnet forking -// vm.createSelectFork("https://cloudflare-eth.com", 19932764); -// assertEq(block.number, 19932764); - -// (alice, alicePk) = makeAddrAndKey("alice"); -// (bob, bobPk) = makeAddrAndKey("bob"); - -// registry = new BoltRegistry(); -// challenger = -// new BoltChallenger(address(registry), relicReliquary, relicBlockHeaderProver, relicAccountInfoProver); - -// vm.deal(alice, 10 ether); -// vm.deal(bob, 10 ether); -// } - -// function testOpenChallengeConditions() public { -// uint256 latestSlot = BeaconChainUtils._getSlotFromTimestamp(block.timestamp); - -// BoltChallenger.SignedCommitment memory sc = BoltChallenger.SignedCommitment({ -// slot: 1, -// nonce: 1, -// gasUsed: 21000, -// transactionHash: bytes32("0x123"), -// signedRawTransaction: "0x456", -// signature: "0x789" -// }); - -// // TEST: bond is less than 1 ether -// vm.expectRevert(IBoltChallenger.InsufficientBond.selector); -// vm.prank(alice); -// challenger.challengeProposer{value: 0.5 ether}(bob, sc); - -// // TEST: challenge opened too late -// vm.expectRevert(IBoltChallenger.TargetSlotTooFarInThePast.selector); -// vm.prank(alice); -// challenger.challengeProposer{value: 1 ether}(bob, sc); - -// sc.slot = latestSlot; - -// // TEST: proposer address is authorized in the registry -// vm.expectRevert(IBoltChallenger.InvalidProposerAddress.selector); -// vm.prank(alice); -// challenger.challengeProposer{value: 1 ether}(bob, sc); - -// vm.prank(bob); -// registry.optIn(); - -// // TEST: mocked commitment signature -// vm.expectRevert(IBoltChallenger.InvalidCommitmentSignature.selector); -// vm.prank(alice); -// challenger.challengeProposer{value: 1 ether}(bob, sc); -// } - -// function testOpenChallengeSignature() public { -// uint256 latestSlot = BeaconChainUtils._getSlotFromTimestamp(block.timestamp); - -// vm.prank(bob); -// registry.optIn(); - -// bytes32 txHash = 0xbe162ae10f376ad2bcf0934233493c7b353836fc1d27c5cb6785ce68d45914ea; -// bytes memory signedRawTx = -// hex"02f87101830f45b3808504acefa159825208944675c7e5baafbffbca748158becba61ef3b0a26387cb62154da95e6480c080a0101d7785433fd38e12fccd911bf9e61a941c88543f372877f07901dacf066b0aa016a75077103f7e175b61b5509e20ef5e8364d322f2ecaade5922717efeb892cd"; - -// bytes32 commitmentDigest = keccak256(abi.encodePacked(latestSlot, txHash, signedRawTx)); -// (uint8 v, bytes32 r, bytes32 s) = vm.sign(bobPk, commitmentDigest); -// bytes memory commitmentSignature = abi.encodePacked(r, s, v); - -// BoltChallenger.SignedCommitment memory sc = BoltChallenger.SignedCommitment({ -// slot: latestSlot, -// nonce: vm.getNonce(0x95222290DD7278Aa3Ddd389Cc1E1d165CC4BAfe5), -// gasUsed: 21000, -// transactionHash: txHash, -// signedRawTransaction: signedRawTx, -// signature: commitmentSignature -// }); - -// // mock the beacon root for the target slot (todo: fix this) -// bytes32 timestampIdx = bytes32(uint256(block.timestamp % 8191)); -// vm.store(beaconRootsContract, timestampIdx, bytes32(block.timestamp)); -// vm.store(beaconRootsContract, bytes32(uint256(timestampIdx) + 8191), bytes32(uint256(123))); - -// // TEST: challenge opened successfully -// // Ignore beacon root verification for now as EIP-4788 is out of scope for this test -// vm.expectRevert(BeaconChainUtils.BeaconRootNotFound.selector); -// vm.prank(alice); -// challenger.challengeProposer{value: 1 ether}(bob, sc); -// } -// } +// SPDX-License-Identifier: MIT +pragma solidity 0.8.25; + +import {Test, console} from "forge-std/Test.sol"; +import {Utils} from "./Utils.sol"; + +import {ECDSA} from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; + +import {BoltParametersV1} from "../src/contracts/BoltParametersV1.sol"; +import {BoltChallengerV1} from "../src/contracts/BoltChallengerV1.sol"; +import {BoltConfig} from "../src/lib/Config.sol"; +import {IBoltChallengerV1} from "../src/interfaces/IBoltChallengerV1.sol"; +import {RLPReader} from "../src/lib/rlp/RLPReader.sol"; +import {RLPWriter} from "../src/lib/rlp/RLPWriter.sol"; +import {BytesUtils} from "../src/lib/BytesUtils.sol"; +import {MerkleTrie} from "../src/lib/trie/MerkleTrie.sol"; +import {SecureMerkleTrie} from "../src/lib/trie/SecureMerkleTrie.sol"; +import {TransactionDecoder} from "../src/lib/TransactionDecoder.sol"; + +// re-export the internal resolver function for testing +contract BoltChallengerExt is BoltChallengerV1 { + function _resolveExt( + bytes32 _challengeID, + bytes32 _trustedBlockHash, + IBoltChallengerV1.Proof calldata _proof + ) external { + _resolve(_challengeID, _trustedBlockHash, _proof); + } + + function _getCurrentSlotExt() external view returns (uint256) { + return _getCurrentSlot(); + } + + function _decodeBlockHeaderRLPExt( + bytes calldata _blockHeaderRLP + ) external pure returns (IBoltChallengerV1.BlockHeaderData memory) { + return _decodeBlockHeaderRLP(_blockHeaderRLP); + } +} + +contract BoltChallengerTest is Test { + using RLPReader for bytes; + using RLPReader for RLPReader.RLPItem; + using BytesUtils for bytes; + using TransactionDecoder for TransactionDecoder.Transaction; + using TransactionDecoder for bytes; + + BoltChallengerExt boltChallenger; + + address admin = makeAddr("admin"); + address challenger = makeAddr("challenger"); + address resolver = makeAddr("resolver"); + + address target; + uint256 targetPK; + + function setUp() public { + vm.pauseGasMetering(); + (target, targetPK) = makeAddrAndKey("target"); + + BoltConfig.Parameters memory config = new Utils().readParameters(); + + BoltParametersV1 parameters = new BoltParametersV1(); + parameters.initialize( + admin, + config.epochDuration, + config.slashingWindow, + config.maxChallengeDuration, + config.allowUnsafeRegistration, + config.challengeBond, + config.blockhashEvmLookback, + config.justificationDelay, + config.eth2GenesisTimestamp, + config.slotTime, + config.minimumOperatorStake + ); + + boltChallenger = new BoltChallengerExt(); + boltChallenger.initialize(admin, address(parameters)); + + vm.deal(challenger, 100 ether); + vm.deal(resolver, 100 ether); + vm.roll(12_456_789); + vm.warp(1_726_564_072); + } + + // =========== Proving data inclusion on-chain =========== + + function testProveHeaderData() public { + // Note: In prod, how we obtain the trusted block hash would depend on the context. + // For recent blocks, we can simply use the blockhash function in the EVM. + bytes32 trustedBlockHash = 0x0fc7c840f5b4b451e99dc8adb0d475eab2ac7d36278d9601d7f4b2dd05e8022f; + + // Read the RLP-encoded block header from a file (obtained via `debug_getRawHeader` RPC call) + string memory file = vm.readFile("./test/testdata/header_20785012.json"); + bytes memory headerRLP = vm.parseJsonBytes(file, ".result"); + + assertEq(keccak256(headerRLP), trustedBlockHash); + + // RLP decode the header + vm.resumeGasMetering(); + IBoltChallengerV1.BlockHeaderData memory header = boltChallenger._decodeBlockHeaderRLPExt(headerRLP); + vm.pauseGasMetering(); + + assertEq(header.stateRoot, 0x214389f55a96edbd4d5295a17ada4dbc68a3b276145bf824b060635f9905cefc); + assertEq(header.txRoot, 0x87bb9183296ce9e3b7a3246f6d3a778b99a5d7daaba2174750707407c7297365); + assertEq(header.blockNumber, 20_785_012); + assertEq(header.timestamp, 1_726_753_391); + assertEq(header.baseFee, 21_575_309_588); + } + + function testProveAccountData() public { + // The account we want to prove + address accountToProve = 0x0D9f5045B604bA0c050b5eb06D0b25d01c525Ea5; + + // Note: in prod the state root should be obtained from the block header proof. + // this way we can trust it comes from the right block number. This comes from Mainnet block 20_728_344. + bytes32 stateRootAtBlock = 0x214389f55a96edbd4d5295a17ada4dbc68a3b276145bf824b060635f9905cefc; + + // Read the RLP-encoded account proof from a file. This is obtained from the `eth_getProof` + // RPC call + ABI-encoding of the resulting accountProof array. + string memory file = vm.readFile("./test/testdata/eth_proof_20785012.json"); + bytes[] memory accountProofJson = vm.parseJsonBytesArray(file, ".result.accountProof"); + bytes memory accountProof = _RLPEncodeList(accountProofJson); + + // Perform a sanity check to see if the state root matches the expected trie node + RLPReader.RLPItem[] memory nodes = RLPReader.readList(accountProof); + MerkleTrie.TrieNode[] memory proof = new MerkleTrie.TrieNode[](nodes.length); + for (uint256 i = 0; i < nodes.length; i++) { + bytes memory encoded = RLPReader.readBytes(nodes[i]); + proof[i] = MerkleTrie.TrieNode({encoded: encoded, decoded: RLPReader.readList(encoded)}); + } + assertEq(keccak256(proof[0].encoded), stateRootAtBlock, "Roots should match"); + + vm.resumeGasMetering(); + (bool exists, bytes memory accountRLP) = + SecureMerkleTrie.get(abi.encodePacked(accountToProve), accountProof, stateRootAtBlock); + vm.pauseGasMetering(); + assertEq(exists, true); + + // decode the account RLP into nonce and balance + RLPReader.RLPItem[] memory accountFields = accountRLP.toRLPItem().readList(); + uint256 nonce = accountFields[0].readUint256(); + uint256 balance = accountFields[1].readUint256(); + + assertEq(nonce, 236); + assertEq(balance, 136_481_368_234_605_997); + } + + function testProveTransactionInclusion() public { + // The transaction we want to prove inclusion of + bytes32 txHash = 0x9ec2c56ca36e445a46bc77ca77510f0ef21795d00834269f3752cbd29d63ba1f; + + // MPT proof, obtained with the `trie-proofs` CLI tool from HerodotusDev + // ref: + string memory file = vm.readFile("./test/testdata/tx_mpt_proof_20785012.json"); + bytes[] memory txProofJson = vm.parseJsonBytesArray(file, ".proof"); + bytes memory txProof = _RLPEncodeList(txProofJson); + + // The transactions root and index in the block, also included in the CLI response + bytes32 txRootAtBlock = vm.parseJsonBytes32(file, ".root"); + uint256 txIndexInBlock = vm.parseJsonUint(file, ".index"); + + bytes memory key = RLPWriter.writeUint(txIndexInBlock); + + vm.resumeGasMetering(); + // Gotcha: SecureMerkleTrie.get expects the key to be hashed with keccak256 + // but the transaction trie skips this step and uses the raw index as the key. + (bool exists, bytes memory transactionRLP) = MerkleTrie.get(key, txProof, txRootAtBlock); + vm.pauseGasMetering(); + + assertEq(exists, true); + assertEq(keccak256(transactionRLP), txHash); + + // Decode the transaction RLP into its fields + TransactionDecoder.Transaction memory decodedTx = transactionRLP.decodeEnveloped(); + assertEq(uint8(decodedTx.txType), 2); + assertEq(decodedTx.chainId, 1); + assertEq(decodedTx.nonce, 0xeb); + assertEq(decodedTx.maxPriorityFeePerGas, 0x73a20d00); + assertEq(decodedTx.maxFeePerGas, 0x7e172a822); + assertEq(decodedTx.gasLimit, 0x5208); + assertEq(decodedTx.to, 0x0ff71973B5243005b192D5BCF552Fc2532b7bdEc); + assertEq(decodedTx.value, 0x15842095ebc4000); + assertEq(decodedTx.data.length, 0); + assertEq(decodedTx.recoverSender(), 0x0D9f5045B604bA0c050b5eb06D0b25d01c525Ea5); + } + + // =========== Verifying Signatures =========== + + function testCommitmentDigestAndSignature() public { + // The test commitment has been created in the Bolt sidecar using the Rust + // methods to compute the digest() and recover the signer from the signature. + IBoltChallengerV1.SignedCommitment memory commitment = _parseTestCommitment(); + + // Reconstruct the commitment digest: `keccak( keccak(signed tx) || le_bytes(slot) )` + bytes32 commitmentID = _computeCommitmentID(commitment.signedTx, commitment.slot); + + assertEq(commitmentID, 0x52ecc7832625c3d107aaba5b55d4509b48cd9f4f7ce375d6696d09bbf3310525); + assertEq(commitment.signature.length, 65); + + // Verify the commitment signature against the digest + vm.resumeGasMetering(); + address commitmentSigner = ECDSA.recover(commitmentID, commitment.signature); + assertEq(commitmentSigner, 0x27083ED52464625660f3e30Aa5B9C20A30D7E110); + vm.pauseGasMetering(); + } + + function testCommitmentSignature() public { + bytes memory signedTx = vm.parseJsonBytes(vm.readFile("./test/testdata/signed_tx_20785012_1.json"), ".raw"); + uint64 slot = 20_728_344; + + // Reconstruct the commitment digest + bytes32 commitmentID = _computeCommitmentID(signedTx, slot); + + // Sign the commitment digest with the target + (uint8 v, bytes32 r, bytes32 s) = vm.sign(targetPK, commitmentID); + bytes memory commitmentSignature = abi.encodePacked(r, s, v); + + // Verify the commitment signature against the digest + vm.resumeGasMetering(); + address commitmentSigner = ECDSA.recover(commitmentID, commitmentSignature); + assertEq(commitmentSigner, target); + vm.pauseGasMetering(); + } + + // =========== Opening a challenge =========== + + function testOpenChallengeSingleTx() public { + IBoltChallengerV1.SignedCommitment[] memory commitments = new IBoltChallengerV1.SignedCommitment[](1); + commitments[0] = _parseTestCommitment(); + + assertEq(challenger.balance, 100 ether); + + // Open a challenge with the commitment + vm.resumeGasMetering(); + vm.prank(challenger); + boltChallenger.openChallenge{value: 1 ether}(commitments); + vm.pauseGasMetering(); + + assertEq(challenger.balance, 99 ether); + + // Check the challenge was opened + IBoltChallengerV1.Challenge[] memory challenges = boltChallenger.getAllChallenges(); + assertEq(challenges.length, 1); + + IBoltChallengerV1.Challenge memory challenge = challenges[0]; + assertEq(challenge.openedAt, block.timestamp); + assertEq(uint256(challenge.status), 0); + assertEq(challenge.challenger, challenger); + assertEq(challenge.commitmentSigner, 0x27083ED52464625660f3e30Aa5B9C20A30D7E110); + assertEq(challenge.targetSlot, commitments[0].slot); + } + + function testOpenChallengeWithIncorrectBond() public { + IBoltChallengerV1.SignedCommitment[] memory commitments = new IBoltChallengerV1.SignedCommitment[](1); + commitments[0] = _parseTestCommitment(); + + // Open a challenge with insufficient bond + vm.resumeGasMetering(); + vm.prank(challenger); + vm.expectRevert(IBoltChallengerV1.IncorrectChallengeBond.selector); + boltChallenger.openChallenge{value: 0.1 ether}(commitments); + vm.pauseGasMetering(); + } + + function testOpenChallengeWithLargebond() public { + IBoltChallengerV1.SignedCommitment[] memory commitments = new IBoltChallengerV1.SignedCommitment[](1); + commitments[0] = _parseTestCommitment(); + + // Open a challenge with a large bond, making sure that the rest is refunded + vm.resumeGasMetering(); + vm.prank(challenger); + vm.expectRevert(IBoltChallengerV1.IncorrectChallengeBond.selector); + boltChallenger.openChallenge{value: 50 ether}(commitments); + vm.pauseGasMetering(); + + assertEq(challenger.balance, 100 ether); + } + + function testOpenAlreadyExistingChallenge() public { + IBoltChallengerV1.SignedCommitment[] memory commitments = new IBoltChallengerV1.SignedCommitment[](1); + commitments[0] = _parseTestCommitment(); + + // Open a challenge + vm.prank(challenger); + boltChallenger.openChallenge{value: 1 ether}(commitments); + + // Try to open the same challenge again + vm.resumeGasMetering(); + vm.prank(challenger); + vm.expectRevert(IBoltChallengerV1.ChallengeAlreadyExists.selector); + boltChallenger.openChallenge{value: 1 ether}(commitments); + vm.pauseGasMetering(); + } + + function testOpenChallengeWithSlotInTheFuture() public { + IBoltChallengerV1.SignedCommitment[] memory commitments = new IBoltChallengerV1.SignedCommitment[](1); + commitments[0] = _parseTestCommitment(); + + commitments[0].slot = uint64(boltChallenger._getCurrentSlotExt()) + 10; + + // Open a challenge with a slot in the future + vm.resumeGasMetering(); + vm.prank(challenger); + vm.expectRevert(IBoltChallengerV1.BlockIsNotFinalized.selector); + boltChallenger.openChallenge{value: 1 ether}(commitments); + vm.pauseGasMetering(); + } + + function testOpenChallengeInvalidSignature() public { + IBoltChallengerV1.SignedCommitment[] memory commitments = new IBoltChallengerV1.SignedCommitment[](1); + commitments[0] = _parseTestCommitment(); + + // Modify the signature to make it invalid + commitments[0].signature[0] = bytes1(uint8(commitments[0].signature[0]) + 5); + + // Open a challenge with an invalid signature + vm.resumeGasMetering(); + vm.prank(challenger); + vm.expectRevert(ECDSA.ECDSAInvalidSignature.selector); + boltChallenger.openChallenge{value: 1 ether}(commitments); + vm.pauseGasMetering(); + } + + // =========== Resolving a challenge =========== + + function testResolveChallengeFullDefenseSingleTx() public { + // Prove the full defense of a challenge: the block headers, account proof, and tx proofs + // are all valid and the proposer has included the transaction in their slot. + + uint256 inclusionBlockNumber = 20_785_012; + IBoltChallengerV1.SignedCommitment[] memory commitments = new IBoltChallengerV1.SignedCommitment[](1); + commitments[0] = _createRecentBoltCommitment(inclusionBlockNumber, 1); + + // Open a challenge + vm.prank(challenger); + boltChallenger.openChallenge{value: 1 ether}(commitments); + + // Get the challenge ID + IBoltChallengerV1.Challenge[] memory challenges = boltChallenger.getAllChallenges(); + assertEq(challenges.length, 1); + bytes32 challengeID = challenges[0].id; + + string memory rawPreviousHeader = vm.readFile("./test/testdata/header_20785011.json"); + string memory rawInclusionHeader = vm.readFile("./test/testdata/header_20785012.json"); + string memory ethProof = vm.readFile("./test/testdata/eth_proof_20785011.json"); + string memory txProof = vm.readFile("./test/testdata/tx_mpt_proof_20785012.json"); + + bytes[] memory txProofs = new bytes[](1); + txProofs[0] = _RLPEncodeList(vm.parseJsonBytesArray(txProof, ".proof")); + + uint256[] memory txIndexesInBlock = new uint256[](1); + txIndexesInBlock[0] = vm.parseJsonUint(txProof, ".index"); + + IBoltChallengerV1.Proof memory proof = IBoltChallengerV1.Proof({ + inclusionBlockNumber: inclusionBlockNumber, + previousBlockHeaderRLP: vm.parseJsonBytes(rawPreviousHeader, ".result"), + inclusionBlockHeaderRLP: vm.parseJsonBytes(rawInclusionHeader, ".result"), + accountMerkleProof: _RLPEncodeList(vm.parseJsonBytesArray(ethProof, ".result.accountProof")), + txMerkleProofs: txProofs, + txIndexesInBlock: txIndexesInBlock + }); + + // check that the inclusion block transactions root matches the root in the tx proof data. + bytes32 inclusionTxRoot = boltChallenger._decodeBlockHeaderRLPExt(proof.inclusionBlockHeaderRLP).txRoot; + assertEq(inclusionTxRoot, vm.parseJsonBytes32(txProof, ".root")); + + bytes32 trustedPreviousBlockHash = 0x6be050fe1f6c7ffe8f30a350250a9ecc08ff3c031d129f65e1c10e5119d7a28b; + + // Resolve the challenge + vm.resumeGasMetering(); + vm.prank(resolver); + vm.expectEmit(); + + emit IBoltChallengerV1.ChallengeDefended(challengeID); + boltChallenger._resolveExt(challengeID, trustedPreviousBlockHash, proof); + } + + function testResolveChallengeFullDefenseStackedTxs() public { + // Prove the full defense of a challenge: the block headers, account proof, and tx proofs + // are all valid and the proposer has included the transaction in their slot. + // This time, the proposer has committed to multiple transactions in their slot. + // + // The test data for this test was generated by querying for an Ethereum block with a + // sender that has sent multiple transactions in the same block. + // Check out https://etherscan.io/block/20817618 + + uint256 inclusionBlockNumber = 20_817_618; + IBoltChallengerV1.SignedCommitment[] memory commitments = new IBoltChallengerV1.SignedCommitment[](5); + commitments[0] = _createRecentBoltCommitment(inclusionBlockNumber, 1); + commitments[1] = _createRecentBoltCommitment(inclusionBlockNumber, 2); + commitments[2] = _createRecentBoltCommitment(inclusionBlockNumber, 3); + commitments[3] = _createRecentBoltCommitment(inclusionBlockNumber, 4); + commitments[4] = _createRecentBoltCommitment(inclusionBlockNumber, 5); + + // Sanity check senders of the transactions: they should all be the same + for (uint256 i = 0; i < commitments.length; i++) { + address recovered = commitments[i].signedTx.decodeEnveloped().recoverSender(); + assertEq(recovered, 0xc21fb45Eeb45D883B838E30ABBd2896aE5AC888c); + } + + // Sanity check signers of the commitments: they should all be the same + for (uint256 i = 0; i < commitments.length; i++) { + bytes32 cid = _computeCommitmentID(commitments[i].signedTx, commitments[i].slot); + address signer = ECDSA.recover(cid, commitments[i].signature); + assertEq(signer, target); + } + + // Open a challenge + vm.prank(challenger); + boltChallenger.openChallenge{value: 1 ether}(commitments); + + // Get the challenge ID + IBoltChallengerV1.Challenge[] memory challenges = boltChallenger.getAllChallenges(); + assertEq(challenges.length, 1); + bytes32 challengeID = challenges[0].id; + + // headers + string memory rawPreviousHeader = vm.readFile("./test/testdata/header_20817617.json"); + string memory rawInclusionHeader = vm.readFile("./test/testdata/header_20817618.json"); + + // account + string memory ethProof = vm.readFile("./test/testdata/eth_proof_20817617.json"); + + // transactions + string memory txProof1 = vm.readFile("./test/testdata/tx_mpt_proof_20817618_1.json"); + string memory txProof2 = vm.readFile("./test/testdata/tx_mpt_proof_20817618_2.json"); + string memory txProof3 = vm.readFile("./test/testdata/tx_mpt_proof_20817618_3.json"); + string memory txProof4 = vm.readFile("./test/testdata/tx_mpt_proof_20817618_4.json"); + string memory txProof5 = vm.readFile("./test/testdata/tx_mpt_proof_20817618_5.json"); + + bytes[] memory txProofs = new bytes[](5); + txProofs[0] = _RLPEncodeList(vm.parseJsonBytesArray(txProof1, ".proof")); + txProofs[1] = _RLPEncodeList(vm.parseJsonBytesArray(txProof2, ".proof")); + txProofs[2] = _RLPEncodeList(vm.parseJsonBytesArray(txProof3, ".proof")); + txProofs[3] = _RLPEncodeList(vm.parseJsonBytesArray(txProof4, ".proof")); + txProofs[4] = _RLPEncodeList(vm.parseJsonBytesArray(txProof5, ".proof")); + + uint256[] memory txIndexesInBlock = new uint256[](5); + txIndexesInBlock[0] = vm.parseJsonUint(txProof1, ".index"); + txIndexesInBlock[1] = vm.parseJsonUint(txProof2, ".index"); + txIndexesInBlock[2] = vm.parseJsonUint(txProof3, ".index"); + txIndexesInBlock[3] = vm.parseJsonUint(txProof4, ".index"); + txIndexesInBlock[4] = vm.parseJsonUint(txProof5, ".index"); + + IBoltChallengerV1.Proof memory proof = IBoltChallengerV1.Proof({ + inclusionBlockNumber: inclusionBlockNumber, + previousBlockHeaderRLP: vm.parseJsonBytes(rawPreviousHeader, ".result"), + inclusionBlockHeaderRLP: vm.parseJsonBytes(rawInclusionHeader, ".result"), + accountMerkleProof: _RLPEncodeList(vm.parseJsonBytesArray(ethProof, ".result.accountProof")), + txMerkleProofs: txProofs, + txIndexesInBlock: txIndexesInBlock + }); + + // check that the inclusion block transactions root matches the root in the tx proof data. + bytes32 inclusionTxRoot = boltChallenger._decodeBlockHeaderRLPExt(proof.inclusionBlockHeaderRLP).txRoot; + assertEq(inclusionTxRoot, vm.parseJsonBytes32(txProof1, ".root")); + + // block hash of https://etherscan.io/block/20817617 + bytes32 trustedPreviousBlockHash = 0xb410d12f92ed268b184c1e6523b7d3fea5fcd0ba3f9bc6c6cb9a7e5b1523d225; + + // Resolve the challenge + vm.resumeGasMetering(); + vm.prank(resolver); + + vm.expectEmit(); + emit IBoltChallengerV1.ChallengeDefended(challengeID); + + boltChallenger._resolveExt(challengeID, trustedPreviousBlockHash, proof); + } + + function testResolveExpiredChallenge() public { + IBoltChallengerV1.SignedCommitment[] memory commitments = new IBoltChallengerV1.SignedCommitment[](1); + commitments[0] = _parseTestCommitment(); + + // Open a challenge with the commitment + vm.resumeGasMetering(); + vm.prank(challenger); + boltChallenger.openChallenge{value: 1 ether}(commitments); + vm.pauseGasMetering(); + + // Check the challenge was opened + IBoltChallengerV1.Challenge[] memory challenges = boltChallenger.getAllChallenges(); + assertEq(challenges.length, 1); + + // Warp time to make the challenge expire + vm.warp(block.timestamp + 2 weeks); + + // Try to resolve the challenge + vm.prank(resolver); + + // Get the challenge + IBoltChallengerV1.Challenge memory challenge = boltChallenger.getAllChallenges()[0]; + + vm.expectEmit(); + emit IBoltChallengerV1.ChallengeBreached(challenge.id); + + boltChallenger.resolveExpiredChallenge(challenge.id); + } + + function testCannotResolveChallengeBeforeExpiration() public { + IBoltChallengerV1.SignedCommitment[] memory commitments = new IBoltChallengerV1.SignedCommitment[](1); + commitments[0] = _parseTestCommitment(); + + // Open a challenge with the commitment + vm.resumeGasMetering(); + vm.prank(challenger); + boltChallenger.openChallenge{value: 1 ether}(commitments); + vm.pauseGasMetering(); + + // Check the challenge was opened + IBoltChallengerV1.Challenge[] memory challenges = boltChallenger.getAllChallenges(); + assertEq(challenges.length, 1); + bytes32 id = challenges[0].id; + + // Try to resolve the challenge before it expires + vm.resumeGasMetering(); + vm.prank(resolver); + vm.expectRevert(IBoltChallengerV1.ChallengeNotExpired.selector); + boltChallenger.resolveExpiredChallenge(id); + vm.pauseGasMetering(); + } + + // =========== Helper functions =========== + + // Helper to create a test commitment with a recent slot, valid for a recent challenge + function _createRecentBoltCommitment( + uint256 blockNumber, + uint256 id + ) internal view returns (IBoltChallengerV1.SignedCommitment memory commitment) { + // pattern: ./test/testdata/signed_tx_{blockNumber}_{id}.json + string memory base = "./test/testdata/signed_tx_"; + string memory extension = string.concat(vm.toString(blockNumber), "_", vm.toString(id), ".json"); + string memory path = string.concat(base, extension); + commitment.signedTx = vm.parseJsonBytes(vm.readFile(path), ".raw"); + + // pick a recent slot, 100 slots behind the current slot + commitment.slot = uint64(boltChallenger._getCurrentSlotExt() - 100); + + // sign the new commitment with the target's private key + bytes32 commitmentID = _computeCommitmentID(commitment.signedTx, commitment.slot); + (uint8 v, bytes32 r, bytes32 s) = vm.sign(targetPK, commitmentID); + commitment.signature = abi.encodePacked(r, s, v); + + // Normalize v to 27 or 28 + if (uint8(commitment.signature[64]) < 27) { + commitment.signature[64] = bytes1(uint8(commitment.signature[64]) + 0x1B); + } + + // Sanity check + assertEq(ECDSA.recover(commitmentID, commitment.signature), target); + + return commitment; + } + + // Helper to parse the test commitment from a file + function _parseTestCommitment() internal view returns (IBoltChallengerV1.SignedCommitment memory) { + string memory file = vm.readFile("./test/testdata/bolt_commitment.json"); + IBoltChallengerV1.SignedCommitment memory commitment = IBoltChallengerV1.SignedCommitment({ + slot: uint64(vm.parseJsonUint(file, ".slot")), + signature: vm.parseJsonBytes(file, ".signature"), + signedTx: vm.parseJsonBytes(file, ".tx") + }); + + // Normalize v to 27 or 28 + if (uint8(commitment.signature[64]) < 27) { + commitment.signature[64] = bytes1(uint8(commitment.signature[64]) + 0x1B); + } + + return commitment; + } + + // Helper to compute the commitment ID + function _computeCommitmentID(bytes memory signedTx, uint64 slot) internal pure returns (bytes32) { + return keccak256(abi.encodePacked(keccak256(signedTx), _toLittleEndian(slot))); + } + + // Helper to encode a list of bytes[] into an RLP list with each item RLP-encoded + function _RLPEncodeList( + bytes[] memory _items + ) internal pure returns (bytes memory) { + bytes[] memory encodedItems = new bytes[](_items.length); + for (uint256 i = 0; i < _items.length; i++) { + encodedItems[i] = RLPWriter.writeBytes(_items[i]); + } + return RLPWriter.writeList(encodedItems); + } + + // Helper to convert a u64 to a little-endian bytes + function _toLittleEndian( + uint64 x + ) internal pure returns (bytes memory) { + bytes memory b = new bytes(8); + for (uint256 i = 0; i < 8; i++) { + b[i] = bytes1(uint8(x >> (8 * i))); + } + return b; + } +} diff --git a/bolt-contracts/test/BoltManager.EigenLayer.t.sol b/bolt-contracts/test/BoltManager.EigenLayer.t.sol new file mode 100644 index 000000000..4a815704f --- /dev/null +++ b/bolt-contracts/test/BoltManager.EigenLayer.t.sol @@ -0,0 +1,270 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.25; + +import {Test, console} from "forge-std/Test.sol"; + +import {BoltValidatorsV1} from "../src/contracts/BoltValidatorsV1.sol"; +import {BoltManagerV1} from "../src/contracts/BoltManagerV1.sol"; +import {BoltParametersV1} from "../src/contracts/BoltParametersV1.sol"; +import {BoltEigenLayerMiddlewareV1} from "../src/contracts/BoltEigenLayerMiddlewareV1.sol"; +import {BoltConfig} from "../src/lib/Config.sol"; +import {IBoltValidatorsV1} from "../src/interfaces/IBoltValidatorsV1.sol"; +import {IBoltManagerV1} from "../src/interfaces/IBoltManagerV1.sol"; +import {IBoltMiddlewareV1} from "../src/interfaces/IBoltMiddlewareV1.sol"; +import {Utils} from "./Utils.sol"; + +import {AVSDirectoryStorage} from "@eigenlayer/src/contracts/core/AVSDirectoryStorage.sol"; +import {DelegationManagerStorage} from "@eigenlayer/src/contracts/core/DelegationManagerStorage.sol"; +import {IDelegationManager} from "@eigenlayer/src/contracts/interfaces/IDelegationManager.sol"; +import {ISignatureUtils} from "@eigenlayer/src/contracts/interfaces/ISignatureUtils.sol"; +import {IAVSDirectory} from "@eigenlayer/src/contracts/interfaces/IAVSDirectory.sol"; +import {IStrategy} from "@eigenlayer/src/contracts/interfaces/IStrategy.sol"; +import {EigenLayerDeployer} from "../test/fixtures/EigenLayerDeployer.f.sol"; + +import {BLS12381} from "../src/lib/bls/BLS12381.sol"; + +contract BoltManagerEigenLayerTest is Test { + using BLS12381 for BLS12381.G1Point; + + uint48 public constant EPOCH_DURATION = 1 days; + + BoltValidatorsV1 public validators; + BoltManagerV1 public manager; + BoltEigenLayerMiddlewareV1 public middleware; + EigenLayerDeployer public eigenLayerDeployer; + + uint128 public constant PRECONF_MAX_GAS_LIMIT = 5_000_000; + + address staker = makeAddr("staker"); + address validator = makeAddr("validator"); + BLS12381.G1Point validatorPubkey = BLS12381.generatorG1(); + address operator; + uint256 operatorSk; + + address admin = makeAddr("admin"); + + function setUp() public { + // Set-up accounts + (operator, operatorSk) = makeAddrAndKey("operator"); + + // Deploy EigenLayer contracts. + // This also deploy a `weth` token and `wethStrat` strategy base available as properties of the contract. + eigenLayerDeployer = new EigenLayerDeployer(staker); + eigenLayerDeployer.setUp(); + + BoltConfig.Parameters memory config = new Utils().readParameters(); + + BoltParametersV1 parameters = new BoltParametersV1(); + parameters.initialize( + admin, + config.epochDuration, + config.slashingWindow, + config.maxChallengeDuration, + config.allowUnsafeRegistration, + config.challengeBond, + config.blockhashEvmLookback, + config.justificationDelay, + config.eth2GenesisTimestamp, + config.slotTime, + config.minimumOperatorStake + ); + + // Deploy Bolt contracts + validators = new BoltValidatorsV1(); + validators.initialize(admin, address(parameters)); + manager = new BoltManagerV1(); + manager.initialize(admin, address(parameters), address(validators)); + middleware = new BoltEigenLayerMiddlewareV1(); + + middleware.initialize( + address(admin), + address(parameters), + address(manager), + address(eigenLayerDeployer.avsDirectory()), + address(eigenLayerDeployer.delegationManager()), + address(eigenLayerDeployer.strategyManager()) + ); + + // Register the middleware in the manager + vm.startPrank(admin); + manager.addRestakingProtocol(address(middleware)); + vm.stopPrank(); + } + + function _adminRoutine() internal { + // PART 0: Admin setup -- Collateral whitelist + // vm.startPrank(admin); + // middleware.addWhitelistedCollateral(address(eigenLayerDeployer.weth())); + // vm.stopPrank(); + // assertEq(middleware.getWhitelistedCollaterals().length, 1); + // assertEq(middleware.getWhitelistedCollaterals()[0], address(eigenLayerDeployer.weth())); + } + + function _eigenLayerOptInRoutine() internal { + _adminRoutine(); + + // PART 1: External EigenLayer opt-in to BOLT AVS + + // 1. As a staker, I deposit some LSTs into a Stategy via the StrategyManager.depositIntoStrategy function. + // After this, I get back some shares that I can use at a later time for withdrawal + + vm.startPrank(staker); + eigenLayerDeployer.weth().approve(address(eigenLayerDeployer.strategyManager()), 1 ether); + uint256 shares = eigenLayerDeployer.strategyManager().depositIntoStrategy( + eigenLayerDeployer.wethStrat(), eigenLayerDeployer.weth(), 1 ether + ); + vm.stopPrank(); + assertEq(eigenLayerDeployer.wethStrat().sharesToUnderlyingView(shares), 1 ether); + + // 2. As a Operator, I register myself into EigenLayer using DelegationManager.registerAsOperator. + // Note that this function doesn’t require specifying anything related to the service I’m going to provide. + // However, a parameter describes who can delegate to me whether it can be anyone or a subset of stakers. + + IDelegationManager.OperatorDetails memory operatorDetails = + IDelegationManager.OperatorDetails(address(0), address(0), 0); + vm.startPrank(operator); + eigenLayerDeployer.delegationManager().registerAsOperator(operatorDetails, "https://boltprotocol.xyz"); + vm.stopPrank(); + + // 3. As a staker, I can start delegating funds to these operators using + // the DelegationManager.delegateTo function and specifying to who I wish + // to delegate my funds + + // NOTE: this signature is not used since the operator allows funds delegated from anyone + ISignatureUtils.SignatureWithExpiry memory signature = ISignatureUtils.SignatureWithExpiry(bytes(""), 0); + console.logAddress(eigenLayerDeployer.delegationManager().delegatedTo(staker)); + vm.startPrank(staker); + eigenLayerDeployer.delegationManager().delegateTo(operator, signature, bytes32(0)); + vm.stopPrank(); + assertEq(eigenLayerDeployer.delegationManager().delegatedTo(staker), operator); + + // 4. As an AVS developer I create an entrypoint contract. + // Upon deploying the contract it is required to make a call to EL’s + // AVSDirectory.updateAVSMetadataURI which takes just a string which is a URI. + // Note that his is not stored anywhere, just an log is emitted. + // Note that msg.sender which is the ServiceManager contract is used to identify the AVS itself + + vm.prank(admin); + middleware.updateAVSMetadataURI("https://boltprotocol.xyz"); + + // 5. As a operator, I can now opt-in into an AVS by interacting with the ServiceManager. + // Two steps happen: + // i. I call the AVS’ ServiceManager.registerOperatorToAVS. The payload is a signature whose digest consists of: + // a. my operator address + // b. the AVS’ ServiceManager contract address + // c. a salt + // d. an expiry + // ii. The contract forwards the call to the AVSDirectory.registerOperatorToAVS to + // that msg.sender is the AVS contract. Upon successful verification of the signature, + // the operator is considered REGISTERED in a mapping avsOperatorStatus[msg.sender][operator]. + + // Calculate the digest hash + bytes32 operatorRegistrationDigestHash = eigenLayerDeployer.avsDirectory() + .calculateOperatorAVSRegistrationDigestHash({ + operator: operator, + avs: address(middleware), + salt: bytes32(0), + expiry: UINT256_MAX + }); + (uint8 v, bytes32 r, bytes32 s) = vm.sign(operatorSk, operatorRegistrationDigestHash); + bytes memory operatorRawSignature = abi.encodePacked(r, s, v); + ISignatureUtils.SignatureWithSaltAndExpiry memory operatorSignature = + ISignatureUtils.SignatureWithSaltAndExpiry(operatorRawSignature, bytes32(0), UINT256_MAX); + vm.expectEmit(true, true, true, true); + emit IAVSDirectory.OperatorAVSRegistrationStatusUpdated( + operator, address(middleware), IAVSDirectory.OperatorAVSRegistrationStatus.REGISTERED + ); + + vm.prank(operator); + middleware.registerOperator("https://bolt-rpc.io", operatorSignature); + + assertEq(manager.isOperatorEnabled(operator), true); + + // PART 2: Validator and proposer opt into BOLT manager + // + // 1. --- Register Validator in BoltValidators --- + + // pubkeys aren't checked, any point will be fine + validatorPubkey = BLS12381.generatorG1(); + + vm.prank(validator); + validators.registerValidatorUnsafe(validatorPubkey, PRECONF_MAX_GAS_LIMIT, operator); + assertEq(validators.getValidatorByPubkey(validatorPubkey).exists, true); + assertEq(validators.getValidatorByPubkey(validatorPubkey).authorizedOperator, operator); + + // 2. --- Operator and strategy registration into BoltManager (middleware) --- + + vm.startPrank(admin); + middleware.registerStrategy(address(eigenLayerDeployer.wethStrat())); + vm.stopPrank(); + assertEq(middleware.isStrategyEnabled(address(eigenLayerDeployer.wethStrat())), true); + } + + function testDeregisterOperatorFromAVS() public { + _eigenLayerOptInRoutine(); + vm.prank(operator); + middleware.deregisterOperator(); + vm.expectRevert(IBoltManagerV1.OperatorNotRegistered.selector); + manager.isOperatorEnabled(operator); + } + + function testGetOperatorStake() public { + _eigenLayerOptInRoutine(); + + uint256 amount = middleware.getOperatorStake(operator, address(eigenLayerDeployer.weth())); + uint256 totalStake = manager.getTotalStake(address(eigenLayerDeployer.weth())); + assertEq(amount, 1 ether); + assertEq(totalStake, 1 ether); + } + + function testProposerStatus() public { + _eigenLayerOptInRoutine(); + + bytes32 pubkeyHash = _pubkeyHash(validatorPubkey); + + IBoltValidatorsV1.ProposerStatus memory status = manager.getProposerStatus(pubkeyHash); + assertEq(status.pubkeyHash, pubkeyHash); + assertEq(status.operator, operator); + assertEq(status.active, true); + assertEq(status.collaterals.length, 1); + assertEq(status.amounts.length, 1); + assertEq(status.collaterals[0], address(eigenLayerDeployer.weth())); + assertEq(status.amounts[0], 1 ether); + } + + function testProposersLookaheadStatus() public { + // This also opts in the operator which is needed + _eigenLayerOptInRoutine(); + bytes32[] memory pubkeyHashes = new bytes32[](10); + + // register 10 proposers with random pubkeys + for (uint256 i = 0; i < 10; i++) { + BLS12381.G1Point memory pubkey = BLS12381.generatorG1(); + pubkey.x[0] = pubkey.x[0] + i + 2; + pubkey.y[0] = pubkey.y[0] + i + 2; + + pubkeyHashes[i] = _pubkeyHash(pubkey); + validators.registerValidatorUnsafe(pubkey, PRECONF_MAX_GAS_LIMIT, operator); + } + + IBoltValidatorsV1.ProposerStatus[] memory statuses = manager.getProposerStatuses(pubkeyHashes); + assertEq(statuses.length, 10); + } + + function testNonExistentProposerStatus() public { + _eigenLayerOptInRoutine(); + + bytes32 pubkeyHash = bytes32(uint256(1)); + + vm.expectRevert(IBoltValidatorsV1.ValidatorDoesNotExist.selector); + manager.getProposerStatus(pubkeyHash); + } + + /// @notice Compute the hash of a BLS public key + function _pubkeyHash( + BLS12381.G1Point memory _pubkey + ) internal pure returns (bytes32) { + uint256[2] memory compressedPubKey = _pubkey.compress(); + return keccak256(abi.encodePacked(compressedPubKey)); + } +} diff --git a/bolt-contracts/test/BoltManager.Symbiotic.t.sol b/bolt-contracts/test/BoltManager.Symbiotic.t.sol new file mode 100644 index 000000000..d64492646 --- /dev/null +++ b/bolt-contracts/test/BoltManager.Symbiotic.t.sol @@ -0,0 +1,373 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.25; + +import {Test, console} from "forge-std/Test.sol"; + +import {INetworkRegistry} from "@symbiotic/interfaces/INetworkRegistry.sol"; +import {IOperatorRegistry} from "@symbiotic/interfaces/IOperatorRegistry.sol"; +import {IVaultFactory} from "@symbiotic/interfaces/IVaultFactory.sol"; +import {IVault} from "@symbiotic/interfaces/vault/IVault.sol"; +import {IOptInService} from "@symbiotic/interfaces/service/IOptInService.sol"; +import {IVaultConfigurator} from "@symbiotic/interfaces/IVaultConfigurator.sol"; +import {IBaseDelegator} from "@symbiotic/interfaces/delegator/IBaseDelegator.sol"; +import {IMetadataService} from "@symbiotic/interfaces/service/IMetadataService.sol"; +import {INetworkRestakeDelegator} from "@symbiotic/interfaces/delegator/INetworkRestakeDelegator.sol"; +import {INetworkMiddlewareService} from "@symbiotic/interfaces/service/INetworkMiddlewareService.sol"; +import {ISlasherFactory} from "@symbiotic/interfaces/ISlasherFactory.sol"; +import {IVetoSlasher} from "@symbiotic/interfaces/slasher/IVetoSlasher.sol"; +import {IDelegatorFactory} from "@symbiotic/interfaces/IDelegatorFactory.sol"; +import {IMigratablesFactory} from "@symbiotic/interfaces/common/IMigratablesFactory.sol"; +import {Subnetwork} from "@symbiotic/contracts/libraries/Subnetwork.sol"; +import {SimpleCollateral} from "@symbiotic/../test/mocks/SimpleCollateral.sol"; + +import {IBoltValidatorsV1} from "../src/interfaces/IBoltValidatorsV1.sol"; +import {IBoltMiddlewareV1} from "../src/interfaces/IBoltMiddlewareV1.sol"; + +import {BoltParametersV1} from "../src/contracts/BoltParametersV1.sol"; +import {BoltValidatorsV1} from "../src/contracts/BoltValidatorsV1.sol"; +import {BoltManagerV1} from "../src/contracts/BoltManagerV1.sol"; +import {BoltSymbioticMiddlewareV1} from "../src/contracts/BoltSymbioticMiddlewareV1.sol"; +import {BLS12381} from "../src/lib/bls/BLS12381.sol"; +import {BoltConfig} from "../src/lib/Config.sol"; +import {Utils} from "./Utils.sol"; + +import {SymbioticSetupFixture} from "./fixtures/SymbioticSetup.f.sol"; + +contract BoltManagerSymbioticTest is Test { + using BLS12381 for BLS12381.G1Point; + using Subnetwork for address; + + uint48 public constant EPOCH_DURATION = 1 days; + uint48 public constant SLASHING_WINDOW = 7 days; + + uint128 public constant PRECONF_MAX_GAS_LIMIT = 5_000_000; + + BoltValidatorsV1 public validators; + BoltManagerV1 public manager; + BoltSymbioticMiddlewareV1 public middleware; + + IVaultFactory public vaultFactory; + IDelegatorFactory public delegatorFactory; + ISlasherFactory public slasherFactory; + INetworkRegistry public networkRegistry; + IOperatorRegistry public operatorRegistry; + IMetadataService public operatorMetadataService; + IMetadataService public networkMetadataService; + INetworkMiddlewareService public networkMiddlewareService; + IOptInService public operatorVaultOptInService; + IOptInService public operatorNetworkOptInService; + IVetoSlasher public vetoSlasher; + IVault public vault; + INetworkRestakeDelegator public networkRestakeDelegator; + IVaultConfigurator public vaultConfigurator; + SimpleCollateral public collateral; + + address deployer = makeAddr("deployer"); + address admin = makeAddr("admin"); + address provider = makeAddr("provider"); + address operator = makeAddr("operator"); + address validator = makeAddr("validator"); + address networkAdmin = makeAddr("networkAdmin"); + address vaultAdmin = makeAddr("vaultAdmin"); + address user = makeAddr("user"); + + uint96 subnetworkId = 0; + bytes32 subnetwork = networkAdmin.subnetwork(subnetworkId); + + function setUp() public { + // fast forward a few days to avoid timestamp underflows + vm.warp(block.timestamp + SLASHING_WINDOW * 3); + + // --- Deploy Symbiotic contracts --- + ( + vaultFactory, + delegatorFactory, + slasherFactory, + networkRegistry, + operatorRegistry, + operatorMetadataService, + networkMetadataService, + networkMiddlewareService, + operatorVaultOptInService, + operatorNetworkOptInService, + vaultConfigurator, + collateral + ) = new SymbioticSetupFixture().setUp(deployer, admin); + + // --- Create vault --- + + address[] memory adminRoleHolders = new address[](1); + adminRoleHolders[0] = vaultAdmin; + + IVaultConfigurator.InitParams memory vaultConfiguratorInitParams = IVaultConfigurator.InitParams({ + version: IMigratablesFactory(vaultConfigurator.VAULT_FACTORY()).lastVersion(), + owner: vaultAdmin, + vaultParams: IVault.InitParams({ + collateral: address(collateral), + delegator: address(0), + slasher: address(0), + burner: address(0xdead), + epochDuration: EPOCH_DURATION, + depositWhitelist: false, + isDepositLimit: false, + depositLimit: 0, + defaultAdminRoleHolder: vaultAdmin, + depositWhitelistSetRoleHolder: vaultAdmin, + depositorWhitelistRoleHolder: vaultAdmin, + isDepositLimitSetRoleHolder: vaultAdmin, + depositLimitSetRoleHolder: vaultAdmin + }), + delegatorIndex: 0, // Use NetworkRestakeDelegator + delegatorParams: abi.encode( + INetworkRestakeDelegator.InitParams({ + baseParams: IBaseDelegator.BaseParams({ + defaultAdminRoleHolder: vaultAdmin, + hook: address(0), // we don't need a hook + hookSetRoleHolder: vaultAdmin + }), + networkLimitSetRoleHolders: adminRoleHolders, + operatorNetworkSharesSetRoleHolders: adminRoleHolders + }) + ), + withSlasher: true, + slasherIndex: 1, // Use VetoSlasher + slasherParams: abi.encode( + IVetoSlasher.InitParams({ + // veto duration must be smaller than epoch duration + vetoDuration: uint48(12 hours), + resolverSetEpochsDelay: 3 + }) + ) + }); + + (address vault_, address networkRestakeDelegator_, address vetoSlasher_) = + vaultConfigurator.create(vaultConfiguratorInitParams); + vault = IVault(vault_); + networkRestakeDelegator = INetworkRestakeDelegator(networkRestakeDelegator_); + vetoSlasher = IVetoSlasher(vetoSlasher_); + + assertEq(address(networkRestakeDelegator), address(vault.delegator())); + assertEq(address(vetoSlasher), address(vault.slasher())); + assertEq(address(vault.collateral()), address(collateral)); + assertEq(vault.epochDuration(), EPOCH_DURATION); + + // --- Deploy Bolt contracts --- + + BoltConfig.Parameters memory config = new Utils().readParameters(); + + BoltParametersV1 parameters = new BoltParametersV1(); + parameters.initialize( + admin, + config.epochDuration, + config.slashingWindow, + config.maxChallengeDuration, + config.allowUnsafeRegistration, + config.challengeBond, + config.blockhashEvmLookback, + config.justificationDelay, + config.eth2GenesisTimestamp, + config.slotTime, + config.minimumOperatorStake + ); + + validators = new BoltValidatorsV1(); + validators.initialize(admin, address(parameters)); + manager = new BoltManagerV1(); + manager.initialize(admin, address(parameters), address(validators)); + + middleware = new BoltSymbioticMiddlewareV1(); + + middleware.initialize( + admin, + address(parameters), + address(manager), + networkAdmin, + address(operatorRegistry), + address(operatorNetworkOptInService), + address(vaultFactory) + ); + + // --- Whitelist collateral in BoltSymbioticMiddleware --- + vm.startPrank(admin); + middleware.registerVault(address(vault)); + manager.addRestakingProtocol(address(middleware)); + vm.stopPrank(); + } + + /// @notice Internal helper to register Symbiotic contracts and opt-in operators and vaults. + /// Should be called inside other tests that need a common setup beyond the default setUp(). + function _symbioticOptInRoutine() internal { + // --- Register Network and Middleware in Symbiotic --- + + vm.prank(networkAdmin); + networkRegistry.registerNetwork(); + + vm.prank(networkAdmin); + networkMiddlewareService.setMiddleware(address(middleware)); + + // --- Register Validator in BoltValidators --- + + // pubkeys aren't checked, any point will be fine + BLS12381.G1Point memory pubkey = BLS12381.generatorG1(); + + vm.prank(validator); + validators.registerValidatorUnsafe(pubkey, PRECONF_MAX_GAS_LIMIT, operator); + assertEq(validators.getValidatorByPubkey(pubkey).exists, true); + assertEq(validators.getValidatorByPubkey(pubkey).authorizedOperator, operator); + + // --- Register Operator in Symbiotic, opt-in network and vault --- + + vm.prank(operator); + operatorRegistry.registerOperator(); + assertEq(operatorRegistry.isEntity(operator), true); + + vm.prank(operator); + operatorNetworkOptInService.optIn(networkAdmin); + assertEq(operatorNetworkOptInService.isOptedIn(operator, networkAdmin), true); + + vm.prank(operator); + operatorVaultOptInService.optIn(address(vault)); + assertEq(operatorVaultOptInService.isOptedIn(operator, address(vault)), true); + + // --- Register Vault and Operator in BoltManager (middleware) --- + assertEq(middleware.isVaultEnabled(address(vault)), true); + + vm.prank(operator); + middleware.registerOperator("https://bolt-rpc.io"); + assertEq(manager.isOperatorEnabled(operator), true); + + // --- Set the stake limit for the Vault --- + + vm.prank(networkAdmin); + networkRestakeDelegator.setMaxNetworkLimit(subnetworkId, 10 ether); + + vm.prank(vaultAdmin); + networkRestakeDelegator.setNetworkLimit(subnetwork, 2 ether); + + // --- Add stake to the Vault --- + + vm.prank(provider); + SimpleCollateral(collateral).mint(1 ether); + + vm.prank(provider); + SimpleCollateral(collateral).approve(address(vault), 1 ether); + + // deposit collateral from "provider" on behalf of "operator" + vm.prank(provider); + (uint256 depositedAmount, uint256 mintedShares) = vault.deposit(operator, 1 ether); + + assertEq(depositedAmount, 1 ether); + assertEq(mintedShares, 1 ether); + assertEq(vault.balanceOf(operator), 1 ether); + assertEq(SimpleCollateral(collateral).balanceOf(address(vault)), 1 ether); + } + + /// @notice Compute the hash of a BLS public key + function _pubkeyHash( + BLS12381.G1Point memory pubkey + ) internal pure returns (bytes32) { + uint256[2] memory compressedPubKey = pubkey.compress(); + return keccak256(abi.encodePacked(compressedPubKey)); + } + + function testReadOperatorStake() public { + _symbioticOptInRoutine(); + + // --- Read the operator stake --- + + // initial state + uint256 shares = networkRestakeDelegator.totalOperatorNetworkShares(subnetwork); + uint256 stakeFromDelegator = networkRestakeDelegator.stake(subnetwork, operator); + uint256 stakeFromMiddleware = middleware.getOperatorStake(operator, address(collateral)); + assertEq(shares, 0); + assertEq(stakeFromMiddleware, stakeFromDelegator); + assertEq(stakeFromMiddleware, 0); + + vm.warp(block.timestamp + EPOCH_DURATION + 1); + assertEq(vault.currentEpoch(), 1); + + // after an epoch has passed + assertEq(vault.totalStake(), 1 ether); + assertEq(vault.activeStake(), 1 ether); + assertEq(vault.activeBalanceOf(operator), 1 ether); + assertEq(vault.activeSharesAt(uint48(0), ""), 0); + assertEq(vault.activeSharesAt(uint48(block.timestamp), ""), 1 ether); + + // there still aren't any shares minted on the delegator + assertEq(networkRestakeDelegator.totalOperatorNetworkShares(subnetwork), 0); + assertEq(networkRestakeDelegator.operatorNetworkShares(subnetwork, operator), 0); + + // we need to mint shares from the vault admin to activate stake + // for the operator in the subnetwork. + vm.prank(vaultAdmin); + networkRestakeDelegator.setOperatorNetworkShares(subnetwork, operator, 100); + assertEq(networkRestakeDelegator.totalOperatorNetworkShares(subnetwork), 100); + assertEq(networkRestakeDelegator.operatorNetworkShares(subnetwork, operator), 100); + + vm.warp(block.timestamp + EPOCH_DURATION + 1); + assertEq(vault.currentEpoch(), 2); + + // it takes 2 epochs to activate the stake + stakeFromDelegator = networkRestakeDelegator.stake(subnetwork, operator); + stakeFromMiddleware = middleware.getOperatorStake(operator, address(collateral)); + assertEq(stakeFromDelegator, stakeFromMiddleware); + assertEq(stakeFromMiddleware, 1 ether); + } + + function testGetProposerStatus() public { + _symbioticOptInRoutine(); + + // we need to mint shares from the vault admin to activate stake + // for the operator in the subnetwork. + vm.prank(vaultAdmin); + networkRestakeDelegator.setOperatorNetworkShares(subnetwork, operator, 100); + assertEq(networkRestakeDelegator.totalOperatorNetworkShares(subnetwork), 100); + assertEq(networkRestakeDelegator.operatorNetworkShares(subnetwork, operator), 100); + + BLS12381.G1Point memory pubkey = BLS12381.generatorG1(); + bytes32 pubkeyHash = _pubkeyHash(pubkey); + + vm.warp(block.timestamp + EPOCH_DURATION * 2 + 1); + assertEq(vault.currentEpoch(), 2); + + IBoltValidatorsV1.ProposerStatus memory status = manager.getProposerStatus(pubkeyHash); + assertEq(status.pubkeyHash, pubkeyHash); + assertEq(status.operator, operator); + assertEq(status.active, true); + assertEq(status.collaterals.length, 1); + assertEq(status.amounts.length, 1); + assertEq(status.collaterals[0], address(collateral)); + assertEq(status.amounts[0], 1 ether); + } + + function testProposersLookaheadStatus() public { + _symbioticOptInRoutine(); + + bytes32[] memory pubkeyHashes = new bytes32[](10); + + // register 10 proposers with random pubkeys + for (uint256 i = 0; i < 10; i++) { + BLS12381.G1Point memory pubkey = BLS12381.generatorG1(); + pubkey.x[0] = pubkey.x[0] + i + 2; + pubkey.y[0] = pubkey.y[0] + i + 2; + + pubkeyHashes[i] = _pubkeyHash(pubkey); + validators.registerValidatorUnsafe(pubkey, PRECONF_MAX_GAS_LIMIT, operator); + } + + vm.warp(block.timestamp + EPOCH_DURATION * 2 + 1); + assertEq(vault.currentEpoch(), 2); + + IBoltValidatorsV1.ProposerStatus[] memory statuses = manager.getProposerStatuses(pubkeyHashes); + assertEq(statuses.length, 10); + } + + function testGetNonExistentProposerStatus() public { + _symbioticOptInRoutine(); + + bytes32 pubkeyHash = bytes32(uint256(1)); + + vm.expectRevert(IBoltValidatorsV1.ValidatorDoesNotExist.selector); + manager.getProposerStatus(pubkeyHash); + } +} diff --git a/bolt-contracts/test/BoltRegistry.t.sol b/bolt-contracts/test/BoltRegistry.t.sol deleted file mode 100644 index 9280a2c67..000000000 --- a/bolt-contracts/test/BoltRegistry.t.sol +++ /dev/null @@ -1,91 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.13; - -import {Test, console} from "forge-std/Test.sol"; -import {BoltRegistry} from "../src/contracts/BoltRegistry.sol"; -import {IBoltRegistry} from "../src/interfaces/IBoltRegistry.sol"; - -contract BoltRegistryTest is Test { - BoltRegistry public registry; - - uint64[] public validatorIndexes; - - address alice = address(0x1); - address bob = address(0x2); - - function setUp() public { - registry = new BoltRegistry(10 ether); - vm.deal(alice, 20 ether); - } - - function testRegistration() public { - vm.prank(alice); - - validatorIndexes.push(1); - validatorIndexes.push(2); - validatorIndexes.push(3); - - registry.register{value: 10 ether}(validatorIndexes, "rpc", ""); - - assertEq( - uint8(registry.getOperatorStatus(alice)), - uint8(IBoltRegistry.Status.ACTIVE) - ); - - assertEq(registry.isActiveOperator(alice), true); - - assertEq(registry.getOperatorForValidator(1).operator, alice); - assertEq(registry.getOperatorForValidator(1).metadata.rpc, "rpc"); - } - - // function testOptOut() public { - // assertEq(registry.isActiveBasedProposer(alice), false); - // vm.expectRevert(IBoltRegistry.BasedProposerDoesNotExist.selector); - // registry.getBasedProposerStatus(alice); - - // vm.prank(alice); - // registry.optIn(); - // assertEq(registry.isActiveBasedProposer(alice), true); - // assertEq( - // uint8(registry.getBasedProposerStatus(alice)), - // uint8(IBoltRegistry.BoltStatus.Active) - // ); - - // vm.prank(alice); - // registry.beginOptOut(); - - // assertEq(registry.isActiveBasedProposer(alice), true); - // assertEq( - // uint8(registry.getBasedProposerStatus(alice)), - // uint8(IBoltRegistry.BoltStatus.Active) - // ); - - // // check that confirmation can't be done immediately - // vm.expectRevert(IBoltRegistry.CooldownNotElapsed.selector); - // vm.prank(alice); - // registry.confirmOptOut(); - - // // wait 1 day - // vm.warp(block.timestamp + 1 days); - - // // check that opt out can be confirmed - // vm.prank(alice); - // vm.expectEmit(address(registry)); - // emit IBoltRegistry.BasedProposerStatusChanged( - // alice, - // IBoltRegistry.BoltStatus.Inactive - // ); - // registry.confirmOptOut(); - - // assertEq(registry.isActiveBasedProposer(alice), false); - // assertEq( - // uint8(registry.getBasedProposerStatus(alice)), - // uint8(IBoltRegistry.BoltStatus.Inactive) - // ); - - // // check that opt out can't be confirmed again - // vm.expectRevert(IBoltRegistry.InvalidStatusChange.selector); - // vm.prank(alice); - // registry.confirmOptOut(); - // } -} diff --git a/bolt-contracts/test/BoltValidators.t.sol b/bolt-contracts/test/BoltValidators.t.sol new file mode 100644 index 000000000..b8b169090 --- /dev/null +++ b/bolt-contracts/test/BoltValidators.t.sol @@ -0,0 +1,91 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.25; + +import {Test, console} from "forge-std/Test.sol"; + +import {BoltParametersV1} from "../src/contracts/BoltParametersV1.sol"; +import {BoltValidatorsV1} from "../src/contracts/BoltValidatorsV1.sol"; +import {IBoltValidatorsV1} from "../src/interfaces/IBoltValidatorsV1.sol"; +import {BLS12381} from "../src/lib/bls/BLS12381.sol"; +import {BoltConfig} from "../src/lib/Config.sol"; +import {Utils} from "./Utils.sol"; + +contract BoltValidatorsTest is Test { + using BLS12381 for BLS12381.G1Point; + + BoltParametersV1 public parameters; + BoltValidatorsV1 public validators; + + uint128 public constant PRECONF_MAX_GAS_LIMIT = 5_000_000; + + address admin = makeAddr("admin"); + address provider = makeAddr("provider"); + address operator = makeAddr("operator"); + address validator = makeAddr("validator"); + + function setUp() public { + BoltConfig.Parameters memory config = new Utils().readParameters(); + + parameters = new BoltParametersV1(); + parameters.initialize( + admin, + config.epochDuration, + config.slashingWindow, + config.maxChallengeDuration, + config.allowUnsafeRegistration, + config.challengeBond, + config.blockhashEvmLookback, + config.justificationDelay, + config.eth2GenesisTimestamp, + config.slotTime, + config.minimumOperatorStake + ); + + validators = new BoltValidatorsV1(); + validators.initialize(admin, address(parameters)); + } + + function testUnsafeRegistration() public { + // pubkeys aren't checked, any point will be fine + BLS12381.G1Point memory pubkey = BLS12381.generatorG1(); + + vm.prank(validator); + validators.registerValidatorUnsafe(pubkey, 1_000_000, operator); + + BoltValidatorsV1.Validator memory registered = validators.getValidatorByPubkey(pubkey); + assertEq(registered.exists, true); + assertEq(registered.maxCommittedGasLimit, 1_000_000); + assertEq(registered.authorizedOperator, operator); + assertEq(registered.controller, validator); + } + + function testUnsafeRegistrationFailsIfAlreadyRegistered() public { + BLS12381.G1Point memory pubkey = BLS12381.generatorG1(); + + vm.prank(validator); + validators.registerValidatorUnsafe(pubkey, PRECONF_MAX_GAS_LIMIT, operator); + + vm.prank(validator); + vm.expectRevert(IBoltValidatorsV1.ValidatorAlreadyExists.selector); + validators.registerValidatorUnsafe(pubkey, PRECONF_MAX_GAS_LIMIT, operator); + } + + function testUnsafeRegistrationWhenNotAllowed() public { + BLS12381.G1Point memory pubkey = BLS12381.generatorG1(); + + vm.prank(admin); + parameters.setAllowUnsafeRegistration(false); + + vm.prank(validator); + vm.expectRevert(IBoltValidatorsV1.UnsafeRegistrationNotAllowed.selector); + validators.registerValidatorUnsafe(pubkey, PRECONF_MAX_GAS_LIMIT, operator); + } + + function testUnsafeRegistrationInvalidOperator() public { + BLS12381.G1Point memory pubkey = BLS12381.generatorG1(); + + vm.prank(validator); + vm.expectRevert(IBoltValidatorsV1.InvalidAuthorizedOperator.selector); + validators.registerValidatorUnsafe(pubkey, PRECONF_MAX_GAS_LIMIT, address(0)); + } +} diff --git a/bolt-contracts/test/StringToUintArrayLib.t.sol b/bolt-contracts/test/StringToUintArrayLib.t.sol deleted file mode 100644 index aaae88073..000000000 --- a/bolt-contracts/test/StringToUintArrayLib.t.sol +++ /dev/null @@ -1,82 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.0; - -import "forge-std/Test.sol"; -import "../script/RegisterValidators.s.sol"; - -contract StringToUintArrayTest is Test { - - function setUp() public {} - - function testParseValidatorIndexes1() public pure { - uint256[] memory indexes = StringToUintArrayLib.fromStr("1,2,3,4"); - uint256[4] memory expected; - expected[0] = 1; - expected[1] = 2; - expected[2] = 3; - expected[3] = 4; - - assertEq(indexes.length, expected.length); - for (uint256 i = 0; i < indexes.length; i++) { - assertEq(indexes[i], expected[i]); - } - } - - function testParseValidatorIndexes2() public pure { - uint256[] memory indexes = StringToUintArrayLib.fromStr("1..4"); - uint256[4] memory expected; - expected[0] = 1; - expected[1] = 2; - expected[2] = 3; - expected[3] = 4; - - assertEq(indexes.length, expected.length); - for (uint256 i = 0; i < indexes.length; i++) { - assertEq(indexes[i], expected[i]); - } - } - - function testParseValidatorIndexes3() public pure { - uint256[] memory indexes = StringToUintArrayLib.fromStr("1..4,6..8"); - uint256[7] memory expected; - expected[0] = 1; - expected[1] = 2; - expected[2] = 3; - expected[3] = 4; - expected[4] = 6; - expected[5] = 7; - expected[6] = 8; - - assertEq(indexes.length, expected.length); - for (uint256 i = 0; i < indexes.length; i++) { - assertEq(indexes[i], expected[i]); - } - } - - function testParseValidatorIndexes4() public pure { - uint256[] memory indexes = StringToUintArrayLib.fromStr("1,2..4,6..8"); - uint256[7] memory expected; - expected[0] = 1; - expected[1] = 2; - expected[2] = 3; - expected[3] = 4; - expected[4] = 6; - expected[5] = 7; - expected[6] = 8; - - assertEq(indexes.length, expected.length); - for (uint256 i = 0; i < indexes.length; i++) { - assertEq(indexes[i], expected[i]); - } - } - - function testParse100Indexes() public pure { - string memory input = "1..100"; - - uint256[] memory indexes = StringToUintArrayLib.fromStr(input); - assertEq(indexes.length, 100); - for (uint256 i = 0; i < indexes.length; i++) { - assertEq(indexes[i], i + 1); - } - } -} \ No newline at end of file diff --git a/bolt-contracts/test/TransactionDecoder.t.sol b/bolt-contracts/test/TransactionDecoder.t.sol new file mode 100644 index 000000000..3f911f85b --- /dev/null +++ b/bolt-contracts/test/TransactionDecoder.t.sol @@ -0,0 +1,223 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.25; + +import {Test, console} from "forge-std/Test.sol"; + +import {TransactionDecoder} from "../src/lib/TransactionDecoder.sol"; +import {BytesUtils} from "../src/lib/BytesUtils.sol"; + +// We use a contract to expose internal library functions +contract DecoderImpl { + function decodeEnveloped( + bytes memory raw + ) public pure returns (TransactionDecoder.Transaction memory) { + return TransactionDecoder.decodeEnveloped(raw); + } + + function preimage( + TransactionDecoder.Transaction memory transaction + ) public pure returns (bytes32) { + return TransactionDecoder.preimage(transaction); + } + + function signature( + TransactionDecoder.Transaction memory transaction + ) public pure returns (bytes memory) { + return TransactionDecoder.signature(transaction); + } +} + +contract TransactionDecoderTest is Test { + using TransactionDecoder for TransactionDecoder.Transaction; + + DecoderImpl decoder; + + struct TestCase { + string name; + uint256 privateKey; + bytes unsignedLegacy; + bytes unsignedEip155; + bytes unsignedBerlin; + bytes unsignedLondon; + bytes unsignedCancun; + bytes signedLegacy; + bytes signedEip155; + bytes signedBerlin; + bytes signedLondon; + bytes signedCancun; + TransactionDecoder.Transaction transaction; + } + + function setUp() public { + vm.pauseGasMetering(); + decoder = new DecoderImpl(); + } + + function testDecodeAllTestCases() public { + uint256 i = 0; + while (true) { + string memory path = _getTestCasePath(i); + if (!vm.isFile(path)) break; + + // Cycle through all test cases and run them one by one + _decodeTestCase(i); + i++; + } + } + + function testDecodeGasUsage() public { + // Decode a single transaction to measure gas usage + bytes memory encoded = _readTestCase(0).signedLondon; + + vm.resumeGasMetering(); + decoder.decodeEnveloped(encoded); + vm.pauseGasMetering(); + } + + function _decodeTestCase( + uint256 id + ) internal view { + TestCase memory testCase = _readTestCase(id); + + // Type 0 pre eip-155 (with chainId = 0) + TransactionDecoder.Transaction memory decodedSignedLegacy = decoder.decodeEnveloped(testCase.signedLegacy); + _assertTransaction(TransactionDecoder.TxType.Legacy, decodedSignedLegacy, testCase.transaction, false); + assertEq(decodedSignedLegacy.unsigned(), testCase.unsignedLegacy); + assertEq(decodedSignedLegacy.recoverSender(), vm.addr(testCase.privateKey)); + + // Type 0 post eip-155 (with optional legacy chainId) + TransactionDecoder.Transaction memory decodedSignedEip155 = decoder.decodeEnveloped(testCase.signedEip155); + _assertTransaction(TransactionDecoder.TxType.Legacy, decodedSignedEip155, testCase.transaction, true); + assertEq(decodedSignedEip155.unsigned(), testCase.unsignedEip155); + assertEq(decodedSignedEip155.recoverSender(), vm.addr(testCase.privateKey)); + + // Type 1 with optional access list + TransactionDecoder.Transaction memory decodedSignedBerlin = decoder.decodeEnveloped(testCase.signedBerlin); + _assertTransaction(TransactionDecoder.TxType.Eip2930, decodedSignedBerlin, testCase.transaction, true); + assertEq(decodedSignedBerlin.unsigned(), testCase.unsignedBerlin); + assertEq(decodedSignedBerlin.recoverSender(), vm.addr(testCase.privateKey)); + + // Type 2 with fee market fields + TransactionDecoder.Transaction memory decodedSignedLondon = decoder.decodeEnveloped(testCase.signedLondon); + _assertTransaction(TransactionDecoder.TxType.Eip1559, decodedSignedLondon, testCase.transaction, true); + assertEq(decodedSignedLondon.unsigned(), testCase.unsignedLondon); + assertEq(decodedSignedLondon.recoverSender(), vm.addr(testCase.privateKey)); + + // Type 3 with blob fields + TransactionDecoder.Transaction memory decodedSignedCancun = decoder.decodeEnveloped(testCase.signedCancun); + _assertTransaction(TransactionDecoder.TxType.Eip4844, decodedSignedCancun, testCase.transaction, true); + assertEq(decodedSignedCancun.unsigned(), testCase.unsignedCancun); + assertEq(decodedSignedCancun.recoverSender(), vm.addr(testCase.privateKey)); + } + + // Helper to get the path of a test case file based on its index + function _getTestCasePath( + uint256 id + ) internal pure returns (string memory) { + // Location of the test cases on disk (relative to the project root) + // Example: ./test/testdata/transactions/random_10.json + return string.concat("./test/testdata/transactions/random_", vm.toString(id), ".json"); + } + + function _assertTransaction( + TransactionDecoder.TxType txType, + TransactionDecoder.Transaction memory decoded, + TransactionDecoder.Transaction memory expected, + bool isEip155 + ) internal pure { + assertEq(uint8(decoded.txType), uint8(txType)); + + if (!isEip155) { + // Pre-EIP-155 transactions have a chainId of 0 + assertEq(decoded.chainId, 0); + } else { + assertEq(decoded.chainId, expected.chainId); + } + + assertEq(decoded.data, expected.data); + assertEq(decoded.gasLimit, expected.gasLimit); + assertEq(decoded.nonce, expected.nonce); + assertEq(decoded.to, expected.to); + assertEq(decoded.value, expected.value); + + if (uint8(txType) < 2) { + assertEq(decoded.gasPrice, expected.gasPrice); + } + + if (uint8(txType) >= 1) { + // We keep access lists as opaque bytes for now, because we simply re-encode + // them to obtain the unsigned transaction. So we can't compare them directly. + // assertEq(decoded.accessList, expected.accessList); + } + + if (uint8(txType) >= 2) { + assertEq(decoded.maxFeePerGas, expected.maxFeePerGas); + assertEq(decoded.maxPriorityFeePerGas, expected.maxPriorityFeePerGas); + } + + if (uint8(txType) == 3) { + assertEq(decoded.maxFeePerBlobGas, expected.maxFeePerBlobGas); + assertEq(decoded.blobVersionedHashes.length, expected.blobVersionedHashes.length); + for (uint256 i = 0; i < decoded.blobVersionedHashes.length; i++) { + assertEq(decoded.blobVersionedHashes[i], expected.blobVersionedHashes[i]); + } + } + } + + function _readTestCase( + uint256 id + ) public view returns (TestCase memory) { + string memory file = vm.readFile(_getTestCasePath(id)); + + TransactionDecoder.Transaction memory transaction = TransactionDecoder.Transaction({ + chainId: uint64(_parseUintFromBytes(vm.parseJsonBytes(file, ".transaction.chainId"))), + data: vm.parseJsonBytes(file, ".transaction.data"), + gasLimit: _parseUintFromBytes(vm.parseJsonBytes(file, ".transaction.gasLimit")), + gasPrice: _parseUintFromBytes(vm.parseJsonBytes(file, ".transaction.gasPrice")), + maxFeePerGas: _parseUintFromBytes(vm.parseJsonBytes(file, ".transaction.maxFeePerGas")), + maxPriorityFeePerGas: _parseUintFromBytes(vm.parseJsonBytes(file, ".transaction.maxPriorityFeePerGas")), + nonce: vm.parseJsonUint(file, ".transaction.nonce"), + to: vm.parseJsonAddress(file, ".transaction.to"), + value: _parseUintFromBytes(vm.parseJsonBytes(file, ".transaction.value")), + maxFeePerBlobGas: _parseUintFromBytes(vm.parseJsonBytes(file, ".transaction.maxFeePerBlobGas")), + blobVersionedHashes: vm.parseJsonBytes32Array(file, ".transaction.blobVersionedHashes"), + // Note: These fields aren't present in the test cases so they can be skipped. + // These are tested indirectly by the signature and preimage checks. + txType: TransactionDecoder.TxType.Legacy, + accessList: new bytes[](0), + // Signature is checked by recovering the sender and comparing it to the pubkey + // derived from the private key in the test case. + sig: "", + // Note: these fields are just internal helpers for the decoder library. + legacyV: 0, + isChainIdSet: false + }); + + return TestCase({ + name: vm.parseJsonString(file, ".name"), + privateKey: uint256(vm.parseJsonBytes32(file, ".privateKey")), + unsignedLegacy: vm.parseJsonBytes(file, ".unsignedLegacy"), + unsignedEip155: vm.parseJsonBytes(file, ".unsignedEip155"), + unsignedBerlin: vm.parseJsonBytes(file, ".unsignedBerlin"), + unsignedLondon: vm.parseJsonBytes(file, ".unsignedLondon"), + unsignedCancun: vm.parseJsonBytes(file, ".unsignedCancun"), + signedLegacy: vm.parseJsonBytes(file, ".signedLegacy"), + signedEip155: vm.parseJsonBytes(file, ".signedEip155"), + signedBerlin: vm.parseJsonBytes(file, ".signedBerlin"), + signedLondon: vm.parseJsonBytes(file, ".signedLondon"), + signedCancun: vm.parseJsonBytes(file, ".signedCancun"), + transaction: transaction + }); + } + + // Helper to parse an uint from bytes padded to the left + function _parseUintFromBytes( + bytes memory data + ) internal pure returns (uint256) { + return uint256(BytesUtils.toBytes32PadLeft(data)); + } + + function _parseOpaqueAccessList( + bytes memory data + ) internal pure returns (bytes[] memory) {} +} diff --git a/bolt-contracts/test/Utils.sol b/bolt-contracts/test/Utils.sol new file mode 100644 index 000000000..731d1de18 --- /dev/null +++ b/bolt-contracts/test/Utils.sol @@ -0,0 +1,38 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.25; + +import {Test, console} from "forge-std/Test.sol"; + +import {BoltConfig} from "../src/lib/Config.sol"; + +contract Utils is Test { + function readParameters() public view returns (BoltConfig.Parameters memory) { + string memory root = vm.projectRoot(); + string memory path = string.concat(root, "/config/test/parameters.json"); + string memory json = vm.readFile(path); + + uint48 epochDuration = uint48(vm.parseJsonUint(json, ".epochDuration")); + uint48 slashingWindow = uint48(vm.parseJsonUint(json, ".slashingWindow")); + uint48 maxChallengeDuration = uint48(vm.parseJsonUint(json, ".maxChallengeDuration")); + bool allowUnsafeRegistration = vm.parseJsonBool(json, ".allowUnsafeRegistration"); + uint256 challengeBond = vm.parseJsonUint(json, ".challengeBond"); + uint256 blockhashEvmLookback = vm.parseJsonUint(json, ".blockhashEvmLookback"); + uint256 justificationDelay = vm.parseJsonUint(json, ".justificationDelay"); + uint256 eth2GenesisTimestamp = vm.parseJsonUint(json, ".eth2GenesisTimestamp"); + uint256 slotTime = vm.parseJsonUint(json, ".slotTime"); + uint256 minimumOperatorStake = vm.parseJsonUint(json, ".minimumOperatorStake"); + + return BoltConfig.Parameters({ + epochDuration: epochDuration, + slashingWindow: slashingWindow, + maxChallengeDuration: maxChallengeDuration, + challengeBond: challengeBond, + blockhashEvmLookback: blockhashEvmLookback, + justificationDelay: justificationDelay, + eth2GenesisTimestamp: eth2GenesisTimestamp, + slotTime: slotTime, + allowUnsafeRegistration: allowUnsafeRegistration, + minimumOperatorStake: minimumOperatorStake + }); + } +} diff --git a/bolt-contracts/test/fixtures/EigenLayerDeployer.f.sol b/bolt-contracts/test/fixtures/EigenLayerDeployer.f.sol new file mode 100644 index 000000000..61e6133e9 --- /dev/null +++ b/bolt-contracts/test/fixtures/EigenLayerDeployer.f.sol @@ -0,0 +1,346 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.12; + +import "@eigenlayer/lib/openzeppelin-contracts/contracts/token/ERC20/presets/ERC20PresetFixedSupply.sol"; +import "@eigenlayer/lib/openzeppelin-contracts/contracts/proxy/transparent/ProxyAdmin.sol"; +import "@eigenlayer/lib/openzeppelin-contracts/contracts/proxy/transparent/TransparentUpgradeableProxy.sol"; +import "@eigenlayer/lib/openzeppelin-contracts/contracts/proxy/beacon/IBeacon.sol"; +import "@eigenlayer/lib/openzeppelin-contracts/contracts/proxy/beacon/UpgradeableBeacon.sol"; + +import "@eigenlayer/src/contracts/interfaces/IDelegationManager.sol"; +import "@eigenlayer/src/contracts/core/DelegationManager.sol"; +import "@eigenlayer/src/contracts/core/AVSDirectory.sol"; + +import "@eigenlayer/src/contracts/interfaces/IETHPOSDeposit.sol"; + +import "@eigenlayer/src/contracts/core/StrategyManager.sol"; +import "@eigenlayer/src/contracts/strategies/StrategyBase.sol"; +import "@eigenlayer/src/contracts/core/Slasher.sol"; + +import "@eigenlayer/src/contracts/pods/EigenPod.sol"; +import "@eigenlayer/src/contracts/pods/EigenPodManager.sol"; + +import "@eigenlayer/src/contracts/permissions/PauserRegistry.sol"; + +import "@eigenlayer/src/test/mocks/LiquidStakingToken.sol"; +import "@eigenlayer/src/test/mocks/EmptyContract.sol"; +import "@eigenlayer/src/test/mocks/ETHDepositMock.sol"; + +import "forge-std/Test.sol"; +import "forge-std/Script.sol"; +import "forge-std/StdJson.sol"; + +address constant HEVM_ADDRESS = address(bytes20(uint160(uint256(keccak256("hevm cheat code"))))); + +contract Operators is Test { + string internal operatorConfigJson; + + constructor() { + operatorConfigJson = vm.readFile("./test/test_data_eigenlayer/operators.json"); + } + + function operatorPrefix( + uint256 index + ) public pure returns (string memory) { + return string.concat(".operators[", string.concat(vm.toString(index), "].")); + } + + function getNumOperators() public view returns (uint256) { + return stdJson.readUint(operatorConfigJson, ".numOperators"); + } + + function getOperatorAddress( + uint256 index + ) public view returns (address) { + return stdJson.readAddress(operatorConfigJson, string.concat(operatorPrefix(index), "Address")); + } + + function getOperatorSecretKey( + uint256 index + ) public view returns (uint256) { + return readUint(operatorConfigJson, index, "SecretKey"); + } + + function readUint(string memory json, uint256 index, string memory key) public pure returns (uint256) { + return stringToUint(stdJson.readString(json, string.concat(operatorPrefix(index), key))); + } + + function stringToUint( + string memory s + ) public pure returns (uint256) { + bytes memory b = bytes(s); + uint256 result = 0; + for (uint256 i = 0; i < b.length; i++) { + if (uint256(uint8(b[i])) >= 48 && uint256(uint8(b[i])) <= 57) { + result = result * 10 + (uint256(uint8(b[i])) - 48); + } + } + return result; + } + + function setOperatorJsonFilePath( + string memory filepath + ) public { + operatorConfigJson = vm.readFile(filepath); + } +} + +contract EigenLayerDeployer is Operators { + Vm cheats = Vm(HEVM_ADDRESS); + + // EigenLayer contracts + ProxyAdmin public eigenLayerProxyAdmin; + PauserRegistry public eigenLayerPauserReg; + + Slasher public slasher; + DelegationManager public delegationManager; + StrategyManager public strategyManager; + EigenPodManager public eigenPodManager; + AVSDirectory public avsDirectory; + IEigenPod public pod; + IETHPOSDeposit public ethPOSDeposit; + IBeacon public eigenPodBeacon; + + // testing/mock contracts + IERC20 public eigenToken; + IERC20 public weth; + StrategyBase public wethStrat; + StrategyBase public eigenStrat; + StrategyBase public baseStrategyImplementation; + EmptyContract public emptyContract; + + mapping(uint256 => IStrategy) public strategies; + + //from testing seed phrase + bytes32 priv_key_0 = 0x1234567812345678123456781234567812345678123456781234567812345678; + bytes32 priv_key_1 = 0x1234567812345678123456781234567812345698123456781234567812348976; + + //strategy indexes for undelegation (see commitUndelegation function) + uint256[] public strategyIndexes; + address sample_registrant = cheats.addr(436_364_636); + + address[] public slashingContracts; + + uint256 wethInitialSupply = 10e50; + address staker; + uint256 public constant eigenTotalSupply = 1000e18; + uint256 nonce = 69; + uint256 public gasLimit = 750_000; + IStrategy[] public initializeStrategiesToSetDelayBlocks; + uint256[] public initializeWithdrawalDelayBlocks; + uint256 minWithdrawalDelayBlocks = 0; + uint32 PARTIAL_WITHDRAWAL_FRAUD_PROOF_PERIOD_BLOCKS = 7 days / 12 seconds; + uint256 REQUIRED_BALANCE_WEI = 32 ether; + uint64 MAX_PARTIAL_WTIHDRAWAL_AMOUNT_GWEI = 1 ether / 1e9; + uint64 GOERLI_GENESIS_TIME = 1_616_508_000; + + address pauser; + address unpauser; + address theMultiSig = address(420); + address operator = address(0x4206904396bF2f8b173350ADdEc5007A52664293); //sk: e88d9d864d5d731226020c5d2f02b62a4ce2a4534a39c225d32d3db795f83319 + address acct_0 = cheats.addr(uint256(priv_key_0)); + address acct_1 = cheats.addr(uint256(priv_key_1)); + address _challenger = address(0x6966904396bF2f8b173350bCcec5007A52669873); + address public eigenLayerReputedMultisig = address(this); + + address eigenLayerProxyAdminAddress; + address eigenLayerPauserRegAddress; + address slasherAddress; + address delegationAddress; + address strategyManagerAddress; + address eigenPodManagerAddress; + address podAddress; + address eigenPodBeaconAddress; + address emptyContractAddress; + address operationsMultisig; + address executorMultisig; + + // addresses excluded from fuzzing due to abnormal behavior. TODO: @Sidu28 define this better and give it a clearer name + mapping(address => bool) fuzzedAddressMapping; + + //ensures that a passed in address is not set to true in the fuzzedAddressMapping + modifier fuzzedAddress( + address addr + ) virtual { + cheats.assume(fuzzedAddressMapping[addr] == false); + _; + } + + modifier cannotReinit() { + cheats.expectRevert(bytes("Initializable: contract is already initialized")); + _; + } + + constructor( + address _staker + ) { + staker = _staker; + } + + //performs basic deployment before each test + function setUp() public virtual { + try vm.envUint("CHAIN_ID") returns (uint256 chainId) { + if (chainId == 31_337) { + _deployEigenLayerContractsLocal(); + } + // If CHAIN_ID ENV is not set, assume local deployment on 31337 + } catch { + _deployEigenLayerContractsLocal(); + } + + fuzzedAddressMapping[address(0)] = true; + fuzzedAddressMapping[address(eigenLayerProxyAdmin)] = true; + fuzzedAddressMapping[address(strategyManager)] = true; + fuzzedAddressMapping[address(eigenPodManager)] = true; + fuzzedAddressMapping[address(delegationManager)] = true; + fuzzedAddressMapping[address(slasher)] = true; + } + + function _deployEigenLayerContractsLocal() internal { + pauser = address(69); + unpauser = address(489); + // deploy proxy admin for ability to upgrade proxy contracts + eigenLayerProxyAdmin = new ProxyAdmin(); + + //deploy pauser registry + address[] memory pausers = new address[](1); + pausers[0] = pauser; + eigenLayerPauserReg = new PauserRegistry(pausers, unpauser); + + /** + * First, deploy upgradeable proxy contracts that **will point** to the implementations. Since the implementation contracts are + * not yet deployed, we give these proxies an empty contract as the initial implementation, to act as if they have no code. + */ + emptyContract = new EmptyContract(); + delegationManager = DelegationManager( + address(new TransparentUpgradeableProxy(address(emptyContract), address(eigenLayerProxyAdmin), "")) + ); + strategyManager = StrategyManager( + address(new TransparentUpgradeableProxy(address(emptyContract), address(eigenLayerProxyAdmin), "")) + ); + slasher = + Slasher(address(new TransparentUpgradeableProxy(address(emptyContract), address(eigenLayerProxyAdmin), ""))); + eigenPodManager = EigenPodManager( + address(new TransparentUpgradeableProxy(address(emptyContract), address(eigenLayerProxyAdmin), "")) + ); + avsDirectory = AVSDirectory( + address(new TransparentUpgradeableProxy(address(emptyContract), address(eigenLayerProxyAdmin), "")) + ); + ethPOSDeposit = new ETHPOSDepositMock(); + pod = new EigenPod(ethPOSDeposit, eigenPodManager, GOERLI_GENESIS_TIME); + + eigenPodBeacon = new UpgradeableBeacon(address(pod)); + + // Second, deploy the *implementation* contracts, using the *proxy contracts* as inputs + DelegationManager delegationImplementation = new DelegationManager(strategyManager, slasher, eigenPodManager); + StrategyManager strategyManagerImplementation = new StrategyManager(delegationManager, eigenPodManager, slasher); + Slasher slasherImplementation = new Slasher(strategyManager, delegationManager); + EigenPodManager eigenPodManagerImplementation = + new EigenPodManager(ethPOSDeposit, eigenPodBeacon, strategyManager, slasher, delegationManager); + AVSDirectory avsDirectoryImplementation = new AVSDirectory(delegationManager); + + // Third, upgrade the proxy contracts to use the correct implementation contracts and initialize them. + eigenLayerProxyAdmin.upgradeAndCall( + TransparentUpgradeableProxy(payable(address(delegationManager))), + address(delegationImplementation), + abi.encodeWithSelector( + DelegationManager.initialize.selector, + eigenLayerReputedMultisig, + eigenLayerPauserReg, + 0, /*initialPausedStatus*/ + minWithdrawalDelayBlocks, + initializeStrategiesToSetDelayBlocks, + initializeWithdrawalDelayBlocks + ) + ); + eigenLayerProxyAdmin.upgradeAndCall( + TransparentUpgradeableProxy(payable(address(strategyManager))), + address(strategyManagerImplementation), + abi.encodeWithSelector( + StrategyManager.initialize.selector, + eigenLayerReputedMultisig, + eigenLayerReputedMultisig, + eigenLayerPauserReg, + 0 /*initialPausedStatus*/ + ) + ); + eigenLayerProxyAdmin.upgradeAndCall( + TransparentUpgradeableProxy(payable(address(slasher))), + address(slasherImplementation), + abi.encodeWithSelector( + Slasher.initialize.selector, eigenLayerReputedMultisig, eigenLayerPauserReg, 0 /*initialPausedStatus*/ + ) + ); + eigenLayerProxyAdmin.upgradeAndCall( + TransparentUpgradeableProxy(payable(address(eigenPodManager))), + address(eigenPodManagerImplementation), + abi.encodeWithSelector( + EigenPodManager.initialize.selector, + eigenLayerReputedMultisig, + eigenLayerPauserReg, + 0 /*initialPausedStatus*/ + ) + ); + eigenLayerProxyAdmin.upgradeAndCall( + TransparentUpgradeableProxy(payable(address(avsDirectory))), + address(avsDirectoryImplementation), + abi.encodeWithSelector( + EigenPodManager.initialize.selector, + eigenLayerReputedMultisig, + eigenLayerPauserReg, + 0 /*initialPausedStatus*/ + ) + ); + + //simple ERC20 (**NOT** WETH-like!), used in a test strategy + weth = new ERC20PresetFixedSupply("weth", "WETH", wethInitialSupply, staker); + + // deploy StrategyBase contract implementation, then create upgradeable proxy that points to implementation and initialize it + baseStrategyImplementation = new StrategyBase(strategyManager); + wethStrat = StrategyBase( + address( + new TransparentUpgradeableProxy( + address(baseStrategyImplementation), + address(eigenLayerProxyAdmin), + abi.encodeWithSelector(StrategyBase.initialize.selector, weth, eigenLayerPauserReg) + ) + ) + ); + + eigenToken = new ERC20PresetFixedSupply("eigen", "EIGEN", wethInitialSupply, staker); + + // deploy upgradeable proxy that points to StrategyBase implementation and initialize it + eigenStrat = StrategyBase( + address( + new TransparentUpgradeableProxy( + address(baseStrategyImplementation), + address(eigenLayerProxyAdmin), + abi.encodeWithSelector(StrategyBase.initialize.selector, eigenToken, eigenLayerPauserReg) + ) + ) + ); + + // Whitelist strategies for deposit + IStrategy[] memory strategiesToWhitelist = new IStrategy[](2); + strategiesToWhitelist[0] = wethStrat; + strategiesToWhitelist[1] = eigenStrat; + + bool[] memory thirdPartyTransfersForbidden = new bool[](2); + + strategyManager.addStrategiesToDepositWhitelist(strategiesToWhitelist, thirdPartyTransfersForbidden); + } + + function _setAddresses( + string memory config + ) internal { + eigenLayerProxyAdminAddress = stdJson.readAddress(config, ".addresses.eigenLayerProxyAdmin"); + eigenLayerPauserRegAddress = stdJson.readAddress(config, ".addresses.eigenLayerPauserReg"); + delegationAddress = stdJson.readAddress(config, ".addresses.delegation"); + strategyManagerAddress = stdJson.readAddress(config, ".addresses.strategyManager"); + slasherAddress = stdJson.readAddress(config, ".addresses.slasher"); + eigenPodManagerAddress = stdJson.readAddress(config, ".addresses.eigenPodManager"); + emptyContractAddress = stdJson.readAddress(config, ".addresses.emptyContract"); + operationsMultisig = stdJson.readAddress(config, ".parameters.operationsMultisig"); + executorMultisig = stdJson.readAddress(config, ".parameters.executorMultisig"); + } +} diff --git a/bolt-contracts/test/fixtures/SymbioticSetup.f.sol b/bolt-contracts/test/fixtures/SymbioticSetup.f.sol new file mode 100644 index 000000000..e23fdafb7 --- /dev/null +++ b/bolt-contracts/test/fixtures/SymbioticSetup.f.sol @@ -0,0 +1,135 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.25; + +import {Test} from "forge-std/Test.sol"; + +import {VaultFactory} from "@symbiotic/contracts/VaultFactory.sol"; +import {DelegatorFactory} from "@symbiotic/contracts/DelegatorFactory.sol"; +import {SlasherFactory} from "@symbiotic/contracts/SlasherFactory.sol"; +import {NetworkRegistry} from "@symbiotic/contracts/NetworkRegistry.sol"; +import {OperatorRegistry} from "@symbiotic/contracts/OperatorRegistry.sol"; +import {MetadataService} from "@symbiotic/contracts/service/MetadataService.sol"; +import {NetworkMiddlewareService} from "@symbiotic/contracts/service/NetworkMiddlewareService.sol"; +import {OptInService} from "@symbiotic/contracts/service/OptInService.sol"; + +import {Vault} from "@symbiotic/contracts/vault/Vault.sol"; +import {NetworkRestakeDelegator} from "@symbiotic/contracts/delegator/NetworkRestakeDelegator.sol"; +import {FullRestakeDelegator} from "@symbiotic/contracts/delegator/FullRestakeDelegator.sol"; +import {Slasher} from "@symbiotic/contracts/slasher/Slasher.sol"; +import {VetoSlasher} from "@symbiotic/contracts/slasher/VetoSlasher.sol"; +import {VaultConfigurator} from "@symbiotic/contracts/VaultConfigurator.sol"; + +import {SimpleCollateral} from "@symbiotic/../test/mocks/SimpleCollateral.sol"; +import {Token} from "../mocks/Token.sol"; + +contract SymbioticSetupFixture is Test { + function setUp( + address deployer, + address owner + ) + public + returns ( + VaultFactory vaultFactory, + DelegatorFactory delegatorFactory, + SlasherFactory slasherFactory, + NetworkRegistry networkRegistry, + OperatorRegistry operatorRegistry, + MetadataService operatorMetadataService, + MetadataService networkMetadataService, + NetworkMiddlewareService networkMiddlewareService, + OptInService operatorVaultOptInService, + OptInService operatorNetworkOptInService, + VaultConfigurator vaultConfigurator, + SimpleCollateral collateral + ) + { + vm.startPrank(deployer); + + VaultFactory vaultFactory_ = new VaultFactory(deployer); + DelegatorFactory delegatorFactory_ = new DelegatorFactory(deployer); + SlasherFactory slasherFactory_ = new SlasherFactory(deployer); + NetworkRegistry networkRegistry_ = new NetworkRegistry(); + OperatorRegistry operatorRegistry_ = new OperatorRegistry(); + MetadataService operatorMetadataService_ = new MetadataService(address(operatorRegistry_)); + MetadataService networkMetadataService_ = new MetadataService(address(networkRegistry_)); + NetworkMiddlewareService networkMiddlewareService_ = new NetworkMiddlewareService(address(networkRegistry_)); + OptInService operatorVaultOptInService_ = new OptInService(address(operatorRegistry_), address(vaultFactory_)); + OptInService operatorNetworkOptInService_ = + new OptInService(address(operatorRegistry_), address(networkRegistry_)); + + Vault vault_ = new Vault(address(delegatorFactory_), address(slasherFactory_), address(vaultFactory_)); + vaultFactory_.whitelist(address(vault_)); + + address networkRestakeDelegator_ = address( + new NetworkRestakeDelegator( + address(networkRegistry_), + address(vaultFactory_), + address(operatorVaultOptInService_), + address(operatorNetworkOptInService_), + address(delegatorFactory_), + delegatorFactory_.totalTypes() + ) + ); + delegatorFactory_.whitelist(networkRestakeDelegator_); + + address fullRestakeDelegator_ = address( + new FullRestakeDelegator( + address(networkRegistry_), + address(vaultFactory_), + address(operatorVaultOptInService_), + address(operatorNetworkOptInService_), + address(delegatorFactory_), + delegatorFactory_.totalTypes() + ) + ); + delegatorFactory_.whitelist(fullRestakeDelegator_); + + address slasher_ = address( + new Slasher( + address(vaultFactory_), + address(networkMiddlewareService_), + address(slasherFactory_), + slasherFactory_.totalTypes() + ) + ); + slasherFactory_.whitelist(slasher_); + + address vetoSlasher_ = address( + new VetoSlasher( + address(vaultFactory_), + address(networkMiddlewareService_), + address(networkRegistry_), + address(slasherFactory_), + slasherFactory_.totalTypes() + ) + ); + slasherFactory_.whitelist(vetoSlasher_); + + VaultConfigurator vaultConfigurator_ = + new VaultConfigurator(address(vaultFactory_), address(delegatorFactory_), address(slasherFactory_)); + + vaultFactory_.transferOwnership(owner); + delegatorFactory_.transferOwnership(owner); + slasherFactory_.transferOwnership(owner); + + Token token_ = new Token("Token"); + SimpleCollateral collateral_ = new SimpleCollateral(address(token_)); + + vm.stopPrank(); + + return ( + vaultFactory_, + delegatorFactory_, + slasherFactory_, + networkRegistry_, + operatorRegistry_, + operatorMetadataService_, + networkMetadataService_, + networkMiddlewareService_, + operatorVaultOptInService_, + operatorNetworkOptInService_, + vaultConfigurator_, + collateral_ + ); + } +} diff --git a/bolt-contracts/test/mocks/Token.sol b/bolt-contracts/test/mocks/Token.sol new file mode 100644 index 000000000..f987de68c --- /dev/null +++ b/bolt-contracts/test/mocks/Token.sol @@ -0,0 +1,12 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.25; + +import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol"; + +contract Token is ERC20 { + constructor( + string memory name_ + ) ERC20(name_, "") { + _mint(msg.sender, 1_000_000 * 10 ** decimals()); + } +} diff --git a/bolt-contracts/test/test_data_eigenlayer/operators.json b/bolt-contracts/test/test_data_eigenlayer/operators.json new file mode 100644 index 000000000..bcf24d006 --- /dev/null +++ b/bolt-contracts/test/test_data_eigenlayer/operators.json @@ -0,0 +1,350 @@ +{ + "numOperators": 15, + "operators": [ + { + "Address": "0x364ea4241059a1880a289ccf6f3e730371e399c2", + "SecretKey": "26538123046512735896321680912735809321651", + "SField": "11819029207127053157019202363815717308230004305264870932201236865394129798528", + "RPoint": { + "X": "4151816577563894587001429602861434330710880584628236143409913584674951374486", + "Y": "14755168631248666267406561486877346692276498564690931377857101897349212004983" + }, + "PubkeyG1": { + "X": "18260007818883133054078754218619977578772505796600400998181738095793040006897", + "Y": "3432351341799135763167709827653955074218841517684851694584291831827675065899" + }, + "PubkeyG2": { + "X": { + "A0": "7873360006442301391509331169041141152067986239770361289399829888515098511089", + "A1": "4819287168576083437987399094513915926762558557241451705488044107651777333471" + }, + "Y": { + "A0": "20134059591429338312012269666175785157377156721420899775744488735642013266015", + "A1": "5180163866630806529018451776671042574814236436776522234046604410442923698614" + } + } + }, + { + "Address": "0x435a2d0799fd5b4f6576d43ddedbfb21eeffd2ed", + "SecretKey": "26538123046512735896321680912735809321652", + "SField": "6841827072565597045205937795412322299165267769171326199068981979305129605253", + "RPoint": { + "X": "12076439550529905259390228862926613756534481189623148993240145246002590976299", + "Y": "974155300103671376671957621510050067124810818694470470430352429136065997968" + }, + "PubkeyG1": { + "X": "20885635058167472198503803626695880018252841373181906566024991517994827983735", + "Y": "11837569400448747146619236953090341530939723755586681794040848163257307322015" + }, + "PubkeyG2": { + "X": { + "A0": "3533647070879242563419359582151690893240073815886344269869120254242419508633", + "A1": "9536422671122585613677052246081291259644057320757694709671580313420208694081" + }, + "Y": { + "A0": "12166486073145345231362662138062912830359628001285426518990520911876160470477", + "A1": "14962020261456080798629037722818062638639517674733500632912397408761625050802" + } + } + }, + { + "Address": "0x523e0a2d296e6bc969296a0541289077d2c43246", + "SecretKey": "26538123046512735896321680912735809321653", + "SField": "6251323578278497984001410170708026455838289583399486055694150917867937589622", + "RPoint": { + "X": "17659335174238407859449322441269022710538670095928217612186082625191038704952", + "Y": "4030658631342963783788534863066598739941334059525264488801360888493204491332" + }, + "PubkeyG1": { + "X": "2470846831436625524441718675125184646728308675024947867896488186792002263384", + "Y": "19398539713444487906856323737585450565673486119324566923739712072838048985181" + }, + "PubkeyG2": { + "X": { + "A0": "18996698705977687383601958641379064195173448823300612508806962257981430138470", + "A1": "11390594476086232146697028608851603084418668227205561974107759331535935107531" + }, + "Y": { + "A0": "10549563568039161023384174670373065438958654243218116252293848148484855972510", + "A1": "8392609726364984764069457200587817879819857139319066962985233014328835122137" + } + } + }, + { + "Address": "0x618a412f2446921d3d942c8bf073000b97728f0f", + "SecretKey": "26538123046512735896321680912735809321654", + "SField": "12061860258274605985652162007024956030804911220114181564759813442108765736942", + "RPoint": { + "X": "7145152916403894254971893757626878750640430645346094737434568772482788991869", + "Y": "5571984232385193687234211544970704499791824743626734155736946702800034693686" + }, + "PubkeyG1": { + "X": "12541368988415564232975583436986866293473603658145394466756402945376729450760", + "Y": "8511301244073806918313939726706790376744658017957805706622009852760184010910" + }, + "PubkeyG2": { + "X": { + "A0": "12496796444461928801423998788871427472219126045995885876471223766691196263411", + "A1": "20900414356950550650632823116879453189806004631615934725359633772870431548864" + }, + "Y": { + "A0": "1850282599163866918562002476972618618179924465313771842342246796868912815924", + "A1": "16267494054236543151546820684638619209010244733864385924658421853804576220788" + } + } + }, + { + "Address": "0x70130e87da895ef840a9f8c41c6ebf0e5b48e824", + "SecretKey": "26538123046512735896321680912735809321655", + "SField": "18246936953360371302513647065102080917080836724122762820676034131128284534276", + "RPoint": { + "X": "14485348008548227125800493276491803315994326328485845085024767746874209352205", + "Y": "8354185564983387451413630826006944059627584517454500268893837380556264839426" + }, + "PubkeyG1": { + "X": "12250988429106632987382001675218698439738318866960678150521924430133077466824", + "Y": "19721708441253375174926671177088389244933199376153285627796664290007532195052" + }, + "PubkeyG2": { + "X": { + "A0": "15382764368631216635417307340007437259725116431665265535534236291634253279005", + "A1": "551113638605800186370795409845956026924016364384585878450724593710757152783" + }, + "Y": { + "A0": "15798745520528389465130300542056950632504309474787848602575280595042835040946", + "A1": "15121681895121404713757090493706231060528428365493560188175592305488154660061" + } + } + }, + { + "Address": "0x7290f60d6666d7be7a41de178216502342562996", + "SecretKey": "26538123046512735896321680912735809321656", + "SField": "18341272017139583935816150534955335402336824558387991233830658246285519689014", + "RPoint": { + "X": "18174419042266772640587019436747343741444240929289718199821728520031148396624", + "Y": "12944489167977623909526722715453960134323253038442885147323841789720380485266" + }, + "PubkeyG1": { + "X": "21781696011906991536752467363456044926552139424863730238878701223169542126809", + "Y": "12365056838489612884478362448314510672391017658276612399537174676963477032436" + }, + "PubkeyG2": { + "X": { + "A0": "7602713822854229266858239462850154602893490834036446886716499673780465081437", + "A1": "6755904671274545514612841103024355730484086518739594420309025281657878144069" + }, + "Y": { + "A0": "19169973646980957540631709981893659820049084634788201359252419558688034251538", + "A1": "3617096885662895416635037761438216245027220053803903379077414864234110502059" + } + } + }, + { + "Address": "0x8d8e96b3bb658c5830aef90760f79e6a2a3bba41", + "SecretKey": "26538123046512735896321680912735809321657", + "SField": "12685816762097293544054119736761928362052971499853660676176419762479313483888", + "RPoint": { + "X": "1845886565323083117137480765392173031492903457678364169791939158881472188429", + "Y": "13621394807982841470261073338436747381963203491878788814653645637014444568601" + }, + "PubkeyG1": { + "X": "3212013732044844428966711160610869391929177932647992063562413834101261373977", + "Y": "15448314027915072279969465354772467136408027411697025021594849451692520530947" + }, + "PubkeyG2": { + "X": { + "A0": "19758878765410158466580323802894117259849215806855698140995410808900358199475", + "A1": "6427725156456990179277796366756163887223625521472017190816516318004039813324" + }, + "Y": { + "A0": "20996191524580276458284414610581896434480007527247384059381015445157582556532", + "A1": "14948621227766242224388430084744009250457802204821770762677931043984889219820" + } + } + }, + { + "Address": "0xa71e43be339f9791235641f457c1ba2da86b9eb3", + "SecretKey": "26538123046512735896321680912735809321658", + "SField": "6042114504025969519448864857463886717993192687698758790871627541311475539318", + "RPoint": { + "X": "2992138105299859744840964524791160337229571931324293630805658245249757407389", + "Y": "15694736019044294046047702836262370586371888280839741425891501314654607706201" + }, + "PubkeyG1": { + "X": "3667070400945503213289817934609709506016134089772708044008927730500827726299", + "Y": "201965492781810222622603023497133228304861253112953784295749598350720860919" + }, + "PubkeyG2": { + "X": { + "A0": "18480475432118436773114589869805528330114616937670136874673576408735271448220", + "A1": "13748787029936230407330641361791178403853002576716058275674223802004495813919" + }, + "Y": { + "A0": "9393505688889091760306178659573951040934743290746925629508348356081460722985", + "A1": "8911229656394169335558484911670452902836715586054327693497766770926601776864" + } + } + }, + { + "Address": "0xca9827a614f6c910c33f7668823f9d079515a6b2", + "SecretKey": "26538123046512735896321680912735809321659", + "SField": "4089036516179531739857184481801462737502679331172598056405360493929192933822", + "RPoint": { + "X": "11344456565890051191982811922569789285426097564838757420328232921661131972594", + "Y": "5214697287561608797059987511134212052053543960787141328755773567687921715591" + }, + "PubkeyG1": { + "X": "10603532593425799461116428547718401984650331452231439217583617357908466102307", + "Y": "9038166977605721429955182587619307974711670991162762169137768185433368026502" + }, + "PubkeyG2": { + "X": { + "A0": "2665943891698121544094051819956376909984132954889653928721123177413544545101", + "A1": "5067301718610021746809469955529953474259097833119436258748751618113087224295" + }, + "Y": { + "A0": "14618865954382816111109898027326987227550002588645252687730089534591097604721", + "A1": "17668847581520475585017930293964643097348730464667967915604118496715335354459" + } + } + }, + { + "Address": "0xdda9a89ddf845c9ee5d2810e0833b4a71d66179d", + "SecretKey": "265381230465127358963216809127358093216510", + "SField": "3494626234115151042064050025070425206800280651375775826757871600988046500493", + "RPoint": { + "X": "2823775573240572793382269280921054559755380883233834426161454792596668870337", + "Y": "2116482807847046775754020075448309148535809630987701014560424524130609194680" + }, + "PubkeyG1": { + "X": "9580940219508129828900474539321198495544615416706116943528153675304087866935", + "Y": "19817948430483568579408524674460630359968264374260052173849319927990972424409" + }, + "PubkeyG2": { + "X": { + "A0": "14146902041950849992065142134350889699589625292784530382058790130731064171060", + "A1": "9643173139567873327873151464253356980578492901466026171946322545605998333728" + }, + "Y": { + "A0": "14418997116441129782787372775003831815699951211438552978754436263436048789176", + "A1": "7555011997614448462339727638657930440787456831044184868102463346992814218544" + } + } + }, + { + "Address": "0xdde7a5dbb45d0f48d74d40772c2dda8acd89a304", + "SecretKey": "265381230465127358963216809127358093216511", + "SField": "19331236222084214974477469493395354532285940046992732403697375380156732141073", + "RPoint": { + "X": "18634736052925172105067492946541508040225080612748659668517401756117768987613", + "Y": "5852722109239017950047732236711408776617783536462429224588732414658414009409" + }, + "PubkeyG1": { + "X": "3369815046108753886283376619953242018939084016540366266511463175341760902943", + "Y": "17472522035733702939562892574753748100276735367509783930154400656979558647072" + }, + "PubkeyG2": { + "X": { + "A0": "12828974775248479668694052242644004695064721299497890085252948770085462880539", + "A1": "7272600703570139069540236030139742611695513123908572460354778844049179248665" + }, + "Y": { + "A0": "6187997634621416381645973736825134854588734099638054268199535544372796244575", + "A1": "3168611351805563509285790274061051556477979031759511765104889273283967743401" + } + } + }, + { + "Address": "0xe3fe83a83f3c7dc625382f062c159809551c580b", + "SecretKey": "265381230465127358963216809127358093216512", + "SField": "5508482612796623641475745691306343682701589066262691055533968657240534344204", + "RPoint": { + "X": "21685944819341458246191809126376074155122173268146956524878025759785902438148", + "Y": "14479737406316720196422920923227363552037206020376032776793814881581188057870" + }, + "PubkeyG1": { + "X": "8070743655895930899886894874393947088184525509008178391331533769906913292208", + "Y": "6166041079211541340267466423045787629866667719971895616570343665150421540281" + }, + "PubkeyG2": { + "X": { + "A0": "2678990229866359665311344452542118509908675024979420599004546926969138331011", + "A1": "2049191090024360768595356041854174484396277833846595743282145840232552333594" + }, + "Y": { + "A0": "3201866794780851807317614975829248389859676087140523014123159053900573945553", + "A1": "6264970183638030024425633356945995283617627028846078571269515762464600802251" + } + } + }, + { + "Address": "0x7e32bdd5ba50eb8d5e5bd7d3eef1e8bb7f0177f0", + "SecretKey": "265381230465127358963216809127358093216513", + "SField": "8232703131218555943214969386347851175399186201805966768750414520371803575150", + "RPoint": { + "X": "19242125080553338551396473264815502757575266047308561121276377861187981762008", + "Y": "6218373850521459529400329711936878862028424392534186108921910846882020330028" + }, + "PubkeyG1": { + "X": "3330687773238963481566047234114671341321386178661830488834900006935732354709", + "Y": "1586247746639542071922023676451514656089805747478597321110273768112339015566" + }, + "PubkeyG2": { + "X": { + "A0": "17613195447478151801754685924971955231494961653094677584006576833833181220337", + "A1": "8248149727711895523076824904570372119241450169122068550957488380013460422052" + }, + "Y": { + "A0": "4921860114926042695859196707091513960894135032516354392120636579531786968732", + "A1": "6497674699404552189948059719808314151701318782389032881121361153353612847519" + } + } + }, + { + "Address": "0x90a89f27673e9b99001d25c5328ea7b248341aee", + "SecretKey": "265381230465127358963216809127358093216514", + "SField": "4235478359213410732582392105113225989686712574423827420889537931357288127804", + "RPoint": { + "X": "20654778647025427886329231590146081813406215959318690452465157767589910386387", + "Y": "16434595237030862998502345611891947093280350242989172641995260786504200724952" + }, + "PubkeyG1": { + "X": "20354234858398388849816932240956270026965879865370684564771582015555136553997", + "Y": "19793685024108212849441227006186442648468124186906966567382667532342323504884" + }, + "PubkeyG2": { + "X": { + "A0": "2406250413451469352424770691776336657864578839352351617783440321552775726788", + "A1": "17191001754715829242845993906554922262830769871842065626630924785145928353563" + }, + "Y": { + "A0": "9681366579801251558910069516165884865963240333390287489273262916032696422128", + "A1": "16915387601971985830991163871960501726831560513559637189932888959407309364443" + } + } + }, + { + "Address": "0x8d26afa6847f837d05c1bad6e9f87c7ac1be1566", + "SecretKey": "265381230465127358963216809127358093216515", + "SField": "11035850218432072216042138989371348095984008884653111551948778896223502226130", + "RPoint": { + "X": "17221699912650368882632599262801462631856236460154101376095314228983311857640", + "Y": "16920232814901492682413294000319977483527313089896426400855455024070752739456" + }, + "PubkeyG1": { + "X": "3168995773972892355858790410620399201792411842283506602925392425152373993765", + "Y": "3166501588898491583880629288124229291720446936681364855672031758818970837581" + }, + "PubkeyG2": { + "X": { + "A0": "1061540865081686461127583443728867079739536618424919508446137160337220400469", + "A1": "14314019642545413347565949557134146221646346593211321744274793627527703796333" + }, + "Y": { + "A0": "5285717062790019556696539193484956838225490023146378618961849297769407066983", + "A1": "14567857613796427090761122799009706481422522677880753883893727561643094726547" + } + } + } + ] +} diff --git a/bolt-contracts/test/testdata/bolt_commitment.json b/bolt-contracts/test/testdata/bolt_commitment.json new file mode 100644 index 000000000..596ee674e --- /dev/null +++ b/bolt-contracts/test/testdata/bolt_commitment.json @@ -0,0 +1,5 @@ +{ + "slot": 633067, + "signature": "0xcdd20b2abbd8cdfb77ec2608e1227f8ce0f66133b9d0ec0ea68102c2152b82193e3be0d6967b7c20b83e1a2530daa3a07713556541dc2aa16a46d922e6145a2b01", + "tx": "0xf86b82016e84042343e0830f424094deaddeaddeaddeaddeaddeaddeaddeaddeaddead0780850344281a21a0e525fc31b5574722ff064bdd127c4441b0fc66de7dc44928e163cb68e9d807e5a00b3ec02fc1e34b0209f252369ad10b745cd5a51c88384a340f7a150d0e45e471" +} diff --git a/bolt-contracts/test/testdata/eth_proof_20785011.json b/bolt-contracts/test/testdata/eth_proof_20785011.json new file mode 100644 index 000000000..bb5045553 --- /dev/null +++ b/bolt-contracts/test/testdata/eth_proof_20785011.json @@ -0,0 +1,22 @@ +{ + "jsonrpc": "2.0", + "id": 1, + "result": { + "address": "0x0d9f5045b604ba0c050b5eb06d0b25d01c525ea5", + "accountProof": [ + "0xf90211a06c5341556789e53964fe2a77e0bf2b540f9c9aef92d508eedbe496183c5fb0d4a088d9fcfc0d596047f887fe0b6e76a22833ed33d90e475a97515c135afc1a358ca0a2d140e4eb080e7154ab4732922dc09ba5e6f7bb66ca085cce4a33a2485cefb3a01550fc55f997ebd8ff3194813c0a83668219d5aab1adea1e4aa8416ed5e086c2a04ce40dbcc710d91c6583912c4bd9e9980998ef0399346a7d488d4807153ffb7fa0a4aed92a1d67dbd794e18c588b0bf690373517a96d1c2028337a17bdb1955522a08a352007fbbbb15880b3ab7f3732a2f0f35513ec3704a97a1c0d6958a855182fa0ab93816f2baef5e33eda09193295d14a76b68ab71b264bbc145f7bbcbab6de44a08c22010d9488cebd377914768e0f105e4468d86843fc8dbe3f0026ea81ea4428a02748a9e18e6560d907ed69f7313239ab489c443e8517a6da153126c1b1f4295ea07e3eac4ab2437783df515ea778b89489991230aa14724147af45cc36027773eda046487f3e599ab8a8d627425936e9490e26c0242526ac83caa777390783efa078a0b9bac1565b8e64fe494b62be9eaf5ba81cfbb477532b5425cb85cc19cdb664f4a024eb0480fbad22741077e53fd0e209b80f721058e000bec1c67904f0a419dc68a09bba4884ab92553333120f0548bc347db14eeb098a903e533b41940a8b3c48d5a0f400d123b1957c29ce7dcab8ffd18a0da9bf7d754c544452391334b05831707280", + "0xf90211a05b4f1080ce2ddb2a07b1093f8248df994bf78d2e0902def84faa02d3e8b4932ba0c75ea41d0704a50f6ef436fede8ff700a2d3c0edbf5c7066f2e7e2346a1b6b15a0a99a258a63cda78ff4fcb4bcdcef99e8f8f125da55ecaba58034408a3c10c03ba08d6ede7b83392f11041c9e9b6403162ddfe0a87de3a26c903b819d81c5d1d68fa09c33fbdc36ea41febe74608a116bd1a6118f9c2a8802d307525e35b31062ed56a0dfd19dec7ac4c6e74eb81bbfe2e9af6dfdb6b8c131f419dcea0bebfba80c4129a06d302c5ad7c218e7772a6fdd426b07ee6a151529f2993e07fa3fe728e03a1016a010184894262fd8c80d8596760e68e39bbe17864fca10d5caf0803c3f9c77ecf1a02c01f8486b37c883b0169b3593a28b70881b03fc4887048a318feb5141140a3da0ecb89cd38ba3499731c19946c2e108cb2977824058bcda585cca702cb231fbada0218c4bba8f633a0c22aa2fe39cae6fc0ddc83c471099709055c4b3953de34685a008948be35bc1607efce7877f13f1cde290b3ec45e026a180d058fe90a314034ca087d839d5da8e87d3fc45f782e128243a4a555b3850ea790f084d2cfc2d775b3da02d09a195dae74c1075bfd63342af74ff5fe4cb6777fc1cc44ea03ab258ba3e43a0219c2f1156d413196b274acb0234c2fcb41ed66aecd1e5011ad83da73b047ce7a090be7be27cf1be666f7835cd80feecb0904d50403adabb86ad1fae00a8cb29da80", + "0xf90211a0a85deaca1adffdb7713b02a33af6ec05b09237d060963af84d7dc1a269178845a0cfd70f233bd4eb4ac42209a0578808f9e24498297af9a3e902233c1839a3a3cda0d6975a90005a1966ddae9b0f978161fb032ea2ca2d8db2453fc6be2710d48388a0d33abb2b6de28462b483a584224b30c079986bed9599023f28c81db7ee324b29a054911488c612caf55eaa494f0cc84ba25485ee49ce40f49b434098951036517da0cc4c54ed2de6d2b8fcc3bcc6d1fc490e8d26e24d4ca1ff3d81c7492fa23e4c3ba06249e67a29f9ed1713043324faf5b666d790977fad710f1a0a9e660ccb48c97da0ed8f59ba4a2766bb4035cc489fbe5f34795c2c36e16bd2d00b7b08a5a6f42507a0d2d4d82c9452c5b78a807c5da31db61357c211c84e90bdb983940f344a9984e3a05c2e1286d8a66758e1c1fcb3c85f28791f95ee85615a39c1d6780f6a54e1b3a6a0ae4bf704ff1b38570d255bb7c2b2917a54b025e6e8185929b4d977f453a8d9e8a0ac9228bf16f7c15155965d5eb5d7aa2e2f31e8f937d52a7203abef348b843632a0a193e1b44b2441cbed3d8cd20862d7f11b0121b957a82d63168b67809dbaf3b2a09aba1e3d0feb612998294bd3645247790107e619bf8728819fbe463fec342f5ca09336f5d7c3d7ddb6e987ac5f7c0e78e3936e05f6da27a7c87813a6e5250e9c38a021c230f247db23bd8d948d68a24891afd58c29864f1d74551060c10067e386a380", + "0xf90211a09d93666285cfbada2d91c81fb96b5e30ff75a51353c69a30c0a1431f51867880a05efd44e1b30767c66f02d79e93b2c45efcd56018050605c08818645ddb7a46c5a05e5bdbdd63d64f58a34f47a42d70b83febf924c08cdfa8d5673db3c6bd71df67a05d67553cf6620c67dead6ee37a48a5dfddddd4adce333c4a7771c25140ecdd92a07d5eb9550d7f807d7e0dd86a52f3d111b4359ac2928ca3e07c34bd6551055537a0fc0e43113716fdd77ec45220d5afcc0953d8357478872de3c6f36fc2d75c9610a03230ce2ec5fa8173fca250ac63df602e3a6d2bff691edc8552ab8bdea458bf07a01e7ad54a56fcd625d6066ec05b6efc30bf90a65b756277f79a65ceb0eb108a32a0089ff36bd32acfeef1b5faf546e54ac7646086d1c2c764239cc9b98da8c460e7a00905f00f493594ce1c13274c2bb5ad018fab388de860f1cae6af64cb3ad3b46fa0ad7d4f9200d8f0ae34bf0cbece04d61b13c6daa0b7ea0e9d732522c5d46feacda075da3fc2991d7d32cb0be70210c7cbe3bd5883cfc52f58e8e04d07b9977f816fa0c7bc6aab1b71efc9ffc459cfda04d930f1652d53179a38b43f8e0980b40ab48ca0ae012ca5c6a3363d5f850e060001c90cd3a022aa44e325d5d832021ebef4fafea06ee58a40e8abba2a3be093fdefd9d9871558f4fa11f6d6721307117a17260b51a07cc44d713e05b70e6749fedd7cb1b4b47d2102638bf93ffd0e8ebf404af6940080", + "0xf90211a0b1689af6f7f511cc59773684cb467f33c3dcc5dc1a08f451c851544dca6b1a3aa039a80eb019a427f8b32dfdd88a0b5d95a21521444c67fca8f1e875f1898e16c4a06652a79c42cf0d23532b84d47d58b6c898076d88147030581ff121d25407e25ba0cd2b62c08885a27becfd0077ccdfdd5d77d4cdc5a0ab1ec2ad1303c93a9aae3ba0333e03e837b7838cc45d8f30b17ec09933b77debdfceea6cc31033c34cb25b88a025c683f0ff00593e3b96caa6a45153e1907e263c2815064492ea29d2ae467d16a0b1f927c724f5f7425c5d47ff064d099a13982ddb528ac5526829ffefb2502c91a086a4177c69624728a862d160c303dda7010e4d317f60b2945a517f2b8cfe71f5a0436081588c2e0ceec3e3b2298c97085410d971d8da9eedcdc48e62fdbd125a2ba02f24c969da88b0cfe6318383edf10cbf2174c883ecd007d6bb79562663723099a0a9cbc30a1d257e3b651b3c304be3c253948c373f6980b22898af778ef9955b0fa0a343404514e213b6e5832f4621e6a6b1f0eb977811d759f06b7ad6da445092fba02eeb6ec27797e59283781b12f56e315f35b16395560a9c34bdd21280e942cfe1a0c874d2114c306baf91f0d43c2c76e30a6ad0da45273fb6d82246565d84c2538ea071a72e0b7d80490d26f95d3714cf8dd0c8b3c7b232980607aee525a9162e6308a04b4d1cf635eab18bbbb9e64109b0d992d759df23a96aab3d4a40e39a7c3becba80", + "0xf90211a023c9514b2fac99cd172f93d2db4d6578ec69fc2e0ef5c79fc76499ded09130eda00c645327db795a8a9c69282d86e3acece2901bdbb8047528ee8c78681b0e14b1a01e76e1109e1936249fdf08a4f583f91c01c6a4672794657bd74cbdd6ab3c2f3aa0a6b7b10c53412201d9ca7461b7210d6457a6b275b95033e2d8ca9778e5071c05a03e6f50fedb2b9f9f963356fc2899800e46a38169a794a1e6906c02ce8e8f0d59a0d4cec6a796cbab7e9cb28063423fcf157bac02de0cf1f0931303a701006019a2a0fb7e5187937b4203cf97bce2f345c9161bfbb9eb431a105356009e6ce18a1fbca0486a40cde9b6a65b5534609ae4f56cc97c00dbc4c4447385f8a42da3b1ac40d3a01c91aad4f4d391df268007fee771caedccc43369e07d6eb22fd45dab6beb0986a0672ca6ccfd8ac83f8974afefc9c9c2b58376991580158b16fd98cb9076cf8ea9a06cb3234503ad7116bb0e66257c646b2bce7603d774474a4f100cf93dde3eb594a085d0406435a6562f3370dd4b5de47a2b7d1084b9f25f0dbf49f54d2f46a2fc32a08bba9249e073397de4c46dd27688e9467b20fb8e61e04fd2594136cae26cd5c7a0a43fd38e14c38b64b84bb579b9a3cfab05cd5cc69c3a812a68e458acf72ea96ca0d848b40a2dbd5d8a4179e89fe8533f21db91982a0b8ecc1542c08ea27aec522aa0e03bc889898881f7ecb7caf0db5fb95125e26ff9e26bb85c33cbe360b50c9f1f80", + "0xf90191a017e9f741d070bfe3d975719f75663dabd5acdeb3678c50bd4872b817d7713d0b80a073811d2e7576e6bdac9804597d153d4e0a4cbf8400b8331f67605f6810ecbd53a0363162c748099d6ed114ead87c481972c1b0c124730b341c4ef1f963e0835a9880a09ef90c2a9ad234a08cbf4ea4ced535f227bc48becd22c1b05f2cbd586f018b73a0ae59576b0c1089b2828db7e54602ad0fabe4eff312ee5e0c21a04ac09b0e04d180a0fdbde0b4dbc02bb2ec472517c6f543de3673d33a6246a602479b43042ae9222da0c8d33ec13b89d8fd0ba487fe9cd61d18deff49bed48dc073384dc086a7361273a021253bb1110de615cede8c708a94cd6b898a61b5b1aaef8760b3341f232d63f0a06e7fbd4a6404e4acae007bb6147c58aebf6f19b3914a8d48b831bc3e50e5c05ea0c72d502ed522a796f823edefb721e64ff9d38904c9913b5be76f1e759c899b9da0fcc3a12a432daa29cb33f4a198624f6391124579ab7cbfae1eb30de076cfdd7980a0ab5019c60fef28581a93f0707739bbb24dcfbc2ffc550fb57aea59d91c79c04780", + "0xf86f9d3a1698215d94a53d304dacf1ae39aec65065a3307b0d96cd2acd29de94b84ff84d81eb88033ee43ef5fd164da056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a0c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470" + ], + "balance": "0x33ee43ef5fd164d", + "codeHash": "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470", + "nonce": "0xeb", + "storageHash": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", + "storageProof": [] + } +} diff --git a/bolt-contracts/test/testdata/eth_proof_20785012.json b/bolt-contracts/test/testdata/eth_proof_20785012.json new file mode 100644 index 000000000..25d98aa9b --- /dev/null +++ b/bolt-contracts/test/testdata/eth_proof_20785012.json @@ -0,0 +1,22 @@ +{ + "jsonrpc": "2.0", + "id": 1, + "result": { + "address": "0x0d9f5045b604ba0c050b5eb06d0b25d01c525ea5", + "accountProof": [ + "0xf90211a097ed06a390ed9c16ec9299e19125791653b9201b784bf9a42d84e0974b664cb3a0f3a9aa3a6f2cdc6b910d4734c0c8fec4b3bd8a676c711eb0c533295a68ce43bfa01f7ed42296ea93615febab61c43d9cc467789f55851549452858136b9efe9e42a077b8504dbe1ba2d05802efbaa36630fa5b10558c6a13de736375997b5e14c9a2a08ea3f8e0bdb950843d81846528ee71d6eca46c9a273528843aea374b5c355ccca0eeaab1a08eb462bb3efe98d852fbea54769095de57beb60de9c8c72331304401a0cd3c794240e0de405bfee95babf6f505979983a37f79590be3b862e53f960807a04dd46005f5c29ebfb69815f918853bcff3af08a2f9c3b3493033fc0dc0a773eca005ca774fdb64b89b25f07f7e960fdfc9b4149ca9c6e7751fcb55824f04eb3ff3a021c51f7e644285567f75772792cb66c39f34f0509a50af2da12af26cf1ac9b10a072ccec3415e7904da03a2bf677708cdcd8d1d29b59c9c0e408ee8608fe3b3823a010d8c53e6bf134e70476d64f41cf8d1e219aece6c969809225a269027eee6a6ca06effeb1ca18e2254df42d2d0cd42825178409d6b210127a0a6c07fc95680dcdfa01c1b854f1606365826b1153586d98342ecacdf0acebe933a00256fdee7af5a59a0306fb9c170aa24d20350f7277392adb78557f48ecb62b3d6cf23698988eac284a0daaa352a43bdd737c9524a8cb3a98802ae5185e3b71eb865c71473540edb7ef280", + "0xf90211a03550162fce21c8be10d224e61acebee31945d54a9d576f11758526190e5bf987a0c75ea41d0704a50f6ef436fede8ff700a2d3c0edbf5c7066f2e7e2346a1b6b15a0a99a258a63cda78ff4fcb4bcdcef99e8f8f125da55ecaba58034408a3c10c03ba028b51ffb8812262e563d86724920a7b7b8d13573ccc747e01b4cc5db45b9c2e9a0ec8f2eab33b3c5a1f2e6c1dc8984299f07a5be7f41cc82e103ec8b5aca0a9e90a0b57d1ae6538304a97d74f19dcf5b5999ce74a8b3818e4f1ba6f2a55c7d3933aca06d302c5ad7c218e7772a6fdd426b07ee6a151529f2993e07fa3fe728e03a1016a010184894262fd8c80d8596760e68e39bbe17864fca10d5caf0803c3f9c77ecf1a04a2fa9ae91770be3477bd857ca876b629e8d74f15b36a2545c888d17396bff0fa0ae4da4e3330f57b5f57956e648c7c7385a5a5d366b0463ddb0add15e47c588f6a0bfcd3d5bf7b55f88d34e1f6d7760bf18b3547ae2004a05db706c6d6d41d2ce0da07d9caa8bb424afe94efc92b3f2207b480231c6a46bd2c6e67e33b8335eabe14fa0c05d5e0666734ea57d97a9ea74f16698b05db5282f973fb6f3f5f3de1c2b458ca0ac81cc5c382467c74df9f355e220cabdba514a01b59ba5779639611e87b88b8da0219c2f1156d413196b274acb0234c2fcb41ed66aecd1e5011ad83da73b047ce7a090be7be27cf1be666f7835cd80feecb0904d50403adabb86ad1fae00a8cb29da80", + "0xf90211a0a85deaca1adffdb7713b02a33af6ec05b09237d060963af84d7dc1a269178845a0cfd70f233bd4eb4ac42209a0578808f9e24498297af9a3e902233c1839a3a3cda0d6975a90005a1966ddae9b0f978161fb032ea2ca2d8db2453fc6be2710d48388a0d33abb2b6de28462b483a584224b30c079986bed9599023f28c81db7ee324b29a054911488c612caf55eaa494f0cc84ba25485ee49ce40f49b434098951036517da0cc4c54ed2de6d2b8fcc3bcc6d1fc490e8d26e24d4ca1ff3d81c7492fa23e4c3ba06249e67a29f9ed1713043324faf5b666d790977fad710f1a0a9e660ccb48c97da0ed8f59ba4a2766bb4035cc489fbe5f34795c2c36e16bd2d00b7b08a5a6f42507a0e2caaa014febfa0bdc9179bf519a963a05e36c634ce616c744ae2bd0b48824c0a05c2e1286d8a66758e1c1fcb3c85f28791f95ee85615a39c1d6780f6a54e1b3a6a05fe7bca3ef5fb8a2c8be1b987976a40c0345cf92e5117b3898159e4ed5ada800a0ac9228bf16f7c15155965d5eb5d7aa2e2f31e8f937d52a7203abef348b843632a0a193e1b44b2441cbed3d8cd20862d7f11b0121b957a82d63168b67809dbaf3b2a09aba1e3d0feb612998294bd3645247790107e619bf8728819fbe463fec342f5ca09336f5d7c3d7ddb6e987ac5f7c0e78e3936e05f6da27a7c87813a6e5250e9c38a021c230f247db23bd8d948d68a24891afd58c29864f1d74551060c10067e386a380", + "0xf90211a09d93666285cfbada2d91c81fb96b5e30ff75a51353c69a30c0a1431f51867880a05efd44e1b30767c66f02d79e93b2c45efcd56018050605c08818645ddb7a46c5a05e5bdbdd63d64f58a34f47a42d70b83febf924c08cdfa8d5673db3c6bd71df67a05d67553cf6620c67dead6ee37a48a5dfddddd4adce333c4a7771c25140ecdd92a07d5eb9550d7f807d7e0dd86a52f3d111b4359ac2928ca3e07c34bd6551055537a0faba45751c6566255746b31a67d0cd73163a115d0aca516c0625668a20439846a03230ce2ec5fa8173fca250ac63df602e3a6d2bff691edc8552ab8bdea458bf07a01e7ad54a56fcd625d6066ec05b6efc30bf90a65b756277f79a65ceb0eb108a32a0089ff36bd32acfeef1b5faf546e54ac7646086d1c2c764239cc9b98da8c460e7a00905f00f493594ce1c13274c2bb5ad018fab388de860f1cae6af64cb3ad3b46fa0ad7d4f9200d8f0ae34bf0cbece04d61b13c6daa0b7ea0e9d732522c5d46feacda075da3fc2991d7d32cb0be70210c7cbe3bd5883cfc52f58e8e04d07b9977f816fa0c7bc6aab1b71efc9ffc459cfda04d930f1652d53179a38b43f8e0980b40ab48ca0ae012ca5c6a3363d5f850e060001c90cd3a022aa44e325d5d832021ebef4fafea06ee58a40e8abba2a3be093fdefd9d9871558f4fa11f6d6721307117a17260b51a07cc44d713e05b70e6749fedd7cb1b4b47d2102638bf93ffd0e8ebf404af6940080", + "0xf90211a0b1689af6f7f511cc59773684cb467f33c3dcc5dc1a08f451c851544dca6b1a3aa039a80eb019a427f8b32dfdd88a0b5d95a21521444c67fca8f1e875f1898e16c4a06652a79c42cf0d23532b84d47d58b6c898076d88147030581ff121d25407e25ba0cd2b62c08885a27becfd0077ccdfdd5d77d4cdc5a0ab1ec2ad1303c93a9aae3ba0333e03e837b7838cc45d8f30b17ec09933b77debdfceea6cc31033c34cb25b88a025c683f0ff00593e3b96caa6a45153e1907e263c2815064492ea29d2ae467d16a0b1f927c724f5f7425c5d47ff064d099a13982ddb528ac5526829ffefb2502c91a0fa6b40da08a9bebf3b0b320e4895d4bee360047a7d48efb0f8a0a6e5fcdcb338a0436081588c2e0ceec3e3b2298c97085410d971d8da9eedcdc48e62fdbd125a2ba02f24c969da88b0cfe6318383edf10cbf2174c883ecd007d6bb79562663723099a0a9cbc30a1d257e3b651b3c304be3c253948c373f6980b22898af778ef9955b0fa0a343404514e213b6e5832f4621e6a6b1f0eb977811d759f06b7ad6da445092fba02eeb6ec27797e59283781b12f56e315f35b16395560a9c34bdd21280e942cfe1a0c874d2114c306baf91f0d43c2c76e30a6ad0da45273fb6d82246565d84c2538ea071a72e0b7d80490d26f95d3714cf8dd0c8b3c7b232980607aee525a9162e6308a04b4d1cf635eab18bbbb9e64109b0d992d759df23a96aab3d4a40e39a7c3becba80", + "0xf90211a023c9514b2fac99cd172f93d2db4d6578ec69fc2e0ef5c79fc76499ded09130eda00c645327db795a8a9c69282d86e3acece2901bdbb8047528ee8c78681b0e14b1a01e76e1109e1936249fdf08a4f583f91c01c6a4672794657bd74cbdd6ab3c2f3aa0a6b7b10c53412201d9ca7461b7210d6457a6b275b95033e2d8ca9778e5071c05a03e6f50fedb2b9f9f963356fc2899800e46a38169a794a1e6906c02ce8e8f0d59a0d4cec6a796cbab7e9cb28063423fcf157bac02de0cf1f0931303a701006019a2a0fb7e5187937b4203cf97bce2f345c9161bfbb9eb431a105356009e6ce18a1fbca02e63f3f119a82cedbf5e64e31aefeaa62892816846065c408c13f9bf9669d3d5a01c91aad4f4d391df268007fee771caedccc43369e07d6eb22fd45dab6beb0986a0672ca6ccfd8ac83f8974afefc9c9c2b58376991580158b16fd98cb9076cf8ea9a06cb3234503ad7116bb0e66257c646b2bce7603d774474a4f100cf93dde3eb594a085d0406435a6562f3370dd4b5de47a2b7d1084b9f25f0dbf49f54d2f46a2fc32a08bba9249e073397de4c46dd27688e9467b20fb8e61e04fd2594136cae26cd5c7a0a43fd38e14c38b64b84bb579b9a3cfab05cd5cc69c3a812a68e458acf72ea96ca0d848b40a2dbd5d8a4179e89fe8533f21db91982a0b8ecc1542c08ea27aec522aa0e03bc889898881f7ecb7caf0db5fb95125e26ff9e26bb85c33cbe360b50c9f1f80", + "0xf90191a017e9f741d070bfe3d975719f75663dabd5acdeb3678c50bd4872b817d7713d0b80a068f1f1d4e6b9697c43cffb6125e3bd1f4f10d57cb9dbd0938388fffd555a6457a0363162c748099d6ed114ead87c481972c1b0c124730b341c4ef1f963e0835a9880a09ef90c2a9ad234a08cbf4ea4ced535f227bc48becd22c1b05f2cbd586f018b73a0ae59576b0c1089b2828db7e54602ad0fabe4eff312ee5e0c21a04ac09b0e04d180a0fdbde0b4dbc02bb2ec472517c6f543de3673d33a6246a602479b43042ae9222da0c8d33ec13b89d8fd0ba487fe9cd61d18deff49bed48dc073384dc086a7361273a021253bb1110de615cede8c708a94cd6b898a61b5b1aaef8760b3341f232d63f0a06e7fbd4a6404e4acae007bb6147c58aebf6f19b3914a8d48b831bc3e50e5c05ea0c72d502ed522a796f823edefb721e64ff9d38904c9913b5be76f1e759c899b9da0fcc3a12a432daa29cb33f4a198624f6391124579ab7cbfae1eb30de076cfdd7980a0ab5019c60fef28581a93f0707739bbb24dcfbc2ffc550fb57aea59d91c79c04780", + "0xf86f9d3a1698215d94a53d304dacf1ae39aec65065a3307b0d96cd2acd29de94b84ff84d81ec8801e4e114d0153dada056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a0c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470" + ], + "balance": "0x1e4e114d0153dad", + "codeHash": "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470", + "nonce": "0xec", + "storageHash": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", + "storageProof": [] + } +} diff --git a/bolt-contracts/test/testdata/eth_proof_20817617.json b/bolt-contracts/test/testdata/eth_proof_20817617.json new file mode 100644 index 000000000..e3447e81c --- /dev/null +++ b/bolt-contracts/test/testdata/eth_proof_20817617.json @@ -0,0 +1,23 @@ +{ + "jsonrpc": "2.0", + "id": 1, + "result": { + "address": "0xc21fb45eeb45d883b838e30abbd2896ae5ac888c", + "accountProof": [ + "0xf90211a09d63f98f913479f3896c84699548d56e7664f7abd187f56544e9b5d8af62d06da09fb4628824ac085d2bd2b5b87ac98aac9fdfe06b8f539f4a811f9127546238fba0908dae3356ca023e940a75e59f9a43ce84aa64144017ec4bbf0c9ce32f8e211da0dead54c42fc1852cf7676bb06c3bf24e7e36a362892eedbb55a2099f46184840a053d3e971dc35dc1d221d50deaf816c55c363748d40b2371762a249eed7177aeba086c7b11320b032936528b27716c71491b08ca99d6020c4969ba41d23229ce0bda0b8318ab3806049d7290b0473916dc890d42cc788c237256e43cf7916eab2d8a9a01d48463ff710835bf05f70632f333a8897fbb98bd94fa11ad0c55e016fcc8e55a02bb21d34e1c563361fac6b818fa815180d93b943284a0094bba706504b09b1e3a0d572335532de9097855e8101578f5e7d784df383c0eccaf1a8e03e8b2531fc0aa01bef35cd30119d0cd52dd30943d60f672ccec38d9e78a526f7147a200a8c920ea0bb7fbbeee717776d53b0605a975f7be626869e3639626edc5d1ea82d7256ceb3a078330e172b00a00b2be82fb1c239a855d3b115c2b05732a0da136b640a4bc991a0034f3050dbf66de7fbc5e7fdb705db7f266136f09d5af946f2ec2d13552e1f60a082a08128a5cd66737106304cad57c04edc61a7cd01e8d8bb34b1aaf2e7d5f384a06e0ee61de15716b08d9bb6b382c7a2935df7aaf3604663c8b141ebe4765f559e80", + "0xf90211a0ca41f42416743bddd631b8d111c47fb6a399adff1dce745e6102c198f42e3934a09b0ed7c6bebeaf23aeb5be74431ff7f654549969cf2657f9be99db7046309e05a0a9432bb9292db7ef90ec70f49fb01604baac0c47a121c8474f6a7b4ac3fee143a0772fca178a87cffb9699d83c4011ea9171fd97f02100dfcfb8104d36419f8ec9a022f78994b65aa6f4c6812c0cbd3dcded7343c5b8e97ce9e31d9595a1809cef52a096b54ba6f10fde5e840add6e9fa04e6243443dee45520f46427d527db7111779a02601c850c714949f180389e6271b74f48db4d1819a97e071d0b930a901e56ebea024d802aea5c6a59daaea1f7633b430a97c5b234e18062eee828e1e80d8f47105a085d478a1022a1e77290b0e7c3603d4af848af379ac734ae98fc01995676d441fa09d016f78675c86c68fe3e8a9c6024d34a753863612c75e1aefd1e83bf1fb7996a0ec7a6ad23010d2524751f8d34e4e14317ebea692f2b3c190989b3c0edfd72a43a00fe8c19621357b2a3cc3aded49523f7e438587b43729e679c5a751382f10a6a7a011e2b91321938e3762866e534fdf8ee6f38addf632f6ab9852f3011f0760da53a0b4142fc246d402037ff282a9ded94b26ca60ed32aee664b9bbcb62f0451bb9b3a0b0a71fbb993cfd1f7d101c0fea2a3221d64e0df142c2a25de846ca70fa04e91da03b3256cc9177a07d8237cc3429fbc0d334a9a723ab6d4e6739aa6ec317fd5b1c80", + "0xf90211a09563f3ad7021bb4ba964dbda89142678f69c9220ddc595a7176fee6a77e6dde2a0c03d7e898d9f019dc509c34a0507beb42e5d81f48d9f89ccfbf888d5622a119fa06c795fb9599852d8a27902d72c94de183fadfa304d8dca88881e5486650e047aa0a1095a0a6206c956ef87213a33863dda73e503eebc6d4d0b0303d304ce380bf5a00c0f870fa662cbabbe0efeb0d7092d794a44ffbc9b9f3dc78a0f5820bdb9ef08a0e3b486d3c7ab4ee5bc82aa53062cc0ac7518e50a28f023a3bec79e098d50a1e1a08fa0db4c80f774c25b45aa693c36ba543a0da17859a5ed298947ff0625d3240ea0719bbb5fa0cbbabf2c58d9de555a4c7fda5a3db3bd8f8dcb52f0331ac50da39fa0aa7b34db1dd20cc8082fa64b4571edf4a425fae384632eb1da1cafe31bf1aa7ea017466a7e9a8b545e8c087f4952f16c07d93ecbacbcd80aa4e9964ee4999da0eca0bfbcee522d04a97e863cb4a143161e6935bf2d9f213caef397f19eb0d444955aa0c603f4da9f3b8ce90e3c1e0692b4203e92d0da7aeb7c199c310573b47353a072a0c69a3e3392e217907b7b2fa341e2d1d753319cc0c6f2f098c6bcda7d07045fcba0a3ffebaca1076434c36a77df31d5149ec533f109e458ec1faf436a8e9cbeb063a0875415f1688fc04aa42448b584d522e647a58a56d8a10754717c91b7792d2079a09be0e333cbdded096276ebacf84a0bec134db2a76c44c909969155dae645bda080", + "0xf90211a0fe04c098ac8b48787e56020b1ecc3b5c5c3c6e5171d10a01cdb0c335c2d51a0ea0fc79b69308a70197e2f0dac13429e9ac40fde63afd94055f99cc4083d7e47a9da07ea9fda13a8b6e625cc3103397841e950c401db0590bffd19c88784d66ae8ac2a0eb80e847a0bb1d9ea51785e9df87b8a3cc2c7952e6b1c8b8936d3dbc711f8903a050c6a7b82b0b885b0e7eba783dcc5b8ae987309cd7dfbca12468b03dd0bac239a0a0ee1c4e530bce49c428d03595a5cbcd1d3a305bf574dc3c31cf7fb5a3c142aba0babb7e1b216a15528e50a34fc0f0df5d7a3f772e16769bfe5cde5dca3676d653a0339248dbbdf1b8ffad0c8a9aa7570d84f9c98f628781a38f91283b0ca3aeb44da04bca75e34b823c7cd1b115f6a9062c86d5cf1adc4eee6c07e1f8d14702f9eee2a0c56ff0772873872ddf79cfc6b5ab00928b5a7fe729007cf862fb5db730479098a0ef63d3c6b20257e68fa2985b5df37f283f264d3cfced52e0616026d67aae5174a05235bf4b2037762e034ae4de3f9ec91a7430c033985b296dd7e4511c38852ed9a0f094fb6c0f946cc1daa5c461b1780f58fc2f7cf8c0267b6ce7dcbc72f706335ba086854e8d0a94808be2decde59cf160dc0d3076f03aa0317572bde03f2b320873a04c4c3c0736b931b89b7852c58058edfc5bbe3d5fa8ecb1b6adf260b5a55169b5a03eb45261c45524b9eb254d1e9be2c454c15dca3e9830c233e65098874962c33680", + "0xf90211a092f305a8404f17b6e931179883c08395739bd9cded1d6d0383c5ea78eba0e9eea01b435be6b1b8953faa119ff904d9dc631a0ce9caefbbcf31238f2f1f85dd3101a08cc07a9faf111aa08259fb765d362816eaf830be9c53f5d0a0f80675ab93b815a0e4d58240ddd3d78e1ce5a2d12e1c45e82dcd2185b13ffc177968099ea757cdf5a0f85d2519788a25516c7686f33b03d76de697ec327cbe5c081f069ad85dd02963a001a4641ea4b49fd406ec717d9cf5bda08da097ff8e8f31f47ed9f81fdb1d277fa015ea349963312a679f9e8a95bd835ab8d10323b8df0e33a0f5649c0c43370646a00eaca4b6b9b0ea916d29627a4fbc27922f319cc1d54e1b79a79e5a577aae02e3a00a036924e0cb949a6f2f9c6bd832875db5c46cf43e1be6349b4b6b6531004928a09ef5c65a8581263ac6664606c7f1a151fa72513a1c42a5c980d34fc7cb52d942a0eb8096a937ff16967616dc01e382a32921301b14f4e342f2db3e0979958fdb93a04432cb0b5a0a6e88c885795324164a59870d38c8784c1c51089a3c667f93dc66a0557b8b232b8845e9b522068e259d5292149773319185be73deb68b2acc00869ba097de648ff70c532cb137c8d451ab7d6f219f4198cea0f7e004a44483290ba517a03b7502565bd4afbbdf3e0e5bf8cdab3bc2d311baf540aedbeb989ea98371e4eca02806b9535ce6e675559225b8d2109260017428129690f6361b6489d87c49fae180", + "0xf90211a0503b618293d23441985112f140f5d7afa5ef9d31d19fa62fb99017d4514902c8a0311ed3758bc90dda00ff970a1660bf6cc4adf0ed5b655077e8eef10e02a20ddda0c97eb281c77a1a0be95849a7e7ef57f1463892c5f1e81c28988e2de89f1003c3a00db3fee85f1671f42cb022c701753a8bd0a327a335706fd98c34fdb191187acda0be40e8d8fbc8e2e2cfe867916e6e29b3b5051b0f9f15a9255dffafc956501d37a049486782a5f47a956d3c393f7373ef8bf89c6dcb605abb3d0554f71ba3f71b47a0ba560b379073ffaac72ede1c7cdae34269b9cf17d21f00587a01d4a1ce05714ca0d77cfbdeeb50804494e76228d8961ddbe6c3d8ea554f2a4aae2a711bddd436a2a0e0d0b4ccc7d6b05e5c32e622a9d55297cf382971b0efd188b717437a677d6e60a039d60e2d14f5bed0190930c46386751cc9469a7016d95273ab09dc0037942cffa02474efc4317192c3188209b5e3a4f0bfe624c586300d65362105aaf02abf3fd4a0990cefa23e70fdd28aaccfb96d9ac861347623dba7ec59d5753bae8e4d0421b8a08207ae46bdb4dc6edf83ca4a30fb9260d9d203e69bb694c11c205c4c924de371a039bb8f12a2ca0e97d5220a439381493a3802117bb73406a85f76e325bcf2ffe9a07e9cf71d04326d2c7344c2cf71a552a75ec095d13af30646e903721baf6f968ea032996daa77895aa8e0383c211146b54270a10aa9b4c58e23880d2a243301eca680", + "0xf90151a0b3ad1158c0656d2fa69da4aa9a3060a85073d90819b84522f0c3377047247540a0a1f7c4b2a015e11c26d62fc727eef67a0faa780040ae0adebe93a1e341a6f4d980a031c20c39dfbdd0c7142ef4e8a9128dec5ac324319ec8475d5a4bd73cecfbe9e780a05ac2833f0e6de75df686dad2955041acdfaa9cbe57b8eb90ee18e0e616e7561f80a0dd57fe766ac44d32e7784778660b650ef59bf3855c6f8ceeb57bac3a14cb4247a0b558e61b73ce931037cb132603bbcd0633d1803aa3df1e6cc4fc6adeb78f117da0fd56b8e86e95cd1b8f123394a96685d273e917f924024f114c1c279599250e9b80a08c1d302bcf60d6253bd7cb2567c5f45aae41ef30cdd0acd16ddac78591c6957680a069f601199a401cb6eb42de37258d32c77956ea7c010c34ea5785b94a264f34b980a0da64c248f908ec75a352f1ddd1992df661cbe60623af5cc90707feda609b111a80", + "0xf8518080808080a031d211162636a06229e43cee3301dc559d43c39dc70a35c1f9db384c8ea8f3b580a052ae106948915f7db6155b87fdc9f0bb2c8cd12e475aa3cccad0b60d18a80794808080808080808080", + "0xf8719d20decd5f57411491cdc23220f75184426f294b3c88918fe2e3b802964fb851f84f823385890936f1a77faadf9ca7a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a0c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470" + ], + "balance": "0x936f1a77faadf9ca7", + "codeHash": "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470", + "nonce": "0x3385", + "storageHash": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", + "storageProof": [] + } +} diff --git a/bolt-contracts/test/testdata/header_20785011.json b/bolt-contracts/test/testdata/header_20785011.json new file mode 100644 index 000000000..c4d63aa8e --- /dev/null +++ b/bolt-contracts/test/testdata/header_20785011.json @@ -0,0 +1,5 @@ +{ + "jsonrpc": "2.0", + "result": "0xf9025aa00595a77df480da86f42f0f339fc55fc6a40886cd320de3a3ec3df6286c867503a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347941f9090aae28b8a3dceadf281b0f12828e676c326a0d6b6ae210398562833e4ac414eb120db14aed11fb8adf5ff5939a20bdd9fbc6fa0a9c4dc0b0e382f1f800d1581d4001bfc33bd384a6f50978e19ab60e92a283f37a020fcda2771063409aefb2669c4e7579d270234d44055c996de915e95ec9caf01b90100d0f9414739b00184f00e04f08d310c80718d32249441f88082897d3830b2a854950fc41563202a9022080028471a29204227c0a8af36a184dae6a6662def6988186c1e0a609449e97ab949b9a07134a1a6210ac1d5651e4114a8c6389926140162011995324058a1346953f321800cf3202e50a05c066d218641101d171b10860450625e0b0c67488d4c09fe03e272d75c514595113100c8112984e2fd90a26a3385185d1f8020044a2190c0ac199d070479854502981a2522046e10d58f1a7b7b583c82005cb3cb29a1823a811124d0328101175569421f64d64002d9b1e630b871202e43622604014484b4810b82dccb057441009700f809c07d01903816558084013d27738401c9c38083a09ebf8466ec2a63917273796e632d6275696c6465722e78797aa0cfe02ed4afd27cd0a42cb0f802a57672daac70511e3b860d7a7ea6b69d4e0d6d880000000000000000850537c96c32a073cb76da1e69871e3400088fa509333d96c3ac5fa307e6f64892202222885f6280830a0000a0ede6709250535a0c48e856b76e415c907d018c3c5f4d1b34e2ae14aa61a8eba1", + "id": 1 +} diff --git a/bolt-contracts/test/testdata/header_20785012.json b/bolt-contracts/test/testdata/header_20785012.json new file mode 100644 index 000000000..49a4b9a6a --- /dev/null +++ b/bolt-contracts/test/testdata/header_20785012.json @@ -0,0 +1,5 @@ +{ + "jsonrpc": "2.0", + "result": "0xf9025ca06be050fe1f6c7ffe8f30a350250a9ecc08ff3c031d129f65e1c10e5119d7a28ba01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d493479495222290dd7278aa3ddd389cc1e1d165cc4bafe5a0214389f55a96edbd4d5295a17ada4dbc68a3b276145bf824b060635f9905cefca087bb9183296ce9e3b7a3246f6d3a778b99a5d7daaba2174750707407c7297365a077c5d55d916e8abd4ca46cc769eb4b70b2693d25c8b67f3036a4a746fc512976b90100936ff6cbcd3707ce33a3e9fcf8b4ba995f7f0d23fedf7227548fefe25c19c8a878efed486844dc1942347a33dddb3fe703b5eadc8abe3cc43d9f1c72012f6132e7b4950acc4e1bed490f5eaab2e853b504b31dfb9e7d6cf8cae89d6c9ff0c6f3af0ce24e667c26f1c5cc78b791983eef172c1f6b80de7e2a1a627836b05ffc1a35683650b3c1e9e9bddf836e46eb4efc4d2a7dcba7e3defe7dbd8aceedbb93f263bf10ef5bb0fb1cf249d0c1ba9dddf3448fc4f5a6b3f3250ab9697089ad3f78119f203f2b3e7b1983198889c4fbd9347a33f93f9463dad70cf5ff6ff6b4e1867bfcb559e1c1bc2df2c5dedbf1244bb65f7cbc816bfeaad22f6fc8217a0516478084013d27748401c9c38084012a47bd8466ec2a6f8f6265617665726275696c642e6f7267a03417ec3f517a20587f89d4802cf478b2faea06b296b1637a8b87200c26433be1880000000000000000850505fd1914a08ae76a6bfb85b813966e007e021e8b4970c8cb02f8be771f5242b45f2e3946028302000083040000a04fa17978dd1135edb87244b53dbee105502454087c389d3638a6b2ac3bc6340c", + "id": 1 +} diff --git a/bolt-contracts/test/testdata/header_20817617.json b/bolt-contracts/test/testdata/header_20817617.json new file mode 100644 index 000000000..96c10c96f --- /dev/null +++ b/bolt-contracts/test/testdata/header_20817617.json @@ -0,0 +1,5 @@ +{ + "jsonrpc": "2.0", + "result": "0xf90261a0f0c271606d68097149e0d89ff622d2690323d65deded9ac9c435c2ea73ed68e6a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347944838b106fce9647bdf1e7877bf73ce8b0bad5f97a07bf2e876a6609017c05940b6020e206c0e80006c0b902e5d073c80efd57ee12aa0f866c5389896ec1b8adb85b5b786841a7f8162d820eb35adc626f932fcd9bbcea06e1b5cb518185b799d3726b482abd5333ad733e5d88019686dd2c2457df5de2ab901001ba5662209ca280836180055961846269b09e1adcc814ca5065145909c6f18f0854367868469022d23305ad00c2439700ef190808bb3282520c92c6785780110e09286187180893f4e0a63aad177d0f48e262201c1d40820aca6b780a5a56604acd13160424ac534093df03402841ff2440f126210f8a404c042a852087b08d1800310d98380c058005c3322f0b9024bf4961ca5b320059c853b44d760932ad59ac48560791aa8520a035ce4ce837d1ccfa34e4642e4a207846e8492421a44414341310ea85090e201028e6c180382d34272082f06086030002bf30a50a8e03068fc2a05ba007640d4dc53604d0f138407ec9100815e40c4903b304050c155568084013da6d18401c9c38083b5043a8466f22aff98546974616e2028746974616e6275696c6465722e78797a29a013ea190d046dbfc659b21f36a6ab93951e04cc4430f979ea1970e828bb02f2878800000000000000008502ebb83b87a0089ec82e885d150f3de310102fb40d5a634dafae12a0dedeaec791ad804c686b80830c0000a08ed15ad048af706cacebcc6acfc3a2a32623f4823fd82a78b9ff5678fdc98b95", + "id": 1 +} diff --git a/bolt-contracts/test/testdata/header_20817618.json b/bolt-contracts/test/testdata/header_20817618.json new file mode 100644 index 000000000..d1a2918d5 --- /dev/null +++ b/bolt-contracts/test/testdata/header_20817618.json @@ -0,0 +1,5 @@ +{ + "jsonrpc": "2.0", + "result": "0xf90262a0b410d12f92ed268b184c1e6523b7d3fea5fcd0ba3f9bc6c6cb9a7e5b1523d225a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347944838b106fce9647bdf1e7877bf73ce8b0bad5f97a0ee16bd5a83967bd2073fae8ddffb5f19aa990fb58e1bc912e5d6e53d3279eb4ba0b152ed53b02e03a51211b1f1260d58f5a90ff87a6f209954bd70e8a108112c13a0b5f80761011b50c50f0a85ea83895cef3454c84b3c982225ea1754c2c921a7aeb9010013e55e8278cd3cb8f26a28f0c437340411c98280c5e10c8fc60b693d4677c7ab61dfaf029410826e020438d82a1b8155cfe98038c9136d31b12aede0853e6510e090e519149281abffa6c80a902fbcf6a93ae62994450e309c340766ebb44ba09cd2f4221accc4372529cc0e49302d3d5228d8601a260cecd4454cfc11591ae2341932dc2084755459cf7629a2b5c8605dba3805f325a13ce42e71d871b21b659b5502f01613a81082cad8e52e25bdce0553a8544c30ce3a0d578d06a09da3248888ec021d6df371118426a783b1b05c52faba46b7d75a3522139a3af0aa702c9a7ae87799101e4da4d547a80053dec2cf8c581152dc56c00810e2116105174f8084013da6d28401c9c3808401c98c1a8466f22b0b98546974616e2028746974616e6275696c6465722e78797a29a05856c04e6738317c95ef58b272f7e8611bc2e31bda48d54d86a8d169d842d7468800000000000000008502d82c7313a07b49c83936b895f1331b9e71b5314c53e8cce3952ec29d015301c72cf87499688083060000a049ccb3d7e4a517a48fe3aaf8d81c73286b0694598d40b5bf13e7428cea4f0f77", + "id": 1 +} diff --git a/bolt-contracts/test/testdata/signed_tx_20785012_1.json b/bolt-contracts/test/testdata/signed_tx_20785012_1.json new file mode 100644 index 000000000..1551308e3 --- /dev/null +++ b/bolt-contracts/test/testdata/signed_tx_20785012_1.json @@ -0,0 +1,6 @@ +{ + "hash": "0x9ec2c56ca36e445a46bc77ca77510f0ef21795d00834269f3752cbd29d63ba1f", + "sender": "0x0D9f5045B604bA0c050b5eb06D0b25d01c525Ea5", + "raw": "0x02f8740181eb8473a20d008507e172a822825208940ff71973b5243005b192d5bcf552fc2532b7bdec88015842095ebc400080c080a07a955ea50e980729226f547245bcf1e0e4ab69467cf82490a4bb0dbe9f35ae84a05f17d1a8f65b60905535ef0c36a8ec6d560c355949aeee2d3ab9791e7fe18fb4", + "blockNumber": 20785012 +} diff --git a/bolt-contracts/test/testdata/signed_tx_20817618_1.json b/bolt-contracts/test/testdata/signed_tx_20817618_1.json new file mode 100644 index 000000000..eb48e2cca --- /dev/null +++ b/bolt-contracts/test/testdata/signed_tx_20817618_1.json @@ -0,0 +1,3 @@ +{ + "raw": "0xf8ab8233858502de59ddef82b59b94dac17f958d2ee523a2206206994597c13d831ec780b844a9059cbb000000000000000000000000331b7670016c6be29d0ac211f6f8aceeb38f0d42000000000000000000000000000000000000000000000000000000000726bbf826a0abf062f0d6ebbb4910392d3bdac921b384f12aea2aa43169a2e866674ae90382a02849acdddc6d86036169e250dea502db87e9a91d6e1ffe8a6a30e54fbb327c5e" +} diff --git a/bolt-contracts/test/testdata/signed_tx_20817618_2.json b/bolt-contracts/test/testdata/signed_tx_20817618_2.json new file mode 100644 index 000000000..1dee3381b --- /dev/null +++ b/bolt-contracts/test/testdata/signed_tx_20817618_2.json @@ -0,0 +1,3 @@ +{ + "raw": "0xf86d8233868504216ca9e78252089415086dc2a2ea37c844e554b65d17c5183f15ab74870326bfc40e1c008025a06db3917cf09951d0107da89bc605ae88623a7690647ac8a5bc3c1638c8c9de59a032a2121306e6a5e1713d99595a65c14cfd1b1978922173cf69bf5f2d4a0b1a60" +} diff --git a/bolt-contracts/test/testdata/signed_tx_20817618_3.json b/bolt-contracts/test/testdata/signed_tx_20817618_3.json new file mode 100644 index 000000000..68339c625 --- /dev/null +++ b/bolt-contracts/test/testdata/signed_tx_20817618_3.json @@ -0,0 +1,3 @@ +{ + "raw": "0xf8ab823387850340375aa882fa2b94dac17f958d2ee523a2206206994597c13d831ec780b844a9059cbb0000000000000000000000009b26d899b1ac6ec07488929c0fdc5b40455275480000000000000000000000000000000000000000000000000000000006e438a025a06194e5ecbf6742f5f7919b39950f452eb0024344977576bb8ceafeff4410e199a007569f10b4d88cd2e185fb30c10f7a507ee6badebfd65b7e4cb884ca8acc5e11" +} diff --git a/bolt-contracts/test/testdata/signed_tx_20817618_4.json b/bolt-contracts/test/testdata/signed_tx_20817618_4.json new file mode 100644 index 000000000..8b2fc67e9 --- /dev/null +++ b/bolt-contracts/test/testdata/signed_tx_20817618_4.json @@ -0,0 +1,3 @@ +{ + "raw": "0xf86d8233888503439d541b8252089415086dc2a2ea37c844e554b65d17c5183f15ab7487027d84f7cd40008026a0d69512e3b47f74fc7d8a2c02f0497580299494544c53860198510433385d6658a043aed084197ff3a3b05e0a0020e4802da7f50c0078dff2e197ad4b0affb36929" +} diff --git a/bolt-contracts/test/testdata/signed_tx_20817618_5.json b/bolt-contracts/test/testdata/signed_tx_20817618_5.json new file mode 100644 index 000000000..eb01965c4 --- /dev/null +++ b/bolt-contracts/test/testdata/signed_tx_20817618_5.json @@ -0,0 +1,3 @@ +{ + "raw": "0xf86d82338985058af3f6c28252089415086dc2a2ea37c844e554b65d17c5183f15ab7487040a50523970008026a05bee04d6213c636154c3c2e87038ffdb3d384c23fb9af65fcc85a890c9cabfe6a02231204d5fe346497f035994acec2dece4efa5109d466e7ce834b7576c237f9e" +} diff --git a/bolt-contracts/test/testdata/transactions/README.md b/bolt-contracts/test/testdata/transactions/README.md new file mode 100644 index 000000000..6ef0d5f28 --- /dev/null +++ b/bolt-contracts/test/testdata/transactions/README.md @@ -0,0 +1,4 @@ +# Ethereum transaction test cases + +This directory contains test cases for Ethereum transactions taken from the +[Ethers.js test suite](https://github.com/ethers-io/ethers.js/blob/5aba4963e3e8ddfc912747076f5b7fe7a743cfe2/testcases/transactions.json.gz). diff --git a/bolt-contracts/test/testdata/transactions/random_0.json b/bolt-contracts/test/testdata/transactions/random_0.json new file mode 100644 index 000000000..6d588f57b --- /dev/null +++ b/bolt-contracts/test/testdata/transactions/random_0.json @@ -0,0 +1,56 @@ +{ + "name": "random-0", + "transaction": { + "to": "0x6Eb893e3466931517a04a17D153a6330c3f2f1dD", + "nonce": 648, + "gasLimit": "0x9d", + "gasPrice": "0x237e", + "maxFeePerGas": "0x346d9246", + "maxPriorityFeePerGas": "0x2c7e63", + "data": "0x889e365e59664fb881554ba1175519b5195b1d20390beb806d8f2cda7893e6f79848195dba4c905db6d7257ffb5eefea35f18ae33c", + "value": "0xc854", + "accessList": [], + "chainId": "0x8404bf1f", + "maxFeePerBlobGas": "0x2ddc4988", + "blobVersionedHashes": [ + "0x01ecbc98026bec75cc99701e9dbf9d1a7edf9db4106e01c171822da8b1f7f392", + "0x016b567cab2d2fa35658d3bac34b81cf3cc667b9a9fbae9ed58ac961dfc5d96b" + ] + }, + "privateKey": "0x2bf558dce44ca98616ee629199215ae5401c97040664637c48e3b74e66bcb3ae", + "unsignedLegacy": "0xf85682028882237e819d946eb893e3466931517a04a17d153a6330c3f2f1dd82c854b5889e365e59664fb881554ba1175519b5195b1d20390beb806d8f2cda7893e6f79848195dba4c905db6d7257ffb5eefea35f18ae33c", + "unsignedEip155": "0xf85d82028882237e819d946eb893e3466931517a04a17d153a6330c3f2f1dd82c854b5889e365e59664fb881554ba1175519b5195b1d20390beb806d8f2cda7893e6f79848195dba4c905db6d7257ffb5eefea35f18ae33c848404bf1f8080", + "unsignedBerlin": "0x01f85c848404bf1f82028882237e819d946eb893e3466931517a04a17d153a6330c3f2f1dd82c854b5889e365e59664fb881554ba1175519b5195b1d20390beb806d8f2cda7893e6f79848195dba4c905db6d7257ffb5eefea35f18ae33cc0", + "unsignedLondon": "0x02f862848404bf1f820288832c7e6384346d9246819d946eb893e3466931517a04a17d153a6330c3f2f1dd82c854b5889e365e59664fb881554ba1175519b5195b1d20390beb806d8f2cda7893e6f79848195dba4c905db6d7257ffb5eefea35f18ae33cc0", + "unsignedCancun": "0x03f8ab848404bf1f820288832c7e6384346d9246819d946eb893e3466931517a04a17d153a6330c3f2f1dd82c854b5889e365e59664fb881554ba1175519b5195b1d20390beb806d8f2cda7893e6f79848195dba4c905db6d7257ffb5eefea35f18ae33cc0842ddc4988f842a001ecbc98026bec75cc99701e9dbf9d1a7edf9db4106e01c171822da8b1f7f392a0016b567cab2d2fa35658d3bac34b81cf3cc667b9a9fbae9ed58ac961dfc5d96b", + "signedLegacy": "0xf89982028882237e819d946eb893e3466931517a04a17d153a6330c3f2f1dd82c854b5889e365e59664fb881554ba1175519b5195b1d20390beb806d8f2cda7893e6f79848195dba4c905db6d7257ffb5eefea35f18ae33c1ba0ba61344dc955b2f0e5dbc3c65e023e1c718539465131acb8a51b2ef75620114aa03366e9f2294bf2eca7322f3954b9b38745c40602239e3d7fa693667206907518", + "signedEip155": "0xf89e82028882237e819d946eb893e3466931517a04a17d153a6330c3f2f1dd82c854b5889e365e59664fb881554ba1175519b5195b1d20390beb806d8f2cda7893e6f79848195dba4c905db6d7257ffb5eefea35f18ae33c850108097e62a0f141fe1b7e2fc1ed5d2b6ea4f04f92053e18f07274e2bda1c6852438c1895229a075553a7ae158a3fd46f75b547e847b59e2876f9a42de7a26d016db33232516de", + "signedBerlin": "0x01f89f848404bf1f82028882237e819d946eb893e3466931517a04a17d153a6330c3f2f1dd82c854b5889e365e59664fb881554ba1175519b5195b1d20390beb806d8f2cda7893e6f79848195dba4c905db6d7257ffb5eefea35f18ae33cc080a0775f29642af1045b40e5beae8e6bce2dc9e222023b7a50372be6824dbb7434fba05dacfff85752a0b9fd860bc751c17235a670d318a8b9494d664c1b87e33ac8dd", + "signedLondon": "0x02f8a5848404bf1f820288832c7e6384346d9246819d946eb893e3466931517a04a17d153a6330c3f2f1dd82c854b5889e365e59664fb881554ba1175519b5195b1d20390beb806d8f2cda7893e6f79848195dba4c905db6d7257ffb5eefea35f18ae33cc080a0f1003f96c6c6620dd46db36d2ae9f12d363947eb0db088c678b6ad1cf494aa6fa06085b5abbf448de5d622dc820da590cfdb6bb77b41c6650962b998a941f8d701", + "signedCancun": "0x03f8ee848404bf1f820288832c7e6384346d9246819d946eb893e3466931517a04a17d153a6330c3f2f1dd82c854b5889e365e59664fb881554ba1175519b5195b1d20390beb806d8f2cda7893e6f79848195dba4c905db6d7257ffb5eefea35f18ae33cc0842ddc4988f842a001ecbc98026bec75cc99701e9dbf9d1a7edf9db4106e01c171822da8b1f7f392a0016b567cab2d2fa35658d3bac34b81cf3cc667b9a9fbae9ed58ac961dfc5d96b01a092109dabedb5dcd157eddaee3ce39c4c467589b338878b3434181d93ea09c099a0015f8b0ca76ad70e4a69a15078232b77871b8c59b30b2279066064dff3afef47", + "signatureLegacy": { + "r": "0xba61344dc955b2f0e5dbc3c65e023e1c718539465131acb8a51b2ef75620114a", + "s": "0x3366e9f2294bf2eca7322f3954b9b38745c40602239e3d7fa693667206907518", + "v": "0x1b" + }, + "signatureEip155": { + "r": "0xf141fe1b7e2fc1ed5d2b6ea4f04f92053e18f07274e2bda1c6852438c1895229", + "s": "0x75553a7ae158a3fd46f75b547e847b59e2876f9a42de7a26d016db33232516de", + "v": "0x108097e62" + }, + "signatureBerlin": { + "r": "0x775f29642af1045b40e5beae8e6bce2dc9e222023b7a50372be6824dbb7434fb", + "s": "0x5dacfff85752a0b9fd860bc751c17235a670d318a8b9494d664c1b87e33ac8dd", + "v": "0x0" + }, + "signatureLondon": { + "r": "0xf1003f96c6c6620dd46db36d2ae9f12d363947eb0db088c678b6ad1cf494aa6f", + "s": "0x6085b5abbf448de5d622dc820da590cfdb6bb77b41c6650962b998a941f8d701", + "v": "0x0" + }, + "signatureCancun": { + "r": "0x92109dabedb5dcd157eddaee3ce39c4c467589b338878b3434181d93ea09c099", + "s": "0x015f8b0ca76ad70e4a69a15078232b77871b8c59b30b2279066064dff3afef47", + "v": "0x1" + } +} diff --git a/bolt-contracts/test/testdata/transactions/random_1.json b/bolt-contracts/test/testdata/transactions/random_1.json new file mode 100644 index 000000000..2fa991680 --- /dev/null +++ b/bolt-contracts/test/testdata/transactions/random_1.json @@ -0,0 +1,55 @@ +{ + "name": "random-1", + "transaction": { + "to": "0x2E806e1F4B2C1Bc500E41b882213648Af39Dd4A2", + "nonce": 613, + "gasLimit": "0x3a", + "gasPrice": "0xf6", + "maxFeePerGas": "0x76cf803ef8c9", + "maxPriorityFeePerGas": "0x45", + "data": "0xb2f1646d", + "value": "0x45", + "accessList": [], + "chainId": "0xdf", + "maxFeePerBlobGas": "0x2e070f634f12e6", + "blobVersionedHashes": [ + "0x0155aa189900c1c076d1a6ca7d1e65f09e62c5e4f0e929c0607c1ab2582b658a" + ] + }, + "privateKey": "0xdde87f54e7dc562364d8559a1777b684a9121d132c4b4237e2534bd5a090166c", + "unsignedLegacy": "0xe182026581f63a942e806e1f4b2c1bc500e41b882213648af39dd4a24584b2f1646d", + "unsignedEip155": "0xe582026581f63a942e806e1f4b2c1bc500e41b882213648af39dd4a24584b2f1646d81df8080", + "unsignedBerlin": "0x01e481df82026581f63a942e806e1f4b2c1bc500e41b882213648af39dd4a24584b2f1646dc0", + "unsignedLondon": "0x02ea81df820265458676cf803ef8c93a942e806e1f4b2c1bc500e41b882213648af39dd4a24584b2f1646dc0", + "unsignedCancun": "0x03f85481df820265458676cf803ef8c93a942e806e1f4b2c1bc500e41b882213648af39dd4a24584b2f1646dc0872e070f634f12e6e1a00155aa189900c1c076d1a6ca7d1e65f09e62c5e4f0e929c0607c1ab2582b658a", + "signedLegacy": "0xf86482026581f63a942e806e1f4b2c1bc500e41b882213648af39dd4a24584b2f1646d1ca0c02ac8b415eefbe6ea2437788d50774aad5eda4c66a8c146b599b08ab67fac62a0491bf160ad32783a281f77c86f12483e1ce9a661680f05e54f6836f3048338f8", + "signedEip155": "0xf86682026581f63a942e806e1f4b2c1bc500e41b882213648af39dd4a24584b2f1646d8201e1a0f08888fa9e155e7bbab69c3c1b30f8979de3dabcfe4ee848c99c5130a2dc6628a05e9b300a85b9ca65dabfa1b4718171abbd8393e9f9f01c6b0c628b575808bc84", + "signedBerlin": "0x01f86781df82026581f63a942e806e1f4b2c1bc500e41b882213648af39dd4a24584b2f1646dc001a0f87200f49113aac563b703d7056f0bc21961cf72d4bd16ce814bb972182d95b8a023fca36c0c37daadeb1d067efb3e574089c77aa1832f83b6e6a5d48f7900ca33", + "signedLondon": "0x02f86d81df820265458676cf803ef8c93a942e806e1f4b2c1bc500e41b882213648af39dd4a24584b2f1646dc001a00633d97e07870e5c1fa9e4f9f8beb169396225d5c691fddcf41ce3a3ea121e1ca0057f715c06932e29d5a4d8b70019c974a34d59e44e4ded4df7f5726ec8e87780", + "signedCancun": "0x03f89781df820265458676cf803ef8c93a942e806e1f4b2c1bc500e41b882213648af39dd4a24584b2f1646dc0872e070f634f12e6e1a00155aa189900c1c076d1a6ca7d1e65f09e62c5e4f0e929c0607c1ab2582b658a80a0392799f17b5f8c1cc8bfb7a29381a717c1b1298e4e3b9218be14c05ec32e50a3a03678448fa6438aee13af5e189fd4e45bd13b014882bc66fdcd9897e9732fb93b", + "signatureLegacy": { + "r": "0xc02ac8b415eefbe6ea2437788d50774aad5eda4c66a8c146b599b08ab67fac62", + "s": "0x491bf160ad32783a281f77c86f12483e1ce9a661680f05e54f6836f3048338f8", + "v": "0x1c" + }, + "signatureEip155": { + "r": "0xf08888fa9e155e7bbab69c3c1b30f8979de3dabcfe4ee848c99c5130a2dc6628", + "s": "0x5e9b300a85b9ca65dabfa1b4718171abbd8393e9f9f01c6b0c628b575808bc84", + "v": "0x1e1" + }, + "signatureBerlin": { + "r": "0xf87200f49113aac563b703d7056f0bc21961cf72d4bd16ce814bb972182d95b8", + "s": "0x23fca36c0c37daadeb1d067efb3e574089c77aa1832f83b6e6a5d48f7900ca33", + "v": "0x1" + }, + "signatureLondon": { + "r": "0x0633d97e07870e5c1fa9e4f9f8beb169396225d5c691fddcf41ce3a3ea121e1c", + "s": "0x057f715c06932e29d5a4d8b70019c974a34d59e44e4ded4df7f5726ec8e87780", + "v": "0x1" + }, + "signatureCancun": { + "r": "0x392799f17b5f8c1cc8bfb7a29381a717c1b1298e4e3b9218be14c05ec32e50a3", + "s": "0x3678448fa6438aee13af5e189fd4e45bd13b014882bc66fdcd9897e9732fb93b", + "v": "0x0" + } +} diff --git a/bolt-contracts/test/testdata/transactions/random_10.json b/bolt-contracts/test/testdata/transactions/random_10.json new file mode 100644 index 000000000..d9d5ee86d --- /dev/null +++ b/bolt-contracts/test/testdata/transactions/random_10.json @@ -0,0 +1,63 @@ +{ + "name": "random-10", + "transaction": { + "to": "0xe80B2a2b7a84c886319faB83dF55E63C7539D2E7", + "nonce": 11, + "gasLimit": "0x66745b0460", + "gasPrice": "0x19b27a8a", + "maxFeePerGas": "0x9bc10525", + "maxPriorityFeePerGas": "0xde6421", + "data": "0xc67a6a742250a5b9144dbfac7342e66a31df07bc2de714d5700d4a328600e6c274a8713cd12df6c64ca6f8088e1a49108dc171457cfcc9a4daf0625786bdf689e568fd61a63a953184", + "value": "0x452f964f", + "accessList": [ + { + "address": "0x9C1010eBD297f0af2110e4Be1356a99e088B70b0", + "storageKeys": [ + "0x8f4489956d5c84285dd2337de059733fd7caff5e3bc562d2e19f4c8416f7adcd", + "0xecacb47c7f3b429a188ae196d5f6905999bdd4909022d3d5b3566399ef44b513" + ] + } + ], + "chainId": "0x4e33268bd8", + "maxFeePerBlobGas": "0x5b8180c72d", + "blobVersionedHashes": [ + "0x01fe39328d1867749d23972d03954f13d7025f6c7db432c678156c81b87ca044" + ] + }, + "privateKey": "0x937f09851dd891844eee05f469dc776fe07a398b47ee3d064460f74fb00b8454", + "unsignedLegacy": "0xf8710b8419b27a8a8566745b046094e80b2a2b7a84c886319fab83df55e63c7539d2e784452f964fb849c67a6a742250a5b9144dbfac7342e66a31df07bc2de714d5700d4a328600e6c274a8713cd12df6c64ca6f8088e1a49108dc171457cfcc9a4daf0625786bdf689e568fd61a63a953184", + "unsignedEip155": "0xf8790b8419b27a8a8566745b046094e80b2a2b7a84c886319fab83df55e63c7539d2e784452f964fb849c67a6a742250a5b9144dbfac7342e66a31df07bc2de714d5700d4a328600e6c274a8713cd12df6c64ca6f8088e1a49108dc171457cfcc9a4daf0625786bdf689e568fd61a63a953184854e33268bd88080", + "unsignedBerlin": "0x01f8d4854e33268bd80b8419b27a8a8566745b046094e80b2a2b7a84c886319fab83df55e63c7539d2e784452f964fb849c67a6a742250a5b9144dbfac7342e66a31df07bc2de714d5700d4a328600e6c274a8713cd12df6c64ca6f8088e1a49108dc171457cfcc9a4daf0625786bdf689e568fd61a63a953184f85bf859949c1010ebd297f0af2110e4be1356a99e088b70b0f842a08f4489956d5c84285dd2337de059733fd7caff5e3bc562d2e19f4c8416f7adcda0ecacb47c7f3b429a188ae196d5f6905999bdd4909022d3d5b3566399ef44b513", + "unsignedLondon": "0x02f8d8854e33268bd80b83de6421849bc105258566745b046094e80b2a2b7a84c886319fab83df55e63c7539d2e784452f964fb849c67a6a742250a5b9144dbfac7342e66a31df07bc2de714d5700d4a328600e6c274a8713cd12df6c64ca6f8088e1a49108dc171457cfcc9a4daf0625786bdf689e568fd61a63a953184f85bf859949c1010ebd297f0af2110e4be1356a99e088b70b0f842a08f4489956d5c84285dd2337de059733fd7caff5e3bc562d2e19f4c8416f7adcda0ecacb47c7f3b429a188ae196d5f6905999bdd4909022d3d5b3566399ef44b513", + "unsignedCancun": "0x03f90100854e33268bd80b83de6421849bc105258566745b046094e80b2a2b7a84c886319fab83df55e63c7539d2e784452f964fb849c67a6a742250a5b9144dbfac7342e66a31df07bc2de714d5700d4a328600e6c274a8713cd12df6c64ca6f8088e1a49108dc171457cfcc9a4daf0625786bdf689e568fd61a63a953184f85bf859949c1010ebd297f0af2110e4be1356a99e088b70b0f842a08f4489956d5c84285dd2337de059733fd7caff5e3bc562d2e19f4c8416f7adcda0ecacb47c7f3b429a188ae196d5f6905999bdd4909022d3d5b3566399ef44b513855b8180c72de1a001fe39328d1867749d23972d03954f13d7025f6c7db432c678156c81b87ca044", + "signedLegacy": "0xf8b40b8419b27a8a8566745b046094e80b2a2b7a84c886319fab83df55e63c7539d2e784452f964fb849c67a6a742250a5b9144dbfac7342e66a31df07bc2de714d5700d4a328600e6c274a8713cd12df6c64ca6f8088e1a49108dc171457cfcc9a4daf0625786bdf689e568fd61a63a9531841ba0f9c1a46fcbbeaa4915050ffc5e33043e372ab5b8e55e40c99bcea824b07ae62ba03a78b26033934d062ab52ac1b527df5b27bd08ced030d392832c89ca779dcc93", + "signedEip155": "0xf8b90b8419b27a8a8566745b046094e80b2a2b7a84c886319fab83df55e63c7539d2e784452f964fb849c67a6a742250a5b9144dbfac7342e66a31df07bc2de714d5700d4a328600e6c274a8713cd12df6c64ca6f8088e1a49108dc171457cfcc9a4daf0625786bdf689e568fd61a63a953184859c664d17d4a008a210072105a1ad68dc2b2811abd43dcd1dabaa2c2d2a2f669fb0dea7903535a028817f47aa0014375ed11fde0b733a5b3b648e5ffb2f66e3b4a0fc8db3000bcf", + "signedBerlin": "0x01f90117854e33268bd80b8419b27a8a8566745b046094e80b2a2b7a84c886319fab83df55e63c7539d2e784452f964fb849c67a6a742250a5b9144dbfac7342e66a31df07bc2de714d5700d4a328600e6c274a8713cd12df6c64ca6f8088e1a49108dc171457cfcc9a4daf0625786bdf689e568fd61a63a953184f85bf859949c1010ebd297f0af2110e4be1356a99e088b70b0f842a08f4489956d5c84285dd2337de059733fd7caff5e3bc562d2e19f4c8416f7adcda0ecacb47c7f3b429a188ae196d5f6905999bdd4909022d3d5b3566399ef44b51380a0a972d4365dac9b214ae1c1cdbdb70d5c10877eac1edaee2398012e6f084b99fca0184054b4a0b98483664b9469564eb21a28439982883f61bb1e538e968f74ce9d", + "signedLondon": "0x02f9011b854e33268bd80b83de6421849bc105258566745b046094e80b2a2b7a84c886319fab83df55e63c7539d2e784452f964fb849c67a6a742250a5b9144dbfac7342e66a31df07bc2de714d5700d4a328600e6c274a8713cd12df6c64ca6f8088e1a49108dc171457cfcc9a4daf0625786bdf689e568fd61a63a953184f85bf859949c1010ebd297f0af2110e4be1356a99e088b70b0f842a08f4489956d5c84285dd2337de059733fd7caff5e3bc562d2e19f4c8416f7adcda0ecacb47c7f3b429a188ae196d5f6905999bdd4909022d3d5b3566399ef44b51301a0abb1f9fd744bd588e3cc73caf61ea9a023aa8b486356f5bc97910ca01fe6453ca062ae4642f50df606c7587957e497e3867510554f88ba57a2bb93a4f8883329e5", + "signedCancun": "0x03f90143854e33268bd80b83de6421849bc105258566745b046094e80b2a2b7a84c886319fab83df55e63c7539d2e784452f964fb849c67a6a742250a5b9144dbfac7342e66a31df07bc2de714d5700d4a328600e6c274a8713cd12df6c64ca6f8088e1a49108dc171457cfcc9a4daf0625786bdf689e568fd61a63a953184f85bf859949c1010ebd297f0af2110e4be1356a99e088b70b0f842a08f4489956d5c84285dd2337de059733fd7caff5e3bc562d2e19f4c8416f7adcda0ecacb47c7f3b429a188ae196d5f6905999bdd4909022d3d5b3566399ef44b513855b8180c72de1a001fe39328d1867749d23972d03954f13d7025f6c7db432c678156c81b87ca04480a042db40cd1ebb1117bc52c8a9d2d467b008203571b1be9ad32db67ba24758cd27a024ac6a399e3a135a5986333a933bd431df1969ca12fdc5217aa7cc778110898b", + "signatureLegacy": { + "r": "0xf9c1a46fcbbeaa4915050ffc5e33043e372ab5b8e55e40c99bcea824b07ae62b", + "s": "0x3a78b26033934d062ab52ac1b527df5b27bd08ced030d392832c89ca779dcc93", + "v": "0x1b" + }, + "signatureEip155": { + "r": "0x08a210072105a1ad68dc2b2811abd43dcd1dabaa2c2d2a2f669fb0dea7903535", + "s": "0x28817f47aa0014375ed11fde0b733a5b3b648e5ffb2f66e3b4a0fc8db3000bcf", + "v": "0x9c664d17d4" + }, + "signatureBerlin": { + "r": "0xa972d4365dac9b214ae1c1cdbdb70d5c10877eac1edaee2398012e6f084b99fc", + "s": "0x184054b4a0b98483664b9469564eb21a28439982883f61bb1e538e968f74ce9d", + "v": "0x0" + }, + "signatureLondon": { + "r": "0xabb1f9fd744bd588e3cc73caf61ea9a023aa8b486356f5bc97910ca01fe6453c", + "s": "0x62ae4642f50df606c7587957e497e3867510554f88ba57a2bb93a4f8883329e5", + "v": "0x1" + }, + "signatureCancun": { + "r": "0x42db40cd1ebb1117bc52c8a9d2d467b008203571b1be9ad32db67ba24758cd27", + "s": "0x24ac6a399e3a135a5986333a933bd431df1969ca12fdc5217aa7cc778110898b", + "v": "0x0" + } +} diff --git a/bolt-contracts/test/testdata/transactions/random_11.json b/bolt-contracts/test/testdata/transactions/random_11.json new file mode 100644 index 000000000..9af3b0e7f --- /dev/null +++ b/bolt-contracts/test/testdata/transactions/random_11.json @@ -0,0 +1,78 @@ +{ + "name": "random-11", + "transaction": { + "to": "0x84c0Ab0b0Bb392Fc645293Fd9F88ce734F520C3d", + "nonce": 152, + "gasLimit": "0x27", + "gasPrice": "0x5d0f93b5b0", + "maxFeePerGas": "0x5966bba5", + "maxPriorityFeePerGas": "0x5017", + "data": "0x0c5c6b4f63948ee9bd3027", + "value": "0x6e555229e2", + "accessList": [ + { + "address": "0x43b314A73dd9368a2C0227F3cc1315818Db914D8", + "storageKeys": [ + "0xe0e82939483d467088aae991d873c23719321b29648506c612ad2a06194051ef" + ] + }, + { + "address": "0x4eA79A74f60B6Bf2688B46fa9Ba5db593098740b", + "storageKeys": [ + "0xe0e82939483d467088aae991d873c23719321b29648506c612ad2a06194051ef" + ] + }, + { + "address": "0x6eB7Fd5fdfD7F48e137da1b368287f2C6Cc12b45", + "storageKeys": [ + "0xe0e82939483d467088aae991d873c23719321b29648506c612ad2a06194051ef" + ] + }, + { + "address": "0xb8EcE452e0840B0b30D0BCB4d1400B545cfb746F", + "storageKeys": [ + "0xe0e82939483d467088aae991d873c23719321b29648506c612ad2a06194051ef" + ] + } + ], + "chainId": "0xdb4137d499", + "maxFeePerBlobGas": "0x1ba30e3498", + "blobVersionedHashes": [] + }, + "privateKey": "0x335dca53da48dba1ac89b63618acd056621774dd60a7ef7f891f64c8c659e7b3", + "unsignedLegacy": "0xf08198855d0f93b5b0279484c0ab0b0bb392fc645293fd9f88ce734f520c3d856e555229e28b0c5c6b4f63948ee9bd3027", + "unsignedEip155": "0xf8388198855d0f93b5b0279484c0ab0b0bb392fc645293fd9f88ce734f520c3d856e555229e28b0c5c6b4f63948ee9bd302785db4137d4998080", + "unsignedBerlin": "0x01f9011885db4137d4998198855d0f93b5b0279484c0ab0b0bb392fc645293fd9f88ce734f520c3d856e555229e28b0c5c6b4f63948ee9bd3027f8e0f79443b314a73dd9368a2c0227f3cc1315818db914d8e1a0e0e82939483d467088aae991d873c23719321b29648506c612ad2a06194051eff7944ea79a74f60b6bf2688b46fa9ba5db593098740be1a0e0e82939483d467088aae991d873c23719321b29648506c612ad2a06194051eff7946eb7fd5fdfd7f48e137da1b368287f2c6cc12b45e1a0e0e82939483d467088aae991d873c23719321b29648506c612ad2a06194051eff794b8ece452e0840b0b30d0bcb4d1400b545cfb746fe1a0e0e82939483d467088aae991d873c23719321b29648506c612ad2a06194051ef", + "unsignedLondon": "0x02f9011a85db4137d4998198825017845966bba5279484c0ab0b0bb392fc645293fd9f88ce734f520c3d856e555229e28b0c5c6b4f63948ee9bd3027f8e0f79443b314a73dd9368a2c0227f3cc1315818db914d8e1a0e0e82939483d467088aae991d873c23719321b29648506c612ad2a06194051eff7944ea79a74f60b6bf2688b46fa9ba5db593098740be1a0e0e82939483d467088aae991d873c23719321b29648506c612ad2a06194051eff7946eb7fd5fdfd7f48e137da1b368287f2c6cc12b45e1a0e0e82939483d467088aae991d873c23719321b29648506c612ad2a06194051eff794b8ece452e0840b0b30d0bcb4d1400b545cfb746fe1a0e0e82939483d467088aae991d873c23719321b29648506c612ad2a06194051ef", + "unsignedCancun": "0x03f9012185db4137d4998198825017845966bba5279484c0ab0b0bb392fc645293fd9f88ce734f520c3d856e555229e28b0c5c6b4f63948ee9bd3027f8e0f79443b314a73dd9368a2c0227f3cc1315818db914d8e1a0e0e82939483d467088aae991d873c23719321b29648506c612ad2a06194051eff7944ea79a74f60b6bf2688b46fa9ba5db593098740be1a0e0e82939483d467088aae991d873c23719321b29648506c612ad2a06194051eff7946eb7fd5fdfd7f48e137da1b368287f2c6cc12b45e1a0e0e82939483d467088aae991d873c23719321b29648506c612ad2a06194051eff794b8ece452e0840b0b30d0bcb4d1400b545cfb746fe1a0e0e82939483d467088aae991d873c23719321b29648506c612ad2a06194051ef851ba30e3498c0", + "signedLegacy": "0xf8738198855d0f93b5b0279484c0ab0b0bb392fc645293fd9f88ce734f520c3d856e555229e28b0c5c6b4f63948ee9bd30271ca07cf7847c9f6bf2ac5b12866c8977bffe203b8674bc35a0c789e60913803298c4a02388e993aa07c5f28a6e31bd8189597ccd314b6ff803b202cc700add8bb907e8", + "signedEip155": "0xf8798198855d0f93b5b0279484c0ab0b0bb392fc645293fd9f88ce734f520c3d856e555229e28b0c5c6b4f63948ee9bd30278601b6826fa956a016821219866f3c43a9d6705be5c99bed3443b663be66036ed3cd6d1b922cf6dca013496a38cc9a45ae5f04a7539adfd4961b6527d8ca203438db0538fa13ee07a3", + "signedBerlin": "0x01f9015b85db4137d4998198855d0f93b5b0279484c0ab0b0bb392fc645293fd9f88ce734f520c3d856e555229e28b0c5c6b4f63948ee9bd3027f8e0f79443b314a73dd9368a2c0227f3cc1315818db914d8e1a0e0e82939483d467088aae991d873c23719321b29648506c612ad2a06194051eff7944ea79a74f60b6bf2688b46fa9ba5db593098740be1a0e0e82939483d467088aae991d873c23719321b29648506c612ad2a06194051eff7946eb7fd5fdfd7f48e137da1b368287f2c6cc12b45e1a0e0e82939483d467088aae991d873c23719321b29648506c612ad2a06194051eff794b8ece452e0840b0b30d0bcb4d1400b545cfb746fe1a0e0e82939483d467088aae991d873c23719321b29648506c612ad2a06194051ef80a0c09b02589bf56592b078fa358f37f0349debe1561378ddda9c195c8cbbad6aaca02ce613405a1a0fb0cf7c11e2ae6a13d16dc05b4b54fa29fe659f0d3486b0748d", + "signedLondon": "0x02f9015d85db4137d4998198825017845966bba5279484c0ab0b0bb392fc645293fd9f88ce734f520c3d856e555229e28b0c5c6b4f63948ee9bd3027f8e0f79443b314a73dd9368a2c0227f3cc1315818db914d8e1a0e0e82939483d467088aae991d873c23719321b29648506c612ad2a06194051eff7944ea79a74f60b6bf2688b46fa9ba5db593098740be1a0e0e82939483d467088aae991d873c23719321b29648506c612ad2a06194051eff7946eb7fd5fdfd7f48e137da1b368287f2c6cc12b45e1a0e0e82939483d467088aae991d873c23719321b29648506c612ad2a06194051eff794b8ece452e0840b0b30d0bcb4d1400b545cfb746fe1a0e0e82939483d467088aae991d873c23719321b29648506c612ad2a06194051ef01a00291267b646d31657e80c87018fac49779ec0b4b8acfea5b5e5875b2bc574389a028c4e6e7dc6aa417199de1a0ba1deb90fbd9c42818cd926c4d50c5de61cf733a", + "signedCancun": "0x03f9016485db4137d4998198825017845966bba5279484c0ab0b0bb392fc645293fd9f88ce734f520c3d856e555229e28b0c5c6b4f63948ee9bd3027f8e0f79443b314a73dd9368a2c0227f3cc1315818db914d8e1a0e0e82939483d467088aae991d873c23719321b29648506c612ad2a06194051eff7944ea79a74f60b6bf2688b46fa9ba5db593098740be1a0e0e82939483d467088aae991d873c23719321b29648506c612ad2a06194051eff7946eb7fd5fdfd7f48e137da1b368287f2c6cc12b45e1a0e0e82939483d467088aae991d873c23719321b29648506c612ad2a06194051eff794b8ece452e0840b0b30d0bcb4d1400b545cfb746fe1a0e0e82939483d467088aae991d873c23719321b29648506c612ad2a06194051ef851ba30e3498c080a0b0b4ec97964ed7aa3a66e0053ab4163c8b346b2150f581ae09ac7c62f810543ba00e27aac4faaa5fc4634ed8b64f50aaf74156b90d561db528b255242fe624ae0e", + "signatureLegacy": { + "r": "0x7cf7847c9f6bf2ac5b12866c8977bffe203b8674bc35a0c789e60913803298c4", + "s": "0x2388e993aa07c5f28a6e31bd8189597ccd314b6ff803b202cc700add8bb907e8", + "v": "0x1c" + }, + "signatureEip155": { + "r": "0x16821219866f3c43a9d6705be5c99bed3443b663be66036ed3cd6d1b922cf6dc", + "s": "0x13496a38cc9a45ae5f04a7539adfd4961b6527d8ca203438db0538fa13ee07a3", + "v": "0x1b6826fa956" + }, + "signatureBerlin": { + "r": "0xc09b02589bf56592b078fa358f37f0349debe1561378ddda9c195c8cbbad6aac", + "s": "0x2ce613405a1a0fb0cf7c11e2ae6a13d16dc05b4b54fa29fe659f0d3486b0748d", + "v": "0x0" + }, + "signatureLondon": { + "r": "0x0291267b646d31657e80c87018fac49779ec0b4b8acfea5b5e5875b2bc574389", + "s": "0x28c4e6e7dc6aa417199de1a0ba1deb90fbd9c42818cd926c4d50c5de61cf733a", + "v": "0x1" + }, + "signatureCancun": { + "r": "0xb0b4ec97964ed7aa3a66e0053ab4163c8b346b2150f581ae09ac7c62f810543b", + "s": "0x0e27aac4faaa5fc4634ed8b64f50aaf74156b90d561db528b255242fe624ae0e", + "v": "0x0" + } +} diff --git a/bolt-contracts/test/testdata/transactions/random_12.json b/bolt-contracts/test/testdata/transactions/random_12.json new file mode 100644 index 000000000..7c104ce7a --- /dev/null +++ b/bolt-contracts/test/testdata/transactions/random_12.json @@ -0,0 +1,78 @@ +{ + "name": "random-12", + "transaction": { + "to": "0x4F18B878B82C8931c4A6d67FC28b857AaFf6D764", + "nonce": 547, + "gasLimit": "0xfa26c05691", + "gasPrice": "0x892d19ea64", + "maxFeePerGas": "0x6bf86f84", + "maxPriorityFeePerGas": "0xf627", + "data": "0x57d10e77f6d8cd44acf5914b7b73eca98b9afe235e07b9db2d1712c4adc287e3e474a377404de19203f5127b676c88c839a76e15e665f183709a3460025556e5fd5f2a2f17f8", + "value": "0x64660ccd", + "accessList": [ + { + "address": "0x275677ef49D8315ed641fe3b6519C4dC3f099112", + "storageKeys": [ + "0x819da32335b67b104a5c786c41f2811e1167a1245b4998eb9297d0b777db457b", + "0xb76e298e9f4a7cb7e30809076edbb99bcbc310ec27d7a563cbc24cddd0a2ae6d" + ] + }, + { + "address": "0xa02363D8A2921bF57F7Dc48B42Ccc04548554E81", + "storageKeys": [ + "0x819da32335b67b104a5c786c41f2811e1167a1245b4998eb9297d0b777db457b", + "0xb76e298e9f4a7cb7e30809076edbb99bcbc310ec27d7a563cbc24cddd0a2ae6d" + ] + }, + { + "address": "0x8D6796be448CEFf2333157dEE56C74AF0aFb0BD5", + "storageKeys": [ + "0x819da32335b67b104a5c786c41f2811e1167a1245b4998eb9297d0b777db457b", + "0xb76e298e9f4a7cb7e30809076edbb99bcbc310ec27d7a563cbc24cddd0a2ae6d" + ] + } + ], + "chainId": "0x44af2c", + "maxFeePerBlobGas": "0x12027a6466afd6", + "blobVersionedHashes": [ + "0x01c67453c9a170645c5784da796dc3aaaf4933d5e8d9c99ae1fd3d8032b0631f", + "0x016865cb3a8b41dabbd03465926a9bedfb91914dd973d05e325f3d77a7db1d8e" + ] + }, + "privateKey": "0x3d04ff4d2657142a58d3cc787e089ca7f20a6b66776ed5b04dc9d4dceef253d7", + "unsignedLegacy": "0xf87182022385892d19ea6485fa26c05691944f18b878b82c8931c4a6d67fc28b857aaff6d7648464660ccdb84657d10e77f6d8cd44acf5914b7b73eca98b9afe235e07b9db2d1712c4adc287e3e474a377404de19203f5127b676c88c839a76e15e665f183709a3460025556e5fd5f2a2f17f8", + "unsignedEip155": "0xf87782022385892d19ea6485fa26c05691944f18b878b82c8931c4a6d67fc28b857aaff6d7648464660ccdb84657d10e77f6d8cd44acf5914b7b73eca98b9afe235e07b9db2d1712c4adc287e3e474a377404de19203f5127b676c88c839a76e15e665f183709a3460025556e5fd5f2a2f17f88344af2c8080", + "unsignedBerlin": "0x01f901898344af2c82022385892d19ea6485fa26c05691944f18b878b82c8931c4a6d67fc28b857aaff6d7648464660ccdb84657d10e77f6d8cd44acf5914b7b73eca98b9afe235e07b9db2d1712c4adc287e3e474a377404de19203f5127b676c88c839a76e15e665f183709a3460025556e5fd5f2a2f17f8f90111f85994275677ef49d8315ed641fe3b6519c4dc3f099112f842a0819da32335b67b104a5c786c41f2811e1167a1245b4998eb9297d0b777db457ba0b76e298e9f4a7cb7e30809076edbb99bcbc310ec27d7a563cbc24cddd0a2ae6df85994a02363d8a2921bf57f7dc48b42ccc04548554e81f842a0819da32335b67b104a5c786c41f2811e1167a1245b4998eb9297d0b777db457ba0b76e298e9f4a7cb7e30809076edbb99bcbc310ec27d7a563cbc24cddd0a2ae6df859948d6796be448ceff2333157dee56c74af0afb0bd5f842a0819da32335b67b104a5c786c41f2811e1167a1245b4998eb9297d0b777db457ba0b76e298e9f4a7cb7e30809076edbb99bcbc310ec27d7a563cbc24cddd0a2ae6d", + "unsignedLondon": "0x02f9018b8344af2c82022382f627846bf86f8485fa26c05691944f18b878b82c8931c4a6d67fc28b857aaff6d7648464660ccdb84657d10e77f6d8cd44acf5914b7b73eca98b9afe235e07b9db2d1712c4adc287e3e474a377404de19203f5127b676c88c839a76e15e665f183709a3460025556e5fd5f2a2f17f8f90111f85994275677ef49d8315ed641fe3b6519c4dc3f099112f842a0819da32335b67b104a5c786c41f2811e1167a1245b4998eb9297d0b777db457ba0b76e298e9f4a7cb7e30809076edbb99bcbc310ec27d7a563cbc24cddd0a2ae6df85994a02363d8a2921bf57f7dc48b42ccc04548554e81f842a0819da32335b67b104a5c786c41f2811e1167a1245b4998eb9297d0b777db457ba0b76e298e9f4a7cb7e30809076edbb99bcbc310ec27d7a563cbc24cddd0a2ae6df859948d6796be448ceff2333157dee56c74af0afb0bd5f842a0819da32335b67b104a5c786c41f2811e1167a1245b4998eb9297d0b777db457ba0b76e298e9f4a7cb7e30809076edbb99bcbc310ec27d7a563cbc24cddd0a2ae6d", + "unsignedCancun": "0x03f901d78344af2c82022382f627846bf86f8485fa26c05691944f18b878b82c8931c4a6d67fc28b857aaff6d7648464660ccdb84657d10e77f6d8cd44acf5914b7b73eca98b9afe235e07b9db2d1712c4adc287e3e474a377404de19203f5127b676c88c839a76e15e665f183709a3460025556e5fd5f2a2f17f8f90111f85994275677ef49d8315ed641fe3b6519c4dc3f099112f842a0819da32335b67b104a5c786c41f2811e1167a1245b4998eb9297d0b777db457ba0b76e298e9f4a7cb7e30809076edbb99bcbc310ec27d7a563cbc24cddd0a2ae6df85994a02363d8a2921bf57f7dc48b42ccc04548554e81f842a0819da32335b67b104a5c786c41f2811e1167a1245b4998eb9297d0b777db457ba0b76e298e9f4a7cb7e30809076edbb99bcbc310ec27d7a563cbc24cddd0a2ae6df859948d6796be448ceff2333157dee56c74af0afb0bd5f842a0819da32335b67b104a5c786c41f2811e1167a1245b4998eb9297d0b777db457ba0b76e298e9f4a7cb7e30809076edbb99bcbc310ec27d7a563cbc24cddd0a2ae6d8712027a6466afd6f842a001c67453c9a170645c5784da796dc3aaaf4933d5e8d9c99ae1fd3d8032b0631fa0016865cb3a8b41dabbd03465926a9bedfb91914dd973d05e325f3d77a7db1d8e", + "signedLegacy": "0xf8b482022385892d19ea6485fa26c05691944f18b878b82c8931c4a6d67fc28b857aaff6d7648464660ccdb84657d10e77f6d8cd44acf5914b7b73eca98b9afe235e07b9db2d1712c4adc287e3e474a377404de19203f5127b676c88c839a76e15e665f183709a3460025556e5fd5f2a2f17f81ca01c1a043ba56d5a6edec9efd4b24695f996204caec12978d32bf086d8e21bcf96a04a5144d22ab0ddd5335bc3e1b22cd24e66bd742865f1a4447e2675ee00a2227f", + "signedEip155": "0xf8b782022385892d19ea6485fa26c05691944f18b878b82c8931c4a6d67fc28b857aaff6d7648464660ccdb84657d10e77f6d8cd44acf5914b7b73eca98b9afe235e07b9db2d1712c4adc287e3e474a377404de19203f5127b676c88c839a76e15e665f183709a3460025556e5fd5f2a2f17f883895e7ca06d75cb007c3b91b7465c095e28b841d93da110bbb66280a0f5920083ad4e80f2a026b07e9427fd6dc320c23ced73fb8aae9872af808f20db04f0bb67287da0373b", + "signedBerlin": "0x01f901cc8344af2c82022385892d19ea6485fa26c05691944f18b878b82c8931c4a6d67fc28b857aaff6d7648464660ccdb84657d10e77f6d8cd44acf5914b7b73eca98b9afe235e07b9db2d1712c4adc287e3e474a377404de19203f5127b676c88c839a76e15e665f183709a3460025556e5fd5f2a2f17f8f90111f85994275677ef49d8315ed641fe3b6519c4dc3f099112f842a0819da32335b67b104a5c786c41f2811e1167a1245b4998eb9297d0b777db457ba0b76e298e9f4a7cb7e30809076edbb99bcbc310ec27d7a563cbc24cddd0a2ae6df85994a02363d8a2921bf57f7dc48b42ccc04548554e81f842a0819da32335b67b104a5c786c41f2811e1167a1245b4998eb9297d0b777db457ba0b76e298e9f4a7cb7e30809076edbb99bcbc310ec27d7a563cbc24cddd0a2ae6df859948d6796be448ceff2333157dee56c74af0afb0bd5f842a0819da32335b67b104a5c786c41f2811e1167a1245b4998eb9297d0b777db457ba0b76e298e9f4a7cb7e30809076edbb99bcbc310ec27d7a563cbc24cddd0a2ae6d80a0f69b776f4eee48cf9a220abb4e132d2cd915b320e409fe99376535aa6984f69ea045b9071a002c820e368070525657f2cd47053ea600d24c1da75b7adbd076cfcf", + "signedLondon": "0x02f901ce8344af2c82022382f627846bf86f8485fa26c05691944f18b878b82c8931c4a6d67fc28b857aaff6d7648464660ccdb84657d10e77f6d8cd44acf5914b7b73eca98b9afe235e07b9db2d1712c4adc287e3e474a377404de19203f5127b676c88c839a76e15e665f183709a3460025556e5fd5f2a2f17f8f90111f85994275677ef49d8315ed641fe3b6519c4dc3f099112f842a0819da32335b67b104a5c786c41f2811e1167a1245b4998eb9297d0b777db457ba0b76e298e9f4a7cb7e30809076edbb99bcbc310ec27d7a563cbc24cddd0a2ae6df85994a02363d8a2921bf57f7dc48b42ccc04548554e81f842a0819da32335b67b104a5c786c41f2811e1167a1245b4998eb9297d0b777db457ba0b76e298e9f4a7cb7e30809076edbb99bcbc310ec27d7a563cbc24cddd0a2ae6df859948d6796be448ceff2333157dee56c74af0afb0bd5f842a0819da32335b67b104a5c786c41f2811e1167a1245b4998eb9297d0b777db457ba0b76e298e9f4a7cb7e30809076edbb99bcbc310ec27d7a563cbc24cddd0a2ae6d01a074ffa09bd37c038326cebdd9794d5f90fe95aa9210e903fb6a5c44a735bbafc6a01605e0f1db258100a878445341892825483f14cfa7b9bde7bd001f4f3e6eed4a", + "signedCancun": "0x03f9021a8344af2c82022382f627846bf86f8485fa26c05691944f18b878b82c8931c4a6d67fc28b857aaff6d7648464660ccdb84657d10e77f6d8cd44acf5914b7b73eca98b9afe235e07b9db2d1712c4adc287e3e474a377404de19203f5127b676c88c839a76e15e665f183709a3460025556e5fd5f2a2f17f8f90111f85994275677ef49d8315ed641fe3b6519c4dc3f099112f842a0819da32335b67b104a5c786c41f2811e1167a1245b4998eb9297d0b777db457ba0b76e298e9f4a7cb7e30809076edbb99bcbc310ec27d7a563cbc24cddd0a2ae6df85994a02363d8a2921bf57f7dc48b42ccc04548554e81f842a0819da32335b67b104a5c786c41f2811e1167a1245b4998eb9297d0b777db457ba0b76e298e9f4a7cb7e30809076edbb99bcbc310ec27d7a563cbc24cddd0a2ae6df859948d6796be448ceff2333157dee56c74af0afb0bd5f842a0819da32335b67b104a5c786c41f2811e1167a1245b4998eb9297d0b777db457ba0b76e298e9f4a7cb7e30809076edbb99bcbc310ec27d7a563cbc24cddd0a2ae6d8712027a6466afd6f842a001c67453c9a170645c5784da796dc3aaaf4933d5e8d9c99ae1fd3d8032b0631fa0016865cb3a8b41dabbd03465926a9bedfb91914dd973d05e325f3d77a7db1d8e01a06bb2134653010ab0cec4aa5e51d66acf4b9c0835477bed4a96949fe0cc15db35a06432039ef421f4cf53ada8b7149e0852cc705668a6713d00aa0ddbfc9b84604f", + "signatureLegacy": { + "r": "0x1c1a043ba56d5a6edec9efd4b24695f996204caec12978d32bf086d8e21bcf96", + "s": "0x4a5144d22ab0ddd5335bc3e1b22cd24e66bd742865f1a4447e2675ee00a2227f", + "v": "0x1c" + }, + "signatureEip155": { + "r": "0x6d75cb007c3b91b7465c095e28b841d93da110bbb66280a0f5920083ad4e80f2", + "s": "0x26b07e9427fd6dc320c23ced73fb8aae9872af808f20db04f0bb67287da0373b", + "v": "0x895e7c" + }, + "signatureBerlin": { + "r": "0xf69b776f4eee48cf9a220abb4e132d2cd915b320e409fe99376535aa6984f69e", + "s": "0x45b9071a002c820e368070525657f2cd47053ea600d24c1da75b7adbd076cfcf", + "v": "0x0" + }, + "signatureLondon": { + "r": "0x74ffa09bd37c038326cebdd9794d5f90fe95aa9210e903fb6a5c44a735bbafc6", + "s": "0x1605e0f1db258100a878445341892825483f14cfa7b9bde7bd001f4f3e6eed4a", + "v": "0x1" + }, + "signatureCancun": { + "r": "0x6bb2134653010ab0cec4aa5e51d66acf4b9c0835477bed4a96949fe0cc15db35", + "s": "0x6432039ef421f4cf53ada8b7149e0852cc705668a6713d00aa0ddbfc9b84604f", + "v": "0x1" + } +} diff --git a/bolt-contracts/test/testdata/transactions/random_13.json b/bolt-contracts/test/testdata/transactions/random_13.json new file mode 100644 index 000000000..e46f587f3 --- /dev/null +++ b/bolt-contracts/test/testdata/transactions/random_13.json @@ -0,0 +1,88 @@ +{ + "name": "random-13", + "transaction": { + "to": "0x02CA24BF68010E087C89FBBBBA726265f75F453f", + "nonce": 230, + "gasLimit": "0x9b", + "gasPrice": "0xee0c40da", + "maxFeePerGas": "0x6911bd4911", + "maxPriorityFeePerGas": "0x8d", + "data": "0x9057177cbea0fcc2e52f03fd99ae750cd5e1df00a897ae7cc0a37f609358089d09690aa727fc2a5514964930ebcdfaa64d48ca730848ba3bdf55dfafcdbc2615fcc0aff5b5804bc592b4a9129b30858f7e56d895e655ee66b8d39a5fd8e22590fec373b18c", + "value": "0xe5cf3dc063", + "accessList": [ + { + "address": "0x9a39070F38D75E2c56dbb0f5992F87D4B8c4516f", + "storageKeys": [ + "0x5a2edcbe00c7f2a4054eb642539fc70f3b18e636eee4a800a307d1b38bf2cf6f", + "0xbb923a5c3ae3b14becbfc2ce1f9bcac3ac7f9e529bf121ebe45589f97ea7a5d6", + "0x573a0bef0d2674001e858247d3fe613ca0c17a6b733979e6516af7fa8ef3ab63" + ] + }, + { + "address": "0x727e5f27d18F70fDA8F234082421872B68C3Dda0", + "storageKeys": [ + "0x5a2edcbe00c7f2a4054eb642539fc70f3b18e636eee4a800a307d1b38bf2cf6f", + "0xbb923a5c3ae3b14becbfc2ce1f9bcac3ac7f9e529bf121ebe45589f97ea7a5d6", + "0x573a0bef0d2674001e858247d3fe613ca0c17a6b733979e6516af7fa8ef3ab63" + ] + }, + { + "address": "0x81df03F5cd0f415eAB92E0593998Dd288B02d583", + "storageKeys": [ + "0x5a2edcbe00c7f2a4054eb642539fc70f3b18e636eee4a800a307d1b38bf2cf6f", + "0xbb923a5c3ae3b14becbfc2ce1f9bcac3ac7f9e529bf121ebe45589f97ea7a5d6", + "0x573a0bef0d2674001e858247d3fe613ca0c17a6b733979e6516af7fa8ef3ab63" + ] + }, + { + "address": "0x3C465948C080239deECAbc2b8A7fccbe4130B3E6", + "storageKeys": [ + "0x5a2edcbe00c7f2a4054eb642539fc70f3b18e636eee4a800a307d1b38bf2cf6f", + "0xbb923a5c3ae3b14becbfc2ce1f9bcac3ac7f9e529bf121ebe45589f97ea7a5d6", + "0x573a0bef0d2674001e858247d3fe613ca0c17a6b733979e6516af7fa8ef3ab63" + ] + } + ], + "chainId": "0x2a", + "maxFeePerBlobGas": "0x8d5e0eac8355", + "blobVersionedHashes": [ + "0x014fe494c02bd67264c92caae4891a3b0888fdc0bd520c0872ba0d540991d2ef" + ] + }, + "privateKey": "0x70dae63ff96bc4831355131fc24468c3e57dbac4c0a82334953baa752df10cff", + "unsignedLegacy": "0xf88b81e684ee0c40da819b9402ca24bf68010e087c89fbbbba726265f75f453f85e5cf3dc063b8659057177cbea0fcc2e52f03fd99ae750cd5e1df00a897ae7cc0a37f609358089d09690aa727fc2a5514964930ebcdfaa64d48ca730848ba3bdf55dfafcdbc2615fcc0aff5b5804bc592b4a9129b30858f7e56d895e655ee66b8d39a5fd8e22590fec373b18c", + "unsignedEip155": "0xf88e81e684ee0c40da819b9402ca24bf68010e087c89fbbbba726265f75f453f85e5cf3dc063b8659057177cbea0fcc2e52f03fd99ae750cd5e1df00a897ae7cc0a37f609358089d09690aa727fc2a5514964930ebcdfaa64d48ca730848ba3bdf55dfafcdbc2615fcc0aff5b5804bc592b4a9129b30858f7e56d895e655ee66b8d39a5fd8e22590fec373b18c2a8080", + "unsignedBerlin": "0x01f9027f2a81e684ee0c40da819b9402ca24bf68010e087c89fbbbba726265f75f453f85e5cf3dc063b8659057177cbea0fcc2e52f03fd99ae750cd5e1df00a897ae7cc0a37f609358089d09690aa727fc2a5514964930ebcdfaa64d48ca730848ba3bdf55dfafcdbc2615fcc0aff5b5804bc592b4a9129b30858f7e56d895e655ee66b8d39a5fd8e22590fec373b18cf901f0f87a949a39070f38d75e2c56dbb0f5992f87d4b8c4516ff863a05a2edcbe00c7f2a4054eb642539fc70f3b18e636eee4a800a307d1b38bf2cf6fa0bb923a5c3ae3b14becbfc2ce1f9bcac3ac7f9e529bf121ebe45589f97ea7a5d6a0573a0bef0d2674001e858247d3fe613ca0c17a6b733979e6516af7fa8ef3ab63f87a94727e5f27d18f70fda8f234082421872b68c3dda0f863a05a2edcbe00c7f2a4054eb642539fc70f3b18e636eee4a800a307d1b38bf2cf6fa0bb923a5c3ae3b14becbfc2ce1f9bcac3ac7f9e529bf121ebe45589f97ea7a5d6a0573a0bef0d2674001e858247d3fe613ca0c17a6b733979e6516af7fa8ef3ab63f87a9481df03f5cd0f415eab92e0593998dd288b02d583f863a05a2edcbe00c7f2a4054eb642539fc70f3b18e636eee4a800a307d1b38bf2cf6fa0bb923a5c3ae3b14becbfc2ce1f9bcac3ac7f9e529bf121ebe45589f97ea7a5d6a0573a0bef0d2674001e858247d3fe613ca0c17a6b733979e6516af7fa8ef3ab63f87a943c465948c080239deecabc2b8a7fccbe4130b3e6f863a05a2edcbe00c7f2a4054eb642539fc70f3b18e636eee4a800a307d1b38bf2cf6fa0bb923a5c3ae3b14becbfc2ce1f9bcac3ac7f9e529bf121ebe45589f97ea7a5d6a0573a0bef0d2674001e858247d3fe613ca0c17a6b733979e6516af7fa8ef3ab63", + "unsignedLondon": "0x02f902822a81e6818d856911bd4911819b9402ca24bf68010e087c89fbbbba726265f75f453f85e5cf3dc063b8659057177cbea0fcc2e52f03fd99ae750cd5e1df00a897ae7cc0a37f609358089d09690aa727fc2a5514964930ebcdfaa64d48ca730848ba3bdf55dfafcdbc2615fcc0aff5b5804bc592b4a9129b30858f7e56d895e655ee66b8d39a5fd8e22590fec373b18cf901f0f87a949a39070f38d75e2c56dbb0f5992f87d4b8c4516ff863a05a2edcbe00c7f2a4054eb642539fc70f3b18e636eee4a800a307d1b38bf2cf6fa0bb923a5c3ae3b14becbfc2ce1f9bcac3ac7f9e529bf121ebe45589f97ea7a5d6a0573a0bef0d2674001e858247d3fe613ca0c17a6b733979e6516af7fa8ef3ab63f87a94727e5f27d18f70fda8f234082421872b68c3dda0f863a05a2edcbe00c7f2a4054eb642539fc70f3b18e636eee4a800a307d1b38bf2cf6fa0bb923a5c3ae3b14becbfc2ce1f9bcac3ac7f9e529bf121ebe45589f97ea7a5d6a0573a0bef0d2674001e858247d3fe613ca0c17a6b733979e6516af7fa8ef3ab63f87a9481df03f5cd0f415eab92e0593998dd288b02d583f863a05a2edcbe00c7f2a4054eb642539fc70f3b18e636eee4a800a307d1b38bf2cf6fa0bb923a5c3ae3b14becbfc2ce1f9bcac3ac7f9e529bf121ebe45589f97ea7a5d6a0573a0bef0d2674001e858247d3fe613ca0c17a6b733979e6516af7fa8ef3ab63f87a943c465948c080239deecabc2b8a7fccbe4130b3e6f863a05a2edcbe00c7f2a4054eb642539fc70f3b18e636eee4a800a307d1b38bf2cf6fa0bb923a5c3ae3b14becbfc2ce1f9bcac3ac7f9e529bf121ebe45589f97ea7a5d6a0573a0bef0d2674001e858247d3fe613ca0c17a6b733979e6516af7fa8ef3ab63", + "unsignedCancun": "0x03f902ab2a81e6818d856911bd4911819b9402ca24bf68010e087c89fbbbba726265f75f453f85e5cf3dc063b8659057177cbea0fcc2e52f03fd99ae750cd5e1df00a897ae7cc0a37f609358089d09690aa727fc2a5514964930ebcdfaa64d48ca730848ba3bdf55dfafcdbc2615fcc0aff5b5804bc592b4a9129b30858f7e56d895e655ee66b8d39a5fd8e22590fec373b18cf901f0f87a949a39070f38d75e2c56dbb0f5992f87d4b8c4516ff863a05a2edcbe00c7f2a4054eb642539fc70f3b18e636eee4a800a307d1b38bf2cf6fa0bb923a5c3ae3b14becbfc2ce1f9bcac3ac7f9e529bf121ebe45589f97ea7a5d6a0573a0bef0d2674001e858247d3fe613ca0c17a6b733979e6516af7fa8ef3ab63f87a94727e5f27d18f70fda8f234082421872b68c3dda0f863a05a2edcbe00c7f2a4054eb642539fc70f3b18e636eee4a800a307d1b38bf2cf6fa0bb923a5c3ae3b14becbfc2ce1f9bcac3ac7f9e529bf121ebe45589f97ea7a5d6a0573a0bef0d2674001e858247d3fe613ca0c17a6b733979e6516af7fa8ef3ab63f87a9481df03f5cd0f415eab92e0593998dd288b02d583f863a05a2edcbe00c7f2a4054eb642539fc70f3b18e636eee4a800a307d1b38bf2cf6fa0bb923a5c3ae3b14becbfc2ce1f9bcac3ac7f9e529bf121ebe45589f97ea7a5d6a0573a0bef0d2674001e858247d3fe613ca0c17a6b733979e6516af7fa8ef3ab63f87a943c465948c080239deecabc2b8a7fccbe4130b3e6f863a05a2edcbe00c7f2a4054eb642539fc70f3b18e636eee4a800a307d1b38bf2cf6fa0bb923a5c3ae3b14becbfc2ce1f9bcac3ac7f9e529bf121ebe45589f97ea7a5d6a0573a0bef0d2674001e858247d3fe613ca0c17a6b733979e6516af7fa8ef3ab63868d5e0eac8355e1a0014fe494c02bd67264c92caae4891a3b0888fdc0bd520c0872ba0d540991d2ef", + "signedLegacy": "0xf8ce81e684ee0c40da819b9402ca24bf68010e087c89fbbbba726265f75f453f85e5cf3dc063b8659057177cbea0fcc2e52f03fd99ae750cd5e1df00a897ae7cc0a37f609358089d09690aa727fc2a5514964930ebcdfaa64d48ca730848ba3bdf55dfafcdbc2615fcc0aff5b5804bc592b4a9129b30858f7e56d895e655ee66b8d39a5fd8e22590fec373b18c1ca01f91e4f825a52eb8bbaa3be1cb1cb861857cf6d0db05543a91f4e201afe63634a030680d1f0dec57bf64f8fa85a835fcfca06aa5108c7a46b60df9da8572518087", + "signedEip155": "0xf8ce81e684ee0c40da819b9402ca24bf68010e087c89fbbbba726265f75f453f85e5cf3dc063b8659057177cbea0fcc2e52f03fd99ae750cd5e1df00a897ae7cc0a37f609358089d09690aa727fc2a5514964930ebcdfaa64d48ca730848ba3bdf55dfafcdbc2615fcc0aff5b5804bc592b4a9129b30858f7e56d895e655ee66b8d39a5fd8e22590fec373b18c78a05cb862c473f3dfdeaa0e8520291cb4ba8c3a62550a6ca0e404d51ef8791cf7f8a052d4ad17f007fa089126d119c609adb461436fd4e07640c0fc487bb444caea99", + "signedBerlin": "0x01f902c22a81e684ee0c40da819b9402ca24bf68010e087c89fbbbba726265f75f453f85e5cf3dc063b8659057177cbea0fcc2e52f03fd99ae750cd5e1df00a897ae7cc0a37f609358089d09690aa727fc2a5514964930ebcdfaa64d48ca730848ba3bdf55dfafcdbc2615fcc0aff5b5804bc592b4a9129b30858f7e56d895e655ee66b8d39a5fd8e22590fec373b18cf901f0f87a949a39070f38d75e2c56dbb0f5992f87d4b8c4516ff863a05a2edcbe00c7f2a4054eb642539fc70f3b18e636eee4a800a307d1b38bf2cf6fa0bb923a5c3ae3b14becbfc2ce1f9bcac3ac7f9e529bf121ebe45589f97ea7a5d6a0573a0bef0d2674001e858247d3fe613ca0c17a6b733979e6516af7fa8ef3ab63f87a94727e5f27d18f70fda8f234082421872b68c3dda0f863a05a2edcbe00c7f2a4054eb642539fc70f3b18e636eee4a800a307d1b38bf2cf6fa0bb923a5c3ae3b14becbfc2ce1f9bcac3ac7f9e529bf121ebe45589f97ea7a5d6a0573a0bef0d2674001e858247d3fe613ca0c17a6b733979e6516af7fa8ef3ab63f87a9481df03f5cd0f415eab92e0593998dd288b02d583f863a05a2edcbe00c7f2a4054eb642539fc70f3b18e636eee4a800a307d1b38bf2cf6fa0bb923a5c3ae3b14becbfc2ce1f9bcac3ac7f9e529bf121ebe45589f97ea7a5d6a0573a0bef0d2674001e858247d3fe613ca0c17a6b733979e6516af7fa8ef3ab63f87a943c465948c080239deecabc2b8a7fccbe4130b3e6f863a05a2edcbe00c7f2a4054eb642539fc70f3b18e636eee4a800a307d1b38bf2cf6fa0bb923a5c3ae3b14becbfc2ce1f9bcac3ac7f9e529bf121ebe45589f97ea7a5d6a0573a0bef0d2674001e858247d3fe613ca0c17a6b733979e6516af7fa8ef3ab6301a04f2bb0a31a56bb2b1aed5089f7a05c1dd10d759fafdc07ad9ed698885981412ba0042167f0f1ba66c56f1b39f5ab4ad95442cf98c2e4b0a4066f4995b22a663bf3", + "signedLondon": "0x02f902c52a81e6818d856911bd4911819b9402ca24bf68010e087c89fbbbba726265f75f453f85e5cf3dc063b8659057177cbea0fcc2e52f03fd99ae750cd5e1df00a897ae7cc0a37f609358089d09690aa727fc2a5514964930ebcdfaa64d48ca730848ba3bdf55dfafcdbc2615fcc0aff5b5804bc592b4a9129b30858f7e56d895e655ee66b8d39a5fd8e22590fec373b18cf901f0f87a949a39070f38d75e2c56dbb0f5992f87d4b8c4516ff863a05a2edcbe00c7f2a4054eb642539fc70f3b18e636eee4a800a307d1b38bf2cf6fa0bb923a5c3ae3b14becbfc2ce1f9bcac3ac7f9e529bf121ebe45589f97ea7a5d6a0573a0bef0d2674001e858247d3fe613ca0c17a6b733979e6516af7fa8ef3ab63f87a94727e5f27d18f70fda8f234082421872b68c3dda0f863a05a2edcbe00c7f2a4054eb642539fc70f3b18e636eee4a800a307d1b38bf2cf6fa0bb923a5c3ae3b14becbfc2ce1f9bcac3ac7f9e529bf121ebe45589f97ea7a5d6a0573a0bef0d2674001e858247d3fe613ca0c17a6b733979e6516af7fa8ef3ab63f87a9481df03f5cd0f415eab92e0593998dd288b02d583f863a05a2edcbe00c7f2a4054eb642539fc70f3b18e636eee4a800a307d1b38bf2cf6fa0bb923a5c3ae3b14becbfc2ce1f9bcac3ac7f9e529bf121ebe45589f97ea7a5d6a0573a0bef0d2674001e858247d3fe613ca0c17a6b733979e6516af7fa8ef3ab63f87a943c465948c080239deecabc2b8a7fccbe4130b3e6f863a05a2edcbe00c7f2a4054eb642539fc70f3b18e636eee4a800a307d1b38bf2cf6fa0bb923a5c3ae3b14becbfc2ce1f9bcac3ac7f9e529bf121ebe45589f97ea7a5d6a0573a0bef0d2674001e858247d3fe613ca0c17a6b733979e6516af7fa8ef3ab6301a03fd00e5a6b50a78f53922d62a73777ccfe7695f1e6e30f8be33323119bc2c0efa0050ac86674b894e33b3292bb153eb1fbefa4efdaf5775eba835391477f6831c4", + "signedCancun": "0x03f902ee2a81e6818d856911bd4911819b9402ca24bf68010e087c89fbbbba726265f75f453f85e5cf3dc063b8659057177cbea0fcc2e52f03fd99ae750cd5e1df00a897ae7cc0a37f609358089d09690aa727fc2a5514964930ebcdfaa64d48ca730848ba3bdf55dfafcdbc2615fcc0aff5b5804bc592b4a9129b30858f7e56d895e655ee66b8d39a5fd8e22590fec373b18cf901f0f87a949a39070f38d75e2c56dbb0f5992f87d4b8c4516ff863a05a2edcbe00c7f2a4054eb642539fc70f3b18e636eee4a800a307d1b38bf2cf6fa0bb923a5c3ae3b14becbfc2ce1f9bcac3ac7f9e529bf121ebe45589f97ea7a5d6a0573a0bef0d2674001e858247d3fe613ca0c17a6b733979e6516af7fa8ef3ab63f87a94727e5f27d18f70fda8f234082421872b68c3dda0f863a05a2edcbe00c7f2a4054eb642539fc70f3b18e636eee4a800a307d1b38bf2cf6fa0bb923a5c3ae3b14becbfc2ce1f9bcac3ac7f9e529bf121ebe45589f97ea7a5d6a0573a0bef0d2674001e858247d3fe613ca0c17a6b733979e6516af7fa8ef3ab63f87a9481df03f5cd0f415eab92e0593998dd288b02d583f863a05a2edcbe00c7f2a4054eb642539fc70f3b18e636eee4a800a307d1b38bf2cf6fa0bb923a5c3ae3b14becbfc2ce1f9bcac3ac7f9e529bf121ebe45589f97ea7a5d6a0573a0bef0d2674001e858247d3fe613ca0c17a6b733979e6516af7fa8ef3ab63f87a943c465948c080239deecabc2b8a7fccbe4130b3e6f863a05a2edcbe00c7f2a4054eb642539fc70f3b18e636eee4a800a307d1b38bf2cf6fa0bb923a5c3ae3b14becbfc2ce1f9bcac3ac7f9e529bf121ebe45589f97ea7a5d6a0573a0bef0d2674001e858247d3fe613ca0c17a6b733979e6516af7fa8ef3ab63868d5e0eac8355e1a0014fe494c02bd67264c92caae4891a3b0888fdc0bd520c0872ba0d540991d2ef01a061188dabcd2879c18c49f3f83cb4663d597da58f856390b87921f64182ea9745a0757d18b772ff141ddff69196b2755bd76ef422a102a3a0fa1ca68b2e901b468c", + "signatureLegacy": { + "r": "0x1f91e4f825a52eb8bbaa3be1cb1cb861857cf6d0db05543a91f4e201afe63634", + "s": "0x30680d1f0dec57bf64f8fa85a835fcfca06aa5108c7a46b60df9da8572518087", + "v": "0x1c" + }, + "signatureEip155": { + "r": "0x5cb862c473f3dfdeaa0e8520291cb4ba8c3a62550a6ca0e404d51ef8791cf7f8", + "s": "0x52d4ad17f007fa089126d119c609adb461436fd4e07640c0fc487bb444caea99", + "v": "0x78" + }, + "signatureBerlin": { + "r": "0x4f2bb0a31a56bb2b1aed5089f7a05c1dd10d759fafdc07ad9ed698885981412b", + "s": "0x042167f0f1ba66c56f1b39f5ab4ad95442cf98c2e4b0a4066f4995b22a663bf3", + "v": "0x1" + }, + "signatureLondon": { + "r": "0x3fd00e5a6b50a78f53922d62a73777ccfe7695f1e6e30f8be33323119bc2c0ef", + "s": "0x050ac86674b894e33b3292bb153eb1fbefa4efdaf5775eba835391477f6831c4", + "v": "0x1" + }, + "signatureCancun": { + "r": "0x61188dabcd2879c18c49f3f83cb4663d597da58f856390b87921f64182ea9745", + "s": "0x757d18b772ff141ddff69196b2755bd76ef422a102a3a0fa1ca68b2e901b468c", + "v": "0x1" + } +} diff --git a/bolt-contracts/test/testdata/transactions/random_14.json b/bolt-contracts/test/testdata/transactions/random_14.json new file mode 100644 index 000000000..b2bec0420 --- /dev/null +++ b/bolt-contracts/test/testdata/transactions/random_14.json @@ -0,0 +1,71 @@ +{ + "name": "random-14", + "transaction": { + "to": "0xd4Ed79414A9F8bF363E9CAaa5A74380716F7dBE0", + "nonce": 355, + "gasLimit": "0xc3393abf8e", + "gasPrice": "0x530f336b", + "maxFeePerGas": "0xc4550ba0e2", + "maxPriorityFeePerGas": "0xf9fe", + "data": "0x9421f8e15d313f08424f56798539e338ff345b530bef1256810200922813e50a176ca7e987dbedb94917acfe82", + "value": "0x5590f0f87b", + "accessList": [ + { + "address": "0x3acE6cc41a8DF5B6518B24e6ecd490c13aCfC677", + "storageKeys": [] + }, + { + "address": "0x65F3540A4A7aa93d074A77313622786513f0199d", + "storageKeys": [] + }, + { + "address": "0x0dad5e012C02f680FB256bf6b0e0d9999c8c74A0", + "storageKeys": [] + } + ], + "chainId": "0x602a", + "maxFeePerBlobGas": "0xe61327d8d776ed", + "blobVersionedHashes": [ + "0x0122070325084d4e3cba54d4dffef3501e22290c965e099620b3dbd48626b731", + "0x018df851a2bdd53f12054dc427d35171bbc81e2141c0ef7afe94964e4cdd3c1f", + "0x01386600d37c5951685dc66bc09b1a3e98c040621dde84b172cf93c30d66842f", + "0x017f6d70f99ec3a8937f73f2da032c78dde8a6f831befc31fd23189ff66e4b07" + ] + }, + "privateKey": "0x6c268eef2efdb15a96ae69774e732b9214a3ebb03c0fd01602bc7a5fcd21c060", + "unsignedLegacy": "0xf85782016384530f336b85c3393abf8e94d4ed79414a9f8bf363e9caaa5a74380716f7dbe0855590f0f87bad9421f8e15d313f08424f56798539e338ff345b530bef1256810200922813e50a176ca7e987dbedb94917acfe82", + "unsignedEip155": "0xf85c82016384530f336b85c3393abf8e94d4ed79414a9f8bf363e9caaa5a74380716f7dbe0855590f0f87bad9421f8e15d313f08424f56798539e338ff345b530bef1256810200922813e50a176ca7e987dbedb94917acfe8282602a8080", + "unsignedBerlin": "0x01f8a182602a82016384530f336b85c3393abf8e94d4ed79414a9f8bf363e9caaa5a74380716f7dbe0855590f0f87bad9421f8e15d313f08424f56798539e338ff345b530bef1256810200922813e50a176ca7e987dbedb94917acfe82f845d6943ace6cc41a8df5b6518b24e6ecd490c13acfc677c0d69465f3540a4a7aa93d074a77313622786513f0199dc0d6940dad5e012c02f680fb256bf6b0e0d9999c8c74a0c0", + "unsignedLondon": "0x02f8a582602a82016382f9fe85c4550ba0e285c3393abf8e94d4ed79414a9f8bf363e9caaa5a74380716f7dbe0855590f0f87bad9421f8e15d313f08424f56798539e338ff345b530bef1256810200922813e50a176ca7e987dbedb94917acfe82f845d6943ace6cc41a8df5b6518b24e6ecd490c13acfc677c0d69465f3540a4a7aa93d074a77313622786513f0199dc0d6940dad5e012c02f680fb256bf6b0e0d9999c8c74a0c0", + "unsignedCancun": "0x03f9013382602a82016382f9fe85c4550ba0e285c3393abf8e94d4ed79414a9f8bf363e9caaa5a74380716f7dbe0855590f0f87bad9421f8e15d313f08424f56798539e338ff345b530bef1256810200922813e50a176ca7e987dbedb94917acfe82f845d6943ace6cc41a8df5b6518b24e6ecd490c13acfc677c0d69465f3540a4a7aa93d074a77313622786513f0199dc0d6940dad5e012c02f680fb256bf6b0e0d9999c8c74a0c087e61327d8d776edf884a00122070325084d4e3cba54d4dffef3501e22290c965e099620b3dbd48626b731a0018df851a2bdd53f12054dc427d35171bbc81e2141c0ef7afe94964e4cdd3c1fa001386600d37c5951685dc66bc09b1a3e98c040621dde84b172cf93c30d66842fa0017f6d70f99ec3a8937f73f2da032c78dde8a6f831befc31fd23189ff66e4b07", + "signedLegacy": "0xf89a82016384530f336b85c3393abf8e94d4ed79414a9f8bf363e9caaa5a74380716f7dbe0855590f0f87bad9421f8e15d313f08424f56798539e338ff345b530bef1256810200922813e50a176ca7e987dbedb94917acfe821ca03e5ece8f862a9b5d42b42c314dceef20c98a55ae8e89e76ea36574f2dbcd0a6fa06cf670f45c89f30ccf175619d7a48c30c0303d723e8c942afe3bf99bb622a365", + "signedEip155": "0xf89c82016384530f336b85c3393abf8e94d4ed79414a9f8bf363e9caaa5a74380716f7dbe0855590f0f87bad9421f8e15d313f08424f56798539e338ff345b530bef1256810200922813e50a176ca7e987dbedb94917acfe8282c077a07984b5662bf52721770ca23a49f719f0dca47263a736a7cb6da28b359c3275d5a0080a94bd846cb4d5d71a41c78f5f4a893f9cd8316a52f3bba02b2e6d5865d13e", + "signedBerlin": "0x01f8e482602a82016384530f336b85c3393abf8e94d4ed79414a9f8bf363e9caaa5a74380716f7dbe0855590f0f87bad9421f8e15d313f08424f56798539e338ff345b530bef1256810200922813e50a176ca7e987dbedb94917acfe82f845d6943ace6cc41a8df5b6518b24e6ecd490c13acfc677c0d69465f3540a4a7aa93d074a77313622786513f0199dc0d6940dad5e012c02f680fb256bf6b0e0d9999c8c74a0c001a0a0983d54539712f6f848fe9fad8631479338d8277207d75ccf4a26ae3ed34e79a00acd07196e5fa670c9109ced9ccf4722f2c33ae49eec06f14ea3766c6d0e7c3e", + "signedLondon": "0x02f8e882602a82016382f9fe85c4550ba0e285c3393abf8e94d4ed79414a9f8bf363e9caaa5a74380716f7dbe0855590f0f87bad9421f8e15d313f08424f56798539e338ff345b530bef1256810200922813e50a176ca7e987dbedb94917acfe82f845d6943ace6cc41a8df5b6518b24e6ecd490c13acfc677c0d69465f3540a4a7aa93d074a77313622786513f0199dc0d6940dad5e012c02f680fb256bf6b0e0d9999c8c74a0c001a0990f968b8f06d13ac77614e1acd01591259050753e76bc7419062bdf6ac71a11a0201883043f4f176b82d7cc14ffd524f28fcf9cc490cea858bacac84d7373261a", + "signedCancun": "0x03f9017682602a82016382f9fe85c4550ba0e285c3393abf8e94d4ed79414a9f8bf363e9caaa5a74380716f7dbe0855590f0f87bad9421f8e15d313f08424f56798539e338ff345b530bef1256810200922813e50a176ca7e987dbedb94917acfe82f845d6943ace6cc41a8df5b6518b24e6ecd490c13acfc677c0d69465f3540a4a7aa93d074a77313622786513f0199dc0d6940dad5e012c02f680fb256bf6b0e0d9999c8c74a0c087e61327d8d776edf884a00122070325084d4e3cba54d4dffef3501e22290c965e099620b3dbd48626b731a0018df851a2bdd53f12054dc427d35171bbc81e2141c0ef7afe94964e4cdd3c1fa001386600d37c5951685dc66bc09b1a3e98c040621dde84b172cf93c30d66842fa0017f6d70f99ec3a8937f73f2da032c78dde8a6f831befc31fd23189ff66e4b0780a0da2f7f2f95d6b3207aeb56b5b6aa5aa30a2674f90d338427674c8e5e7850f078a0646d4c7d050a38c73bf1f22b8ba41e2a84430c3550ec20843e885248cc90b53b", + "signatureLegacy": { + "r": "0x3e5ece8f862a9b5d42b42c314dceef20c98a55ae8e89e76ea36574f2dbcd0a6f", + "s": "0x6cf670f45c89f30ccf175619d7a48c30c0303d723e8c942afe3bf99bb622a365", + "v": "0x1c" + }, + "signatureEip155": { + "r": "0x7984b5662bf52721770ca23a49f719f0dca47263a736a7cb6da28b359c3275d5", + "s": "0x080a94bd846cb4d5d71a41c78f5f4a893f9cd8316a52f3bba02b2e6d5865d13e", + "v": "0xc077" + }, + "signatureBerlin": { + "r": "0xa0983d54539712f6f848fe9fad8631479338d8277207d75ccf4a26ae3ed34e79", + "s": "0x0acd07196e5fa670c9109ced9ccf4722f2c33ae49eec06f14ea3766c6d0e7c3e", + "v": "0x1" + }, + "signatureLondon": { + "r": "0x990f968b8f06d13ac77614e1acd01591259050753e76bc7419062bdf6ac71a11", + "s": "0x201883043f4f176b82d7cc14ffd524f28fcf9cc490cea858bacac84d7373261a", + "v": "0x1" + }, + "signatureCancun": { + "r": "0xda2f7f2f95d6b3207aeb56b5b6aa5aa30a2674f90d338427674c8e5e7850f078", + "s": "0x646d4c7d050a38c73bf1f22b8ba41e2a84430c3550ec20843e885248cc90b53b", + "v": "0x0" + } +} diff --git a/bolt-contracts/test/testdata/transactions/random_15.json b/bolt-contracts/test/testdata/transactions/random_15.json new file mode 100644 index 000000000..1be8cfac2 --- /dev/null +++ b/bolt-contracts/test/testdata/transactions/random_15.json @@ -0,0 +1,76 @@ +{ + "name": "random-15", + "transaction": { + "to": "0x2C4Ad0E9a08DD6fE31Ba497fd4a893A0C1fc6B34", + "nonce": 252, + "gasLimit": "0x50ef8434", + "gasPrice": "0xd9ca73", + "maxFeePerGas": "0x280244cd52ab", + "maxPriorityFeePerGas": "0x143b17", + "data": "0x59a1b826916e4a012ffaaf5e21251e27317a1f565a716b8ff184d8c689f93f5a7359abcbffd18d7bea36ee123ce5cddbbdd92d", + "value": "0x6f9f", + "accessList": [ + { + "address": "0x3266CE0AB053240CE5d95993d420c6b9a1dA1fDC", + "storageKeys": [ + "0xa0568d48a6cc9e0c92080dec2155382a276181f25c746da97f8199f3a8464211", + "0x80b165a49183e897da692ca460c5c88a0597e5e4ad7b43471bac25bfd9a780e0", + "0xa628e4d1cfbcc69964473b21b3df585f15eca27bdd2e0c0d7e2f1db3e2c0a1f8", + "0x35167ab9f9061b5f986f5c960aebe912340a5a72935aa334afa8fbfe586d05b1" + ] + }, + { + "address": "0xf199b9f871065E2E18291CdeC3BF7C1eD32FBa0a", + "storageKeys": [ + "0xa0568d48a6cc9e0c92080dec2155382a276181f25c746da97f8199f3a8464211", + "0x80b165a49183e897da692ca460c5c88a0597e5e4ad7b43471bac25bfd9a780e0", + "0xa628e4d1cfbcc69964473b21b3df585f15eca27bdd2e0c0d7e2f1db3e2c0a1f8", + "0x35167ab9f9061b5f986f5c960aebe912340a5a72935aa334afa8fbfe586d05b1" + ] + } + ], + "chainId": "0x18314c", + "maxFeePerBlobGas": "0x81ec2df3d713c4", + "blobVersionedHashes": [ + "0x01d63574eb4fd029b67d6233aeae60815e225597f647de565b8050d2b5a876b3", + "0x01224249cfed8dce6d9785f85c2ca29b6496d91ca16dbafabf1581ef75f47b29", + "0x01a4547275516123b7b2ea3413c57c4caea24441dd9956f7308f09b5bb0947df" + ] + }, + "privateKey": "0xffe260f37af5da6e4a18799bc6b7f0fab7d57bc456346bca7261a53cb04fb48d", + "unsignedLegacy": "0xf85781fc83d9ca738450ef8434942c4ad0e9a08dd6fe31ba497fd4a893a0c1fc6b34826f9fb359a1b826916e4a012ffaaf5e21251e27317a1f565a716b8ff184d8c689f93f5a7359abcbffd18d7bea36ee123ce5cddbbdd92d", + "unsignedEip155": "0xf85d81fc83d9ca738450ef8434942c4ad0e9a08dd6fe31ba497fd4a893a0c1fc6b34826f9fb359a1b826916e4a012ffaaf5e21251e27317a1f565a716b8ff184d8c689f93f5a7359abcbffd18d7bea36ee123ce5cddbbdd92d8318314c8080", + "unsignedBerlin": "0x01f901988318314c81fc83d9ca738450ef8434942c4ad0e9a08dd6fe31ba497fd4a893a0c1fc6b34826f9fb359a1b826916e4a012ffaaf5e21251e27317a1f565a716b8ff184d8c689f93f5a7359abcbffd18d7bea36ee123ce5cddbbdd92df9013af89b943266ce0ab053240ce5d95993d420c6b9a1da1fdcf884a0a0568d48a6cc9e0c92080dec2155382a276181f25c746da97f8199f3a8464211a080b165a49183e897da692ca460c5c88a0597e5e4ad7b43471bac25bfd9a780e0a0a628e4d1cfbcc69964473b21b3df585f15eca27bdd2e0c0d7e2f1db3e2c0a1f8a035167ab9f9061b5f986f5c960aebe912340a5a72935aa334afa8fbfe586d05b1f89b94f199b9f871065e2e18291cdec3bf7c1ed32fba0af884a0a0568d48a6cc9e0c92080dec2155382a276181f25c746da97f8199f3a8464211a080b165a49183e897da692ca460c5c88a0597e5e4ad7b43471bac25bfd9a780e0a0a628e4d1cfbcc69964473b21b3df585f15eca27bdd2e0c0d7e2f1db3e2c0a1f8a035167ab9f9061b5f986f5c960aebe912340a5a72935aa334afa8fbfe586d05b1", + "unsignedLondon": "0x02f9019f8318314c81fc83143b1786280244cd52ab8450ef8434942c4ad0e9a08dd6fe31ba497fd4a893a0c1fc6b34826f9fb359a1b826916e4a012ffaaf5e21251e27317a1f565a716b8ff184d8c689f93f5a7359abcbffd18d7bea36ee123ce5cddbbdd92df9013af89b943266ce0ab053240ce5d95993d420c6b9a1da1fdcf884a0a0568d48a6cc9e0c92080dec2155382a276181f25c746da97f8199f3a8464211a080b165a49183e897da692ca460c5c88a0597e5e4ad7b43471bac25bfd9a780e0a0a628e4d1cfbcc69964473b21b3df585f15eca27bdd2e0c0d7e2f1db3e2c0a1f8a035167ab9f9061b5f986f5c960aebe912340a5a72935aa334afa8fbfe586d05b1f89b94f199b9f871065e2e18291cdec3bf7c1ed32fba0af884a0a0568d48a6cc9e0c92080dec2155382a276181f25c746da97f8199f3a8464211a080b165a49183e897da692ca460c5c88a0597e5e4ad7b43471bac25bfd9a780e0a0a628e4d1cfbcc69964473b21b3df585f15eca27bdd2e0c0d7e2f1db3e2c0a1f8a035167ab9f9061b5f986f5c960aebe912340a5a72935aa334afa8fbfe586d05b1", + "unsignedCancun": "0x03f9020c8318314c81fc83143b1786280244cd52ab8450ef8434942c4ad0e9a08dd6fe31ba497fd4a893a0c1fc6b34826f9fb359a1b826916e4a012ffaaf5e21251e27317a1f565a716b8ff184d8c689f93f5a7359abcbffd18d7bea36ee123ce5cddbbdd92df9013af89b943266ce0ab053240ce5d95993d420c6b9a1da1fdcf884a0a0568d48a6cc9e0c92080dec2155382a276181f25c746da97f8199f3a8464211a080b165a49183e897da692ca460c5c88a0597e5e4ad7b43471bac25bfd9a780e0a0a628e4d1cfbcc69964473b21b3df585f15eca27bdd2e0c0d7e2f1db3e2c0a1f8a035167ab9f9061b5f986f5c960aebe912340a5a72935aa334afa8fbfe586d05b1f89b94f199b9f871065e2e18291cdec3bf7c1ed32fba0af884a0a0568d48a6cc9e0c92080dec2155382a276181f25c746da97f8199f3a8464211a080b165a49183e897da692ca460c5c88a0597e5e4ad7b43471bac25bfd9a780e0a0a628e4d1cfbcc69964473b21b3df585f15eca27bdd2e0c0d7e2f1db3e2c0a1f8a035167ab9f9061b5f986f5c960aebe912340a5a72935aa334afa8fbfe586d05b18781ec2df3d713c4f863a001d63574eb4fd029b67d6233aeae60815e225597f647de565b8050d2b5a876b3a001224249cfed8dce6d9785f85c2ca29b6496d91ca16dbafabf1581ef75f47b29a001a4547275516123b7b2ea3413c57c4caea24441dd9956f7308f09b5bb0947df", + "signedLegacy": "0xf89a81fc83d9ca738450ef8434942c4ad0e9a08dd6fe31ba497fd4a893a0c1fc6b34826f9fb359a1b826916e4a012ffaaf5e21251e27317a1f565a716b8ff184d8c689f93f5a7359abcbffd18d7bea36ee123ce5cddbbdd92d1ba0c32aaeca7f3f7d189f6493edfb99452baa5e5a4c3ba136275b92c28a722a59f7a07073e03733555ceb1a3479c3193cfac043530c657b1c26cf2d12ffac912d132d", + "signedEip155": "0xf89d81fc83d9ca738450ef8434942c4ad0e9a08dd6fe31ba497fd4a893a0c1fc6b34826f9fb359a1b826916e4a012ffaaf5e21251e27317a1f565a716b8ff184d8c689f93f5a7359abcbffd18d7bea36ee123ce5cddbbdd92d833062bca02a5acf0ccf63515e394899176fdc70cfc880e8f34ba5199de5baa72e0dc11e8fa07560075eef0250df34dd68a1854864b219ab0972e96e3d70017b21f608c5e928", + "signedBerlin": "0x01f901db8318314c81fc83d9ca738450ef8434942c4ad0e9a08dd6fe31ba497fd4a893a0c1fc6b34826f9fb359a1b826916e4a012ffaaf5e21251e27317a1f565a716b8ff184d8c689f93f5a7359abcbffd18d7bea36ee123ce5cddbbdd92df9013af89b943266ce0ab053240ce5d95993d420c6b9a1da1fdcf884a0a0568d48a6cc9e0c92080dec2155382a276181f25c746da97f8199f3a8464211a080b165a49183e897da692ca460c5c88a0597e5e4ad7b43471bac25bfd9a780e0a0a628e4d1cfbcc69964473b21b3df585f15eca27bdd2e0c0d7e2f1db3e2c0a1f8a035167ab9f9061b5f986f5c960aebe912340a5a72935aa334afa8fbfe586d05b1f89b94f199b9f871065e2e18291cdec3bf7c1ed32fba0af884a0a0568d48a6cc9e0c92080dec2155382a276181f25c746da97f8199f3a8464211a080b165a49183e897da692ca460c5c88a0597e5e4ad7b43471bac25bfd9a780e0a0a628e4d1cfbcc69964473b21b3df585f15eca27bdd2e0c0d7e2f1db3e2c0a1f8a035167ab9f9061b5f986f5c960aebe912340a5a72935aa334afa8fbfe586d05b101a0c46f5930fbf08b22c97615c2d8db15697dfa5d0a269bb91a9afd7ddb7ac44768a066e4c7ad9c783be0dd722b4b85dc6518892f8c632c09b2ec2c0980c84fb897b7", + "signedLondon": "0x02f901e28318314c81fc83143b1786280244cd52ab8450ef8434942c4ad0e9a08dd6fe31ba497fd4a893a0c1fc6b34826f9fb359a1b826916e4a012ffaaf5e21251e27317a1f565a716b8ff184d8c689f93f5a7359abcbffd18d7bea36ee123ce5cddbbdd92df9013af89b943266ce0ab053240ce5d95993d420c6b9a1da1fdcf884a0a0568d48a6cc9e0c92080dec2155382a276181f25c746da97f8199f3a8464211a080b165a49183e897da692ca460c5c88a0597e5e4ad7b43471bac25bfd9a780e0a0a628e4d1cfbcc69964473b21b3df585f15eca27bdd2e0c0d7e2f1db3e2c0a1f8a035167ab9f9061b5f986f5c960aebe912340a5a72935aa334afa8fbfe586d05b1f89b94f199b9f871065e2e18291cdec3bf7c1ed32fba0af884a0a0568d48a6cc9e0c92080dec2155382a276181f25c746da97f8199f3a8464211a080b165a49183e897da692ca460c5c88a0597e5e4ad7b43471bac25bfd9a780e0a0a628e4d1cfbcc69964473b21b3df585f15eca27bdd2e0c0d7e2f1db3e2c0a1f8a035167ab9f9061b5f986f5c960aebe912340a5a72935aa334afa8fbfe586d05b180a0bdebff14388befbaf0d7c8509f82355aafd0cbdad7ba5d721fa6357e239cf380a06efc7089e04a05f4e52b43fb3799e6ebd9274128566e870efef293a9d8624271", + "signedCancun": "0x03f9024f8318314c81fc83143b1786280244cd52ab8450ef8434942c4ad0e9a08dd6fe31ba497fd4a893a0c1fc6b34826f9fb359a1b826916e4a012ffaaf5e21251e27317a1f565a716b8ff184d8c689f93f5a7359abcbffd18d7bea36ee123ce5cddbbdd92df9013af89b943266ce0ab053240ce5d95993d420c6b9a1da1fdcf884a0a0568d48a6cc9e0c92080dec2155382a276181f25c746da97f8199f3a8464211a080b165a49183e897da692ca460c5c88a0597e5e4ad7b43471bac25bfd9a780e0a0a628e4d1cfbcc69964473b21b3df585f15eca27bdd2e0c0d7e2f1db3e2c0a1f8a035167ab9f9061b5f986f5c960aebe912340a5a72935aa334afa8fbfe586d05b1f89b94f199b9f871065e2e18291cdec3bf7c1ed32fba0af884a0a0568d48a6cc9e0c92080dec2155382a276181f25c746da97f8199f3a8464211a080b165a49183e897da692ca460c5c88a0597e5e4ad7b43471bac25bfd9a780e0a0a628e4d1cfbcc69964473b21b3df585f15eca27bdd2e0c0d7e2f1db3e2c0a1f8a035167ab9f9061b5f986f5c960aebe912340a5a72935aa334afa8fbfe586d05b18781ec2df3d713c4f863a001d63574eb4fd029b67d6233aeae60815e225597f647de565b8050d2b5a876b3a001224249cfed8dce6d9785f85c2ca29b6496d91ca16dbafabf1581ef75f47b29a001a4547275516123b7b2ea3413c57c4caea24441dd9956f7308f09b5bb0947df80a04214e832f45d5ba706f01b7dce5941397c8f0850ddfc30d0bf70e896e542bbd1a027208b27598709a564ca5981d54c0c3aa4b33b7c618de45f378576b229bf07f5", + "signatureLegacy": { + "r": "0xc32aaeca7f3f7d189f6493edfb99452baa5e5a4c3ba136275b92c28a722a59f7", + "s": "0x7073e03733555ceb1a3479c3193cfac043530c657b1c26cf2d12ffac912d132d", + "v": "0x1b" + }, + "signatureEip155": { + "r": "0x2a5acf0ccf63515e394899176fdc70cfc880e8f34ba5199de5baa72e0dc11e8f", + "s": "0x7560075eef0250df34dd68a1854864b219ab0972e96e3d70017b21f608c5e928", + "v": "0x3062bc" + }, + "signatureBerlin": { + "r": "0xc46f5930fbf08b22c97615c2d8db15697dfa5d0a269bb91a9afd7ddb7ac44768", + "s": "0x66e4c7ad9c783be0dd722b4b85dc6518892f8c632c09b2ec2c0980c84fb897b7", + "v": "0x1" + }, + "signatureLondon": { + "r": "0xbdebff14388befbaf0d7c8509f82355aafd0cbdad7ba5d721fa6357e239cf380", + "s": "0x6efc7089e04a05f4e52b43fb3799e6ebd9274128566e870efef293a9d8624271", + "v": "0x0" + }, + "signatureCancun": { + "r": "0x4214e832f45d5ba706f01b7dce5941397c8f0850ddfc30d0bf70e896e542bbd1", + "s": "0x27208b27598709a564ca5981d54c0c3aa4b33b7c618de45f378576b229bf07f5", + "v": "0x0" + } +} diff --git a/bolt-contracts/test/testdata/transactions/random_16.json b/bolt-contracts/test/testdata/transactions/random_16.json new file mode 100644 index 000000000..10608fce7 --- /dev/null +++ b/bolt-contracts/test/testdata/transactions/random_16.json @@ -0,0 +1,64 @@ +{ + "name": "random-16", + "transaction": { + "to": "0xb26832F9Eebe1bb94cb67D5C6bABF358d15f834E", + "nonce": 306, + "gasLimit": "0xfb38", + "gasPrice": "0x7029692f45", + "maxFeePerGas": "0xf631d7b0a81f", + "maxPriorityFeePerGas": "0xb5", + "data": "0x3c5c6cbe06f0c46868590f0951afc1b55f38598f5caf4d2f811ed3bbb8a87de83f074bc2cf19ca7225a65533cbe03aed1f43fa7cd1ef178e1ec2de6076d3ec1a22729c8645de5442bc8e3c3c7781d68293d34ae667c247189089a4959ef7c0d35e4600be23b5b059629779", + "value": "0xb3", + "accessList": [ + { + "address": "0x820A408021ed575Bd94e89AF5672C9C4FaAA93AD", + "storageKeys": [] + } + ], + "chainId": "0x03", + "maxFeePerBlobGas": "0x9b643a13", + "blobVersionedHashes": [ + "0x0106f3b8583c3b187099347666d5b01b3ae85b5458a2291ca45884084bf8a728", + "0x012d5ba14538fe600b4f258429247337527a13d6a28c3b4b445f00d3098df94f", + "0x011a4647e1b61123ab33b318dff3609fc11da8c4cfa9abe25f99c41923a9f581", + "0x01cb8b689043f5bdcc56fce5c26490ca28a88b0a2f53a6d1f51e3f298e65d295", + "0x01531f010e1e10d3d3332b2caef340f0a6af1e366a98126ef035c3ce80b8a1db" + ] + }, + "privateKey": "0x504264c14141b98fdad31cff441c443d69458e4033c11eeb0b4928ce9452c5a1", + "unsignedLegacy": "0xf890820132857029692f4582fb3894b26832f9eebe1bb94cb67d5c6babf358d15f834e81b3b86b3c5c6cbe06f0c46868590f0951afc1b55f38598f5caf4d2f811ed3bbb8a87de83f074bc2cf19ca7225a65533cbe03aed1f43fa7cd1ef178e1ec2de6076d3ec1a22729c8645de5442bc8e3c3c7781d68293d34ae667c247189089a4959ef7c0d35e4600be23b5b059629779", + "unsignedEip155": "0xf893820132857029692f4582fb3894b26832f9eebe1bb94cb67d5c6babf358d15f834e81b3b86b3c5c6cbe06f0c46868590f0951afc1b55f38598f5caf4d2f811ed3bbb8a87de83f074bc2cf19ca7225a65533cbe03aed1f43fa7cd1ef178e1ec2de6076d3ec1a22729c8645de5442bc8e3c3c7781d68293d34ae667c247189089a4959ef7c0d35e4600be23b5b059629779038080", + "unsignedBerlin": "0x01f8a903820132857029692f4582fb3894b26832f9eebe1bb94cb67d5c6babf358d15f834e81b3b86b3c5c6cbe06f0c46868590f0951afc1b55f38598f5caf4d2f811ed3bbb8a87de83f074bc2cf19ca7225a65533cbe03aed1f43fa7cd1ef178e1ec2de6076d3ec1a22729c8645de5442bc8e3c3c7781d68293d34ae667c247189089a4959ef7c0d35e4600be23b5b059629779d7d694820a408021ed575bd94e89af5672c9c4faaa93adc0", + "unsignedLondon": "0x02f8ac0382013281b586f631d7b0a81f82fb3894b26832f9eebe1bb94cb67d5c6babf358d15f834e81b3b86b3c5c6cbe06f0c46868590f0951afc1b55f38598f5caf4d2f811ed3bbb8a87de83f074bc2cf19ca7225a65533cbe03aed1f43fa7cd1ef178e1ec2de6076d3ec1a22729c8645de5442bc8e3c3c7781d68293d34ae667c247189089a4959ef7c0d35e4600be23b5b059629779d7d694820a408021ed575bd94e89af5672c9c4faaa93adc0", + "unsignedCancun": "0x03f901580382013281b586f631d7b0a81f82fb3894b26832f9eebe1bb94cb67d5c6babf358d15f834e81b3b86b3c5c6cbe06f0c46868590f0951afc1b55f38598f5caf4d2f811ed3bbb8a87de83f074bc2cf19ca7225a65533cbe03aed1f43fa7cd1ef178e1ec2de6076d3ec1a22729c8645de5442bc8e3c3c7781d68293d34ae667c247189089a4959ef7c0d35e4600be23b5b059629779d7d694820a408021ed575bd94e89af5672c9c4faaa93adc0849b643a13f8a5a00106f3b8583c3b187099347666d5b01b3ae85b5458a2291ca45884084bf8a728a0012d5ba14538fe600b4f258429247337527a13d6a28c3b4b445f00d3098df94fa0011a4647e1b61123ab33b318dff3609fc11da8c4cfa9abe25f99c41923a9f581a001cb8b689043f5bdcc56fce5c26490ca28a88b0a2f53a6d1f51e3f298e65d295a001531f010e1e10d3d3332b2caef340f0a6af1e366a98126ef035c3ce80b8a1db", + "signedLegacy": "0xf8d3820132857029692f4582fb3894b26832f9eebe1bb94cb67d5c6babf358d15f834e81b3b86b3c5c6cbe06f0c46868590f0951afc1b55f38598f5caf4d2f811ed3bbb8a87de83f074bc2cf19ca7225a65533cbe03aed1f43fa7cd1ef178e1ec2de6076d3ec1a22729c8645de5442bc8e3c3c7781d68293d34ae667c247189089a4959ef7c0d35e4600be23b5b0596297791ca09eb445c3fb16cea6f435cca3fd89869351b0152442c77013ee2787ab9363c64fa012daa8f1bbc55ef013376a0b10a608634e9fe03ce051bcf11874132f4b37b1c1", + "signedEip155": "0xf8d3820132857029692f4582fb3894b26832f9eebe1bb94cb67d5c6babf358d15f834e81b3b86b3c5c6cbe06f0c46868590f0951afc1b55f38598f5caf4d2f811ed3bbb8a87de83f074bc2cf19ca7225a65533cbe03aed1f43fa7cd1ef178e1ec2de6076d3ec1a22729c8645de5442bc8e3c3c7781d68293d34ae667c247189089a4959ef7c0d35e4600be23b5b05962977929a03aacb5053d4586e75d7c2f59a16aa559f1b335a1e5b81d5e04ee3f10370e0b78a00c4062ee9c4f3c10b72331bdbbe8b8f896cecaa7f79d291dca3cb6182473d6f3", + "signedBerlin": "0x01f8ec03820132857029692f4582fb3894b26832f9eebe1bb94cb67d5c6babf358d15f834e81b3b86b3c5c6cbe06f0c46868590f0951afc1b55f38598f5caf4d2f811ed3bbb8a87de83f074bc2cf19ca7225a65533cbe03aed1f43fa7cd1ef178e1ec2de6076d3ec1a22729c8645de5442bc8e3c3c7781d68293d34ae667c247189089a4959ef7c0d35e4600be23b5b059629779d7d694820a408021ed575bd94e89af5672c9c4faaa93adc080a0cc30fef0ed85c2d3df5dae8321402cd6ee6cfec5778a191674d295a861be14b3a00874a36ad883e39d70744772063a504ff5887f020ece23afa159ec3d092f7dac", + "signedLondon": "0x02f8ef0382013281b586f631d7b0a81f82fb3894b26832f9eebe1bb94cb67d5c6babf358d15f834e81b3b86b3c5c6cbe06f0c46868590f0951afc1b55f38598f5caf4d2f811ed3bbb8a87de83f074bc2cf19ca7225a65533cbe03aed1f43fa7cd1ef178e1ec2de6076d3ec1a22729c8645de5442bc8e3c3c7781d68293d34ae667c247189089a4959ef7c0d35e4600be23b5b059629779d7d694820a408021ed575bd94e89af5672c9c4faaa93adc080a0fe006607f7e8089cc51cafca96b281cd8c3a95d6f88a5a1175b3a8cc958b6b74a0105054c0d7d120b19d8eb03a67dccd82c2add7b8c04e6438fd30ab6e31cd4364", + "signedCancun": "0x03f9019b0382013281b586f631d7b0a81f82fb3894b26832f9eebe1bb94cb67d5c6babf358d15f834e81b3b86b3c5c6cbe06f0c46868590f0951afc1b55f38598f5caf4d2f811ed3bbb8a87de83f074bc2cf19ca7225a65533cbe03aed1f43fa7cd1ef178e1ec2de6076d3ec1a22729c8645de5442bc8e3c3c7781d68293d34ae667c247189089a4959ef7c0d35e4600be23b5b059629779d7d694820a408021ed575bd94e89af5672c9c4faaa93adc0849b643a13f8a5a00106f3b8583c3b187099347666d5b01b3ae85b5458a2291ca45884084bf8a728a0012d5ba14538fe600b4f258429247337527a13d6a28c3b4b445f00d3098df94fa0011a4647e1b61123ab33b318dff3609fc11da8c4cfa9abe25f99c41923a9f581a001cb8b689043f5bdcc56fce5c26490ca28a88b0a2f53a6d1f51e3f298e65d295a001531f010e1e10d3d3332b2caef340f0a6af1e366a98126ef035c3ce80b8a1db80a0bbe878a38e5cf8d2e439fe021d2063d07b41b2a2ffe78edde5e50ae8c908c6dfa00386dfa5733210ac6b5fb0ddc0fdd493190417ddbd8573c3db375b811b59f970", + "signatureLegacy": { + "r": "0x9eb445c3fb16cea6f435cca3fd89869351b0152442c77013ee2787ab9363c64f", + "s": "0x12daa8f1bbc55ef013376a0b10a608634e9fe03ce051bcf11874132f4b37b1c1", + "v": "0x1c" + }, + "signatureEip155": { + "r": "0x3aacb5053d4586e75d7c2f59a16aa559f1b335a1e5b81d5e04ee3f10370e0b78", + "s": "0x0c4062ee9c4f3c10b72331bdbbe8b8f896cecaa7f79d291dca3cb6182473d6f3", + "v": "0x29" + }, + "signatureBerlin": { + "r": "0xcc30fef0ed85c2d3df5dae8321402cd6ee6cfec5778a191674d295a861be14b3", + "s": "0x0874a36ad883e39d70744772063a504ff5887f020ece23afa159ec3d092f7dac", + "v": "0x0" + }, + "signatureLondon": { + "r": "0xfe006607f7e8089cc51cafca96b281cd8c3a95d6f88a5a1175b3a8cc958b6b74", + "s": "0x105054c0d7d120b19d8eb03a67dccd82c2add7b8c04e6438fd30ab6e31cd4364", + "v": "0x0" + }, + "signatureCancun": { + "r": "0xbbe878a38e5cf8d2e439fe021d2063d07b41b2a2ffe78edde5e50ae8c908c6df", + "s": "0x0386dfa5733210ac6b5fb0ddc0fdd493190417ddbd8573c3db375b811b59f970", + "v": "0x0" + } +} diff --git a/bolt-contracts/test/testdata/transactions/random_17.json b/bolt-contracts/test/testdata/transactions/random_17.json new file mode 100644 index 000000000..7f2762712 --- /dev/null +++ b/bolt-contracts/test/testdata/transactions/random_17.json @@ -0,0 +1,92 @@ +{ + "name": "random-17", + "transaction": { + "to": "0x7b9830530eFD81a5BB21Ed03A1E20ac977c13003", + "nonce": 103, + "gasLimit": "0xa193e9", + "gasPrice": "0xfcb9", + "maxFeePerGas": "0x9288e311910556", + "maxPriorityFeePerGas": "0xac", + "data": "0xee2035dab48b94002f3fa4ba662590127a2fcb9e978af4", + "value": "0xf9eff8", + "accessList": [ + { + "address": "0x469ED43B08A100e13e84b7746fA7206d3A11a5F7", + "storageKeys": [ + "0x434cab87455b41c8023691c4e836ec1e1d90fd639e1207274b07574059394950", + "0x56498d76fcc165010f94437317015d8a9a939ab1fd21216c5fcfed335f5d6ad5", + "0xe83f7815e6bf7d521dd0341e9445415ab565b2471ab4dbbf0a0ddcc564bdb94e" + ] + }, + { + "address": "0x588C330B029E06B820Dfb1d3a5b774EdcF6a9FEE", + "storageKeys": [ + "0x434cab87455b41c8023691c4e836ec1e1d90fd639e1207274b07574059394950", + "0x56498d76fcc165010f94437317015d8a9a939ab1fd21216c5fcfed335f5d6ad5", + "0xe83f7815e6bf7d521dd0341e9445415ab565b2471ab4dbbf0a0ddcc564bdb94e" + ] + }, + { + "address": "0xd7Bb918eB068B37E44b12A19402F5112dE656566", + "storageKeys": [ + "0x434cab87455b41c8023691c4e836ec1e1d90fd639e1207274b07574059394950", + "0x56498d76fcc165010f94437317015d8a9a939ab1fd21216c5fcfed335f5d6ad5", + "0xe83f7815e6bf7d521dd0341e9445415ab565b2471ab4dbbf0a0ddcc564bdb94e" + ] + }, + { + "address": "0x29EFB653e6b98eA36995EC81968b0EAC36678A60", + "storageKeys": [ + "0x434cab87455b41c8023691c4e836ec1e1d90fd639e1207274b07574059394950", + "0x56498d76fcc165010f94437317015d8a9a939ab1fd21216c5fcfed335f5d6ad5", + "0xe83f7815e6bf7d521dd0341e9445415ab565b2471ab4dbbf0a0ddcc564bdb94e" + ] + } + ], + "chainId": "0x6653a6", + "maxFeePerBlobGas": "0xe34d8405aa", + "blobVersionedHashes": [ + "0x01bd0963b45ef67408366b1293b5f94b4fb19340d15f8337a4512a83ae40004d", + "0x013058ace831dd6002ac010bbe261fa4b1087a1a22ef376e8af21c68881cd6a6", + "0x01529e39d8bda59658a661cd10747b680b8f944a5f4276f794859265c718c25c", + "0x010b500df39903a4f96523b8fc5e742898222ec23fcc06facf7384ebd6712236", + "0x01d8fc2341c6b3a4c2aa2aba23344939f851119c19299c9aac4594425577e5ee" + ] + }, + "privateKey": "0x58f3d57fde2c0dae771d5cb66092b8905ba5691226740d7398459c60712a5793", + "unsignedLegacy": "0xf8396782fcb983a193e9947b9830530efd81a5bb21ed03a1e20ac977c1300383f9eff897ee2035dab48b94002f3fa4ba662590127a2fcb9e978af4", + "unsignedEip155": "0xf83f6782fcb983a193e9947b9830530efd81a5bb21ed03a1e20ac977c1300383f9eff897ee2035dab48b94002f3fa4ba662590127a2fcb9e978af4836653a68080", + "unsignedBerlin": "0x01f90230836653a66782fcb983a193e9947b9830530efd81a5bb21ed03a1e20ac977c1300383f9eff897ee2035dab48b94002f3fa4ba662590127a2fcb9e978af4f901f0f87a94469ed43b08a100e13e84b7746fa7206d3a11a5f7f863a0434cab87455b41c8023691c4e836ec1e1d90fd639e1207274b07574059394950a056498d76fcc165010f94437317015d8a9a939ab1fd21216c5fcfed335f5d6ad5a0e83f7815e6bf7d521dd0341e9445415ab565b2471ab4dbbf0a0ddcc564bdb94ef87a94588c330b029e06b820dfb1d3a5b774edcf6a9feef863a0434cab87455b41c8023691c4e836ec1e1d90fd639e1207274b07574059394950a056498d76fcc165010f94437317015d8a9a939ab1fd21216c5fcfed335f5d6ad5a0e83f7815e6bf7d521dd0341e9445415ab565b2471ab4dbbf0a0ddcc564bdb94ef87a94d7bb918eb068b37e44b12a19402f5112de656566f863a0434cab87455b41c8023691c4e836ec1e1d90fd639e1207274b07574059394950a056498d76fcc165010f94437317015d8a9a939ab1fd21216c5fcfed335f5d6ad5a0e83f7815e6bf7d521dd0341e9445415ab565b2471ab4dbbf0a0ddcc564bdb94ef87a9429efb653e6b98ea36995ec81968b0eac36678a60f863a0434cab87455b41c8023691c4e836ec1e1d90fd639e1207274b07574059394950a056498d76fcc165010f94437317015d8a9a939ab1fd21216c5fcfed335f5d6ad5a0e83f7815e6bf7d521dd0341e9445415ab565b2471ab4dbbf0a0ddcc564bdb94e", + "unsignedLondon": "0x02f90237836653a66781ac879288e31191055683a193e9947b9830530efd81a5bb21ed03a1e20ac977c1300383f9eff897ee2035dab48b94002f3fa4ba662590127a2fcb9e978af4f901f0f87a94469ed43b08a100e13e84b7746fa7206d3a11a5f7f863a0434cab87455b41c8023691c4e836ec1e1d90fd639e1207274b07574059394950a056498d76fcc165010f94437317015d8a9a939ab1fd21216c5fcfed335f5d6ad5a0e83f7815e6bf7d521dd0341e9445415ab565b2471ab4dbbf0a0ddcc564bdb94ef87a94588c330b029e06b820dfb1d3a5b774edcf6a9feef863a0434cab87455b41c8023691c4e836ec1e1d90fd639e1207274b07574059394950a056498d76fcc165010f94437317015d8a9a939ab1fd21216c5fcfed335f5d6ad5a0e83f7815e6bf7d521dd0341e9445415ab565b2471ab4dbbf0a0ddcc564bdb94ef87a94d7bb918eb068b37e44b12a19402f5112de656566f863a0434cab87455b41c8023691c4e836ec1e1d90fd639e1207274b07574059394950a056498d76fcc165010f94437317015d8a9a939ab1fd21216c5fcfed335f5d6ad5a0e83f7815e6bf7d521dd0341e9445415ab565b2471ab4dbbf0a0ddcc564bdb94ef87a9429efb653e6b98ea36995ec81968b0eac36678a60f863a0434cab87455b41c8023691c4e836ec1e1d90fd639e1207274b07574059394950a056498d76fcc165010f94437317015d8a9a939ab1fd21216c5fcfed335f5d6ad5a0e83f7815e6bf7d521dd0341e9445415ab565b2471ab4dbbf0a0ddcc564bdb94e", + "unsignedCancun": "0x03f902e4836653a66781ac879288e31191055683a193e9947b9830530efd81a5bb21ed03a1e20ac977c1300383f9eff897ee2035dab48b94002f3fa4ba662590127a2fcb9e978af4f901f0f87a94469ed43b08a100e13e84b7746fa7206d3a11a5f7f863a0434cab87455b41c8023691c4e836ec1e1d90fd639e1207274b07574059394950a056498d76fcc165010f94437317015d8a9a939ab1fd21216c5fcfed335f5d6ad5a0e83f7815e6bf7d521dd0341e9445415ab565b2471ab4dbbf0a0ddcc564bdb94ef87a94588c330b029e06b820dfb1d3a5b774edcf6a9feef863a0434cab87455b41c8023691c4e836ec1e1d90fd639e1207274b07574059394950a056498d76fcc165010f94437317015d8a9a939ab1fd21216c5fcfed335f5d6ad5a0e83f7815e6bf7d521dd0341e9445415ab565b2471ab4dbbf0a0ddcc564bdb94ef87a94d7bb918eb068b37e44b12a19402f5112de656566f863a0434cab87455b41c8023691c4e836ec1e1d90fd639e1207274b07574059394950a056498d76fcc165010f94437317015d8a9a939ab1fd21216c5fcfed335f5d6ad5a0e83f7815e6bf7d521dd0341e9445415ab565b2471ab4dbbf0a0ddcc564bdb94ef87a9429efb653e6b98ea36995ec81968b0eac36678a60f863a0434cab87455b41c8023691c4e836ec1e1d90fd639e1207274b07574059394950a056498d76fcc165010f94437317015d8a9a939ab1fd21216c5fcfed335f5d6ad5a0e83f7815e6bf7d521dd0341e9445415ab565b2471ab4dbbf0a0ddcc564bdb94e85e34d8405aaf8a5a001bd0963b45ef67408366b1293b5f94b4fb19340d15f8337a4512a83ae40004da0013058ace831dd6002ac010bbe261fa4b1087a1a22ef376e8af21c68881cd6a6a001529e39d8bda59658a661cd10747b680b8f944a5f4276f794859265c718c25ca0010b500df39903a4f96523b8fc5e742898222ec23fcc06facf7384ebd6712236a001d8fc2341c6b3a4c2aa2aba23344939f851119c19299c9aac4594425577e5ee", + "signedLegacy": "0xf87c6782fcb983a193e9947b9830530efd81a5bb21ed03a1e20ac977c1300383f9eff897ee2035dab48b94002f3fa4ba662590127a2fcb9e978af41ca0b889cf400786bc43a9bc32317a5368a1f0898b5f60fec9c4da7931867fe5c3fca06dd97c8b894c3a14ec5099eaa39b76f369f49c0ad9330dd852f797b22fb6962e", + "signedEip155": "0xf87f6782fcb983a193e9947b9830530efd81a5bb21ed03a1e20ac977c1300383f9eff897ee2035dab48b94002f3fa4ba662590127a2fcb9e978af483cca770a04ad07bd0c147962802aee8e943f48a5adfadbc5d14c502e10fe108b6a3206691a039150202e4bacfbda9757c56c93858d0dc2ae5c36d69e7f5d61e6d2fb9a4d7e0", + "signedBerlin": "0x01f90273836653a66782fcb983a193e9947b9830530efd81a5bb21ed03a1e20ac977c1300383f9eff897ee2035dab48b94002f3fa4ba662590127a2fcb9e978af4f901f0f87a94469ed43b08a100e13e84b7746fa7206d3a11a5f7f863a0434cab87455b41c8023691c4e836ec1e1d90fd639e1207274b07574059394950a056498d76fcc165010f94437317015d8a9a939ab1fd21216c5fcfed335f5d6ad5a0e83f7815e6bf7d521dd0341e9445415ab565b2471ab4dbbf0a0ddcc564bdb94ef87a94588c330b029e06b820dfb1d3a5b774edcf6a9feef863a0434cab87455b41c8023691c4e836ec1e1d90fd639e1207274b07574059394950a056498d76fcc165010f94437317015d8a9a939ab1fd21216c5fcfed335f5d6ad5a0e83f7815e6bf7d521dd0341e9445415ab565b2471ab4dbbf0a0ddcc564bdb94ef87a94d7bb918eb068b37e44b12a19402f5112de656566f863a0434cab87455b41c8023691c4e836ec1e1d90fd639e1207274b07574059394950a056498d76fcc165010f94437317015d8a9a939ab1fd21216c5fcfed335f5d6ad5a0e83f7815e6bf7d521dd0341e9445415ab565b2471ab4dbbf0a0ddcc564bdb94ef87a9429efb653e6b98ea36995ec81968b0eac36678a60f863a0434cab87455b41c8023691c4e836ec1e1d90fd639e1207274b07574059394950a056498d76fcc165010f94437317015d8a9a939ab1fd21216c5fcfed335f5d6ad5a0e83f7815e6bf7d521dd0341e9445415ab565b2471ab4dbbf0a0ddcc564bdb94e80a0bbeb29da87bb1efcc76efc18dc3fb4d06c2fbe9d0a8695e3bc887f84ab35796ea046f04724d13b40f6e5f8ca963862c821857f58772169706ab4a286feec54196f", + "signedLondon": "0x02f9027a836653a66781ac879288e31191055683a193e9947b9830530efd81a5bb21ed03a1e20ac977c1300383f9eff897ee2035dab48b94002f3fa4ba662590127a2fcb9e978af4f901f0f87a94469ed43b08a100e13e84b7746fa7206d3a11a5f7f863a0434cab87455b41c8023691c4e836ec1e1d90fd639e1207274b07574059394950a056498d76fcc165010f94437317015d8a9a939ab1fd21216c5fcfed335f5d6ad5a0e83f7815e6bf7d521dd0341e9445415ab565b2471ab4dbbf0a0ddcc564bdb94ef87a94588c330b029e06b820dfb1d3a5b774edcf6a9feef863a0434cab87455b41c8023691c4e836ec1e1d90fd639e1207274b07574059394950a056498d76fcc165010f94437317015d8a9a939ab1fd21216c5fcfed335f5d6ad5a0e83f7815e6bf7d521dd0341e9445415ab565b2471ab4dbbf0a0ddcc564bdb94ef87a94d7bb918eb068b37e44b12a19402f5112de656566f863a0434cab87455b41c8023691c4e836ec1e1d90fd639e1207274b07574059394950a056498d76fcc165010f94437317015d8a9a939ab1fd21216c5fcfed335f5d6ad5a0e83f7815e6bf7d521dd0341e9445415ab565b2471ab4dbbf0a0ddcc564bdb94ef87a9429efb653e6b98ea36995ec81968b0eac36678a60f863a0434cab87455b41c8023691c4e836ec1e1d90fd639e1207274b07574059394950a056498d76fcc165010f94437317015d8a9a939ab1fd21216c5fcfed335f5d6ad5a0e83f7815e6bf7d521dd0341e9445415ab565b2471ab4dbbf0a0ddcc564bdb94e80a0d6593cf036acec58fa43ac9b1a19b961b26acf1b27f23ed3cfa5fc2dd63b1b73a06cf24189b044480b469c1b6bbe97bd3caf522e0a9c141d9d8a5b7e5ad225bebc", + "signedCancun": "0x03f90327836653a66781ac879288e31191055683a193e9947b9830530efd81a5bb21ed03a1e20ac977c1300383f9eff897ee2035dab48b94002f3fa4ba662590127a2fcb9e978af4f901f0f87a94469ed43b08a100e13e84b7746fa7206d3a11a5f7f863a0434cab87455b41c8023691c4e836ec1e1d90fd639e1207274b07574059394950a056498d76fcc165010f94437317015d8a9a939ab1fd21216c5fcfed335f5d6ad5a0e83f7815e6bf7d521dd0341e9445415ab565b2471ab4dbbf0a0ddcc564bdb94ef87a94588c330b029e06b820dfb1d3a5b774edcf6a9feef863a0434cab87455b41c8023691c4e836ec1e1d90fd639e1207274b07574059394950a056498d76fcc165010f94437317015d8a9a939ab1fd21216c5fcfed335f5d6ad5a0e83f7815e6bf7d521dd0341e9445415ab565b2471ab4dbbf0a0ddcc564bdb94ef87a94d7bb918eb068b37e44b12a19402f5112de656566f863a0434cab87455b41c8023691c4e836ec1e1d90fd639e1207274b07574059394950a056498d76fcc165010f94437317015d8a9a939ab1fd21216c5fcfed335f5d6ad5a0e83f7815e6bf7d521dd0341e9445415ab565b2471ab4dbbf0a0ddcc564bdb94ef87a9429efb653e6b98ea36995ec81968b0eac36678a60f863a0434cab87455b41c8023691c4e836ec1e1d90fd639e1207274b07574059394950a056498d76fcc165010f94437317015d8a9a939ab1fd21216c5fcfed335f5d6ad5a0e83f7815e6bf7d521dd0341e9445415ab565b2471ab4dbbf0a0ddcc564bdb94e85e34d8405aaf8a5a001bd0963b45ef67408366b1293b5f94b4fb19340d15f8337a4512a83ae40004da0013058ace831dd6002ac010bbe261fa4b1087a1a22ef376e8af21c68881cd6a6a001529e39d8bda59658a661cd10747b680b8f944a5f4276f794859265c718c25ca0010b500df39903a4f96523b8fc5e742898222ec23fcc06facf7384ebd6712236a001d8fc2341c6b3a4c2aa2aba23344939f851119c19299c9aac4594425577e5ee01a0cff1301e6d636143fc801005349c6d64687f4e6d0c3cf0efcaf4f04863adb321a00f46aa1d9c1c2006e42981f19e75db3c5087f7535b759faabafa3642cde03879", + "signatureLegacy": { + "r": "0xb889cf400786bc43a9bc32317a5368a1f0898b5f60fec9c4da7931867fe5c3fc", + "s": "0x6dd97c8b894c3a14ec5099eaa39b76f369f49c0ad9330dd852f797b22fb6962e", + "v": "0x1c" + }, + "signatureEip155": { + "r": "0x4ad07bd0c147962802aee8e943f48a5adfadbc5d14c502e10fe108b6a3206691", + "s": "0x39150202e4bacfbda9757c56c93858d0dc2ae5c36d69e7f5d61e6d2fb9a4d7e0", + "v": "0xcca770" + }, + "signatureBerlin": { + "r": "0xbbeb29da87bb1efcc76efc18dc3fb4d06c2fbe9d0a8695e3bc887f84ab35796e", + "s": "0x46f04724d13b40f6e5f8ca963862c821857f58772169706ab4a286feec54196f", + "v": "0x0" + }, + "signatureLondon": { + "r": "0xd6593cf036acec58fa43ac9b1a19b961b26acf1b27f23ed3cfa5fc2dd63b1b73", + "s": "0x6cf24189b044480b469c1b6bbe97bd3caf522e0a9c141d9d8a5b7e5ad225bebc", + "v": "0x0" + }, + "signatureCancun": { + "r": "0xcff1301e6d636143fc801005349c6d64687f4e6d0c3cf0efcaf4f04863adb321", + "s": "0x0f46aa1d9c1c2006e42981f19e75db3c5087f7535b759faabafa3642cde03879", + "v": "0x1" + } +} diff --git a/bolt-contracts/test/testdata/transactions/random_18.json b/bolt-contracts/test/testdata/transactions/random_18.json new file mode 100644 index 000000000..7a6eaf1f3 --- /dev/null +++ b/bolt-contracts/test/testdata/transactions/random_18.json @@ -0,0 +1,65 @@ +{ + "name": "random-18", + "transaction": { + "to": "0x0bbe326563703D26cB074Ef29Ed04404Ab1Fd0cC", + "nonce": 503, + "gasLimit": "0xa5cdc1", + "gasPrice": "0x2a", + "maxFeePerGas": "0x52b5f1114c1a", + "maxPriorityFeePerGas": "0x59d1ad", + "data": "0xb477bf440adf79ace31d2590393c7bbd5c191c2e40b4523e94b9c75afce3a871", + "value": "0x8a1d", + "accessList": [ + { + "address": "0x47421DbF48444B1c15748fFB721AC9b341c35347", + "storageKeys": [ + "0x1e4e2be542b128bf053d245b5efe9be008785740189d3b5b2324a395630c3279", + "0xed8dbc976b685b16dc203291d38dd688d247ae09d2fea99189d5171ddcd7c90e", + "0x4ba4902e9df30d2ea5dc662cfa7887d327dc09fcd28d3dbf5c64f5e78f5e599b" + ] + } + ], + "chainId": "0x27", + "maxFeePerBlobGas": "0x6a1ab8a28e74c2", + "blobVersionedHashes": [ + "0x0135a251a4d47cbb2e3ec7eab672f3505d49261cb433895e559539ba21441daa", + "0x0125f41da5f8e7146e8e5e4508967f0379dd4ae378ddf56f6f379814c056913a" + ] + }, + "privateKey": "0xea795d7cacc6025f7bd6650aedd689f688617f9d8ca86236fa8ca5e654b767c4", + "unsignedLegacy": "0xf8418201f72a83a5cdc1940bbe326563703d26cb074ef29ed04404ab1fd0cc828a1da0b477bf440adf79ace31d2590393c7bbd5c191c2e40b4523e94b9c75afce3a871", + "unsignedEip155": "0xf8448201f72a83a5cdc1940bbe326563703d26cb074ef29ed04404ab1fd0cc828a1da0b477bf440adf79ace31d2590393c7bbd5c191c2e40b4523e94b9c75afce3a871278080", + "unsignedBerlin": "0x01f8c0278201f72a83a5cdc1940bbe326563703d26cb074ef29ed04404ab1fd0cc828a1da0b477bf440adf79ace31d2590393c7bbd5c191c2e40b4523e94b9c75afce3a871f87cf87a9447421dbf48444b1c15748ffb721ac9b341c35347f863a01e4e2be542b128bf053d245b5efe9be008785740189d3b5b2324a395630c3279a0ed8dbc976b685b16dc203291d38dd688d247ae09d2fea99189d5171ddcd7c90ea04ba4902e9df30d2ea5dc662cfa7887d327dc09fcd28d3dbf5c64f5e78f5e599b", + "unsignedLondon": "0x02f8ca278201f78359d1ad8652b5f1114c1a83a5cdc1940bbe326563703d26cb074ef29ed04404ab1fd0cc828a1da0b477bf440adf79ace31d2590393c7bbd5c191c2e40b4523e94b9c75afce3a871f87cf87a9447421dbf48444b1c15748ffb721ac9b341c35347f863a01e4e2be542b128bf053d245b5efe9be008785740189d3b5b2324a395630c3279a0ed8dbc976b685b16dc203291d38dd688d247ae09d2fea99189d5171ddcd7c90ea04ba4902e9df30d2ea5dc662cfa7887d327dc09fcd28d3dbf5c64f5e78f5e599b", + "unsignedCancun": "0x03f90116278201f78359d1ad8652b5f1114c1a83a5cdc1940bbe326563703d26cb074ef29ed04404ab1fd0cc828a1da0b477bf440adf79ace31d2590393c7bbd5c191c2e40b4523e94b9c75afce3a871f87cf87a9447421dbf48444b1c15748ffb721ac9b341c35347f863a01e4e2be542b128bf053d245b5efe9be008785740189d3b5b2324a395630c3279a0ed8dbc976b685b16dc203291d38dd688d247ae09d2fea99189d5171ddcd7c90ea04ba4902e9df30d2ea5dc662cfa7887d327dc09fcd28d3dbf5c64f5e78f5e599b876a1ab8a28e74c2f842a00135a251a4d47cbb2e3ec7eab672f3505d49261cb433895e559539ba21441daaa00125f41da5f8e7146e8e5e4508967f0379dd4ae378ddf56f6f379814c056913a", + "signedLegacy": "0xf8848201f72a83a5cdc1940bbe326563703d26cb074ef29ed04404ab1fd0cc828a1da0b477bf440adf79ace31d2590393c7bbd5c191c2e40b4523e94b9c75afce3a8711ca0c888e9068c768fa7d14a6298902c6b4be841794498ab8f5a5b4ec8027197ee14a04561fbe2803fd2d4ffd8f491c6371816b213832776a93dc48c922887e8bbc671", + "signedEip155": "0xf8848201f72a83a5cdc1940bbe326563703d26cb074ef29ed04404ab1fd0cc828a1da0b477bf440adf79ace31d2590393c7bbd5c191c2e40b4523e94b9c75afce3a87172a052aea6be6b07d11486dc584c42cf1bd34ff8ec9096894fbd34cb7cc0d1aecce8a074220e1ac0c3ef2b2b60d1dea95db96d17872e1a55dc55b4835333d2c4ecdf95", + "signedBerlin": "0x01f90103278201f72a83a5cdc1940bbe326563703d26cb074ef29ed04404ab1fd0cc828a1da0b477bf440adf79ace31d2590393c7bbd5c191c2e40b4523e94b9c75afce3a871f87cf87a9447421dbf48444b1c15748ffb721ac9b341c35347f863a01e4e2be542b128bf053d245b5efe9be008785740189d3b5b2324a395630c3279a0ed8dbc976b685b16dc203291d38dd688d247ae09d2fea99189d5171ddcd7c90ea04ba4902e9df30d2ea5dc662cfa7887d327dc09fcd28d3dbf5c64f5e78f5e599b80a0e6a87722ab1ce90d062a73fe2fe3ebf7135d9b3946b7bd7edb3b4b298504e8c5a01cfad4408ddaa8e38d031b99277a81b7cd118f760acc8946b5025d2d77200197", + "signedLondon": "0x02f9010d278201f78359d1ad8652b5f1114c1a83a5cdc1940bbe326563703d26cb074ef29ed04404ab1fd0cc828a1da0b477bf440adf79ace31d2590393c7bbd5c191c2e40b4523e94b9c75afce3a871f87cf87a9447421dbf48444b1c15748ffb721ac9b341c35347f863a01e4e2be542b128bf053d245b5efe9be008785740189d3b5b2324a395630c3279a0ed8dbc976b685b16dc203291d38dd688d247ae09d2fea99189d5171ddcd7c90ea04ba4902e9df30d2ea5dc662cfa7887d327dc09fcd28d3dbf5c64f5e78f5e599b01a0f8825407a5607db5184718258869e3ba1eef9bd4c1bf4826315cfc8693593894a07aad01abea832c052973a749a3a623d63922fbbb090f5bbdb4582992d5bd508f", + "signedCancun": "0x03f90159278201f78359d1ad8652b5f1114c1a83a5cdc1940bbe326563703d26cb074ef29ed04404ab1fd0cc828a1da0b477bf440adf79ace31d2590393c7bbd5c191c2e40b4523e94b9c75afce3a871f87cf87a9447421dbf48444b1c15748ffb721ac9b341c35347f863a01e4e2be542b128bf053d245b5efe9be008785740189d3b5b2324a395630c3279a0ed8dbc976b685b16dc203291d38dd688d247ae09d2fea99189d5171ddcd7c90ea04ba4902e9df30d2ea5dc662cfa7887d327dc09fcd28d3dbf5c64f5e78f5e599b876a1ab8a28e74c2f842a00135a251a4d47cbb2e3ec7eab672f3505d49261cb433895e559539ba21441daaa00125f41da5f8e7146e8e5e4508967f0379dd4ae378ddf56f6f379814c056913a01a0dd72f86ce46f065e917d86c73aa3ca7e683bfeb711a84bc10d3f253161a30cdaa00c4e90ba71f6c3ee0f989353e221408330e877d65cbd0adea67e2414dd5308d7", + "signatureLegacy": { + "r": "0xc888e9068c768fa7d14a6298902c6b4be841794498ab8f5a5b4ec8027197ee14", + "s": "0x4561fbe2803fd2d4ffd8f491c6371816b213832776a93dc48c922887e8bbc671", + "v": "0x1c" + }, + "signatureEip155": { + "r": "0x52aea6be6b07d11486dc584c42cf1bd34ff8ec9096894fbd34cb7cc0d1aecce8", + "s": "0x74220e1ac0c3ef2b2b60d1dea95db96d17872e1a55dc55b4835333d2c4ecdf95", + "v": "0x72" + }, + "signatureBerlin": { + "r": "0xe6a87722ab1ce90d062a73fe2fe3ebf7135d9b3946b7bd7edb3b4b298504e8c5", + "s": "0x1cfad4408ddaa8e38d031b99277a81b7cd118f760acc8946b5025d2d77200197", + "v": "0x0" + }, + "signatureLondon": { + "r": "0xf8825407a5607db5184718258869e3ba1eef9bd4c1bf4826315cfc8693593894", + "s": "0x7aad01abea832c052973a749a3a623d63922fbbb090f5bbdb4582992d5bd508f", + "v": "0x1" + }, + "signatureCancun": { + "r": "0xdd72f86ce46f065e917d86c73aa3ca7e683bfeb711a84bc10d3f253161a30cda", + "s": "0x0c4e90ba71f6c3ee0f989353e221408330e877d65cbd0adea67e2414dd5308d7", + "v": "0x1" + } +} diff --git a/bolt-contracts/test/testdata/transactions/random_19.json b/bolt-contracts/test/testdata/transactions/random_19.json new file mode 100644 index 000000000..5a46f9935 --- /dev/null +++ b/bolt-contracts/test/testdata/transactions/random_19.json @@ -0,0 +1,73 @@ +{ + "name": "random-19", + "transaction": { + "to": "0x6D9186AD35Ba39086D341c03B212a22dAd55A70c", + "nonce": 47, + "gasLimit": "0x8e2d", + "gasPrice": "0xed", + "maxFeePerGas": "0x8f001cace664", + "maxPriorityFeePerGas": "0x86495d", + "data": "0xa6e0079be77374ee2617550eadd15e206a8c7eb76eca2de96d48c4824b4646206720dfeb24956f1e712b58b9ef96fc92e637cf4c30614d86532d3110f670c5a70e5bdeef3dc5cf94ef8d3afffc50151c00a5ad1289fae8168bb120e9dbdb24c422eacfc4d8291c6dc942a872545c76b5c697806c84f478d361026a2ad9", + "value": "0x4028", + "accessList": [ + { + "address": "0x27CBC99f4161665Fc37a013015B0B99A959f6fcb", + "storageKeys": [] + }, + { + "address": "0x296e9Be34584A14761c7b4cE82E99d4394A5f109", + "storageKeys": [] + }, + { + "address": "0xeD1C55cDE477E627081Ac4D17376f4Bf9A2dc51E", + "storageKeys": [] + }, + { + "address": "0x7a46971171Aa87E7Ef026C381053940714f4Ad91", + "storageKeys": [] + } + ], + "chainId": "0xf9bf2e", + "maxFeePerBlobGas": "0xfcaa613f0b5f", + "blobVersionedHashes": [ + "0x010e2214c267eb6c514aaf6eab956c7043760e0cf06f8b854ce38a637c886a92", + "0x01971b32a91a83e04ef14d049bf10a13377c3f18bde9c5ea313f8c2da061bb62" + ] + }, + "privateKey": "0x1b01010d8955e5cecc6705b73064baa37a703d65cb970a5b97c1cc9b76aa9473", + "unsignedLegacy": "0xf89d2f81ed828e2d946d9186ad35ba39086d341c03b212a22dad55a70c824028b87da6e0079be77374ee2617550eadd15e206a8c7eb76eca2de96d48c4824b4646206720dfeb24956f1e712b58b9ef96fc92e637cf4c30614d86532d3110f670c5a70e5bdeef3dc5cf94ef8d3afffc50151c00a5ad1289fae8168bb120e9dbdb24c422eacfc4d8291c6dc942a872545c76b5c697806c84f478d361026a2ad9", + "unsignedEip155": "0xf8a32f81ed828e2d946d9186ad35ba39086d341c03b212a22dad55a70c824028b87da6e0079be77374ee2617550eadd15e206a8c7eb76eca2de96d48c4824b4646206720dfeb24956f1e712b58b9ef96fc92e637cf4c30614d86532d3110f670c5a70e5bdeef3dc5cf94ef8d3afffc50151c00a5ad1289fae8168bb120e9dbdb24c422eacfc4d8291c6dc942a872545c76b5c697806c84f478d361026a2ad983f9bf2e8080", + "unsignedBerlin": "0x01f8ff83f9bf2e2f81ed828e2d946d9186ad35ba39086d341c03b212a22dad55a70c824028b87da6e0079be77374ee2617550eadd15e206a8c7eb76eca2de96d48c4824b4646206720dfeb24956f1e712b58b9ef96fc92e637cf4c30614d86532d3110f670c5a70e5bdeef3dc5cf94ef8d3afffc50151c00a5ad1289fae8168bb120e9dbdb24c422eacfc4d8291c6dc942a872545c76b5c697806c84f478d361026a2ad9f85cd69427cbc99f4161665fc37a013015b0b99a959f6fcbc0d694296e9be34584a14761c7b4ce82e99d4394a5f109c0d694ed1c55cde477e627081ac4d17376f4bf9a2dc51ec0d6947a46971171aa87e7ef026c381053940714f4ad91c0", + "unsignedLondon": "0x02f9010883f9bf2e2f8386495d868f001cace664828e2d946d9186ad35ba39086d341c03b212a22dad55a70c824028b87da6e0079be77374ee2617550eadd15e206a8c7eb76eca2de96d48c4824b4646206720dfeb24956f1e712b58b9ef96fc92e637cf4c30614d86532d3110f670c5a70e5bdeef3dc5cf94ef8d3afffc50151c00a5ad1289fae8168bb120e9dbdb24c422eacfc4d8291c6dc942a872545c76b5c697806c84f478d361026a2ad9f85cd69427cbc99f4161665fc37a013015b0b99a959f6fcbc0d694296e9be34584a14761c7b4ce82e99d4394a5f109c0d694ed1c55cde477e627081ac4d17376f4bf9a2dc51ec0d6947a46971171aa87e7ef026c381053940714f4ad91c0", + "unsignedCancun": "0x03f9015383f9bf2e2f8386495d868f001cace664828e2d946d9186ad35ba39086d341c03b212a22dad55a70c824028b87da6e0079be77374ee2617550eadd15e206a8c7eb76eca2de96d48c4824b4646206720dfeb24956f1e712b58b9ef96fc92e637cf4c30614d86532d3110f670c5a70e5bdeef3dc5cf94ef8d3afffc50151c00a5ad1289fae8168bb120e9dbdb24c422eacfc4d8291c6dc942a872545c76b5c697806c84f478d361026a2ad9f85cd69427cbc99f4161665fc37a013015b0b99a959f6fcbc0d694296e9be34584a14761c7b4ce82e99d4394a5f109c0d694ed1c55cde477e627081ac4d17376f4bf9a2dc51ec0d6947a46971171aa87e7ef026c381053940714f4ad91c086fcaa613f0b5ff842a0010e2214c267eb6c514aaf6eab956c7043760e0cf06f8b854ce38a637c886a92a001971b32a91a83e04ef14d049bf10a13377c3f18bde9c5ea313f8c2da061bb62", + "signedLegacy": "0xf8e02f81ed828e2d946d9186ad35ba39086d341c03b212a22dad55a70c824028b87da6e0079be77374ee2617550eadd15e206a8c7eb76eca2de96d48c4824b4646206720dfeb24956f1e712b58b9ef96fc92e637cf4c30614d86532d3110f670c5a70e5bdeef3dc5cf94ef8d3afffc50151c00a5ad1289fae8168bb120e9dbdb24c422eacfc4d8291c6dc942a872545c76b5c697806c84f478d361026a2ad91ba0804162984a09f440a312f2231b192dc7b43fb49546ede924e1ef2b4b54abcd31a04635d7370c171b9e6ac7fa3bca544d0c227d32f0a1c5d065cc0bb02b79e8eaa1", + "signedEip155": "0xf8e42f81ed828e2d946d9186ad35ba39086d341c03b212a22dad55a70c824028b87da6e0079be77374ee2617550eadd15e206a8c7eb76eca2de96d48c4824b4646206720dfeb24956f1e712b58b9ef96fc92e637cf4c30614d86532d3110f670c5a70e5bdeef3dc5cf94ef8d3afffc50151c00a5ad1289fae8168bb120e9dbdb24c422eacfc4d8291c6dc942a872545c76b5c697806c84f478d361026a2ad98401f37e80a05914063f10cf79eea0d3c5cf10bf7765ffc648638c656ddf0c678057b6d570aaa00a89d79bcfa95f96290ec7938ab7c5552423736a16e0a39d84b140c5307a8bab", + "signedBerlin": "0x01f9014283f9bf2e2f81ed828e2d946d9186ad35ba39086d341c03b212a22dad55a70c824028b87da6e0079be77374ee2617550eadd15e206a8c7eb76eca2de96d48c4824b4646206720dfeb24956f1e712b58b9ef96fc92e637cf4c30614d86532d3110f670c5a70e5bdeef3dc5cf94ef8d3afffc50151c00a5ad1289fae8168bb120e9dbdb24c422eacfc4d8291c6dc942a872545c76b5c697806c84f478d361026a2ad9f85cd69427cbc99f4161665fc37a013015b0b99a959f6fcbc0d694296e9be34584a14761c7b4ce82e99d4394a5f109c0d694ed1c55cde477e627081ac4d17376f4bf9a2dc51ec0d6947a46971171aa87e7ef026c381053940714f4ad91c080a0888f5e0b4c258b4f7c488d4e1f7dafc82d7835381b5470bc1e9571ad6bd21929a07a8a19227f4f2cb93bccbc7b4bcb0b731701a0284df8ed3fb90936e6ac0a18d1", + "signedLondon": "0x02f9014b83f9bf2e2f8386495d868f001cace664828e2d946d9186ad35ba39086d341c03b212a22dad55a70c824028b87da6e0079be77374ee2617550eadd15e206a8c7eb76eca2de96d48c4824b4646206720dfeb24956f1e712b58b9ef96fc92e637cf4c30614d86532d3110f670c5a70e5bdeef3dc5cf94ef8d3afffc50151c00a5ad1289fae8168bb120e9dbdb24c422eacfc4d8291c6dc942a872545c76b5c697806c84f478d361026a2ad9f85cd69427cbc99f4161665fc37a013015b0b99a959f6fcbc0d694296e9be34584a14761c7b4ce82e99d4394a5f109c0d694ed1c55cde477e627081ac4d17376f4bf9a2dc51ec0d6947a46971171aa87e7ef026c381053940714f4ad91c001a0f9ba41389da7c4a5471d8af8235fb765d90ec76b370516ec0108d43eda880c84a00bca4f8cb35a6023cbeee50d38e0b62588b6cfc47d3e8023413f2bdfb04f3a8e", + "signedCancun": "0x03f9019683f9bf2e2f8386495d868f001cace664828e2d946d9186ad35ba39086d341c03b212a22dad55a70c824028b87da6e0079be77374ee2617550eadd15e206a8c7eb76eca2de96d48c4824b4646206720dfeb24956f1e712b58b9ef96fc92e637cf4c30614d86532d3110f670c5a70e5bdeef3dc5cf94ef8d3afffc50151c00a5ad1289fae8168bb120e9dbdb24c422eacfc4d8291c6dc942a872545c76b5c697806c84f478d361026a2ad9f85cd69427cbc99f4161665fc37a013015b0b99a959f6fcbc0d694296e9be34584a14761c7b4ce82e99d4394a5f109c0d694ed1c55cde477e627081ac4d17376f4bf9a2dc51ec0d6947a46971171aa87e7ef026c381053940714f4ad91c086fcaa613f0b5ff842a0010e2214c267eb6c514aaf6eab956c7043760e0cf06f8b854ce38a637c886a92a001971b32a91a83e04ef14d049bf10a13377c3f18bde9c5ea313f8c2da061bb6201a0e5af277a499dbd89c8b25adfab873a4bade8e963e403dbaaba3398c1b4f6d0c1a00282b21217a21a6e57c3713da3700265dc4e86803f364175d26d3ab727f812e3", + "signatureLegacy": { + "r": "0x804162984a09f440a312f2231b192dc7b43fb49546ede924e1ef2b4b54abcd31", + "s": "0x4635d7370c171b9e6ac7fa3bca544d0c227d32f0a1c5d065cc0bb02b79e8eaa1", + "v": "0x1b" + }, + "signatureEip155": { + "r": "0x5914063f10cf79eea0d3c5cf10bf7765ffc648638c656ddf0c678057b6d570aa", + "s": "0x0a89d79bcfa95f96290ec7938ab7c5552423736a16e0a39d84b140c5307a8bab", + "v": "0x1f37e80" + }, + "signatureBerlin": { + "r": "0x888f5e0b4c258b4f7c488d4e1f7dafc82d7835381b5470bc1e9571ad6bd21929", + "s": "0x7a8a19227f4f2cb93bccbc7b4bcb0b731701a0284df8ed3fb90936e6ac0a18d1", + "v": "0x0" + }, + "signatureLondon": { + "r": "0xf9ba41389da7c4a5471d8af8235fb765d90ec76b370516ec0108d43eda880c84", + "s": "0x0bca4f8cb35a6023cbeee50d38e0b62588b6cfc47d3e8023413f2bdfb04f3a8e", + "v": "0x1" + }, + "signatureCancun": { + "r": "0xe5af277a499dbd89c8b25adfab873a4bade8e963e403dbaaba3398c1b4f6d0c1", + "s": "0x0282b21217a21a6e57c3713da3700265dc4e86803f364175d26d3ab727f812e3", + "v": "0x1" + } +} diff --git a/bolt-contracts/test/testdata/transactions/random_2.json b/bolt-contracts/test/testdata/transactions/random_2.json new file mode 100644 index 000000000..f42ea1ed5 --- /dev/null +++ b/bolt-contracts/test/testdata/transactions/random_2.json @@ -0,0 +1,64 @@ +{ + "name": "random-2", + "transaction": { + "to": "0xD0F118AFa9C2c9cc50F10d94cCC1cbDda2758b36", + "nonce": 15, + "gasLimit": "0x331bce0f90", + "gasPrice": "0xe15a1b", + "maxFeePerGas": "0x58e8d1dda1", + "maxPriorityFeePerGas": "0x91bcff", + "data": "0xe0d1a7227d34c2ca72e3c0", + "value": "0x91e32e2f5a", + "accessList": [ + { + "address": "0x032f5546f1B932555cB43e2b16C844d118078430", + "storageKeys": [] + } + ], + "chainId": "0xac9f74e3", + "maxFeePerBlobGas": "0xb301566e1b", + "blobVersionedHashes": [ + "0x01ffa0354a85a918e71cda7e37145c0a2f7c6fb1f10ff992ef1de8ebe3c40fd5", + "0x01cf50b584016c19732d845cc9c8d3a43ce413626a4f67acb98ccb3b2f1843cd", + "0x01d391295779890c162f2833ea186b025e6808b615b03854e2ec2664686711bc", + "0x01eac2c29509cb2843deba916c97d9607d460eb0cd6e58744d823f568ab4e89e", + "0x01873bc19156620621a96fe8bc4a0038cbac0737aa0453777816cc6ba916e9c2" + ] + }, + "privateKey": "0xa248ca1e9bd9c10fb163baefd567da6658f7419f3b7ec8bb13800ef2c546cdc9", + "unsignedLegacy": "0xf20f83e15a1b85331bce0f9094d0f118afa9c2c9cc50f10d94ccc1cbdda2758b368591e32e2f5a8be0d1a7227d34c2ca72e3c0", + "unsignedEip155": "0xf8390f83e15a1b85331bce0f9094d0f118afa9c2c9cc50f10d94ccc1cbdda2758b368591e32e2f5a8be0d1a7227d34c2ca72e3c084ac9f74e38080", + "unsignedBerlin": "0x01f84f84ac9f74e30f83e15a1b85331bce0f9094d0f118afa9c2c9cc50f10d94ccc1cbdda2758b368591e32e2f5a8be0d1a7227d34c2ca72e3c0d7d694032f5546f1b932555cb43e2b16c844d118078430c0", + "unsignedLondon": "0x02f85584ac9f74e30f8391bcff8558e8d1dda185331bce0f9094d0f118afa9c2c9cc50f10d94ccc1cbdda2758b368591e32e2f5a8be0d1a7227d34c2ca72e3c0d7d694032f5546f1b932555cb43e2b16c844d118078430c0", + "unsignedCancun": "0x03f9010284ac9f74e30f8391bcff8558e8d1dda185331bce0f9094d0f118afa9c2c9cc50f10d94ccc1cbdda2758b368591e32e2f5a8be0d1a7227d34c2ca72e3c0d7d694032f5546f1b932555cb43e2b16c844d118078430c085b301566e1bf8a5a001ffa0354a85a918e71cda7e37145c0a2f7c6fb1f10ff992ef1de8ebe3c40fd5a001cf50b584016c19732d845cc9c8d3a43ce413626a4f67acb98ccb3b2f1843cda001d391295779890c162f2833ea186b025e6808b615b03854e2ec2664686711bca001eac2c29509cb2843deba916c97d9607d460eb0cd6e58744d823f568ab4e89ea001873bc19156620621a96fe8bc4a0038cbac0737aa0453777816cc6ba916e9c2", + "signedLegacy": "0xf8750f83e15a1b85331bce0f9094d0f118afa9c2c9cc50f10d94ccc1cbdda2758b368591e32e2f5a8be0d1a7227d34c2ca72e3c01ba0bde01f2c9907b076015708bff05e24058fd153bfc2f551a0adafcd7d61a088a0a01035bfd65df2799749536217c832d8d81b915448ac0ab31935d50917136e4c18", + "signedEip155": "0xf87a0f83e15a1b85331bce0f9094d0f118afa9c2c9cc50f10d94ccc1cbdda2758b368591e32e2f5a8be0d1a7227d34c2ca72e3c08501593ee9e9a0f42dbeea4337d8b48ed8f2bc257c83eb16725af6f3077e99fba6bac48c5e65f8a01869eaf77bc621843ba6c8b23919f7c9ebf0ac0001a840045609e1e96f14ab5a", + "signedBerlin": "0x01f89284ac9f74e30f83e15a1b85331bce0f9094d0f118afa9c2c9cc50f10d94ccc1cbdda2758b368591e32e2f5a8be0d1a7227d34c2ca72e3c0d7d694032f5546f1b932555cb43e2b16c844d118078430c080a05cc5457e6561e9831b8e52fb09765dc00eaa07ba68f05dc7558ad0dab2d2b8c3a020c19425fafafdc458dd119f6a59780fc4dd3bbd7ae65f351d3adf5b217d2f4e", + "signedLondon": "0x02f89884ac9f74e30f8391bcff8558e8d1dda185331bce0f9094d0f118afa9c2c9cc50f10d94ccc1cbdda2758b368591e32e2f5a8be0d1a7227d34c2ca72e3c0d7d694032f5546f1b932555cb43e2b16c844d118078430c001a0e97853a3f394ebb19b1a152324c7f3f7ffde2e2a0dfa295dc61f8788dcfebe00a02f0c9626e967c4494ccd02f4e60c840ecce64216438f051efa95f0b8f196f7c9", + "signedCancun": "0x03f9014584ac9f74e30f8391bcff8558e8d1dda185331bce0f9094d0f118afa9c2c9cc50f10d94ccc1cbdda2758b368591e32e2f5a8be0d1a7227d34c2ca72e3c0d7d694032f5546f1b932555cb43e2b16c844d118078430c085b301566e1bf8a5a001ffa0354a85a918e71cda7e37145c0a2f7c6fb1f10ff992ef1de8ebe3c40fd5a001cf50b584016c19732d845cc9c8d3a43ce413626a4f67acb98ccb3b2f1843cda001d391295779890c162f2833ea186b025e6808b615b03854e2ec2664686711bca001eac2c29509cb2843deba916c97d9607d460eb0cd6e58744d823f568ab4e89ea001873bc19156620621a96fe8bc4a0038cbac0737aa0453777816cc6ba916e9c201a0840ee8400c949196bddbf386e82d5479a7607e5356f03a03360af4c466b309eaa026456727b69c25a095f29fed6375752693270a9989bfa3bb45fa01df37faf4dd", + "signatureLegacy": { + "r": "0xbde01f2c9907b076015708bff05e24058fd153bfc2f551a0adafcd7d61a088a0", + "s": "0x1035bfd65df2799749536217c832d8d81b915448ac0ab31935d50917136e4c18", + "v": "0x1b" + }, + "signatureEip155": { + "r": "0xf42dbeea4337d8b48ed8f2bc257c83eb16725af6f3077e99fba6bac48c5e65f8", + "s": "0x1869eaf77bc621843ba6c8b23919f7c9ebf0ac0001a840045609e1e96f14ab5a", + "v": "0x1593ee9e9" + }, + "signatureBerlin": { + "r": "0x5cc5457e6561e9831b8e52fb09765dc00eaa07ba68f05dc7558ad0dab2d2b8c3", + "s": "0x20c19425fafafdc458dd119f6a59780fc4dd3bbd7ae65f351d3adf5b217d2f4e", + "v": "0x0" + }, + "signatureLondon": { + "r": "0xe97853a3f394ebb19b1a152324c7f3f7ffde2e2a0dfa295dc61f8788dcfebe00", + "s": "0x2f0c9626e967c4494ccd02f4e60c840ecce64216438f051efa95f0b8f196f7c9", + "v": "0x1" + }, + "signatureCancun": { + "r": "0x840ee8400c949196bddbf386e82d5479a7607e5356f03a03360af4c466b309ea", + "s": "0x26456727b69c25a095f29fed6375752693270a9989bfa3bb45fa01df37faf4dd", + "v": "0x1" + } +} diff --git a/bolt-contracts/test/testdata/transactions/random_20.json b/bolt-contracts/test/testdata/transactions/random_20.json new file mode 100644 index 000000000..75ecbcc3b --- /dev/null +++ b/bolt-contracts/test/testdata/transactions/random_20.json @@ -0,0 +1,75 @@ +{ + "name": "random-20", + "transaction": { + "to": "0x60fC630156D46bF9b1BF5B7d251C6CbE64702BF3", + "nonce": 812, + "gasLimit": "0xe010b5", + "gasPrice": "0x41f1af255d", + "maxFeePerGas": "0xc2214fa5", + "maxPriorityFeePerGas": "0x4a", + "data": "0x647daf6b789306d2daeb51be942c40d66194d5e0e9dca946aa830d686d493c1d1a07090d9cde8d73c6b982f5d00161b6ee0b302cdc24f29ebd146e99149adc7666f4d1d12146f077cc6e7f78addae71d399b445a14ff15a67efb8f39dcfd32af41968639dd3665d282e4065caef58441e6ac3ebc4b8d94", + "value": "0x6e7b", + "accessList": [ + { + "address": "0x36aAaa454aAB9A9409b890C32E9304C42EC8b05A", + "storageKeys": [] + }, + { + "address": "0x7F7Ac0f60Be35f1e8F7c70A7AA5d596bC7648745", + "storageKeys": [] + }, + { + "address": "0xf9Fb9Bb09b08C0Fa137E76615BBd50B5cc63BCc4", + "storageKeys": [] + }, + { + "address": "0x69C6c93f57A0C11a765F2B9076a9575c4b015d03", + "storageKeys": [] + } + ], + "chainId": "0x08454b", + "maxFeePerBlobGas": "0xdd2ab7977e4979", + "blobVersionedHashes": [ + "0x013b327d88c9e88e3e246eeca1cc70db85b0bb0f98984ad796047d4baaea4373", + "0x01b7bd6841fda1dfb6cebfe85366cedd754408fdf1431865ab3a5744a4e17507", + "0x01136b560d707867f29fdaa25db50e2b926bb89e032a28fbd78f06a995088288", + "0x01d5dc7ebc9a67d8b006f15c809dfb1e4dfc5298eb1ba049d24847314bb3ac62" + ] + }, + "privateKey": "0x6efb2ce4981ad5555974b8b6b4f767d774c32f9e09e0009d5d914e29614ec739", + "unsignedLegacy": "0xf89e82032c8541f1af255d83e010b59460fc630156d46bf9b1bf5b7d251c6cbe64702bf3826e7bb877647daf6b789306d2daeb51be942c40d66194d5e0e9dca946aa830d686d493c1d1a07090d9cde8d73c6b982f5d00161b6ee0b302cdc24f29ebd146e99149adc7666f4d1d12146f077cc6e7f78addae71d399b445a14ff15a67efb8f39dcfd32af41968639dd3665d282e4065caef58441e6ac3ebc4b8d94", + "unsignedEip155": "0xf8a482032c8541f1af255d83e010b59460fc630156d46bf9b1bf5b7d251c6cbe64702bf3826e7bb877647daf6b789306d2daeb51be942c40d66194d5e0e9dca946aa830d686d493c1d1a07090d9cde8d73c6b982f5d00161b6ee0b302cdc24f29ebd146e99149adc7666f4d1d12146f077cc6e7f78addae71d399b445a14ff15a67efb8f39dcfd32af41968639dd3665d282e4065caef58441e6ac3ebc4b8d948308454b8080", + "unsignedBerlin": "0x01f901008308454b82032c8541f1af255d83e010b59460fc630156d46bf9b1bf5b7d251c6cbe64702bf3826e7bb877647daf6b789306d2daeb51be942c40d66194d5e0e9dca946aa830d686d493c1d1a07090d9cde8d73c6b982f5d00161b6ee0b302cdc24f29ebd146e99149adc7666f4d1d12146f077cc6e7f78addae71d399b445a14ff15a67efb8f39dcfd32af41968639dd3665d282e4065caef58441e6ac3ebc4b8d94f85cd69436aaaa454aab9a9409b890c32e9304c42ec8b05ac0d6947f7ac0f60be35f1e8f7c70a7aa5d596bc7648745c0d694f9fb9bb09b08c0fa137e76615bbd50b5cc63bcc4c0d69469c6c93f57a0c11a765f2b9076a9575c4b015d03c0", + "unsignedLondon": "0x02f901008308454b82032c4a84c2214fa583e010b59460fc630156d46bf9b1bf5b7d251c6cbe64702bf3826e7bb877647daf6b789306d2daeb51be942c40d66194d5e0e9dca946aa830d686d493c1d1a07090d9cde8d73c6b982f5d00161b6ee0b302cdc24f29ebd146e99149adc7666f4d1d12146f077cc6e7f78addae71d399b445a14ff15a67efb8f39dcfd32af41968639dd3665d282e4065caef58441e6ac3ebc4b8d94f85cd69436aaaa454aab9a9409b890c32e9304c42ec8b05ac0d6947f7ac0f60be35f1e8f7c70a7aa5d596bc7648745c0d694f9fb9bb09b08c0fa137e76615bbd50b5cc63bcc4c0d69469c6c93f57a0c11a765f2b9076a9575c4b015d03c0", + "unsignedCancun": "0x03f9018e8308454b82032c4a84c2214fa583e010b59460fc630156d46bf9b1bf5b7d251c6cbe64702bf3826e7bb877647daf6b789306d2daeb51be942c40d66194d5e0e9dca946aa830d686d493c1d1a07090d9cde8d73c6b982f5d00161b6ee0b302cdc24f29ebd146e99149adc7666f4d1d12146f077cc6e7f78addae71d399b445a14ff15a67efb8f39dcfd32af41968639dd3665d282e4065caef58441e6ac3ebc4b8d94f85cd69436aaaa454aab9a9409b890c32e9304c42ec8b05ac0d6947f7ac0f60be35f1e8f7c70a7aa5d596bc7648745c0d694f9fb9bb09b08c0fa137e76615bbd50b5cc63bcc4c0d69469c6c93f57a0c11a765f2b9076a9575c4b015d03c087dd2ab7977e4979f884a0013b327d88c9e88e3e246eeca1cc70db85b0bb0f98984ad796047d4baaea4373a001b7bd6841fda1dfb6cebfe85366cedd754408fdf1431865ab3a5744a4e17507a001136b560d707867f29fdaa25db50e2b926bb89e032a28fbd78f06a995088288a001d5dc7ebc9a67d8b006f15c809dfb1e4dfc5298eb1ba049d24847314bb3ac62", + "signedLegacy": "0xf8e182032c8541f1af255d83e010b59460fc630156d46bf9b1bf5b7d251c6cbe64702bf3826e7bb877647daf6b789306d2daeb51be942c40d66194d5e0e9dca946aa830d686d493c1d1a07090d9cde8d73c6b982f5d00161b6ee0b302cdc24f29ebd146e99149adc7666f4d1d12146f077cc6e7f78addae71d399b445a14ff15a67efb8f39dcfd32af41968639dd3665d282e4065caef58441e6ac3ebc4b8d941ca0707fd3dbc3b260752493e64be389a2689bf3b8b181a4e7baa7eeb0176ba84283a04868672b9d487c34906166770fd51f0598b026b3368bb5f4d0a99ca4fc0706e9", + "signedEip155": "0xf8e482032c8541f1af255d83e010b59460fc630156d46bf9b1bf5b7d251c6cbe64702bf3826e7bb877647daf6b789306d2daeb51be942c40d66194d5e0e9dca946aa830d686d493c1d1a07090d9cde8d73c6b982f5d00161b6ee0b302cdc24f29ebd146e99149adc7666f4d1d12146f077cc6e7f78addae71d399b445a14ff15a67efb8f39dcfd32af41968639dd3665d282e4065caef58441e6ac3ebc4b8d9483108abaa014c49179d7be5caf2590610b76eeace610c536ff3e1d1eab3b43c164fe90e472a06bb4caad9a679371ce6527da4f3a404d3cd1b36712a47774112f19add1ca4d2e", + "signedBerlin": "0x01f901438308454b82032c8541f1af255d83e010b59460fc630156d46bf9b1bf5b7d251c6cbe64702bf3826e7bb877647daf6b789306d2daeb51be942c40d66194d5e0e9dca946aa830d686d493c1d1a07090d9cde8d73c6b982f5d00161b6ee0b302cdc24f29ebd146e99149adc7666f4d1d12146f077cc6e7f78addae71d399b445a14ff15a67efb8f39dcfd32af41968639dd3665d282e4065caef58441e6ac3ebc4b8d94f85cd69436aaaa454aab9a9409b890c32e9304c42ec8b05ac0d6947f7ac0f60be35f1e8f7c70a7aa5d596bc7648745c0d694f9fb9bb09b08c0fa137e76615bbd50b5cc63bcc4c0d69469c6c93f57a0c11a765f2b9076a9575c4b015d03c001a0b445c3c80cfa7df6401a7f0c46094a0fcb63749ff073b44d72c6d1f88f5212a6a07d418e8c1f5bb3a928ea11e3049943f7c60bacbaef1c78c90f9899331574336e", + "signedLondon": "0x02f901438308454b82032c4a84c2214fa583e010b59460fc630156d46bf9b1bf5b7d251c6cbe64702bf3826e7bb877647daf6b789306d2daeb51be942c40d66194d5e0e9dca946aa830d686d493c1d1a07090d9cde8d73c6b982f5d00161b6ee0b302cdc24f29ebd146e99149adc7666f4d1d12146f077cc6e7f78addae71d399b445a14ff15a67efb8f39dcfd32af41968639dd3665d282e4065caef58441e6ac3ebc4b8d94f85cd69436aaaa454aab9a9409b890c32e9304c42ec8b05ac0d6947f7ac0f60be35f1e8f7c70a7aa5d596bc7648745c0d694f9fb9bb09b08c0fa137e76615bbd50b5cc63bcc4c0d69469c6c93f57a0c11a765f2b9076a9575c4b015d03c080a0dafce44a782685fd338895386a5b13b4183706b3621c55b92dc0e43b3dd947d0a0697a9b17a80710efd85c62104a68156c67399c69b50bc951c4888a2f6446cad4", + "signedCancun": "0x03f901d18308454b82032c4a84c2214fa583e010b59460fc630156d46bf9b1bf5b7d251c6cbe64702bf3826e7bb877647daf6b789306d2daeb51be942c40d66194d5e0e9dca946aa830d686d493c1d1a07090d9cde8d73c6b982f5d00161b6ee0b302cdc24f29ebd146e99149adc7666f4d1d12146f077cc6e7f78addae71d399b445a14ff15a67efb8f39dcfd32af41968639dd3665d282e4065caef58441e6ac3ebc4b8d94f85cd69436aaaa454aab9a9409b890c32e9304c42ec8b05ac0d6947f7ac0f60be35f1e8f7c70a7aa5d596bc7648745c0d694f9fb9bb09b08c0fa137e76615bbd50b5cc63bcc4c0d69469c6c93f57a0c11a765f2b9076a9575c4b015d03c087dd2ab7977e4979f884a0013b327d88c9e88e3e246eeca1cc70db85b0bb0f98984ad796047d4baaea4373a001b7bd6841fda1dfb6cebfe85366cedd754408fdf1431865ab3a5744a4e17507a001136b560d707867f29fdaa25db50e2b926bb89e032a28fbd78f06a995088288a001d5dc7ebc9a67d8b006f15c809dfb1e4dfc5298eb1ba049d24847314bb3ac6201a0ffd7654221802fa30c1c1be235fb78ca7b16d538a395476d890f0ff4132e852da05977fdaa1bf697432d5c6533adb63e77251c2ca149b2b2a629c752838a36f749", + "signatureLegacy": { + "r": "0x707fd3dbc3b260752493e64be389a2689bf3b8b181a4e7baa7eeb0176ba84283", + "s": "0x4868672b9d487c34906166770fd51f0598b026b3368bb5f4d0a99ca4fc0706e9", + "v": "0x1c" + }, + "signatureEip155": { + "r": "0x14c49179d7be5caf2590610b76eeace610c536ff3e1d1eab3b43c164fe90e472", + "s": "0x6bb4caad9a679371ce6527da4f3a404d3cd1b36712a47774112f19add1ca4d2e", + "v": "0x108aba" + }, + "signatureBerlin": { + "r": "0xb445c3c80cfa7df6401a7f0c46094a0fcb63749ff073b44d72c6d1f88f5212a6", + "s": "0x7d418e8c1f5bb3a928ea11e3049943f7c60bacbaef1c78c90f9899331574336e", + "v": "0x1" + }, + "signatureLondon": { + "r": "0xdafce44a782685fd338895386a5b13b4183706b3621c55b92dc0e43b3dd947d0", + "s": "0x697a9b17a80710efd85c62104a68156c67399c69b50bc951c4888a2f6446cad4", + "v": "0x0" + }, + "signatureCancun": { + "r": "0xffd7654221802fa30c1c1be235fb78ca7b16d538a395476d890f0ff4132e852d", + "s": "0x5977fdaa1bf697432d5c6533adb63e77251c2ca149b2b2a629c752838a36f749", + "v": "0x1" + } +} diff --git a/bolt-contracts/test/testdata/transactions/random_3.json b/bolt-contracts/test/testdata/transactions/random_3.json new file mode 100644 index 000000000..4fdd11f6a --- /dev/null +++ b/bolt-contracts/test/testdata/transactions/random_3.json @@ -0,0 +1,58 @@ +{ + "name": "random-3", + "transaction": { + "to": "0x8Bee13B1D7E29953a7484A3aE8e344D86b4Ad2C5", + "nonce": 530, + "gasLimit": "0xccb4", + "gasPrice": "0x18956c24", + "maxFeePerGas": "0x57f3c57a41", + "maxPriorityFeePerGas": "0x5a492a", + "data": "0x4c4aaeceaa35f9cd275639e2319057a18f3c4d8bf242707b08a1a2a32531d3975a4dc67b8a92f5a11adbf12b6f8d8809d1544aed6f9da9b1f5e753307165e3e85afdbd", + "value": "0x3b04", + "accessList": [], + "chainId": "0xb3", + "maxFeePerBlobGas": "0x7b0f34f9", + "blobVersionedHashes": [ + "0x01b64c9600d28c83309131198d984f79284d6e2a5dd8ffeebc05f8bdcc21b03b", + "0x013305753b09589c2a8015bb6f975e2654a2164a41dc51812a439560bdd6d16d", + "0x013e6ffbf81a5911cc365712aa5511e93c28b14e4ead53ac786db6506caa1b4a", + "0x0146c688afbb49d2c0798642bbdbe25bfe7cdaa37632c32023aacce933f48dae" + ] + }, + "privateKey": "0xa9e5fdc17c2302fce888f2dc9d6ec2b3d3fc06aa212ec06b07f4035f64fcc58f", + "unsignedLegacy": "0xf8688202128418956c2482ccb4948bee13b1d7e29953a7484a3ae8e344d86b4ad2c5823b04b8434c4aaeceaa35f9cd275639e2319057a18f3c4d8bf242707b08a1a2a32531d3975a4dc67b8a92f5a11adbf12b6f8d8809d1544aed6f9da9b1f5e753307165e3e85afdbd", + "unsignedEip155": "0xf86c8202128418956c2482ccb4948bee13b1d7e29953a7484a3ae8e344d86b4ad2c5823b04b8434c4aaeceaa35f9cd275639e2319057a18f3c4d8bf242707b08a1a2a32531d3975a4dc67b8a92f5a11adbf12b6f8d8809d1544aed6f9da9b1f5e753307165e3e85afdbd81b38080", + "unsignedBerlin": "0x01f86b81b38202128418956c2482ccb4948bee13b1d7e29953a7484a3ae8e344d86b4ad2c5823b04b8434c4aaeceaa35f9cd275639e2319057a18f3c4d8bf242707b08a1a2a32531d3975a4dc67b8a92f5a11adbf12b6f8d8809d1544aed6f9da9b1f5e753307165e3e85afdbdc0", + "unsignedLondon": "0x02f87081b3820212835a492a8557f3c57a4182ccb4948bee13b1d7e29953a7484a3ae8e344d86b4ad2c5823b04b8434c4aaeceaa35f9cd275639e2319057a18f3c4d8bf242707b08a1a2a32531d3975a4dc67b8a92f5a11adbf12b6f8d8809d1544aed6f9da9b1f5e753307165e3e85afdbdc0", + "unsignedCancun": "0x03f8fb81b3820212835a492a8557f3c57a4182ccb4948bee13b1d7e29953a7484a3ae8e344d86b4ad2c5823b04b8434c4aaeceaa35f9cd275639e2319057a18f3c4d8bf242707b08a1a2a32531d3975a4dc67b8a92f5a11adbf12b6f8d8809d1544aed6f9da9b1f5e753307165e3e85afdbdc0847b0f34f9f884a001b64c9600d28c83309131198d984f79284d6e2a5dd8ffeebc05f8bdcc21b03ba0013305753b09589c2a8015bb6f975e2654a2164a41dc51812a439560bdd6d16da0013e6ffbf81a5911cc365712aa5511e93c28b14e4ead53ac786db6506caa1b4aa00146c688afbb49d2c0798642bbdbe25bfe7cdaa37632c32023aacce933f48dae", + "signedLegacy": "0xf8ab8202128418956c2482ccb4948bee13b1d7e29953a7484a3ae8e344d86b4ad2c5823b04b8434c4aaeceaa35f9cd275639e2319057a18f3c4d8bf242707b08a1a2a32531d3975a4dc67b8a92f5a11adbf12b6f8d8809d1544aed6f9da9b1f5e753307165e3e85afdbd1ca07be350194f44b6a86cd7199f2c6a2dea34815d20abbd0f389cf3c55ef5a13566a0239482099fd9289999f06db6fb95194ac1a92fcd13c1639d2e99e3b66ab2f0b1", + "signedEip155": "0xf8ad8202128418956c2482ccb4948bee13b1d7e29953a7484a3ae8e344d86b4ad2c5823b04b8434c4aaeceaa35f9cd275639e2319057a18f3c4d8bf242707b08a1a2a32531d3975a4dc67b8a92f5a11adbf12b6f8d8809d1544aed6f9da9b1f5e753307165e3e85afdbd820189a0541bc2b78ae53dd11ac2c9845fe30878c493d34311513dfda43a92dbee1b4121a03f4ff8d77c247fc2238c02e2d5ca68dcd77e8f7a20d0d2f99145925af07beec0", + "signedBerlin": "0x01f8ae81b38202128418956c2482ccb4948bee13b1d7e29953a7484a3ae8e344d86b4ad2c5823b04b8434c4aaeceaa35f9cd275639e2319057a18f3c4d8bf242707b08a1a2a32531d3975a4dc67b8a92f5a11adbf12b6f8d8809d1544aed6f9da9b1f5e753307165e3e85afdbdc001a035a7324dd7552976ee86c94623e89be43fddfa1ceed1354822a33726db13d9fba047347aee2460910314bbf20fbc091befb91a0ac236d30905312f828af49b2420", + "signedLondon": "0x02f8b381b3820212835a492a8557f3c57a4182ccb4948bee13b1d7e29953a7484a3ae8e344d86b4ad2c5823b04b8434c4aaeceaa35f9cd275639e2319057a18f3c4d8bf242707b08a1a2a32531d3975a4dc67b8a92f5a11adbf12b6f8d8809d1544aed6f9da9b1f5e753307165e3e85afdbdc080a0c458cee1b97cfb1f6fa52b2adaa135d8501d63ba908c113f48796d40884496b1a03abaf292f68e0fc6a5c1012455d49d2d566cb4cf2eeebc8d52f1f5e3e20c808b", + "signedCancun": "0x03f9013e81b3820212835a492a8557f3c57a4182ccb4948bee13b1d7e29953a7484a3ae8e344d86b4ad2c5823b04b8434c4aaeceaa35f9cd275639e2319057a18f3c4d8bf242707b08a1a2a32531d3975a4dc67b8a92f5a11adbf12b6f8d8809d1544aed6f9da9b1f5e753307165e3e85afdbdc0847b0f34f9f884a001b64c9600d28c83309131198d984f79284d6e2a5dd8ffeebc05f8bdcc21b03ba0013305753b09589c2a8015bb6f975e2654a2164a41dc51812a439560bdd6d16da0013e6ffbf81a5911cc365712aa5511e93c28b14e4ead53ac786db6506caa1b4aa00146c688afbb49d2c0798642bbdbe25bfe7cdaa37632c32023aacce933f48dae01a0fb445840df9e94369942979f7c47b640f948a2fe812b497350ce00a04d0205ffa02c555565381a70547f13b807647a202705f67535104fa92524c5bd5507ba010f", + "signatureLegacy": { + "r": "0x7be350194f44b6a86cd7199f2c6a2dea34815d20abbd0f389cf3c55ef5a13566", + "s": "0x239482099fd9289999f06db6fb95194ac1a92fcd13c1639d2e99e3b66ab2f0b1", + "v": "0x1c" + }, + "signatureEip155": { + "r": "0x541bc2b78ae53dd11ac2c9845fe30878c493d34311513dfda43a92dbee1b4121", + "s": "0x3f4ff8d77c247fc2238c02e2d5ca68dcd77e8f7a20d0d2f99145925af07beec0", + "v": "0x189" + }, + "signatureBerlin": { + "r": "0x35a7324dd7552976ee86c94623e89be43fddfa1ceed1354822a33726db13d9fb", + "s": "0x47347aee2460910314bbf20fbc091befb91a0ac236d30905312f828af49b2420", + "v": "0x1" + }, + "signatureLondon": { + "r": "0xc458cee1b97cfb1f6fa52b2adaa135d8501d63ba908c113f48796d40884496b1", + "s": "0x3abaf292f68e0fc6a5c1012455d49d2d566cb4cf2eeebc8d52f1f5e3e20c808b", + "v": "0x0" + }, + "signatureCancun": { + "r": "0xfb445840df9e94369942979f7c47b640f948a2fe812b497350ce00a04d0205ff", + "s": "0x2c555565381a70547f13b807647a202705f67535104fa92524c5bd5507ba010f", + "v": "0x1" + } +} diff --git a/bolt-contracts/test/testdata/transactions/random_4.json b/bolt-contracts/test/testdata/transactions/random_4.json new file mode 100644 index 000000000..d5eb37ae5 --- /dev/null +++ b/bolt-contracts/test/testdata/transactions/random_4.json @@ -0,0 +1,58 @@ +{ + "name": "random-4", + "transaction": { + "to": "0xf5c58b3aC42d718024e914d90a0C17CeB4487F08", + "nonce": 150, + "gasLimit": "0x9b", + "gasPrice": "0x75fa", + "maxFeePerGas": "0x92719aae8f72c2", + "maxPriorityFeePerGas": "0xa16d", + "data": "0xfdcd0c62ddc8b8c99d6768d3fe3097a66b388998e8f6294b69c95751da898430b9c9e7b02602393a9dc2026918c7741f4097e1de373a5b8d884d02e66b06742b29cc539a22675f5713a1ba9d247b9c9eaabf54f9cb48cc8f7429c80c31b3cbfa8a", + "value": "0x9c5b0d", + "accessList": [], + "chainId": "0x100800", + "maxFeePerBlobGas": "0xeac37693", + "blobVersionedHashes": [ + "0x017a6fb74091418b95e7679cdb19efd23cb1a7de4f8d818ae3206e33b8e524eb", + "0x01e4398d25da2fec20b713fe51b4cc7f555a473c4928e7e5a0f7be47761d4d9c", + "0x01fa7aff790b19c63de804524b5e37b459fd5b8614008d0ef4fd513dcb9361fc", + "0x01af1c40ffd00db8e91a537456d387c8e788276b13212ce917514456bb7b69bb" + ] + }, + "privateKey": "0xaf7e4892d96c6cd39bb8048b699386b24539fb8fb078f67e190fd3703f972f55", + "unsignedLegacy": "0xf88381968275fa819b94f5c58b3ac42d718024e914d90a0c17ceb4487f08839c5b0db861fdcd0c62ddc8b8c99d6768d3fe3097a66b388998e8f6294b69c95751da898430b9c9e7b02602393a9dc2026918c7741f4097e1de373a5b8d884d02e66b06742b29cc539a22675f5713a1ba9d247b9c9eaabf54f9cb48cc8f7429c80c31b3cbfa8a", + "unsignedEip155": "0xf88981968275fa819b94f5c58b3ac42d718024e914d90a0c17ceb4487f08839c5b0db861fdcd0c62ddc8b8c99d6768d3fe3097a66b388998e8f6294b69c95751da898430b9c9e7b02602393a9dc2026918c7741f4097e1de373a5b8d884d02e66b06742b29cc539a22675f5713a1ba9d247b9c9eaabf54f9cb48cc8f7429c80c31b3cbfa8a831008008080", + "unsignedBerlin": "0x01f8888310080081968275fa819b94f5c58b3ac42d718024e914d90a0c17ceb4487f08839c5b0db861fdcd0c62ddc8b8c99d6768d3fe3097a66b388998e8f6294b69c95751da898430b9c9e7b02602393a9dc2026918c7741f4097e1de373a5b8d884d02e66b06742b29cc539a22675f5713a1ba9d247b9c9eaabf54f9cb48cc8f7429c80c31b3cbfa8ac0", + "unsignedLondon": "0x02f89083100800819682a16d8792719aae8f72c2819b94f5c58b3ac42d718024e914d90a0c17ceb4487f08839c5b0db861fdcd0c62ddc8b8c99d6768d3fe3097a66b388998e8f6294b69c95751da898430b9c9e7b02602393a9dc2026918c7741f4097e1de373a5b8d884d02e66b06742b29cc539a22675f5713a1ba9d247b9c9eaabf54f9cb48cc8f7429c80c31b3cbfa8ac0", + "unsignedCancun": "0x03f9011b83100800819682a16d8792719aae8f72c2819b94f5c58b3ac42d718024e914d90a0c17ceb4487f08839c5b0db861fdcd0c62ddc8b8c99d6768d3fe3097a66b388998e8f6294b69c95751da898430b9c9e7b02602393a9dc2026918c7741f4097e1de373a5b8d884d02e66b06742b29cc539a22675f5713a1ba9d247b9c9eaabf54f9cb48cc8f7429c80c31b3cbfa8ac084eac37693f884a0017a6fb74091418b95e7679cdb19efd23cb1a7de4f8d818ae3206e33b8e524eba001e4398d25da2fec20b713fe51b4cc7f555a473c4928e7e5a0f7be47761d4d9ca001fa7aff790b19c63de804524b5e37b459fd5b8614008d0ef4fd513dcb9361fca001af1c40ffd00db8e91a537456d387c8e788276b13212ce917514456bb7b69bb", + "signedLegacy": "0xf8c681968275fa819b94f5c58b3ac42d718024e914d90a0c17ceb4487f08839c5b0db861fdcd0c62ddc8b8c99d6768d3fe3097a66b388998e8f6294b69c95751da898430b9c9e7b02602393a9dc2026918c7741f4097e1de373a5b8d884d02e66b06742b29cc539a22675f5713a1ba9d247b9c9eaabf54f9cb48cc8f7429c80c31b3cbfa8a1ba079230da6c89f320a59fe2f291f51e758e1e642851e2c6b6b8c4b3a32b1a75c4ba04a160e0e100e4244596580586abe09d7d3423e967ca417d3d3980a6609e94030", + "signedEip155": "0xf8c981968275fa819b94f5c58b3ac42d718024e914d90a0c17ceb4487f08839c5b0db861fdcd0c62ddc8b8c99d6768d3fe3097a66b388998e8f6294b69c95751da898430b9c9e7b02602393a9dc2026918c7741f4097e1de373a5b8d884d02e66b06742b29cc539a22675f5713a1ba9d247b9c9eaabf54f9cb48cc8f7429c80c31b3cbfa8a83201023a08cbd6e5a98913d543e56fc8b8865adb020e081501c3549d54bfa5f1250666e84a04c364c7a1fdbc76a890b4468e2f7957fb340c78ea98793c304141a449adfa9b0", + "signedBerlin": "0x01f8cb8310080081968275fa819b94f5c58b3ac42d718024e914d90a0c17ceb4487f08839c5b0db861fdcd0c62ddc8b8c99d6768d3fe3097a66b388998e8f6294b69c95751da898430b9c9e7b02602393a9dc2026918c7741f4097e1de373a5b8d884d02e66b06742b29cc539a22675f5713a1ba9d247b9c9eaabf54f9cb48cc8f7429c80c31b3cbfa8ac080a05c5fd1ac646c437d17e22ea102147265371d1358814c57346d1448ed6703d042a01e525dbe3024c1ac10b1229c7d69b4af119c60ae3b45eab73ee9c61926dbd91a", + "signedLondon": "0x02f8d383100800819682a16d8792719aae8f72c2819b94f5c58b3ac42d718024e914d90a0c17ceb4487f08839c5b0db861fdcd0c62ddc8b8c99d6768d3fe3097a66b388998e8f6294b69c95751da898430b9c9e7b02602393a9dc2026918c7741f4097e1de373a5b8d884d02e66b06742b29cc539a22675f5713a1ba9d247b9c9eaabf54f9cb48cc8f7429c80c31b3cbfa8ac080a0c5817c5fca256b8084f994686f21a22e815f49049c1d7126dba60578a8dcecfba07756dd3accb21c6994c079795e3edc068e1c434c33109580215073701958f29d", + "signedCancun": "0x03f9015e83100800819682a16d8792719aae8f72c2819b94f5c58b3ac42d718024e914d90a0c17ceb4487f08839c5b0db861fdcd0c62ddc8b8c99d6768d3fe3097a66b388998e8f6294b69c95751da898430b9c9e7b02602393a9dc2026918c7741f4097e1de373a5b8d884d02e66b06742b29cc539a22675f5713a1ba9d247b9c9eaabf54f9cb48cc8f7429c80c31b3cbfa8ac084eac37693f884a0017a6fb74091418b95e7679cdb19efd23cb1a7de4f8d818ae3206e33b8e524eba001e4398d25da2fec20b713fe51b4cc7f555a473c4928e7e5a0f7be47761d4d9ca001fa7aff790b19c63de804524b5e37b459fd5b8614008d0ef4fd513dcb9361fca001af1c40ffd00db8e91a537456d387c8e788276b13212ce917514456bb7b69bb01a0b96f5303bcbb749f5fbf6c1d918eb08bfefe05242bfd2c4b9a9a55efb5b686ffa050b83d2a4dfcaa66b0339df55910ea553bbce6f22d4ab5e8c8562ddb15a03999", + "signatureLegacy": { + "r": "0x79230da6c89f320a59fe2f291f51e758e1e642851e2c6b6b8c4b3a32b1a75c4b", + "s": "0x4a160e0e100e4244596580586abe09d7d3423e967ca417d3d3980a6609e94030", + "v": "0x1b" + }, + "signatureEip155": { + "r": "0x8cbd6e5a98913d543e56fc8b8865adb020e081501c3549d54bfa5f1250666e84", + "s": "0x4c364c7a1fdbc76a890b4468e2f7957fb340c78ea98793c304141a449adfa9b0", + "v": "0x201023" + }, + "signatureBerlin": { + "r": "0x5c5fd1ac646c437d17e22ea102147265371d1358814c57346d1448ed6703d042", + "s": "0x1e525dbe3024c1ac10b1229c7d69b4af119c60ae3b45eab73ee9c61926dbd91a", + "v": "0x0" + }, + "signatureLondon": { + "r": "0xc5817c5fca256b8084f994686f21a22e815f49049c1d7126dba60578a8dcecfb", + "s": "0x7756dd3accb21c6994c079795e3edc068e1c434c33109580215073701958f29d", + "v": "0x0" + }, + "signatureCancun": { + "r": "0xb96f5303bcbb749f5fbf6c1d918eb08bfefe05242bfd2c4b9a9a55efb5b686ff", + "s": "0x50b83d2a4dfcaa66b0339df55910ea553bbce6f22d4ab5e8c8562ddb15a03999", + "v": "0x1" + } +} diff --git a/bolt-contracts/test/testdata/transactions/random_5.json b/bolt-contracts/test/testdata/transactions/random_5.json new file mode 100644 index 000000000..de4413b82 --- /dev/null +++ b/bolt-contracts/test/testdata/transactions/random_5.json @@ -0,0 +1,92 @@ +{ + "name": "random-5", + "transaction": { + "to": "0x4d1060d970674619005137921969b4bfe3EeA6B8", + "nonce": 577, + "gasLimit": "0xbe431918", + "gasPrice": "0xb3b1aaeb58", + "maxFeePerGas": "0x6bb02a65c7", + "maxPriorityFeePerGas": "0xa5", + "data": "0x7f2239c398167e747939f64b2ed9458db8aa10eb367bfab1976a0bc6693cf152dd8d13aa16e4d655a38d6ac64eae0932e13d649f9516fca834cd5a49c7b6e5ba1286a30eea1ac2e89c78441c5418250f8e307cc72c95ef36a8e4cee60b56ece34984ed3302672e6183d28ef30f3e43d60f", + "value": "0x95927b2d76", + "accessList": [ + { + "address": "0x8A632C23BF807681570c3fb6632Ce99FD98BdB23", + "storageKeys": [ + "0x1c3124f271ea52d9e881bdd52c63020fb7c08a1b96263030415e4bc8146db25c", + "0x2b6d4aa754fa44f0e86e6fa0a936048674ffc4fef24c5a2b317c740630901919", + "0xc266c51508b93a8f933e2e64505e458ac26cdb93e8e0bc7bd1609552b6210aa5", + "0xf49934500a155bedea4f0bf25bfc62161fcb74fbf17ca480333f4747d5ad824e" + ] + }, + { + "address": "0x2D78B31bA09E8a2888d655e3d000FE95c63789c4", + "storageKeys": [ + "0x1c3124f271ea52d9e881bdd52c63020fb7c08a1b96263030415e4bc8146db25c", + "0x2b6d4aa754fa44f0e86e6fa0a936048674ffc4fef24c5a2b317c740630901919", + "0xc266c51508b93a8f933e2e64505e458ac26cdb93e8e0bc7bd1609552b6210aa5", + "0xf49934500a155bedea4f0bf25bfc62161fcb74fbf17ca480333f4747d5ad824e" + ] + }, + { + "address": "0x3199b3433EE7f3eDcAE901cbce64C4E81125F7da", + "storageKeys": [ + "0x1c3124f271ea52d9e881bdd52c63020fb7c08a1b96263030415e4bc8146db25c", + "0x2b6d4aa754fa44f0e86e6fa0a936048674ffc4fef24c5a2b317c740630901919", + "0xc266c51508b93a8f933e2e64505e458ac26cdb93e8e0bc7bd1609552b6210aa5", + "0xf49934500a155bedea4f0bf25bfc62161fcb74fbf17ca480333f4747d5ad824e" + ] + }, + { + "address": "0xb8d669949683a728f76919fe2CC9896216E00A81", + "storageKeys": [ + "0x1c3124f271ea52d9e881bdd52c63020fb7c08a1b96263030415e4bc8146db25c", + "0x2b6d4aa754fa44f0e86e6fa0a936048674ffc4fef24c5a2b317c740630901919", + "0xc266c51508b93a8f933e2e64505e458ac26cdb93e8e0bc7bd1609552b6210aa5", + "0xf49934500a155bedea4f0bf25bfc62161fcb74fbf17ca480333f4747d5ad824e" + ] + } + ], + "chainId": "0x60a7", + "maxFeePerBlobGas": "0x13c4c123e2373d", + "blobVersionedHashes": [ + "0x014a782765b51a031ac55ad44ba18bc6f003634892312bc06ee169486aabeab0" + ] + }, + "privateKey": "0x77065b8ddb2f89d3d2d83f46d0147efc081e3a3f1012406c698a9ce364b324e9", + "unsignedLegacy": "0xf89c82024185b3b1aaeb5884be431918944d1060d970674619005137921969b4bfe3eea6b88595927b2d76b8717f2239c398167e747939f64b2ed9458db8aa10eb367bfab1976a0bc6693cf152dd8d13aa16e4d655a38d6ac64eae0932e13d649f9516fca834cd5a49c7b6e5ba1286a30eea1ac2e89c78441c5418250f8e307cc72c95ef36a8e4cee60b56ece34984ed3302672e6183d28ef30f3e43d60f", + "unsignedEip155": "0xf8a182024185b3b1aaeb5884be431918944d1060d970674619005137921969b4bfe3eea6b88595927b2d76b8717f2239c398167e747939f64b2ed9458db8aa10eb367bfab1976a0bc6693cf152dd8d13aa16e4d655a38d6ac64eae0932e13d649f9516fca834cd5a49c7b6e5ba1286a30eea1ac2e89c78441c5418250f8e307cc72c95ef36a8e4cee60b56ece34984ed3302672e6183d28ef30f3e43d60f8260a78080", + "unsignedBerlin": "0x01f903168260a782024185b3b1aaeb5884be431918944d1060d970674619005137921969b4bfe3eea6b88595927b2d76b8717f2239c398167e747939f64b2ed9458db8aa10eb367bfab1976a0bc6693cf152dd8d13aa16e4d655a38d6ac64eae0932e13d649f9516fca834cd5a49c7b6e5ba1286a30eea1ac2e89c78441c5418250f8e307cc72c95ef36a8e4cee60b56ece34984ed3302672e6183d28ef30f3e43d60ff90274f89b948a632c23bf807681570c3fb6632ce99fd98bdb23f884a01c3124f271ea52d9e881bdd52c63020fb7c08a1b96263030415e4bc8146db25ca02b6d4aa754fa44f0e86e6fa0a936048674ffc4fef24c5a2b317c740630901919a0c266c51508b93a8f933e2e64505e458ac26cdb93e8e0bc7bd1609552b6210aa5a0f49934500a155bedea4f0bf25bfc62161fcb74fbf17ca480333f4747d5ad824ef89b942d78b31ba09e8a2888d655e3d000fe95c63789c4f884a01c3124f271ea52d9e881bdd52c63020fb7c08a1b96263030415e4bc8146db25ca02b6d4aa754fa44f0e86e6fa0a936048674ffc4fef24c5a2b317c740630901919a0c266c51508b93a8f933e2e64505e458ac26cdb93e8e0bc7bd1609552b6210aa5a0f49934500a155bedea4f0bf25bfc62161fcb74fbf17ca480333f4747d5ad824ef89b943199b3433ee7f3edcae901cbce64c4e81125f7daf884a01c3124f271ea52d9e881bdd52c63020fb7c08a1b96263030415e4bc8146db25ca02b6d4aa754fa44f0e86e6fa0a936048674ffc4fef24c5a2b317c740630901919a0c266c51508b93a8f933e2e64505e458ac26cdb93e8e0bc7bd1609552b6210aa5a0f49934500a155bedea4f0bf25bfc62161fcb74fbf17ca480333f4747d5ad824ef89b94b8d669949683a728f76919fe2cc9896216e00a81f884a01c3124f271ea52d9e881bdd52c63020fb7c08a1b96263030415e4bc8146db25ca02b6d4aa754fa44f0e86e6fa0a936048674ffc4fef24c5a2b317c740630901919a0c266c51508b93a8f933e2e64505e458ac26cdb93e8e0bc7bd1609552b6210aa5a0f49934500a155bedea4f0bf25bfc62161fcb74fbf17ca480333f4747d5ad824e", + "unsignedLondon": "0x02f903188260a782024181a5856bb02a65c784be431918944d1060d970674619005137921969b4bfe3eea6b88595927b2d76b8717f2239c398167e747939f64b2ed9458db8aa10eb367bfab1976a0bc6693cf152dd8d13aa16e4d655a38d6ac64eae0932e13d649f9516fca834cd5a49c7b6e5ba1286a30eea1ac2e89c78441c5418250f8e307cc72c95ef36a8e4cee60b56ece34984ed3302672e6183d28ef30f3e43d60ff90274f89b948a632c23bf807681570c3fb6632ce99fd98bdb23f884a01c3124f271ea52d9e881bdd52c63020fb7c08a1b96263030415e4bc8146db25ca02b6d4aa754fa44f0e86e6fa0a936048674ffc4fef24c5a2b317c740630901919a0c266c51508b93a8f933e2e64505e458ac26cdb93e8e0bc7bd1609552b6210aa5a0f49934500a155bedea4f0bf25bfc62161fcb74fbf17ca480333f4747d5ad824ef89b942d78b31ba09e8a2888d655e3d000fe95c63789c4f884a01c3124f271ea52d9e881bdd52c63020fb7c08a1b96263030415e4bc8146db25ca02b6d4aa754fa44f0e86e6fa0a936048674ffc4fef24c5a2b317c740630901919a0c266c51508b93a8f933e2e64505e458ac26cdb93e8e0bc7bd1609552b6210aa5a0f49934500a155bedea4f0bf25bfc62161fcb74fbf17ca480333f4747d5ad824ef89b943199b3433ee7f3edcae901cbce64c4e81125f7daf884a01c3124f271ea52d9e881bdd52c63020fb7c08a1b96263030415e4bc8146db25ca02b6d4aa754fa44f0e86e6fa0a936048674ffc4fef24c5a2b317c740630901919a0c266c51508b93a8f933e2e64505e458ac26cdb93e8e0bc7bd1609552b6210aa5a0f49934500a155bedea4f0bf25bfc62161fcb74fbf17ca480333f4747d5ad824ef89b94b8d669949683a728f76919fe2cc9896216e00a81f884a01c3124f271ea52d9e881bdd52c63020fb7c08a1b96263030415e4bc8146db25ca02b6d4aa754fa44f0e86e6fa0a936048674ffc4fef24c5a2b317c740630901919a0c266c51508b93a8f933e2e64505e458ac26cdb93e8e0bc7bd1609552b6210aa5a0f49934500a155bedea4f0bf25bfc62161fcb74fbf17ca480333f4747d5ad824e", + "unsignedCancun": "0x03f903428260a782024181a5856bb02a65c784be431918944d1060d970674619005137921969b4bfe3eea6b88595927b2d76b8717f2239c398167e747939f64b2ed9458db8aa10eb367bfab1976a0bc6693cf152dd8d13aa16e4d655a38d6ac64eae0932e13d649f9516fca834cd5a49c7b6e5ba1286a30eea1ac2e89c78441c5418250f8e307cc72c95ef36a8e4cee60b56ece34984ed3302672e6183d28ef30f3e43d60ff90274f89b948a632c23bf807681570c3fb6632ce99fd98bdb23f884a01c3124f271ea52d9e881bdd52c63020fb7c08a1b96263030415e4bc8146db25ca02b6d4aa754fa44f0e86e6fa0a936048674ffc4fef24c5a2b317c740630901919a0c266c51508b93a8f933e2e64505e458ac26cdb93e8e0bc7bd1609552b6210aa5a0f49934500a155bedea4f0bf25bfc62161fcb74fbf17ca480333f4747d5ad824ef89b942d78b31ba09e8a2888d655e3d000fe95c63789c4f884a01c3124f271ea52d9e881bdd52c63020fb7c08a1b96263030415e4bc8146db25ca02b6d4aa754fa44f0e86e6fa0a936048674ffc4fef24c5a2b317c740630901919a0c266c51508b93a8f933e2e64505e458ac26cdb93e8e0bc7bd1609552b6210aa5a0f49934500a155bedea4f0bf25bfc62161fcb74fbf17ca480333f4747d5ad824ef89b943199b3433ee7f3edcae901cbce64c4e81125f7daf884a01c3124f271ea52d9e881bdd52c63020fb7c08a1b96263030415e4bc8146db25ca02b6d4aa754fa44f0e86e6fa0a936048674ffc4fef24c5a2b317c740630901919a0c266c51508b93a8f933e2e64505e458ac26cdb93e8e0bc7bd1609552b6210aa5a0f49934500a155bedea4f0bf25bfc62161fcb74fbf17ca480333f4747d5ad824ef89b94b8d669949683a728f76919fe2cc9896216e00a81f884a01c3124f271ea52d9e881bdd52c63020fb7c08a1b96263030415e4bc8146db25ca02b6d4aa754fa44f0e86e6fa0a936048674ffc4fef24c5a2b317c740630901919a0c266c51508b93a8f933e2e64505e458ac26cdb93e8e0bc7bd1609552b6210aa5a0f49934500a155bedea4f0bf25bfc62161fcb74fbf17ca480333f4747d5ad824e8713c4c123e2373de1a0014a782765b51a031ac55ad44ba18bc6f003634892312bc06ee169486aabeab0", + "signedLegacy": "0xf8df82024185b3b1aaeb5884be431918944d1060d970674619005137921969b4bfe3eea6b88595927b2d76b8717f2239c398167e747939f64b2ed9458db8aa10eb367bfab1976a0bc6693cf152dd8d13aa16e4d655a38d6ac64eae0932e13d649f9516fca834cd5a49c7b6e5ba1286a30eea1ac2e89c78441c5418250f8e307cc72c95ef36a8e4cee60b56ece34984ed3302672e6183d28ef30f3e43d60f1ba098cadec7a4cd4297f92828b79459baed65817cd8fcd40cb2025b750c8bb3d046a00c2e6848654d045d1502a9c1777bc917a63718e8a494dae94720f519ff07b3db", + "signedEip155": "0xf8e182024185b3b1aaeb5884be431918944d1060d970674619005137921969b4bfe3eea6b88595927b2d76b8717f2239c398167e747939f64b2ed9458db8aa10eb367bfab1976a0bc6693cf152dd8d13aa16e4d655a38d6ac64eae0932e13d649f9516fca834cd5a49c7b6e5ba1286a30eea1ac2e89c78441c5418250f8e307cc72c95ef36a8e4cee60b56ece34984ed3302672e6183d28ef30f3e43d60f82c172a009f309ce844417f1035eff7a0b3bc191253843fb29068769f09fed81a464e622a0722660636b83fcac33759285b9704a5976b7cf9aeeceacab451702ff454469dc", + "signedBerlin": "0x01f903598260a782024185b3b1aaeb5884be431918944d1060d970674619005137921969b4bfe3eea6b88595927b2d76b8717f2239c398167e747939f64b2ed9458db8aa10eb367bfab1976a0bc6693cf152dd8d13aa16e4d655a38d6ac64eae0932e13d649f9516fca834cd5a49c7b6e5ba1286a30eea1ac2e89c78441c5418250f8e307cc72c95ef36a8e4cee60b56ece34984ed3302672e6183d28ef30f3e43d60ff90274f89b948a632c23bf807681570c3fb6632ce99fd98bdb23f884a01c3124f271ea52d9e881bdd52c63020fb7c08a1b96263030415e4bc8146db25ca02b6d4aa754fa44f0e86e6fa0a936048674ffc4fef24c5a2b317c740630901919a0c266c51508b93a8f933e2e64505e458ac26cdb93e8e0bc7bd1609552b6210aa5a0f49934500a155bedea4f0bf25bfc62161fcb74fbf17ca480333f4747d5ad824ef89b942d78b31ba09e8a2888d655e3d000fe95c63789c4f884a01c3124f271ea52d9e881bdd52c63020fb7c08a1b96263030415e4bc8146db25ca02b6d4aa754fa44f0e86e6fa0a936048674ffc4fef24c5a2b317c740630901919a0c266c51508b93a8f933e2e64505e458ac26cdb93e8e0bc7bd1609552b6210aa5a0f49934500a155bedea4f0bf25bfc62161fcb74fbf17ca480333f4747d5ad824ef89b943199b3433ee7f3edcae901cbce64c4e81125f7daf884a01c3124f271ea52d9e881bdd52c63020fb7c08a1b96263030415e4bc8146db25ca02b6d4aa754fa44f0e86e6fa0a936048674ffc4fef24c5a2b317c740630901919a0c266c51508b93a8f933e2e64505e458ac26cdb93e8e0bc7bd1609552b6210aa5a0f49934500a155bedea4f0bf25bfc62161fcb74fbf17ca480333f4747d5ad824ef89b94b8d669949683a728f76919fe2cc9896216e00a81f884a01c3124f271ea52d9e881bdd52c63020fb7c08a1b96263030415e4bc8146db25ca02b6d4aa754fa44f0e86e6fa0a936048674ffc4fef24c5a2b317c740630901919a0c266c51508b93a8f933e2e64505e458ac26cdb93e8e0bc7bd1609552b6210aa5a0f49934500a155bedea4f0bf25bfc62161fcb74fbf17ca480333f4747d5ad824e01a07c8b37dd82c37b9783d328b99528bdbf4abdc7956d5e3da3a65f4a165e4acb74a04db2486fbb725825041d85e055d0adf75e02790041d668d13662717695436410", + "signedLondon": "0x02f9035b8260a782024181a5856bb02a65c784be431918944d1060d970674619005137921969b4bfe3eea6b88595927b2d76b8717f2239c398167e747939f64b2ed9458db8aa10eb367bfab1976a0bc6693cf152dd8d13aa16e4d655a38d6ac64eae0932e13d649f9516fca834cd5a49c7b6e5ba1286a30eea1ac2e89c78441c5418250f8e307cc72c95ef36a8e4cee60b56ece34984ed3302672e6183d28ef30f3e43d60ff90274f89b948a632c23bf807681570c3fb6632ce99fd98bdb23f884a01c3124f271ea52d9e881bdd52c63020fb7c08a1b96263030415e4bc8146db25ca02b6d4aa754fa44f0e86e6fa0a936048674ffc4fef24c5a2b317c740630901919a0c266c51508b93a8f933e2e64505e458ac26cdb93e8e0bc7bd1609552b6210aa5a0f49934500a155bedea4f0bf25bfc62161fcb74fbf17ca480333f4747d5ad824ef89b942d78b31ba09e8a2888d655e3d000fe95c63789c4f884a01c3124f271ea52d9e881bdd52c63020fb7c08a1b96263030415e4bc8146db25ca02b6d4aa754fa44f0e86e6fa0a936048674ffc4fef24c5a2b317c740630901919a0c266c51508b93a8f933e2e64505e458ac26cdb93e8e0bc7bd1609552b6210aa5a0f49934500a155bedea4f0bf25bfc62161fcb74fbf17ca480333f4747d5ad824ef89b943199b3433ee7f3edcae901cbce64c4e81125f7daf884a01c3124f271ea52d9e881bdd52c63020fb7c08a1b96263030415e4bc8146db25ca02b6d4aa754fa44f0e86e6fa0a936048674ffc4fef24c5a2b317c740630901919a0c266c51508b93a8f933e2e64505e458ac26cdb93e8e0bc7bd1609552b6210aa5a0f49934500a155bedea4f0bf25bfc62161fcb74fbf17ca480333f4747d5ad824ef89b94b8d669949683a728f76919fe2cc9896216e00a81f884a01c3124f271ea52d9e881bdd52c63020fb7c08a1b96263030415e4bc8146db25ca02b6d4aa754fa44f0e86e6fa0a936048674ffc4fef24c5a2b317c740630901919a0c266c51508b93a8f933e2e64505e458ac26cdb93e8e0bc7bd1609552b6210aa5a0f49934500a155bedea4f0bf25bfc62161fcb74fbf17ca480333f4747d5ad824e01a09f9feb4b5bf570aa4f121fcc51de36dd7de80d0e4398f3dc99a6a615d12570c9a05d6f01ed28fda456c5f63f5f652735161cb5eede641262e296d62d94c7b628f8", + "signedCancun": "0x03f903858260a782024181a5856bb02a65c784be431918944d1060d970674619005137921969b4bfe3eea6b88595927b2d76b8717f2239c398167e747939f64b2ed9458db8aa10eb367bfab1976a0bc6693cf152dd8d13aa16e4d655a38d6ac64eae0932e13d649f9516fca834cd5a49c7b6e5ba1286a30eea1ac2e89c78441c5418250f8e307cc72c95ef36a8e4cee60b56ece34984ed3302672e6183d28ef30f3e43d60ff90274f89b948a632c23bf807681570c3fb6632ce99fd98bdb23f884a01c3124f271ea52d9e881bdd52c63020fb7c08a1b96263030415e4bc8146db25ca02b6d4aa754fa44f0e86e6fa0a936048674ffc4fef24c5a2b317c740630901919a0c266c51508b93a8f933e2e64505e458ac26cdb93e8e0bc7bd1609552b6210aa5a0f49934500a155bedea4f0bf25bfc62161fcb74fbf17ca480333f4747d5ad824ef89b942d78b31ba09e8a2888d655e3d000fe95c63789c4f884a01c3124f271ea52d9e881bdd52c63020fb7c08a1b96263030415e4bc8146db25ca02b6d4aa754fa44f0e86e6fa0a936048674ffc4fef24c5a2b317c740630901919a0c266c51508b93a8f933e2e64505e458ac26cdb93e8e0bc7bd1609552b6210aa5a0f49934500a155bedea4f0bf25bfc62161fcb74fbf17ca480333f4747d5ad824ef89b943199b3433ee7f3edcae901cbce64c4e81125f7daf884a01c3124f271ea52d9e881bdd52c63020fb7c08a1b96263030415e4bc8146db25ca02b6d4aa754fa44f0e86e6fa0a936048674ffc4fef24c5a2b317c740630901919a0c266c51508b93a8f933e2e64505e458ac26cdb93e8e0bc7bd1609552b6210aa5a0f49934500a155bedea4f0bf25bfc62161fcb74fbf17ca480333f4747d5ad824ef89b94b8d669949683a728f76919fe2cc9896216e00a81f884a01c3124f271ea52d9e881bdd52c63020fb7c08a1b96263030415e4bc8146db25ca02b6d4aa754fa44f0e86e6fa0a936048674ffc4fef24c5a2b317c740630901919a0c266c51508b93a8f933e2e64505e458ac26cdb93e8e0bc7bd1609552b6210aa5a0f49934500a155bedea4f0bf25bfc62161fcb74fbf17ca480333f4747d5ad824e8713c4c123e2373de1a0014a782765b51a031ac55ad44ba18bc6f003634892312bc06ee169486aabeab001a033925f057eeeebb09785c26ff18a561741d30577c4eb97a064262592c279995ca04dd3a7404520f2d9b23fddc520c09fddccf0757248627616a6f7ea4f97e9e452", + "signatureLegacy": { + "r": "0x98cadec7a4cd4297f92828b79459baed65817cd8fcd40cb2025b750c8bb3d046", + "s": "0x0c2e6848654d045d1502a9c1777bc917a63718e8a494dae94720f519ff07b3db", + "v": "0x1b" + }, + "signatureEip155": { + "r": "0x09f309ce844417f1035eff7a0b3bc191253843fb29068769f09fed81a464e622", + "s": "0x722660636b83fcac33759285b9704a5976b7cf9aeeceacab451702ff454469dc", + "v": "0xc172" + }, + "signatureBerlin": { + "r": "0x7c8b37dd82c37b9783d328b99528bdbf4abdc7956d5e3da3a65f4a165e4acb74", + "s": "0x4db2486fbb725825041d85e055d0adf75e02790041d668d13662717695436410", + "v": "0x1" + }, + "signatureLondon": { + "r": "0x9f9feb4b5bf570aa4f121fcc51de36dd7de80d0e4398f3dc99a6a615d12570c9", + "s": "0x5d6f01ed28fda456c5f63f5f652735161cb5eede641262e296d62d94c7b628f8", + "v": "0x1" + }, + "signatureCancun": { + "r": "0x33925f057eeeebb09785c26ff18a561741d30577c4eb97a064262592c279995c", + "s": "0x4dd3a7404520f2d9b23fddc520c09fddccf0757248627616a6f7ea4f97e9e452", + "v": "0x1" + } +} diff --git a/bolt-contracts/test/testdata/transactions/random_6.json b/bolt-contracts/test/testdata/transactions/random_6.json new file mode 100644 index 000000000..bf778827d --- /dev/null +++ b/bolt-contracts/test/testdata/transactions/random_6.json @@ -0,0 +1,56 @@ +{ + "name": "random-6", + "transaction": { + "to": "0x47F8627a7925083e80e0d94dBB979ce2c44A2c74", + "nonce": 432, + "gasLimit": "0xa1fca9f195", + "gasPrice": "0x39", + "maxFeePerGas": "0x98501809f1", + "maxPriorityFeePerGas": "0xfb", + "data": "0xc90411dca095641b87f3229df9e7613b2e73477c57fc4e28b4c06f436f9825b5aa4d839c3d07a8", + "value": "0x79ef27", + "accessList": [], + "chainId": "0xf76e64", + "maxFeePerBlobGas": "0x50799034", + "blobVersionedHashes": [ + "0x01b7a799435fabc095b12caf67e311027aa01ac411f8c004f4ea493a982f3995", + "0x01ec2843771aeee34beaff5727081888ad2e7367f88c14eec961fceb476b63a1" + ] + }, + "privateKey": "0xd9f6e52112da85a6f822054409e83460b9da070ce6c48843e4793c5720ad6910", + "unsignedLegacy": "0xf84b8201b03985a1fca9f1959447f8627a7925083e80e0d94dbb979ce2c44a2c748379ef27a7c90411dca095641b87f3229df9e7613b2e73477c57fc4e28b4c06f436f9825b5aa4d839c3d07a8", + "unsignedEip155": "0xf8518201b03985a1fca9f1959447f8627a7925083e80e0d94dbb979ce2c44a2c748379ef27a7c90411dca095641b87f3229df9e7613b2e73477c57fc4e28b4c06f436f9825b5aa4d839c3d07a883f76e648080", + "unsignedBerlin": "0x01f85083f76e648201b03985a1fca9f1959447f8627a7925083e80e0d94dbb979ce2c44a2c748379ef27a7c90411dca095641b87f3229df9e7613b2e73477c57fc4e28b4c06f436f9825b5aa4d839c3d07a8c0", + "unsignedLondon": "0x02f85783f76e648201b081fb8598501809f185a1fca9f1959447f8627a7925083e80e0d94dbb979ce2c44a2c748379ef27a7c90411dca095641b87f3229df9e7613b2e73477c57fc4e28b4c06f436f9825b5aa4d839c3d07a8c0", + "unsignedCancun": "0x03f8a083f76e648201b081fb8598501809f185a1fca9f1959447f8627a7925083e80e0d94dbb979ce2c44a2c748379ef27a7c90411dca095641b87f3229df9e7613b2e73477c57fc4e28b4c06f436f9825b5aa4d839c3d07a8c08450799034f842a001b7a799435fabc095b12caf67e311027aa01ac411f8c004f4ea493a982f3995a001ec2843771aeee34beaff5727081888ad2e7367f88c14eec961fceb476b63a1", + "signedLegacy": "0xf88e8201b03985a1fca9f1959447f8627a7925083e80e0d94dbb979ce2c44a2c748379ef27a7c90411dca095641b87f3229df9e7613b2e73477c57fc4e28b4c06f436f9825b5aa4d839c3d07a81ba091ad22bc10dbc50ad469a26c6be65d65213a54b5de2ea949f8b7f47ee641d0aba03e69a58405b8bdcd6e41097b828b222b02fa2bfd6b001616a15241c41275c2df", + "signedEip155": "0xf8928201b03985a1fca9f1959447f8627a7925083e80e0d94dbb979ce2c44a2c748379ef27a7c90411dca095641b87f3229df9e7613b2e73477c57fc4e28b4c06f436f9825b5aa4d839c3d07a88401eedceba0b19933ddedae4fae6d63ed9ea7a4df4d1e48be652394203245429ef61e2728d6a033d8ab838c3eb536725ccd32ce73e75551eb660acd0608344887e838c41d7e3f", + "signedBerlin": "0x01f89383f76e648201b03985a1fca9f1959447f8627a7925083e80e0d94dbb979ce2c44a2c748379ef27a7c90411dca095641b87f3229df9e7613b2e73477c57fc4e28b4c06f436f9825b5aa4d839c3d07a8c001a0538d5ec11ad5bc2755057e684243d103469145984c437d317a231cd4ed7f891fa02f6051be9067d1e9322dc667dbd22f423a27354134b5070819f262eb85e8f754", + "signedLondon": "0x02f89a83f76e648201b081fb8598501809f185a1fca9f1959447f8627a7925083e80e0d94dbb979ce2c44a2c748379ef27a7c90411dca095641b87f3229df9e7613b2e73477c57fc4e28b4c06f436f9825b5aa4d839c3d07a8c080a07e88de3723e5ed269a26ebb27c5edf39da43e0246506f7c20861c286ed2e0118a0304a806691fee1223b8b505dc97142ad8eefaf949a7d8828e0483659accecfdd", + "signedCancun": "0x03f8e383f76e648201b081fb8598501809f185a1fca9f1959447f8627a7925083e80e0d94dbb979ce2c44a2c748379ef27a7c90411dca095641b87f3229df9e7613b2e73477c57fc4e28b4c06f436f9825b5aa4d839c3d07a8c08450799034f842a001b7a799435fabc095b12caf67e311027aa01ac411f8c004f4ea493a982f3995a001ec2843771aeee34beaff5727081888ad2e7367f88c14eec961fceb476b63a180a0b5f9efc37373be1d360b8f6a5dc88e111e4cad78b320aee3196fa8a413034a19a00747b3ad2aa3abce5b73d940d7f10990cd7ef496e6315e8143b122f83b9ae149", + "signatureLegacy": { + "r": "0x91ad22bc10dbc50ad469a26c6be65d65213a54b5de2ea949f8b7f47ee641d0ab", + "s": "0x3e69a58405b8bdcd6e41097b828b222b02fa2bfd6b001616a15241c41275c2df", + "v": "0x1b" + }, + "signatureEip155": { + "r": "0xb19933ddedae4fae6d63ed9ea7a4df4d1e48be652394203245429ef61e2728d6", + "s": "0x33d8ab838c3eb536725ccd32ce73e75551eb660acd0608344887e838c41d7e3f", + "v": "0x1eedceb" + }, + "signatureBerlin": { + "r": "0x538d5ec11ad5bc2755057e684243d103469145984c437d317a231cd4ed7f891f", + "s": "0x2f6051be9067d1e9322dc667dbd22f423a27354134b5070819f262eb85e8f754", + "v": "0x1" + }, + "signatureLondon": { + "r": "0x7e88de3723e5ed269a26ebb27c5edf39da43e0246506f7c20861c286ed2e0118", + "s": "0x304a806691fee1223b8b505dc97142ad8eefaf949a7d8828e0483659accecfdd", + "v": "0x0" + }, + "signatureCancun": { + "r": "0xb5f9efc37373be1d360b8f6a5dc88e111e4cad78b320aee3196fa8a413034a19", + "s": "0x0747b3ad2aa3abce5b73d940d7f10990cd7ef496e6315e8143b122f83b9ae149", + "v": "0x0" + } +} diff --git a/bolt-contracts/test/testdata/transactions/random_7.json b/bolt-contracts/test/testdata/transactions/random_7.json new file mode 100644 index 000000000..e4804a03c --- /dev/null +++ b/bolt-contracts/test/testdata/transactions/random_7.json @@ -0,0 +1,84 @@ +{ + "name": "random-7", + "transaction": { + "to": "0xD6E75AAf5C27963b31DD4Ad638e4C1d07b2Cc010", + "nonce": 667, + "gasLimit": "0x95ead5c7", + "gasPrice": "0x86", + "maxFeePerGas": "0x2e54dcae", + "maxPriorityFeePerGas": "0x0cb9", + "data": "0xe931d4333e6bb3d32f215bfdb13209a0905f740b180cadcea26405d9c79e108e50e87b78a1e7192c313e22f09632ed2882774f8e7536b7dfa987f7d063fc8db54ad5bd8d4977910294", + "value": "0x632af6c3", + "accessList": [ + { + "address": "0xD0a4e08ACDA2A8B3AE50Db94DB3246C4a2F34b60", + "storageKeys": [ + "0xe85938a8c29ab7b82264cc2e0822673fe17637364d6b384eb49f89e1adf61a11", + "0x2c10ec7831da9a49dbf10818882d783f6511dda96c06b8ad2999aeb9f9f82d0b" + ] + }, + { + "address": "0x3e4eE3DE26502A40C8dc33886c1bB7e079916194", + "storageKeys": [ + "0xe85938a8c29ab7b82264cc2e0822673fe17637364d6b384eb49f89e1adf61a11", + "0x2c10ec7831da9a49dbf10818882d783f6511dda96c06b8ad2999aeb9f9f82d0b" + ] + }, + { + "address": "0xe54E80bd490910C719D5F11602a97FaE4D5C0F11", + "storageKeys": [ + "0xe85938a8c29ab7b82264cc2e0822673fe17637364d6b384eb49f89e1adf61a11", + "0x2c10ec7831da9a49dbf10818882d783f6511dda96c06b8ad2999aeb9f9f82d0b" + ] + }, + { + "address": "0x6D1fd21ed3E7D4DE1F58361cB78De7a882dEcC79", + "storageKeys": [ + "0xe85938a8c29ab7b82264cc2e0822673fe17637364d6b384eb49f89e1adf61a11", + "0x2c10ec7831da9a49dbf10818882d783f6511dda96c06b8ad2999aeb9f9f82d0b" + ] + } + ], + "chainId": "0x295d", + "maxFeePerBlobGas": "0xd76e69ca95e7", + "blobVersionedHashes": [ + "0x01a7921375f6c2798377acd9c4db7719b9980b28c49dbc0ef3962381d961d725" + ] + }, + "privateKey": "0xa6fe9bfe96da66b7788f98b3107c588bee30ccc844e129a7772df540c3193239", + "unsignedLegacy": "0xf86f82029b81868495ead5c794d6e75aaf5c27963b31dd4ad638e4c1d07b2cc01084632af6c3b849e931d4333e6bb3d32f215bfdb13209a0905f740b180cadcea26405d9c79e108e50e87b78a1e7192c313e22f09632ed2882774f8e7536b7dfa987f7d063fc8db54ad5bd8d4977910294", + "unsignedEip155": "0xf87482029b81868495ead5c794d6e75aaf5c27963b31dd4ad638e4c1d07b2cc01084632af6c3b849e931d4333e6bb3d32f215bfdb13209a0905f740b180cadcea26405d9c79e108e50e87b78a1e7192c313e22f09632ed2882774f8e7536b7dfa987f7d063fc8db54ad5bd8d497791029482295d8080", + "unsignedBerlin": "0x01f901e182295d82029b81868495ead5c794d6e75aaf5c27963b31dd4ad638e4c1d07b2cc01084632af6c3b849e931d4333e6bb3d32f215bfdb13209a0905f740b180cadcea26405d9c79e108e50e87b78a1e7192c313e22f09632ed2882774f8e7536b7dfa987f7d063fc8db54ad5bd8d4977910294f9016cf85994d0a4e08acda2a8b3ae50db94db3246c4a2f34b60f842a0e85938a8c29ab7b82264cc2e0822673fe17637364d6b384eb49f89e1adf61a11a02c10ec7831da9a49dbf10818882d783f6511dda96c06b8ad2999aeb9f9f82d0bf859943e4ee3de26502a40c8dc33886c1bb7e079916194f842a0e85938a8c29ab7b82264cc2e0822673fe17637364d6b384eb49f89e1adf61a11a02c10ec7831da9a49dbf10818882d783f6511dda96c06b8ad2999aeb9f9f82d0bf85994e54e80bd490910c719d5f11602a97fae4d5c0f11f842a0e85938a8c29ab7b82264cc2e0822673fe17637364d6b384eb49f89e1adf61a11a02c10ec7831da9a49dbf10818882d783f6511dda96c06b8ad2999aeb9f9f82d0bf859946d1fd21ed3e7d4de1f58361cb78de7a882decc79f842a0e85938a8c29ab7b82264cc2e0822673fe17637364d6b384eb49f89e1adf61a11a02c10ec7831da9a49dbf10818882d783f6511dda96c06b8ad2999aeb9f9f82d0b", + "unsignedLondon": "0x02f901e782295d82029b820cb9842e54dcae8495ead5c794d6e75aaf5c27963b31dd4ad638e4c1d07b2cc01084632af6c3b849e931d4333e6bb3d32f215bfdb13209a0905f740b180cadcea26405d9c79e108e50e87b78a1e7192c313e22f09632ed2882774f8e7536b7dfa987f7d063fc8db54ad5bd8d4977910294f9016cf85994d0a4e08acda2a8b3ae50db94db3246c4a2f34b60f842a0e85938a8c29ab7b82264cc2e0822673fe17637364d6b384eb49f89e1adf61a11a02c10ec7831da9a49dbf10818882d783f6511dda96c06b8ad2999aeb9f9f82d0bf859943e4ee3de26502a40c8dc33886c1bb7e079916194f842a0e85938a8c29ab7b82264cc2e0822673fe17637364d6b384eb49f89e1adf61a11a02c10ec7831da9a49dbf10818882d783f6511dda96c06b8ad2999aeb9f9f82d0bf85994e54e80bd490910c719d5f11602a97fae4d5c0f11f842a0e85938a8c29ab7b82264cc2e0822673fe17637364d6b384eb49f89e1adf61a11a02c10ec7831da9a49dbf10818882d783f6511dda96c06b8ad2999aeb9f9f82d0bf859946d1fd21ed3e7d4de1f58361cb78de7a882decc79f842a0e85938a8c29ab7b82264cc2e0822673fe17637364d6b384eb49f89e1adf61a11a02c10ec7831da9a49dbf10818882d783f6511dda96c06b8ad2999aeb9f9f82d0b", + "unsignedCancun": "0x03f9021082295d82029b820cb9842e54dcae8495ead5c794d6e75aaf5c27963b31dd4ad638e4c1d07b2cc01084632af6c3b849e931d4333e6bb3d32f215bfdb13209a0905f740b180cadcea26405d9c79e108e50e87b78a1e7192c313e22f09632ed2882774f8e7536b7dfa987f7d063fc8db54ad5bd8d4977910294f9016cf85994d0a4e08acda2a8b3ae50db94db3246c4a2f34b60f842a0e85938a8c29ab7b82264cc2e0822673fe17637364d6b384eb49f89e1adf61a11a02c10ec7831da9a49dbf10818882d783f6511dda96c06b8ad2999aeb9f9f82d0bf859943e4ee3de26502a40c8dc33886c1bb7e079916194f842a0e85938a8c29ab7b82264cc2e0822673fe17637364d6b384eb49f89e1adf61a11a02c10ec7831da9a49dbf10818882d783f6511dda96c06b8ad2999aeb9f9f82d0bf85994e54e80bd490910c719d5f11602a97fae4d5c0f11f842a0e85938a8c29ab7b82264cc2e0822673fe17637364d6b384eb49f89e1adf61a11a02c10ec7831da9a49dbf10818882d783f6511dda96c06b8ad2999aeb9f9f82d0bf859946d1fd21ed3e7d4de1f58361cb78de7a882decc79f842a0e85938a8c29ab7b82264cc2e0822673fe17637364d6b384eb49f89e1adf61a11a02c10ec7831da9a49dbf10818882d783f6511dda96c06b8ad2999aeb9f9f82d0b86d76e69ca95e7e1a001a7921375f6c2798377acd9c4db7719b9980b28c49dbc0ef3962381d961d725", + "signedLegacy": "0xf8b282029b81868495ead5c794d6e75aaf5c27963b31dd4ad638e4c1d07b2cc01084632af6c3b849e931d4333e6bb3d32f215bfdb13209a0905f740b180cadcea26405d9c79e108e50e87b78a1e7192c313e22f09632ed2882774f8e7536b7dfa987f7d063fc8db54ad5bd8d49779102941ca02bacdc3053183b5f2a6f56ed6c2f126a6bbb592ef64d912bc704293d5620e188a0163d3e7cb0ad4ab63d10314c24728248699e5f5acebf84e84d67ca21ebe835ca", + "signedEip155": "0xf8b482029b81868495ead5c794d6e75aaf5c27963b31dd4ad638e4c1d07b2cc01084632af6c3b849e931d4333e6bb3d32f215bfdb13209a0905f740b180cadcea26405d9c79e108e50e87b78a1e7192c313e22f09632ed2882774f8e7536b7dfa987f7d063fc8db54ad5bd8d49779102948252dda0988356e8e6b000200035901fa00b736677e25d01022d8e7ff0c6f9a9f44cb530a01d04ca133497f2a7f07a36022a132027df576c9d53935fee792fa6a01e2d18d0", + "signedBerlin": "0x01f9022482295d82029b81868495ead5c794d6e75aaf5c27963b31dd4ad638e4c1d07b2cc01084632af6c3b849e931d4333e6bb3d32f215bfdb13209a0905f740b180cadcea26405d9c79e108e50e87b78a1e7192c313e22f09632ed2882774f8e7536b7dfa987f7d063fc8db54ad5bd8d4977910294f9016cf85994d0a4e08acda2a8b3ae50db94db3246c4a2f34b60f842a0e85938a8c29ab7b82264cc2e0822673fe17637364d6b384eb49f89e1adf61a11a02c10ec7831da9a49dbf10818882d783f6511dda96c06b8ad2999aeb9f9f82d0bf859943e4ee3de26502a40c8dc33886c1bb7e079916194f842a0e85938a8c29ab7b82264cc2e0822673fe17637364d6b384eb49f89e1adf61a11a02c10ec7831da9a49dbf10818882d783f6511dda96c06b8ad2999aeb9f9f82d0bf85994e54e80bd490910c719d5f11602a97fae4d5c0f11f842a0e85938a8c29ab7b82264cc2e0822673fe17637364d6b384eb49f89e1adf61a11a02c10ec7831da9a49dbf10818882d783f6511dda96c06b8ad2999aeb9f9f82d0bf859946d1fd21ed3e7d4de1f58361cb78de7a882decc79f842a0e85938a8c29ab7b82264cc2e0822673fe17637364d6b384eb49f89e1adf61a11a02c10ec7831da9a49dbf10818882d783f6511dda96c06b8ad2999aeb9f9f82d0b01a0f243d534dfae5cfb18cf59a41daa31520d91f03fc9b5830ee8a512dca82bbdcda02507b456d89080c847848fa2199998ffc363cbb0d4835ce8d03621980a3f135a", + "signedLondon": "0x02f9022a82295d82029b820cb9842e54dcae8495ead5c794d6e75aaf5c27963b31dd4ad638e4c1d07b2cc01084632af6c3b849e931d4333e6bb3d32f215bfdb13209a0905f740b180cadcea26405d9c79e108e50e87b78a1e7192c313e22f09632ed2882774f8e7536b7dfa987f7d063fc8db54ad5bd8d4977910294f9016cf85994d0a4e08acda2a8b3ae50db94db3246c4a2f34b60f842a0e85938a8c29ab7b82264cc2e0822673fe17637364d6b384eb49f89e1adf61a11a02c10ec7831da9a49dbf10818882d783f6511dda96c06b8ad2999aeb9f9f82d0bf859943e4ee3de26502a40c8dc33886c1bb7e079916194f842a0e85938a8c29ab7b82264cc2e0822673fe17637364d6b384eb49f89e1adf61a11a02c10ec7831da9a49dbf10818882d783f6511dda96c06b8ad2999aeb9f9f82d0bf85994e54e80bd490910c719d5f11602a97fae4d5c0f11f842a0e85938a8c29ab7b82264cc2e0822673fe17637364d6b384eb49f89e1adf61a11a02c10ec7831da9a49dbf10818882d783f6511dda96c06b8ad2999aeb9f9f82d0bf859946d1fd21ed3e7d4de1f58361cb78de7a882decc79f842a0e85938a8c29ab7b82264cc2e0822673fe17637364d6b384eb49f89e1adf61a11a02c10ec7831da9a49dbf10818882d783f6511dda96c06b8ad2999aeb9f9f82d0b80a08e1d67786ffd5edb50e2aff66566c76a1523e9dcabc667b92f540f536d47bccea05d6a00e50ad155e59fbba841afda7592ec16788da4cfd895b447f4c13197d1ab", + "signedCancun": "0x03f9025382295d82029b820cb9842e54dcae8495ead5c794d6e75aaf5c27963b31dd4ad638e4c1d07b2cc01084632af6c3b849e931d4333e6bb3d32f215bfdb13209a0905f740b180cadcea26405d9c79e108e50e87b78a1e7192c313e22f09632ed2882774f8e7536b7dfa987f7d063fc8db54ad5bd8d4977910294f9016cf85994d0a4e08acda2a8b3ae50db94db3246c4a2f34b60f842a0e85938a8c29ab7b82264cc2e0822673fe17637364d6b384eb49f89e1adf61a11a02c10ec7831da9a49dbf10818882d783f6511dda96c06b8ad2999aeb9f9f82d0bf859943e4ee3de26502a40c8dc33886c1bb7e079916194f842a0e85938a8c29ab7b82264cc2e0822673fe17637364d6b384eb49f89e1adf61a11a02c10ec7831da9a49dbf10818882d783f6511dda96c06b8ad2999aeb9f9f82d0bf85994e54e80bd490910c719d5f11602a97fae4d5c0f11f842a0e85938a8c29ab7b82264cc2e0822673fe17637364d6b384eb49f89e1adf61a11a02c10ec7831da9a49dbf10818882d783f6511dda96c06b8ad2999aeb9f9f82d0bf859946d1fd21ed3e7d4de1f58361cb78de7a882decc79f842a0e85938a8c29ab7b82264cc2e0822673fe17637364d6b384eb49f89e1adf61a11a02c10ec7831da9a49dbf10818882d783f6511dda96c06b8ad2999aeb9f9f82d0b86d76e69ca95e7e1a001a7921375f6c2798377acd9c4db7719b9980b28c49dbc0ef3962381d961d72501a006f7d3d75727c212f2e3fe74af34e4b7db7825291f919e1f291a36161c4d7867a07706e6e9c6b49b83157519cb113dab63707214ad2d9d3af77c65b671bd2958d8", + "signatureLegacy": { + "r": "0x2bacdc3053183b5f2a6f56ed6c2f126a6bbb592ef64d912bc704293d5620e188", + "s": "0x163d3e7cb0ad4ab63d10314c24728248699e5f5acebf84e84d67ca21ebe835ca", + "v": "0x1c" + }, + "signatureEip155": { + "r": "0x988356e8e6b000200035901fa00b736677e25d01022d8e7ff0c6f9a9f44cb530", + "s": "0x1d04ca133497f2a7f07a36022a132027df576c9d53935fee792fa6a01e2d18d0", + "v": "0x52dd" + }, + "signatureBerlin": { + "r": "0xf243d534dfae5cfb18cf59a41daa31520d91f03fc9b5830ee8a512dca82bbdcd", + "s": "0x2507b456d89080c847848fa2199998ffc363cbb0d4835ce8d03621980a3f135a", + "v": "0x1" + }, + "signatureLondon": { + "r": "0x8e1d67786ffd5edb50e2aff66566c76a1523e9dcabc667b92f540f536d47bcce", + "s": "0x5d6a00e50ad155e59fbba841afda7592ec16788da4cfd895b447f4c13197d1ab", + "v": "0x0" + }, + "signatureCancun": { + "r": "0x06f7d3d75727c212f2e3fe74af34e4b7db7825291f919e1f291a36161c4d7867", + "s": "0x7706e6e9c6b49b83157519cb113dab63707214ad2d9d3af77c65b671bd2958d8", + "v": "0x1" + } +} diff --git a/bolt-contracts/test/testdata/transactions/random_8.json b/bolt-contracts/test/testdata/transactions/random_8.json new file mode 100644 index 000000000..edc2787d4 --- /dev/null +++ b/bolt-contracts/test/testdata/transactions/random_8.json @@ -0,0 +1,60 @@ +{ + "name": "random-8", + "transaction": { + "to": "0x74A3Ddaa5CD03Ba0e9B7cd5535BDa1E23F691932", + "nonce": 965, + "gasLimit": "0x35", + "gasPrice": "0x11ac8f05", + "maxFeePerGas": "0xae07efb83ed7ae", + "maxPriorityFeePerGas": "0xdeb8", + "data": "0xb7c4964ed0cebae0f12c2cbb2adadd2b7ef1b2183c1d5df71354ba8da3bff86e14975031ee08b2239ded1369a235c68a72cc994af2744f70a7214babec31160cd6f142", + "value": "0xa62ec77a", + "accessList": [ + { + "address": "0xE125F9D848135f52ecc65FcF216146541688f4ec", + "storageKeys": [] + } + ], + "chainId": "0x473eafa7", + "maxFeePerBlobGas": "0x1a6c2938773134", + "blobVersionedHashes": [ + "0x01244e9f27106e77da21d39868d5400854a0cf90e2f58a1134b655704acb213e" + ] + }, + "privateKey": "0xbcc0a8d59a4ebe22dbcc8a52e2975f634f6e3095716602f08e214e4017684180", + "unsignedLegacy": "0xf8688203c58411ac8f05359474a3ddaa5cd03ba0e9b7cd5535bda1e23f69193284a62ec77ab843b7c4964ed0cebae0f12c2cbb2adadd2b7ef1b2183c1d5df71354ba8da3bff86e14975031ee08b2239ded1369a235c68a72cc994af2744f70a7214babec31160cd6f142", + "unsignedEip155": "0xf86f8203c58411ac8f05359474a3ddaa5cd03ba0e9b7cd5535bda1e23f69193284a62ec77ab843b7c4964ed0cebae0f12c2cbb2adadd2b7ef1b2183c1d5df71354ba8da3bff86e14975031ee08b2239ded1369a235c68a72cc994af2744f70a7214babec31160cd6f14284473eafa78080", + "unsignedBerlin": "0x01f88584473eafa78203c58411ac8f05359474a3ddaa5cd03ba0e9b7cd5535bda1e23f69193284a62ec77ab843b7c4964ed0cebae0f12c2cbb2adadd2b7ef1b2183c1d5df71354ba8da3bff86e14975031ee08b2239ded1369a235c68a72cc994af2744f70a7214babec31160cd6f142d7d694e125f9d848135f52ecc65fcf216146541688f4ecc0", + "unsignedLondon": "0x02f88b84473eafa78203c582deb887ae07efb83ed7ae359474a3ddaa5cd03ba0e9b7cd5535bda1e23f69193284a62ec77ab843b7c4964ed0cebae0f12c2cbb2adadd2b7ef1b2183c1d5df71354ba8da3bff86e14975031ee08b2239ded1369a235c68a72cc994af2744f70a7214babec31160cd6f142d7d694e125f9d848135f52ecc65fcf216146541688f4ecc0", + "unsignedCancun": "0x03f8b584473eafa78203c582deb887ae07efb83ed7ae359474a3ddaa5cd03ba0e9b7cd5535bda1e23f69193284a62ec77ab843b7c4964ed0cebae0f12c2cbb2adadd2b7ef1b2183c1d5df71354ba8da3bff86e14975031ee08b2239ded1369a235c68a72cc994af2744f70a7214babec31160cd6f142d7d694e125f9d848135f52ecc65fcf216146541688f4ecc0871a6c2938773134e1a001244e9f27106e77da21d39868d5400854a0cf90e2f58a1134b655704acb213e", + "signedLegacy": "0xf8ab8203c58411ac8f05359474a3ddaa5cd03ba0e9b7cd5535bda1e23f69193284a62ec77ab843b7c4964ed0cebae0f12c2cbb2adadd2b7ef1b2183c1d5df71354ba8da3bff86e14975031ee08b2239ded1369a235c68a72cc994af2744f70a7214babec31160cd6f1421ba049a9667e37b4619f39fb925cac786d8ec36b44f7f524cf936ce36cc97ebb04eda074679d25c7bd881450bb623002cb1d5b09b45e9435ebc469b9cea2d05feae86a", + "signedEip155": "0xf8af8203c58411ac8f05359474a3ddaa5cd03ba0e9b7cd5535bda1e23f69193284a62ec77ab843b7c4964ed0cebae0f12c2cbb2adadd2b7ef1b2183c1d5df71354ba8da3bff86e14975031ee08b2239ded1369a235c68a72cc994af2744f70a7214babec31160cd6f142848e7d5f72a0e211ba6a21a4ae8bf4cafb29b98d2382bcee9435181a5261964f517ca91b33ffa038351fa3cfe37c88b2bd3aa16421bd37abf6356491f05789624c9f0e25682a38", + "signedBerlin": "0x01f8c884473eafa78203c58411ac8f05359474a3ddaa5cd03ba0e9b7cd5535bda1e23f69193284a62ec77ab843b7c4964ed0cebae0f12c2cbb2adadd2b7ef1b2183c1d5df71354ba8da3bff86e14975031ee08b2239ded1369a235c68a72cc994af2744f70a7214babec31160cd6f142d7d694e125f9d848135f52ecc65fcf216146541688f4ecc001a0cf7dfd4507fb9331f4344fe787ecdbfb74d5c09cf71f2fcccb84d12be2a11f5fa07359816992767a631758506f7000bd4cb576cc62388bc490922ab96ee353cc82", + "signedLondon": "0x02f8ce84473eafa78203c582deb887ae07efb83ed7ae359474a3ddaa5cd03ba0e9b7cd5535bda1e23f69193284a62ec77ab843b7c4964ed0cebae0f12c2cbb2adadd2b7ef1b2183c1d5df71354ba8da3bff86e14975031ee08b2239ded1369a235c68a72cc994af2744f70a7214babec31160cd6f142d7d694e125f9d848135f52ecc65fcf216146541688f4ecc080a04b1e128faa1365b45eacf1da245d78a3c6c10619a265e268657bd8db50c2ac3ea02538dcbc52e6648a320c641268b5eef3bd9eb24d5f17aa1a3535c194b1404e58", + "signedCancun": "0x03f8f884473eafa78203c582deb887ae07efb83ed7ae359474a3ddaa5cd03ba0e9b7cd5535bda1e23f69193284a62ec77ab843b7c4964ed0cebae0f12c2cbb2adadd2b7ef1b2183c1d5df71354ba8da3bff86e14975031ee08b2239ded1369a235c68a72cc994af2744f70a7214babec31160cd6f142d7d694e125f9d848135f52ecc65fcf216146541688f4ecc0871a6c2938773134e1a001244e9f27106e77da21d39868d5400854a0cf90e2f58a1134b655704acb213e80a0e0e7119acfae63aabe8754ff93738fba1714ef044cbf0cf00bb155e5fd32a629a064c04c7a22c0070cc3f6fa061677be23c3336359cdb428f02f53945d73fd4e77", + "signatureLegacy": { + "r": "0x49a9667e37b4619f39fb925cac786d8ec36b44f7f524cf936ce36cc97ebb04ed", + "s": "0x74679d25c7bd881450bb623002cb1d5b09b45e9435ebc469b9cea2d05feae86a", + "v": "0x1b" + }, + "signatureEip155": { + "r": "0xe211ba6a21a4ae8bf4cafb29b98d2382bcee9435181a5261964f517ca91b33ff", + "s": "0x38351fa3cfe37c88b2bd3aa16421bd37abf6356491f05789624c9f0e25682a38", + "v": "0x8e7d5f72" + }, + "signatureBerlin": { + "r": "0xcf7dfd4507fb9331f4344fe787ecdbfb74d5c09cf71f2fcccb84d12be2a11f5f", + "s": "0x7359816992767a631758506f7000bd4cb576cc62388bc490922ab96ee353cc82", + "v": "0x1" + }, + "signatureLondon": { + "r": "0x4b1e128faa1365b45eacf1da245d78a3c6c10619a265e268657bd8db50c2ac3e", + "s": "0x2538dcbc52e6648a320c641268b5eef3bd9eb24d5f17aa1a3535c194b1404e58", + "v": "0x0" + }, + "signatureCancun": { + "r": "0xe0e7119acfae63aabe8754ff93738fba1714ef044cbf0cf00bb155e5fd32a629", + "s": "0x64c04c7a22c0070cc3f6fa061677be23c3336359cdb428f02f53945d73fd4e77", + "v": "0x0" + } +} diff --git a/bolt-contracts/test/testdata/transactions/random_9.json b/bolt-contracts/test/testdata/transactions/random_9.json new file mode 100644 index 000000000..3e9f1867a --- /dev/null +++ b/bolt-contracts/test/testdata/transactions/random_9.json @@ -0,0 +1,56 @@ +{ + "name": "random-9", + "transaction": { + "to": "0x671a63481F6AdF6E231296c3ec838D21919b5Ec0", + "nonce": 776, + "gasLimit": "0x905469", + "gasPrice": "0xe8", + "maxFeePerGas": "0x64b085b92ba07f", + "maxPriorityFeePerGas": "0xbe", + "data": "0x6e8a5785fbcffa9d6303b7a22818c0c67aa5e3b1c097aa35af45f95038224f7b1e0724b14a2de88e07469fa80c6c1e172353269d5bd75af8c91b77585a6aa1b99c462b9fbf7010a9b8f4194260bfb307da9ba5b6202da6f0ef6f", + "value": "0x4f88", + "accessList": [], + "chainId": "0x348f3fcb", + "maxFeePerBlobGas": "0xb8cc0be94a", + "blobVersionedHashes": [ + "0x0134b8b105e49b6c97a9aade76ec97bd4357053c030b7f4370241fca10ab6ef3", + "0x0198df85d8142d33554e7369ed5695dc0a351b38741626e573f918812fa994ae" + ] + }, + "privateKey": "0x63dd633e51d725e1f444b9a724729fb8bd052ec4737eceba804e846a32b4530c", + "unsignedLegacy": "0xf87d82030881e88390546994671a63481f6adf6e231296c3ec838d21919b5ec0824f88b85a6e8a5785fbcffa9d6303b7a22818c0c67aa5e3b1c097aa35af45f95038224f7b1e0724b14a2de88e07469fa80c6c1e172353269d5bd75af8c91b77585a6aa1b99c462b9fbf7010a9b8f4194260bfb307da9ba5b6202da6f0ef6f", + "unsignedEip155": "0xf88482030881e88390546994671a63481f6adf6e231296c3ec838d21919b5ec0824f88b85a6e8a5785fbcffa9d6303b7a22818c0c67aa5e3b1c097aa35af45f95038224f7b1e0724b14a2de88e07469fa80c6c1e172353269d5bd75af8c91b77585a6aa1b99c462b9fbf7010a9b8f4194260bfb307da9ba5b6202da6f0ef6f84348f3fcb8080", + "unsignedBerlin": "0x01f88384348f3fcb82030881e88390546994671a63481f6adf6e231296c3ec838d21919b5ec0824f88b85a6e8a5785fbcffa9d6303b7a22818c0c67aa5e3b1c097aa35af45f95038224f7b1e0724b14a2de88e07469fa80c6c1e172353269d5bd75af8c91b77585a6aa1b99c462b9fbf7010a9b8f4194260bfb307da9ba5b6202da6f0ef6fc0", + "unsignedLondon": "0x02f88b84348f3fcb82030881be8764b085b92ba07f8390546994671a63481f6adf6e231296c3ec838d21919b5ec0824f88b85a6e8a5785fbcffa9d6303b7a22818c0c67aa5e3b1c097aa35af45f95038224f7b1e0724b14a2de88e07469fa80c6c1e172353269d5bd75af8c91b77585a6aa1b99c462b9fbf7010a9b8f4194260bfb307da9ba5b6202da6f0ef6fc0", + "unsignedCancun": "0x03f8d584348f3fcb82030881be8764b085b92ba07f8390546994671a63481f6adf6e231296c3ec838d21919b5ec0824f88b85a6e8a5785fbcffa9d6303b7a22818c0c67aa5e3b1c097aa35af45f95038224f7b1e0724b14a2de88e07469fa80c6c1e172353269d5bd75af8c91b77585a6aa1b99c462b9fbf7010a9b8f4194260bfb307da9ba5b6202da6f0ef6fc085b8cc0be94af842a00134b8b105e49b6c97a9aade76ec97bd4357053c030b7f4370241fca10ab6ef3a00198df85d8142d33554e7369ed5695dc0a351b38741626e573f918812fa994ae", + "signedLegacy": "0xf8c082030881e88390546994671a63481f6adf6e231296c3ec838d21919b5ec0824f88b85a6e8a5785fbcffa9d6303b7a22818c0c67aa5e3b1c097aa35af45f95038224f7b1e0724b14a2de88e07469fa80c6c1e172353269d5bd75af8c91b77585a6aa1b99c462b9fbf7010a9b8f4194260bfb307da9ba5b6202da6f0ef6f1ba0d567c29e1775043d150df4e740d0e581d723f4950b2d35a5adb5a3a1b527686ea00fbcddbd1e8f33eb73a957c985b1ab85c333a42074c8801fe230ace1f246f826", + "signedEip155": "0xf8c482030881e88390546994671a63481f6adf6e231296c3ec838d21919b5ec0824f88b85a6e8a5785fbcffa9d6303b7a22818c0c67aa5e3b1c097aa35af45f95038224f7b1e0724b14a2de88e07469fa80c6c1e172353269d5bd75af8c91b77585a6aa1b99c462b9fbf7010a9b8f4194260bfb307da9ba5b6202da6f0ef6f84691e7fb9a0900464b1bcd8e581911e8761960913c5b23ef063886533b4a9759c74ffcd4d80a0319be70fd62d7b2722aa1572b456755ae71fc7d1d3c02cdaafebbf4941033409", + "signedBerlin": "0x01f8c684348f3fcb82030881e88390546994671a63481f6adf6e231296c3ec838d21919b5ec0824f88b85a6e8a5785fbcffa9d6303b7a22818c0c67aa5e3b1c097aa35af45f95038224f7b1e0724b14a2de88e07469fa80c6c1e172353269d5bd75af8c91b77585a6aa1b99c462b9fbf7010a9b8f4194260bfb307da9ba5b6202da6f0ef6fc080a0d2418f0ef98efb8120c5f35803fcc2b0def21e7bad27537fa8be144169393df7a0561b3ee3efe7f4cc07ff4289897c8b316eb6d4de32f60eddd71833d70c13c488", + "signedLondon": "0x02f8ce84348f3fcb82030881be8764b085b92ba07f8390546994671a63481f6adf6e231296c3ec838d21919b5ec0824f88b85a6e8a5785fbcffa9d6303b7a22818c0c67aa5e3b1c097aa35af45f95038224f7b1e0724b14a2de88e07469fa80c6c1e172353269d5bd75af8c91b77585a6aa1b99c462b9fbf7010a9b8f4194260bfb307da9ba5b6202da6f0ef6fc080a04dfe6f43e3da767dcb2d717d7555f67448a47d8329ba40ba0b25e19ef217837da01612656de7ebf9929a66b7479a63927bb895b225af797adf3d32a890b279ab82", + "signedCancun": "0x03f9011884348f3fcb82030881be8764b085b92ba07f8390546994671a63481f6adf6e231296c3ec838d21919b5ec0824f88b85a6e8a5785fbcffa9d6303b7a22818c0c67aa5e3b1c097aa35af45f95038224f7b1e0724b14a2de88e07469fa80c6c1e172353269d5bd75af8c91b77585a6aa1b99c462b9fbf7010a9b8f4194260bfb307da9ba5b6202da6f0ef6fc085b8cc0be94af842a00134b8b105e49b6c97a9aade76ec97bd4357053c030b7f4370241fca10ab6ef3a00198df85d8142d33554e7369ed5695dc0a351b38741626e573f918812fa994ae01a051e0cac495af080a3d0777ccd5d6a514edfbcc156c0aa4f4218a8bb9259a5e3da00c17cfe7183fd3a1df95478e37f1b75442f4229f9f8ed61e364cfd5407b1a6d2", + "signatureLegacy": { + "r": "0xd567c29e1775043d150df4e740d0e581d723f4950b2d35a5adb5a3a1b527686e", + "s": "0x0fbcddbd1e8f33eb73a957c985b1ab85c333a42074c8801fe230ace1f246f826", + "v": "0x1b" + }, + "signatureEip155": { + "r": "0x900464b1bcd8e581911e8761960913c5b23ef063886533b4a9759c74ffcd4d80", + "s": "0x319be70fd62d7b2722aa1572b456755ae71fc7d1d3c02cdaafebbf4941033409", + "v": "0x691e7fb9" + }, + "signatureBerlin": { + "r": "0xd2418f0ef98efb8120c5f35803fcc2b0def21e7bad27537fa8be144169393df7", + "s": "0x561b3ee3efe7f4cc07ff4289897c8b316eb6d4de32f60eddd71833d70c13c488", + "v": "0x0" + }, + "signatureLondon": { + "r": "0x4dfe6f43e3da767dcb2d717d7555f67448a47d8329ba40ba0b25e19ef217837d", + "s": "0x1612656de7ebf9929a66b7479a63927bb895b225af797adf3d32a890b279ab82", + "v": "0x0" + }, + "signatureCancun": { + "r": "0x51e0cac495af080a3d0777ccd5d6a514edfbcc156c0aa4f4218a8bb9259a5e3d", + "s": "0x0c17cfe7183fd3a1df95478e37f1b75442f4229f9f8ed61e364cfd5407b1a6d2", + "v": "0x1" + } +} diff --git a/bolt-contracts/test/testdata/tx_mpt_proof_20785012.json b/bolt-contracts/test/testdata/tx_mpt_proof_20785012.json new file mode 100644 index 000000000..82b1faeae --- /dev/null +++ b/bolt-contracts/test/testdata/tx_mpt_proof_20785012.json @@ -0,0 +1,11 @@ +{ + "root": "0x87bb9183296ce9e3b7a3246f6d3a778b99a5d7daaba2174750707407c7297365", + "proof": [ + "f90131a060b2a2267b5925093805abbff6a9427865d9b0277666178def5e6d7f74cbea7da02d03b55bc0b1007e572b92e13d09e02cd1d740d81fc52c25fb522dcdc3c25717a08ce7808266f8a29dffb636d860c7fc13dccb47cfb5c1869d50da5f847faee9dda02d872b0e5b790bfd8f8851a539bdd67c8324812698ef8420bd3db06063f23383a046d720b85dbd71b53f9b46dc5908aafd65e0dc8d2a73bde7b548a6e59cdc1789a076e14ef77554b242d92b4832fbce89211b7b140ae202a4e4f465ef823204c9f7a0af30522b1024223eb65217ebcb5ddc22c1a62fbf43147fb43413e96eef821f66a02b8f47754c761574aa5ee4c348efc736b533c61aa7fa17051a5ea59fbeb3d7d2a08c8cc819bcd01c3ac2ba2d4926fba56579bc06722695d883d4ca1ea303a8f8c68080808080808080", + "f851a083516ffb4e11ae53aeb06863db356a756ef78cebe38802d69417148d369b986ca0351cee818717275e8e3962a54e1fa98e4b1b990aaad376b3d3e346cfb8fe4be6808080808080808080808080808080", + "f8d18080808080808080a05fd1f4eb5d94e828602fc1e19f03aae255efe360d81b9fc59099e21099327a7ba0219f99f84f7c704ca56575a4a3c59eaef6ee3ee3209cdc3ee4abe33c3c070c24a0afb31c1b8e96e6123e8a992d07ad7c0fe67648fbf5a6e4596f09be1affe80915a0572ca737439b54219818239a4420435dcd1546106e9f8074e00249eaf3127bb7a048862625cbbaffc46c2660d39f118c330e9fb3a3ceb2ca014e1ce285cf3e5e3ea0aec03c27f570694d19c676635151ee2abee5f19c2bf8c4701d5200df98d12ccd808080", + "f90211a008159cadb4be04f22dc7d9ee2f553720d7376797be70180f9860d9e6678d2b5fa0ba8725a1ca2023b727390f3fdb6dbddc51f23f95688c62a965a2d92d592dc074a0d27651ea59c6b3c5a7aa0b85d72b738d67a4cb9a1b6641e27fce330c12cbcc90a0448680ef94e3a42fa2045aa621bd984539c977927828b1690cd3f490f8f28202a0ddfa57de1a8e6477bfca9e07f7d567e8f9ead53e12dd6b26e46af8bb9ab2d42ba098de76237020aed42e4764dacf726cab9549f279a811158ef7d0a1865da49981a05ee4fbdd47c61ae9d9af574b90b3b14cbe146b3a377b6168662393a5799c53b0a05a710ac33be934301f2ec6d0d0eb911112c9ad90a783f1be56b3ea7a7751e856a0bf6171383c71ae373d3f86f4c1bf2bf0b34e117ea83ee8cdb0127e6f995b1b72a090cd396cf68946d396acc5b6b52474360ff402f21b622131b5e7ba0d370c8c07a06eb177bcd78f1db1138999efd996a1bfa9b32fe044b098bb0f8812798bfcad59a05cb6ed66df666831a1f9e01d2c809020d162024e066d5aa1248c4eb8137e4a71a020289d8787475543406639e2a9757c3e271b4947117ba617cd3f4791a7a0bd9aa0306aba70af61802a577df56b1dbe4bc93658e75fc6300f53e80ec8fd4f9fd8b6a0c88b6ebfdee7e968ef2f0eeec5d9d9001111df281a806b2326499472f3705937a0aa2a3290eda38ae66d13186f452410332f1a0e30ee9c5719cd420871779b830380", + "f87a20b87702f8740181eb8473a20d008507e172a822825208940ff71973b5243005b192d5bcf552fc2532b7bdec88015842095ebc400080c080a07a955ea50e980729226f547245bcf1e0e4ab69467cf82490a4bb0dbe9f35ae84a05f17d1a8f65b60905535ef0c36a8ec6d560c355949aeee2d3ab9791e7fe18fb4" + ], + "index": 179 +} diff --git a/bolt-contracts/test/testdata/tx_mpt_proof_20817618_1.json b/bolt-contracts/test/testdata/tx_mpt_proof_20817618_1.json new file mode 100644 index 000000000..168a80b8f --- /dev/null +++ b/bolt-contracts/test/testdata/tx_mpt_proof_20817618_1.json @@ -0,0 +1,10 @@ +{ + "txHash": "0x7387d381729e956b176a46c892ade005b674f3c1c700eeae7fd5212fd87cf85d", + "root": "0xb152ed53b02e03a51211b1f1260d58f5a90ff87a6f209954bd70e8a108112c13", + "proof": [ + "f90131a08054ac06deeff9e3269b58b3cf8cfead2012586326e2d51edfdc64603ea92fcfa0c6974714547104b94fac6f0da4e95b50534d960d1ee5e1f21932a212d9a4f42ca052dae1479e618740ffff9885fe9b55a17026903922d16cfd4b1c0733b18f2f24a0acbe9dc865421d745cb159af2cfcd8be5deaf64683d19c77857e5fc25744ac2ca00a6a250aff5ab4711dc9cea5b812f8080caceafa95d5ec578d43285ab27fd562a09a719127a63e2242a33fc5b9232c0d87892c8314c3ed5933527cb6fb95f50278a018cfbac9d7ed346d221124c0e28dfd4e92be373f3170cc62421327084e2bc221a0b74eae762f631f76b434632d03de4862d45e2d5c6a23f5d8b6b4fb806f0070d7a00cc848fcc361f3e01dd562909867bc8a7ab1d329aab2410a500a888f1b6f968a8080808080808080", + "f90211a089437c8fb79d4e0278f780cd90e1d47d2c25096271a1a680e762077778f498e5a065f5057f982b971e6624cfbb2d2acab8ae4a10425bd81b4401b69bc1a2ab31d6a0418f8fc6dceed29c186fa782f71f88f91b133c9070ec598eff02cbcf93d7d634a0d26549a8dd9285213e60286a8e2f8714c86a02bd13939c33fedde54c505a5573a02755be69f4b8e2560a1e4c8476dda8cb80334118bcbeb1f4621ee96aa7f8a418a0ef2ceefc457b6af22d2da599e6017e3ffe7c64dd6a229cad956189ddfeba521ba0df34b08748c2fad457380901f01d06550edab0fb2f176b85fe1b8e8767b58b08a07d30d5330a32f2831e471ecd3249d9561a6ebb91c17627b3cf836d77c9e2f8b3a087e09e649a43bc9c669e21844332f14d207c13cc4455ace13580bf6ef483a0eea005a73cbf870ca0c564dfa07b9f90cdc7d3e47f8545f6f06b9556e5a6fcf30f22a0eebdad0cdb60e30a3b3626d77ece2219a9f25eea0d8c2bb8de751fd727a371f1a0adc316aafc459d35adea06c3b4df1929fe9e48e043b2adc80154666634038352a04cc9eef47849e1aff9e18af286a80a0a72bfdb67c4d9ccad3866806993caa3b0a0fa37400267248ba993971f439e7cd650c0b5a65477e08b5df55c1b4159d23337a066314a563a10591582bdb2f69e0cb6160a2faa91d73a00aad0b54d7e24c6f3cda0376f6e26446a1600cbaa22f7f611482c109f149cf0d27f9f1bc48664b4d21c2d80", + "f8b020b8adf8ab8233858502de59ddef82b59b94dac17f958d2ee523a2206206994597c13d831ec780b844a9059cbb000000000000000000000000331b7670016c6be29d0ac211f6f8aceeb38f0d42000000000000000000000000000000000000000000000000000000000726bbf826a0abf062f0d6ebbb4910392d3bdac921b384f12aea2aa43169a2e866674ae90382a02849acdddc6d86036169e250dea502db87e9a91d6e1ffe8a6a30e54fbb327c5e" + ], + "index": 77 +} diff --git a/bolt-contracts/test/testdata/tx_mpt_proof_20817618_2.json b/bolt-contracts/test/testdata/tx_mpt_proof_20817618_2.json new file mode 100644 index 000000000..3b4f33f65 --- /dev/null +++ b/bolt-contracts/test/testdata/tx_mpt_proof_20817618_2.json @@ -0,0 +1,10 @@ +{ + "txHash": "0x9646df26d6c781600205f63717ffc428c78f4e17cf65511c9ce1893bfcc8939e", + "root": "0xb152ed53b02e03a51211b1f1260d58f5a90ff87a6f209954bd70e8a108112c13", + "proof": [ + "f90131a08054ac06deeff9e3269b58b3cf8cfead2012586326e2d51edfdc64603ea92fcfa0c6974714547104b94fac6f0da4e95b50534d960d1ee5e1f21932a212d9a4f42ca052dae1479e618740ffff9885fe9b55a17026903922d16cfd4b1c0733b18f2f24a0acbe9dc865421d745cb159af2cfcd8be5deaf64683d19c77857e5fc25744ac2ca00a6a250aff5ab4711dc9cea5b812f8080caceafa95d5ec578d43285ab27fd562a09a719127a63e2242a33fc5b9232c0d87892c8314c3ed5933527cb6fb95f50278a018cfbac9d7ed346d221124c0e28dfd4e92be373f3170cc62421327084e2bc221a0b74eae762f631f76b434632d03de4862d45e2d5c6a23f5d8b6b4fb806f0070d7a00cc848fcc361f3e01dd562909867bc8a7ab1d329aab2410a500a888f1b6f968a8080808080808080", + "f90211a089437c8fb79d4e0278f780cd90e1d47d2c25096271a1a680e762077778f498e5a065f5057f982b971e6624cfbb2d2acab8ae4a10425bd81b4401b69bc1a2ab31d6a0418f8fc6dceed29c186fa782f71f88f91b133c9070ec598eff02cbcf93d7d634a0d26549a8dd9285213e60286a8e2f8714c86a02bd13939c33fedde54c505a5573a02755be69f4b8e2560a1e4c8476dda8cb80334118bcbeb1f4621ee96aa7f8a418a0ef2ceefc457b6af22d2da599e6017e3ffe7c64dd6a229cad956189ddfeba521ba0df34b08748c2fad457380901f01d06550edab0fb2f176b85fe1b8e8767b58b08a07d30d5330a32f2831e471ecd3249d9561a6ebb91c17627b3cf836d77c9e2f8b3a087e09e649a43bc9c669e21844332f14d207c13cc4455ace13580bf6ef483a0eea005a73cbf870ca0c564dfa07b9f90cdc7d3e47f8545f6f06b9556e5a6fcf30f22a0eebdad0cdb60e30a3b3626d77ece2219a9f25eea0d8c2bb8de751fd727a371f1a0adc316aafc459d35adea06c3b4df1929fe9e48e043b2adc80154666634038352a04cc9eef47849e1aff9e18af286a80a0a72bfdb67c4d9ccad3866806993caa3b0a0fa37400267248ba993971f439e7cd650c0b5a65477e08b5df55c1b4159d23337a066314a563a10591582bdb2f69e0cb6160a2faa91d73a00aad0b54d7e24c6f3cda0376f6e26446a1600cbaa22f7f611482c109f149cf0d27f9f1bc48664b4d21c2d80", + "f87220b86ff86d8233868504216ca9e78252089415086dc2a2ea37c844e554b65d17c5183f15ab74870326bfc40e1c008025a06db3917cf09951d0107da89bc605ae88623a7690647ac8a5bc3c1638c8c9de59a032a2121306e6a5e1713d99595a65c14cfd1b1978922173cf69bf5f2d4a0b1a60" + ], + "index": 78 +} diff --git a/bolt-contracts/test/testdata/tx_mpt_proof_20817618_3.json b/bolt-contracts/test/testdata/tx_mpt_proof_20817618_3.json new file mode 100644 index 000000000..5f8c1475d --- /dev/null +++ b/bolt-contracts/test/testdata/tx_mpt_proof_20817618_3.json @@ -0,0 +1,10 @@ +{ + "txHash": "0x134665d7d342a678a6c1033df5fe09a4f993708dfca7d1208f4b97662840efc9", + "root": "0xb152ed53b02e03a51211b1f1260d58f5a90ff87a6f209954bd70e8a108112c13", + "proof": [ + "f90131a08054ac06deeff9e3269b58b3cf8cfead2012586326e2d51edfdc64603ea92fcfa0c6974714547104b94fac6f0da4e95b50534d960d1ee5e1f21932a212d9a4f42ca052dae1479e618740ffff9885fe9b55a17026903922d16cfd4b1c0733b18f2f24a0acbe9dc865421d745cb159af2cfcd8be5deaf64683d19c77857e5fc25744ac2ca00a6a250aff5ab4711dc9cea5b812f8080caceafa95d5ec578d43285ab27fd562a09a719127a63e2242a33fc5b9232c0d87892c8314c3ed5933527cb6fb95f50278a018cfbac9d7ed346d221124c0e28dfd4e92be373f3170cc62421327084e2bc221a0b74eae762f631f76b434632d03de4862d45e2d5c6a23f5d8b6b4fb806f0070d7a00cc848fcc361f3e01dd562909867bc8a7ab1d329aab2410a500a888f1b6f968a8080808080808080", + "f90211a089437c8fb79d4e0278f780cd90e1d47d2c25096271a1a680e762077778f498e5a065f5057f982b971e6624cfbb2d2acab8ae4a10425bd81b4401b69bc1a2ab31d6a0418f8fc6dceed29c186fa782f71f88f91b133c9070ec598eff02cbcf93d7d634a0d26549a8dd9285213e60286a8e2f8714c86a02bd13939c33fedde54c505a5573a02755be69f4b8e2560a1e4c8476dda8cb80334118bcbeb1f4621ee96aa7f8a418a0ef2ceefc457b6af22d2da599e6017e3ffe7c64dd6a229cad956189ddfeba521ba0df34b08748c2fad457380901f01d06550edab0fb2f176b85fe1b8e8767b58b08a07d30d5330a32f2831e471ecd3249d9561a6ebb91c17627b3cf836d77c9e2f8b3a087e09e649a43bc9c669e21844332f14d207c13cc4455ace13580bf6ef483a0eea005a73cbf870ca0c564dfa07b9f90cdc7d3e47f8545f6f06b9556e5a6fcf30f22a0eebdad0cdb60e30a3b3626d77ece2219a9f25eea0d8c2bb8de751fd727a371f1a0adc316aafc459d35adea06c3b4df1929fe9e48e043b2adc80154666634038352a04cc9eef47849e1aff9e18af286a80a0a72bfdb67c4d9ccad3866806993caa3b0a0fa37400267248ba993971f439e7cd650c0b5a65477e08b5df55c1b4159d23337a066314a563a10591582bdb2f69e0cb6160a2faa91d73a00aad0b54d7e24c6f3cda0376f6e26446a1600cbaa22f7f611482c109f149cf0d27f9f1bc48664b4d21c2d80", + "f8b020b8adf8ab823387850340375aa882fa2b94dac17f958d2ee523a2206206994597c13d831ec780b844a9059cbb0000000000000000000000009b26d899b1ac6ec07488929c0fdc5b40455275480000000000000000000000000000000000000000000000000000000006e438a025a06194e5ecbf6742f5f7919b39950f452eb0024344977576bb8ceafeff4410e199a007569f10b4d88cd2e185fb30c10f7a507ee6badebfd65b7e4cb884ca8acc5e11" + ], + "index": 79 +} diff --git a/bolt-contracts/test/testdata/tx_mpt_proof_20817618_4.json b/bolt-contracts/test/testdata/tx_mpt_proof_20817618_4.json new file mode 100644 index 000000000..45a9a9ae7 --- /dev/null +++ b/bolt-contracts/test/testdata/tx_mpt_proof_20817618_4.json @@ -0,0 +1,10 @@ +{ + "txHash": "0x66cd8977690a1b8bcc4c0dccf4e241146d8ecd08f65aeaaa2b96002fa06a8b28", + "root": "0xb152ed53b02e03a51211b1f1260d58f5a90ff87a6f209954bd70e8a108112c13", + "proof": [ + "f90131a08054ac06deeff9e3269b58b3cf8cfead2012586326e2d51edfdc64603ea92fcfa0c6974714547104b94fac6f0da4e95b50534d960d1ee5e1f21932a212d9a4f42ca052dae1479e618740ffff9885fe9b55a17026903922d16cfd4b1c0733b18f2f24a0acbe9dc865421d745cb159af2cfcd8be5deaf64683d19c77857e5fc25744ac2ca00a6a250aff5ab4711dc9cea5b812f8080caceafa95d5ec578d43285ab27fd562a09a719127a63e2242a33fc5b9232c0d87892c8314c3ed5933527cb6fb95f50278a018cfbac9d7ed346d221124c0e28dfd4e92be373f3170cc62421327084e2bc221a0b74eae762f631f76b434632d03de4862d45e2d5c6a23f5d8b6b4fb806f0070d7a00cc848fcc361f3e01dd562909867bc8a7ab1d329aab2410a500a888f1b6f968a8080808080808080", + "f90211a035c6919f9a75bb19e1af47bef00f47c3f702234fe69a3c2e596711cb9aa129a9a0fa06ad9a8d7a5c5bb841ccdbda97bf92dafa1288837287eb7f1fefac33d00980a0197b18a0170309d9510ec708102c29dd0a06fbfe3a7ed8ec7aacf4cf5ab7a625a0a1be1616a0c134caba1d7d24e3bdb10ad529017eb0f3b98f64ef6a8ab3562674a03eaafff1a78cb8ae018ccd58e8c318e928666c6499ad08cdb693fbab847adc59a032d58358743500d2fa2383200527972324910b8ecee76a859003163aee609549a09f0da441bddf2bbb5fce05c8a07385d29a09e21e3423401bce63fcdfb85cc97ea0e464722c37f377e3999e77caa5d38bd6240b9462457e93faf2d3b1bda3d788dfa05149eee71192d612c65d00ac434ce8b6f2f7d4f4c77012e919a37f4fb9ebc4f5a019dcf5d5b265d9263758e66afb5c24d7fd36cbbf55b7ff7799f361230a066361a06166f16fd0dc59da6313987d228e6a1d178ad5b0f2d3bfaceafd45c09780020fa07e460796b13f8e4855b8d0aac4f517872aad6b5d8e6aadddd0a799c23cd9deaca09f8690880be03f5f355f2cbcc4a8925d1f8d3b42d082f874098ce41581fec299a0ae79026df571e097a3a9e181e49a2a392a3a01f2fb882398c0df93b3b0668641a0479579fc287b5558020279332ed9735dbfbcb46eaff28b0bdca8950f6eec7c30a0baa5c5d8f1b5dd556ab4149ba70d847727b8755b6bba972ae6791d1c8d9751f480", + "f87220b86ff86d8233888503439d541b8252089415086dc2a2ea37c844e554b65d17c5183f15ab7487027d84f7cd40008026a0d69512e3b47f74fc7d8a2c02f0497580299494544c53860198510433385d6658a043aed084197ff3a3b05e0a0020e4802da7f50c0078dff2e197ad4b0affb36929" + ], + "index": 80 +} diff --git a/bolt-contracts/test/testdata/tx_mpt_proof_20817618_5.json b/bolt-contracts/test/testdata/tx_mpt_proof_20817618_5.json new file mode 100644 index 000000000..6264b659d --- /dev/null +++ b/bolt-contracts/test/testdata/tx_mpt_proof_20817618_5.json @@ -0,0 +1,10 @@ +{ + "txHash": "0x5f505bec39cdbbe03b9566085e87a4fc19f9a3346e128d01fa0088a6b274f654", + "root": "0xb152ed53b02e03a51211b1f1260d58f5a90ff87a6f209954bd70e8a108112c13", + "proof": [ + "f90131a08054ac06deeff9e3269b58b3cf8cfead2012586326e2d51edfdc64603ea92fcfa0c6974714547104b94fac6f0da4e95b50534d960d1ee5e1f21932a212d9a4f42ca052dae1479e618740ffff9885fe9b55a17026903922d16cfd4b1c0733b18f2f24a0acbe9dc865421d745cb159af2cfcd8be5deaf64683d19c77857e5fc25744ac2ca00a6a250aff5ab4711dc9cea5b812f8080caceafa95d5ec578d43285ab27fd562a09a719127a63e2242a33fc5b9232c0d87892c8314c3ed5933527cb6fb95f50278a018cfbac9d7ed346d221124c0e28dfd4e92be373f3170cc62421327084e2bc221a0b74eae762f631f76b434632d03de4862d45e2d5c6a23f5d8b6b4fb806f0070d7a00cc848fcc361f3e01dd562909867bc8a7ab1d329aab2410a500a888f1b6f968a8080808080808080", + "f90211a035c6919f9a75bb19e1af47bef00f47c3f702234fe69a3c2e596711cb9aa129a9a0fa06ad9a8d7a5c5bb841ccdbda97bf92dafa1288837287eb7f1fefac33d00980a0197b18a0170309d9510ec708102c29dd0a06fbfe3a7ed8ec7aacf4cf5ab7a625a0a1be1616a0c134caba1d7d24e3bdb10ad529017eb0f3b98f64ef6a8ab3562674a03eaafff1a78cb8ae018ccd58e8c318e928666c6499ad08cdb693fbab847adc59a032d58358743500d2fa2383200527972324910b8ecee76a859003163aee609549a09f0da441bddf2bbb5fce05c8a07385d29a09e21e3423401bce63fcdfb85cc97ea0e464722c37f377e3999e77caa5d38bd6240b9462457e93faf2d3b1bda3d788dfa05149eee71192d612c65d00ac434ce8b6f2f7d4f4c77012e919a37f4fb9ebc4f5a019dcf5d5b265d9263758e66afb5c24d7fd36cbbf55b7ff7799f361230a066361a06166f16fd0dc59da6313987d228e6a1d178ad5b0f2d3bfaceafd45c09780020fa07e460796b13f8e4855b8d0aac4f517872aad6b5d8e6aadddd0a799c23cd9deaca09f8690880be03f5f355f2cbcc4a8925d1f8d3b42d082f874098ce41581fec299a0ae79026df571e097a3a9e181e49a2a392a3a01f2fb882398c0df93b3b0668641a0479579fc287b5558020279332ed9735dbfbcb46eaff28b0bdca8950f6eec7c30a0baa5c5d8f1b5dd556ab4149ba70d847727b8755b6bba972ae6791d1c8d9751f480", + "f87220b86ff86d82338985058af3f6c28252089415086dc2a2ea37c844e554b65d17c5183f15ab7487040a50523970008026a05bee04d6213c636154c3c2e87038ffdb3d384c23fb9af65fcc85a890c9cabfe6a02231204d5fe346497f035994acec2dece4efa5109d466e7ce834b7576c237f9e" + ], + "index": 81 +} diff --git a/bolt-sidecar/src/client/constraints_client.rs b/bolt-sidecar/src/client/constraints_client.rs index 90730361c..47c4f926c 100644 --- a/bolt-sidecar/src/client/constraints_client.rs +++ b/bolt-sidecar/src/client/constraints_client.rs @@ -255,7 +255,7 @@ impl ConstraintsApi for ConstraintsClient { mod tests { use reqwest::Url; - use crate::ConstraintsClient; + use super::ConstraintsClient; #[test] fn test_join_endpoints() { diff --git a/bolt-sidecar/src/primitives/commitment.rs b/bolt-sidecar/src/primitives/commitment.rs index 403808550..c8d69958e 100644 --- a/bolt-sidecar/src/primitives/commitment.rs +++ b/bolt-sidecar/src/primitives/commitment.rs @@ -261,8 +261,38 @@ impl ECDSASignatureExt for Signature { #[cfg(test)] mod tests { + use std::str::FromStr; + + use alloy::{ + hex, + primitives::{Address, Signature}, + }; + use super::{CommitmentRequest, InclusionRequest}; + #[test] + fn test_create_digest() { + let json_req = r#"{ + "slot": 633067, + "txs": ["0xf86b82016e84042343e0830f424094deaddeaddeaddeaddeaddeaddeaddeaddeaddead0780850344281a21a0e525fc31b5574722ff064bdd127c4441b0fc66de7dc44928e163cb68e9d807e5a00b3ec02fc1e34b0209f252369ad10b745cd5a51c88384a340f7a150d0e45e471"] + }"#; + + let req: InclusionRequest = serde_json::from_str(json_req).unwrap(); + let digest = req.digest(); + assert_eq!( + hex::encode(digest.as_slice()), + "52ecc7832625c3d107aaba5b55d4509b48cd9f4f7ce375d6696d09bbf3310525" + ); + + // Verify signature over the digest + let sig = Signature::from_str("0xcdd20b2abbd8cdfb77ec2608e1227f8ce0f66133b9d0ec0ea68102c2152b82193e3be0d6967b7c20b83e1a2530daa3a07713556541dc2aa16a46d922e6145a2b01").unwrap(); + let recovered = sig.recover_address_from_prehash(&digest).unwrap(); + assert_eq!( + recovered, + Address::from_str("0x27083ED52464625660f3e30Aa5B9C20A30D7E110").unwrap() + ); + } + #[test] fn test_deserialize_inclusion_request() { let json_req = r#"{