Commit 49dedea9 authored by Georgios Konstantopoulos's avatar Georgios Konstantopoulos Committed by GitHub

fix: Allow Multiple Fraud Proofs per State Root (#72)

* feat: allow multiple state transitioners per pre-state root by tx hash

* test: specify tx hash when finalizing fraud verification

* test: specify tx hash when recording gas spent

* test: add test for multiple fraud proofs per stateroot

* chore: remove redundant test

* fix up test indexing and mock interface
Co-authored-by: default avatarben-chain <ben@pseudonym.party>
parent 4a3ef8f6
...@@ -65,9 +65,9 @@ contract OVM_BondManager is iOVM_BondManager, Lib_AddressResolver { ...@@ -65,9 +65,9 @@ contract OVM_BondManager is iOVM_BondManager, Lib_AddressResolver {
********************/ ********************/
/// Adds `who` to the list of witnessProviders for the provided `preStateRoot`. /// Adds `who` to the list of witnessProviders for the provided `preStateRoot`.
function recordGasSpent(bytes32 _preStateRoot, address who, uint256 gasSpent) override public { function recordGasSpent(bytes32 _preStateRoot, bytes32 _txHash, address who, uint256 gasSpent) override public {
// The sender must be the transitioner that corresponds to the claimed pre-state root // The sender must be the transitioner that corresponds to the claimed pre-state root
address transitioner = address(iOVM_FraudVerifier(resolve("OVM_FraudVerifier")).getStateTransitioner(_preStateRoot)); address transitioner = address(iOVM_FraudVerifier(resolve("OVM_FraudVerifier")).getStateTransitioner(_preStateRoot, _txHash));
require(transitioner == msg.sender, Errors.ONLY_TRANSITIONER); require(transitioner == msg.sender, Errors.ONLY_TRANSITIONER);
witnessProviders[_preStateRoot].total += gasSpent; witnessProviders[_preStateRoot].total += gasSpent;
......
...@@ -9,10 +9,10 @@ import { Lib_AddressResolver } from "../../libraries/resolver/Lib_AddressResolve ...@@ -9,10 +9,10 @@ import { Lib_AddressResolver } from "../../libraries/resolver/Lib_AddressResolve
abstract contract OVM_FraudContributor is Lib_AddressResolver { abstract contract OVM_FraudContributor is Lib_AddressResolver {
/// Decorate your functions with this modifier to store how much total gas was /// Decorate your functions with this modifier to store how much total gas was
/// consumed by the sender, to reward users fairly /// consumed by the sender, to reward users fairly
modifier contributesToFraudProof(bytes32 preStateRoot) { modifier contributesToFraudProof(bytes32 preStateRoot, bytes32 txHash) {
uint startGas = gasleft(); uint startGas = gasleft();
_; _;
uint gasSpent = startGas - gasleft(); uint gasSpent = startGas - gasleft();
iOVM_BondManager(resolve('OVM_BondManager')).recordGasSpent(preStateRoot, msg.sender, gasSpent); iOVM_BondManager(resolve('OVM_BondManager')).recordGasSpent(preStateRoot, txHash, msg.sender, gasSpent);
} }
} }
...@@ -52,7 +52,8 @@ contract OVM_FraudVerifier is Lib_AddressResolver, OVM_FraudContributor, iOVM_Fr ...@@ -52,7 +52,8 @@ contract OVM_FraudVerifier is Lib_AddressResolver, OVM_FraudContributor, iOVM_Fr
* @return _transitioner Corresponding state transitioner contract. * @return _transitioner Corresponding state transitioner contract.
*/ */
function getStateTransitioner( function getStateTransitioner(
bytes32 _preStateRoot bytes32 _preStateRoot,
bytes32 _txHash
) )
override override
public public
...@@ -61,7 +62,7 @@ contract OVM_FraudVerifier is Lib_AddressResolver, OVM_FraudContributor, iOVM_Fr ...@@ -61,7 +62,7 @@ contract OVM_FraudVerifier is Lib_AddressResolver, OVM_FraudContributor, iOVM_Fr
iOVM_StateTransitioner _transitioner iOVM_StateTransitioner _transitioner
) )
{ {
return transitioners[_preStateRoot]; return transitioners[keccak256(abi.encodePacked(_preStateRoot, _txHash))];
} }
...@@ -90,9 +91,11 @@ contract OVM_FraudVerifier is Lib_AddressResolver, OVM_FraudContributor, iOVM_Fr ...@@ -90,9 +91,11 @@ contract OVM_FraudVerifier is Lib_AddressResolver, OVM_FraudContributor, iOVM_Fr
) )
override override
public public
contributesToFraudProof(_preStateRoot) contributesToFraudProof(_preStateRoot, Lib_OVMCodec.hashTransaction(_transaction))
{ {
if (_hasStateTransitioner(_preStateRoot)) { bytes32 _txHash = Lib_OVMCodec.hashTransaction(_transaction);
if (_hasStateTransitioner(_preStateRoot, _txHash)) {
return; return;
} }
...@@ -123,13 +126,18 @@ contract OVM_FraudVerifier is Lib_AddressResolver, OVM_FraudContributor, iOVM_Fr ...@@ -123,13 +126,18 @@ contract OVM_FraudVerifier is Lib_AddressResolver, OVM_FraudContributor, iOVM_Fr
"Pre-state root global index must equal to the transaction root global index." "Pre-state root global index must equal to the transaction root global index."
); );
transitioners[_preStateRoot] = iOVM_StateTransitionerFactory( deployTransitioner(_preStateRoot, _txHash, _preStateRootProof.index);
}
// NB: Stack too deep :/
function deployTransitioner(bytes32 _preStateRoot, bytes32 _txHash, uint256 _stateTransitionIndex) private {
transitioners[keccak256(abi.encodePacked(_preStateRoot, _txHash))] = iOVM_StateTransitionerFactory(
resolve("OVM_StateTransitionerFactory") resolve("OVM_StateTransitionerFactory")
).create( ).create(
address(libAddressManager), address(libAddressManager),
_preStateRootProof.index, _stateTransitionIndex,
_preStateRoot, _preStateRoot,
Lib_OVMCodec.hashTransaction(_transaction) _txHash
); );
} }
...@@ -138,6 +146,7 @@ contract OVM_FraudVerifier is Lib_AddressResolver, OVM_FraudContributor, iOVM_Fr ...@@ -138,6 +146,7 @@ contract OVM_FraudVerifier is Lib_AddressResolver, OVM_FraudContributor, iOVM_Fr
* @param _preStateRoot State root before the fraudulent transaction. * @param _preStateRoot State root before the fraudulent transaction.
* @param _preStateRootBatchHeader Batch header for the provided pre-state root. * @param _preStateRootBatchHeader Batch header for the provided pre-state root.
* @param _preStateRootProof Inclusion proof for the provided pre-state root. * @param _preStateRootProof Inclusion proof for the provided pre-state root.
* @param _txHash The transaction for the state root
* @param _postStateRoot State root after the fraudulent transaction. * @param _postStateRoot State root after the fraudulent transaction.
* @param _postStateRootBatchHeader Batch header for the provided post-state root. * @param _postStateRootBatchHeader Batch header for the provided post-state root.
* @param _postStateRootProof Inclusion proof for the provided post-state root. * @param _postStateRootProof Inclusion proof for the provided post-state root.
...@@ -146,15 +155,16 @@ contract OVM_FraudVerifier is Lib_AddressResolver, OVM_FraudContributor, iOVM_Fr ...@@ -146,15 +155,16 @@ contract OVM_FraudVerifier is Lib_AddressResolver, OVM_FraudContributor, iOVM_Fr
bytes32 _preStateRoot, bytes32 _preStateRoot,
Lib_OVMCodec.ChainBatchHeader memory _preStateRootBatchHeader, Lib_OVMCodec.ChainBatchHeader memory _preStateRootBatchHeader,
Lib_OVMCodec.ChainInclusionProof memory _preStateRootProof, Lib_OVMCodec.ChainInclusionProof memory _preStateRootProof,
bytes32 _txHash,
bytes32 _postStateRoot, bytes32 _postStateRoot,
Lib_OVMCodec.ChainBatchHeader memory _postStateRootBatchHeader, Lib_OVMCodec.ChainBatchHeader memory _postStateRootBatchHeader,
Lib_OVMCodec.ChainInclusionProof memory _postStateRootProof Lib_OVMCodec.ChainInclusionProof memory _postStateRootProof
) )
override override
public public
contributesToFraudProof(_preStateRoot) contributesToFraudProof(_preStateRoot, _txHash)
{ {
iOVM_StateTransitioner transitioner = transitioners[_preStateRoot]; iOVM_StateTransitioner transitioner = getStateTransitioner(_preStateRoot, _txHash);
iOVM_StateCommitmentChain ovmStateCommitmentChain = iOVM_StateCommitmentChain(resolve("OVM_StateCommitmentChain")); iOVM_StateCommitmentChain ovmStateCommitmentChain = iOVM_StateCommitmentChain(resolve("OVM_StateCommitmentChain"));
iOVM_BondManager ovmBondManager = iOVM_BondManager(resolve("OVM_BondManager")); iOVM_BondManager ovmBondManager = iOVM_BondManager(resolve("OVM_BondManager"));
...@@ -192,6 +202,16 @@ contract OVM_FraudVerifier is Lib_AddressResolver, OVM_FraudContributor, iOVM_Fr ...@@ -192,6 +202,16 @@ contract OVM_FraudVerifier is Lib_AddressResolver, OVM_FraudContributor, iOVM_Fr
"State transition has not been proven fraudulent." "State transition has not been proven fraudulent."
); );
cancelStateTransition(_postStateRootBatchHeader, _preStateRoot);
}
// NB: Stack too deep :/
function cancelStateTransition(
Lib_OVMCodec.ChainBatchHeader memory _postStateRootBatchHeader,
bytes32 _preStateRoot
) private {
iOVM_StateCommitmentChain ovmStateCommitmentChain = iOVM_StateCommitmentChain(resolve("OVM_StateCommitmentChain"));
iOVM_BondManager ovmBondManager = iOVM_BondManager(resolve("OVM_BondManager"));
// delete the state batch // delete the state batch
ovmStateCommitmentChain.deleteStateBatch( ovmStateCommitmentChain.deleteStateBatch(
_postStateRootBatchHeader _postStateRootBatchHeader
...@@ -219,7 +239,8 @@ contract OVM_FraudVerifier is Lib_AddressResolver, OVM_FraudContributor, iOVM_Fr ...@@ -219,7 +239,8 @@ contract OVM_FraudVerifier is Lib_AddressResolver, OVM_FraudContributor, iOVM_Fr
* @return _exists Whether or not we already have a transitioner for the root. * @return _exists Whether or not we already have a transitioner for the root.
*/ */
function _hasStateTransitioner( function _hasStateTransitioner(
bytes32 _preStateRoot bytes32 _preStateRoot,
bytes32 _txHash
) )
internal internal
view view
...@@ -227,6 +248,6 @@ contract OVM_FraudVerifier is Lib_AddressResolver, OVM_FraudContributor, iOVM_Fr ...@@ -227,6 +248,6 @@ contract OVM_FraudVerifier is Lib_AddressResolver, OVM_FraudContributor, iOVM_Fr
bool _exists bool _exists
) )
{ {
return address(transitioners[_preStateRoot]) != address(0); return address(getStateTransitioner(_preStateRoot, _txHash)) != address(0);
} }
} }
...@@ -167,7 +167,7 @@ contract OVM_StateTransitioner is Lib_AddressResolver, OVM_FraudContributor, iOV ...@@ -167,7 +167,7 @@ contract OVM_StateTransitioner is Lib_AddressResolver, OVM_FraudContributor, iOV
override override
public public
onlyDuringPhase(TransitionPhase.PRE_EXECUTION) onlyDuringPhase(TransitionPhase.PRE_EXECUTION)
contributesToFraudProof(preStateRoot) contributesToFraudProof(preStateRoot, transactionHash)
{ {
// Exit quickly to avoid unnecessary work. // Exit quickly to avoid unnecessary work.
require( require(
...@@ -215,7 +215,7 @@ contract OVM_StateTransitioner is Lib_AddressResolver, OVM_FraudContributor, iOV ...@@ -215,7 +215,7 @@ contract OVM_StateTransitioner is Lib_AddressResolver, OVM_FraudContributor, iOV
override override
public public
onlyDuringPhase(TransitionPhase.PRE_EXECUTION) onlyDuringPhase(TransitionPhase.PRE_EXECUTION)
contributesToFraudProof(preStateRoot) contributesToFraudProof(preStateRoot, transactionHash)
{ {
// Exit quickly to avoid unnecessary work. // Exit quickly to avoid unnecessary work.
require( require(
...@@ -251,7 +251,7 @@ contract OVM_StateTransitioner is Lib_AddressResolver, OVM_FraudContributor, iOV ...@@ -251,7 +251,7 @@ contract OVM_StateTransitioner is Lib_AddressResolver, OVM_FraudContributor, iOV
override override
public public
onlyDuringPhase(TransitionPhase.PRE_EXECUTION) onlyDuringPhase(TransitionPhase.PRE_EXECUTION)
contributesToFraudProof(preStateRoot) contributesToFraudProof(preStateRoot, transactionHash)
{ {
// Exit quickly to avoid unnecessary work. // Exit quickly to avoid unnecessary work.
require( require(
...@@ -307,7 +307,7 @@ contract OVM_StateTransitioner is Lib_AddressResolver, OVM_FraudContributor, iOV ...@@ -307,7 +307,7 @@ contract OVM_StateTransitioner is Lib_AddressResolver, OVM_FraudContributor, iOV
override override
public public
onlyDuringPhase(TransitionPhase.PRE_EXECUTION) onlyDuringPhase(TransitionPhase.PRE_EXECUTION)
contributesToFraudProof(preStateRoot) contributesToFraudProof(preStateRoot, transactionHash)
{ {
require( require(
Lib_OVMCodec.hashTransaction(_transaction) == transactionHash, Lib_OVMCodec.hashTransaction(_transaction) == transactionHash,
...@@ -346,7 +346,7 @@ contract OVM_StateTransitioner is Lib_AddressResolver, OVM_FraudContributor, iOV ...@@ -346,7 +346,7 @@ contract OVM_StateTransitioner is Lib_AddressResolver, OVM_FraudContributor, iOV
override override
public public
onlyDuringPhase(TransitionPhase.POST_EXECUTION) onlyDuringPhase(TransitionPhase.POST_EXECUTION)
contributesToFraudProof(preStateRoot) contributesToFraudProof(preStateRoot, transactionHash)
{ {
require( require(
ovmStateManager.commitAccount(_ovmContractAddress) == true, ovmStateManager.commitAccount(_ovmContractAddress) == true,
...@@ -381,7 +381,7 @@ contract OVM_StateTransitioner is Lib_AddressResolver, OVM_FraudContributor, iOV ...@@ -381,7 +381,7 @@ contract OVM_StateTransitioner is Lib_AddressResolver, OVM_FraudContributor, iOV
override override
public public
onlyDuringPhase(TransitionPhase.POST_EXECUTION) onlyDuringPhase(TransitionPhase.POST_EXECUTION)
contributesToFraudProof(preStateRoot) contributesToFraudProof(preStateRoot, transactionHash)
{ {
require( require(
ovmStateManager.commitContractStorage(_ovmContractAddress, _key) == true, ovmStateManager.commitContractStorage(_ovmContractAddress, _key) == true,
......
...@@ -76,6 +76,7 @@ interface iOVM_BondManager { ...@@ -76,6 +76,7 @@ interface iOVM_BondManager {
function recordGasSpent( function recordGasSpent(
bytes32 _preStateRoot, bytes32 _preStateRoot,
bytes32 _txHash,
address _who, address _who,
uint256 _gasSpent uint256 _gasSpent
) external; ) external;
......
...@@ -17,7 +17,7 @@ interface iOVM_FraudVerifier { ...@@ -17,7 +17,7 @@ interface iOVM_FraudVerifier {
* Public Functions: Transition Status * * Public Functions: Transition Status *
***************************************/ ***************************************/
function getStateTransitioner(bytes32 _preStateRoot) external view returns (iOVM_StateTransitioner _transitioner); function getStateTransitioner(bytes32 _preStateRoot, bytes32 _txHash) external view returns (iOVM_StateTransitioner _transitioner);
/**************************************** /****************************************
...@@ -38,6 +38,7 @@ interface iOVM_FraudVerifier { ...@@ -38,6 +38,7 @@ interface iOVM_FraudVerifier {
bytes32 _preStateRoot, bytes32 _preStateRoot,
Lib_OVMCodec.ChainBatchHeader calldata _preStateRootBatchHeader, Lib_OVMCodec.ChainBatchHeader calldata _preStateRootBatchHeader,
Lib_OVMCodec.ChainInclusionProof calldata _preStateRootProof, Lib_OVMCodec.ChainInclusionProof calldata _preStateRootProof,
bytes32 _txHash,
bytes32 _postStateRoot, bytes32 _postStateRoot,
Lib_OVMCodec.ChainBatchHeader calldata _postStateRootBatchHeader, Lib_OVMCodec.ChainBatchHeader calldata _postStateRootBatchHeader,
Lib_OVMCodec.ChainInclusionProof calldata _postStateRootProof Lib_OVMCodec.ChainInclusionProof calldata _postStateRootProof
......
...@@ -10,6 +10,7 @@ import { iOVM_BondManager } from "../../iOVM/verification/iOVM_BondManager.sol"; ...@@ -10,6 +10,7 @@ import { iOVM_BondManager } from "../../iOVM/verification/iOVM_BondManager.sol";
contract mockOVM_BondManager is iOVM_BondManager { contract mockOVM_BondManager is iOVM_BondManager {
function recordGasSpent( function recordGasSpent(
bytes32 _preStateRoot, bytes32 _preStateRoot,
bytes32 _txHash,
address _who, address _who,
uint256 _gasSpent uint256 _gasSpent
) )
......
...@@ -12,12 +12,21 @@ contract Mock_FraudVerifier { ...@@ -12,12 +12,21 @@ contract Mock_FraudVerifier {
bondManager = _bondManager; bondManager = _bondManager;
} }
function setStateTransitioner(bytes32 preStateRoot, address addr) public { function setStateTransitioner(bytes32 preStateRoot, bytes32 txHash, address addr) public {
transitioners[preStateRoot] = addr; transitioners[keccak256(abi.encodePacked(preStateRoot, txHash))] = addr;
} }
function getStateTransitioner(bytes32 preStateRoot) public view returns (address) { function getStateTransitioner(
return transitioners[preStateRoot]; bytes32 _preStateRoot,
bytes32 _txHash
)
public
view
returns (
address
)
{
return transitioners[keccak256(abi.encodePacked(_preStateRoot, _txHash))];
} }
function finalize(bytes32 _preStateRoot, address publisher, uint256 timestamp) public { function finalize(bytes32 _preStateRoot, address publisher, uint256 timestamp) public {
......
...@@ -22,6 +22,7 @@ describe('BondManager', () => { ...@@ -22,6 +22,7 @@ describe('BondManager', () => {
const witnessProvider2 = wallets[5] const witnessProvider2 = wallets[5]
const sender = wallets[0].address const sender = wallets[0].address
const txHash = ethers.constants.HashZero
const ONE_WEEK = 3600 * 24 * 7 const ONE_WEEK = 3600 * 24 * 7
...@@ -56,6 +57,7 @@ describe('BondManager', () => { ...@@ -56,6 +57,7 @@ describe('BondManager', () => {
).deploy() ).deploy()
await fraudVerifier.setStateTransitioner( await fraudVerifier.setStateTransitioner(
preStateRoot, preStateRoot,
txHash,
stateTransitioner.address stateTransitioner.address
) )
await manager.setAddress('OVM_FraudVerifier', fraudVerifier.address) await manager.setAddress('OVM_FraudVerifier', fraudVerifier.address)
...@@ -143,13 +145,28 @@ describe('BondManager', () => { ...@@ -143,13 +145,28 @@ describe('BondManager', () => {
beforeEach(async () => { beforeEach(async () => {
await bondManager await bondManager
.connect(stateTransitioner) .connect(stateTransitioner)
.recordGasSpent(preStateRoot, witnessProvider.address, user1Gas[0]) .recordGasSpent(
preStateRoot,
txHash,
witnessProvider.address,
user1Gas[0]
)
await bondManager await bondManager
.connect(stateTransitioner) .connect(stateTransitioner)
.recordGasSpent(preStateRoot, witnessProvider.address, user1Gas[1]) .recordGasSpent(
preStateRoot,
txHash,
witnessProvider.address,
user1Gas[1]
)
await bondManager await bondManager
.connect(stateTransitioner) .connect(stateTransitioner)
.recordGasSpent(preStateRoot, witnessProvider2.address, user2Gas) .recordGasSpent(
preStateRoot,
txHash,
witnessProvider2.address,
user2Gas
)
}) })
describe('post witnesses', () => { describe('post witnesses', () => {
...@@ -167,7 +184,12 @@ describe('BondManager', () => { ...@@ -167,7 +184,12 @@ describe('BondManager', () => {
it('cannot post witnesses from non-transitioners for that state root', async () => { it('cannot post witnesses from non-transitioners for that state root', async () => {
await expect( await expect(
bondManager.recordGasSpent(preStateRoot, witnessProvider.address, 100) bondManager.recordGasSpent(
preStateRoot,
txHash,
witnessProvider.address,
100
)
).to.be.revertedWith(Errors.ONLY_TRANSITIONER) ).to.be.revertedWith(Errors.ONLY_TRANSITIONER)
}) })
}) })
......
...@@ -100,7 +100,8 @@ describe('OVM_ProxyEOA', () => { ...@@ -100,7 +100,8 @@ describe('OVM_ProxyEOA', () => {
}) })
}) })
describe('upgrade()', () => { describe('upgrade()', () => {
const implSlotKey = '0xdeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddead' const implSlotKey =
'0xdeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddead'
it(`should upgrade the proxy implementation`, async () => { it(`should upgrade the proxy implementation`, async () => {
const newImpl = `0x${'81'.repeat(20)}` const newImpl = `0x${'81'.repeat(20)}`
const newImplBytes32 = addrToBytes32(newImpl) const newImplBytes32 = addrToBytes32(newImpl)
......
...@@ -14,18 +14,21 @@ import { ...@@ -14,18 +14,21 @@ import {
DUMMY_OVM_TRANSACTIONS, DUMMY_OVM_TRANSACTIONS,
NON_NULL_BYTES32, NON_NULL_BYTES32,
NULL_BYTES32, NULL_BYTES32,
hashTransaction,
} from '../../../helpers' } from '../../../helpers'
const DUMMY_TX_CHAIN_ELEMENTS = [...Array(10)].map(() => { const DUMMY_TX_CHAIN_ELEMENTS = [...Array(10).keys()].map((i) => {
return { return {
isSequenced: false, isSequenced: false,
queueIndex: BigNumber.from(0), queueIndex: BigNumber.from(0),
timestamp: BigNumber.from(0), timestamp: BigNumber.from(i),
blockNumber: BigNumber.from(0), blockNumber: BigNumber.from(0),
txData: NULL_BYTES32, txData: NULL_BYTES32,
} }
}) })
const DUMMY_HASH = hashTransaction(DUMMY_OVM_TRANSACTIONS[0])
const DUMMY_BATCH_PROOFS_WITH_INDEX = [ const DUMMY_BATCH_PROOFS_WITH_INDEX = [
{ {
index: 11, index: 11,
...@@ -181,7 +184,10 @@ describe('OVM_FraudVerifier', () => { ...@@ -181,7 +184,10 @@ describe('OVM_FraudVerifier', () => {
).to.not.be.reverted ).to.not.be.reverted
expect( expect(
await OVM_FraudVerifier.getStateTransitioner(NULL_BYTES32) await OVM_FraudVerifier.getStateTransitioner(
NULL_BYTES32,
DUMMY_HASH
)
).to.equal(Mock__OVM_StateTransitioner.address) ).to.equal(Mock__OVM_StateTransitioner.address)
}) })
...@@ -233,6 +239,7 @@ describe('OVM_FraudVerifier', () => { ...@@ -233,6 +239,7 @@ describe('OVM_FraudVerifier', () => {
NULL_BYTES32, NULL_BYTES32,
DUMMY_BATCH_HEADERS[0], DUMMY_BATCH_HEADERS[0],
DUMMY_BATCH_PROOFS[0], DUMMY_BATCH_PROOFS[0],
DUMMY_HASH,
NON_NULL_BYTES32, NON_NULL_BYTES32,
DUMMY_BATCH_HEADERS[0], DUMMY_BATCH_HEADERS[0],
DUMMY_BATCH_PROOFS[0] DUMMY_BATCH_PROOFS[0]
...@@ -260,6 +267,7 @@ describe('OVM_FraudVerifier', () => { ...@@ -260,6 +267,7 @@ describe('OVM_FraudVerifier', () => {
NULL_BYTES32, NULL_BYTES32,
DUMMY_BATCH_HEADERS[0], DUMMY_BATCH_HEADERS[0],
DUMMY_BATCH_PROOFS[0], DUMMY_BATCH_PROOFS[0],
DUMMY_HASH,
NON_NULL_BYTES32, NON_NULL_BYTES32,
DUMMY_BATCH_HEADERS[0], DUMMY_BATCH_HEADERS[0],
batchProof batchProof
...@@ -287,6 +295,7 @@ describe('OVM_FraudVerifier', () => { ...@@ -287,6 +295,7 @@ describe('OVM_FraudVerifier', () => {
NULL_BYTES32, NULL_BYTES32,
DUMMY_BATCH_HEADERS[0], DUMMY_BATCH_HEADERS[0],
DUMMY_BATCH_PROOFS[0], DUMMY_BATCH_PROOFS[0],
DUMMY_HASH,
NON_NULL_BYTES32, NON_NULL_BYTES32,
DUMMY_BATCH_HEADERS[0], DUMMY_BATCH_HEADERS[0],
batchProof batchProof
...@@ -317,6 +326,7 @@ describe('OVM_FraudVerifier', () => { ...@@ -317,6 +326,7 @@ describe('OVM_FraudVerifier', () => {
NULL_BYTES32, NULL_BYTES32,
DUMMY_BATCH_HEADERS[0], DUMMY_BATCH_HEADERS[0],
DUMMY_BATCH_PROOFS[0], DUMMY_BATCH_PROOFS[0],
DUMMY_HASH,
NON_NULL_BYTES32, NON_NULL_BYTES32,
DUMMY_BATCH_HEADERS[0], DUMMY_BATCH_HEADERS[0],
batchProof batchProof
...@@ -345,6 +355,7 @@ describe('OVM_FraudVerifier', () => { ...@@ -345,6 +355,7 @@ describe('OVM_FraudVerifier', () => {
NULL_BYTES32, NULL_BYTES32,
DUMMY_BATCH_HEADERS[0], DUMMY_BATCH_HEADERS[0],
DUMMY_BATCH_PROOFS[0], DUMMY_BATCH_PROOFS[0],
DUMMY_HASH,
NON_NULL_BYTES32, NON_NULL_BYTES32,
DUMMY_BATCH_HEADERS[0], DUMMY_BATCH_HEADERS[0],
batchProof batchProof
...@@ -367,6 +378,7 @@ describe('OVM_FraudVerifier', () => { ...@@ -367,6 +378,7 @@ describe('OVM_FraudVerifier', () => {
NULL_BYTES32, NULL_BYTES32,
DUMMY_BATCH_HEADERS[0], DUMMY_BATCH_HEADERS[0],
DUMMY_BATCH_PROOFS[0], DUMMY_BATCH_PROOFS[0],
DUMMY_HASH,
NON_NULL_BYTES32, NON_NULL_BYTES32,
DUMMY_BATCH_HEADERS[0], DUMMY_BATCH_HEADERS[0],
batchProof batchProof
...@@ -387,6 +399,155 @@ describe('OVM_FraudVerifier', () => { ...@@ -387,6 +399,155 @@ describe('OVM_FraudVerifier', () => {
}) })
}) })
}) })
describe('multiple fraud proofs for the same pre-execution state', () => {
let state2: any
let DUMMY_HASH_2 = hashTransaction(DUMMY_OVM_TRANSACTIONS[1])
beforeEach(async () => {
state2 = smockit(
await ethers.getContractFactory('OVM_StateTransitioner')
)
Mock__OVM_StateTransitionerFactory.smocked.create.will.return.with(
state2.address
)
Mock__OVM_StateTransitioner.smocked.getPostStateRoot.will.return.with(
NULL_BYTES32
)
state2.smocked.getPostStateRoot.will.return.with(NULL_BYTES32)
})
it('creates multiple state transitioners per tx hash', async () => {
await expect(
OVM_FraudVerifier.initializeFraudVerification(
NULL_BYTES32,
DUMMY_BATCH_HEADERS[0],
DUMMY_BATCH_PROOFS[0],
DUMMY_OVM_TRANSACTIONS[1],
DUMMY_TX_CHAIN_ELEMENTS[0],
DUMMY_BATCH_HEADERS[0],
DUMMY_BATCH_PROOFS[0]
)
).to.not.be.reverted
expect(
await OVM_FraudVerifier.getStateTransitioner(
NULL_BYTES32,
DUMMY_HASH
)
).to.equal(Mock__OVM_StateTransitioner.address)
expect(
await OVM_FraudVerifier.getStateTransitioner(
NULL_BYTES32,
DUMMY_HASH_2
)
).to.equal(state2.address)
})
const batchProof = {
...DUMMY_BATCH_PROOFS[0],
index: DUMMY_BATCH_PROOFS[0].index + 1,
}
it('Case 1: allows proving fraud on the same pre-state root twice', async () => {
// finalize previous fraud
await OVM_FraudVerifier.finalizeFraudVerification(
NULL_BYTES32,
DUMMY_BATCH_HEADERS[0],
DUMMY_BATCH_PROOFS[0],
DUMMY_HASH,
NON_NULL_BYTES32,
DUMMY_BATCH_HEADERS[0],
batchProof
)
// start new fraud
await OVM_FraudVerifier.initializeFraudVerification(
NULL_BYTES32,
DUMMY_BATCH_HEADERS[0],
DUMMY_BATCH_PROOFS[0],
DUMMY_OVM_TRANSACTIONS[1],
DUMMY_TX_CHAIN_ELEMENTS[1],
DUMMY_BATCH_HEADERS[1],
DUMMY_BATCH_PROOFS[0]
)
// finalize it as well
await OVM_FraudVerifier.finalizeFraudVerification(
NULL_BYTES32,
DUMMY_BATCH_HEADERS[0],
DUMMY_BATCH_PROOFS[0],
DUMMY_HASH_2,
NON_NULL_BYTES32,
DUMMY_BATCH_HEADERS[1],
batchProof
)
// the new batch was deleted
expect(
Mock__OVM_StateCommitmentChain.smocked.deleteStateBatch.calls[0]
).to.deep.equal([
Object.values(DUMMY_BATCH_HEADERS[1]).map((value) => {
return Number.isInteger(value) ? BigNumber.from(value) : value
}),
])
})
it('Case 2: does not get blocked by the first transitioner', async () => {
// start new fraud
await OVM_FraudVerifier.initializeFraudVerification(
NULL_BYTES32,
DUMMY_BATCH_HEADERS[0],
DUMMY_BATCH_PROOFS[0],
DUMMY_OVM_TRANSACTIONS[1],
DUMMY_TX_CHAIN_ELEMENTS[1],
DUMMY_BATCH_HEADERS[1],
DUMMY_BATCH_PROOFS[0]
)
// finalize the new fraud first
await OVM_FraudVerifier.finalizeFraudVerification(
NULL_BYTES32,
DUMMY_BATCH_HEADERS[0],
DUMMY_BATCH_PROOFS[0],
DUMMY_HASH_2,
NON_NULL_BYTES32,
DUMMY_BATCH_HEADERS[1],
batchProof
)
// the new fraud's batch was deleted
expect(
Mock__OVM_StateCommitmentChain.smocked.deleteStateBatch.calls[0]
).to.deep.equal([
Object.values(DUMMY_BATCH_HEADERS[1]).map((value) => {
return Number.isInteger(value) ? BigNumber.from(value) : value
}),
])
// finalize previous fraud
await OVM_FraudVerifier.finalizeFraudVerification(
NULL_BYTES32,
DUMMY_BATCH_HEADERS[0],
DUMMY_BATCH_PROOFS[0],
DUMMY_HASH,
NON_NULL_BYTES32,
DUMMY_BATCH_HEADERS[0],
batchProof
)
// the old fraud's batch was deleted
expect(
Mock__OVM_StateCommitmentChain.smocked.deleteStateBatch.calls[0]
).to.deep.equal([
Object.values(DUMMY_BATCH_HEADERS[0]).map((value) => {
return Number.isInteger(value) ? BigNumber.from(value) : value
}),
])
})
})
}) })
}) })
}) })
...@@ -12,6 +12,16 @@ export const DUMMY_BATCH_HEADERS = [ ...@@ -12,6 +12,16 @@ export const DUMMY_BATCH_HEADERS = [
[NULL_BYTES32, NON_ZERO_ADDRESS] [NULL_BYTES32, NON_ZERO_ADDRESS]
), ),
}, },
{
batchIndex: 1,
batchRoot: NULL_BYTES32,
batchSize: 0,
prevTotalElements: 0,
extraData: ethers.utils.defaultAbiCoder.encode(
['uint256', 'address'],
[NULL_BYTES32, NON_ZERO_ADDRESS]
),
},
] ]
export const DUMMY_BATCH_PROOFS = [ export const DUMMY_BATCH_PROOFS = [
...@@ -19,4 +29,8 @@ export const DUMMY_BATCH_PROOFS = [ ...@@ -19,4 +29,8 @@ export const DUMMY_BATCH_PROOFS = [
index: 0, index: 0,
siblings: [NULL_BYTES32], siblings: [NULL_BYTES32],
}, },
{
index: 1,
siblings: [NULL_BYTES32],
},
] ]
import { ZERO_ADDRESS, NULL_BYTES32 } from '../constants' import { ZERO_ADDRESS, NULL_BYTES32 } from '../constants'
import { ethers } from 'ethers'
export const DUMMY_OVM_TRANSACTIONS = [ export interface Transaction {
{ timestamp: number
timestamp: 0, blockNumber: number
l1QueueOrigin: number
l1TxOrigin: string
entrypoint: string
gasLimit: number
data: string
}
export const DUMMY_OVM_TRANSACTIONS: Array<Transaction> = [
...Array(10).keys(),
].map((i) => {
return {
timestamp: i,
blockNumber: 0, blockNumber: 0,
l1QueueOrigin: 0, l1QueueOrigin: 0,
l1TxOrigin: ZERO_ADDRESS, l1TxOrigin: ZERO_ADDRESS,
entrypoint: ZERO_ADDRESS, entrypoint: ZERO_ADDRESS,
gasLimit: 0, gasLimit: 0,
data: NULL_BYTES32, data: NULL_BYTES32,
}, }
] })
export const hashTransaction = ({
timestamp,
blockNumber,
l1QueueOrigin,
l1TxOrigin,
entrypoint,
gasLimit,
data,
}: Transaction): string => {
return ethers.utils.solidityKeccak256(
['uint256', 'uint256', 'uint8', 'address', 'address', 'uint256', 'bytes'],
[
timestamp,
blockNumber,
l1QueueOrigin,
l1TxOrigin,
entrypoint,
gasLimit,
data,
]
)
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment