Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
N
nebula
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
exchain
nebula
Commits
86773832
Unverified
Commit
86773832
authored
May 10, 2023
by
mergify[bot]
Committed by
GitHub
May 10, 2023
Browse files
Options
Browse Files
Download
Plain Diff
Merge branch 'develop' into felipe/consensus-limit-block-lagging
parents
05829202
7ee57edf
Changes
14
Expand all
Show whitespace changes
Inline
Side-by-side
Showing
14 changed files
with
820 additions
and
30 deletions
+820
-30
migrate.go
op-chain-ops/crossdomain/migrate.go
+38
-5
node.go
op-node/node/node.go
+9
-0
node_test.go
op-node/node/node_test.go
+13
-0
BondManager.sol
packages/contracts-bedrock/contracts/dispute/BondManager.sol
+177
-0
DisputeGameFactory.sol
...ontracts-bedrock/contracts/dispute/DisputeGameFactory.sol
+0
-1
IBondManager.sol
...ages/contracts-bedrock/contracts/dispute/IBondManager.sol
+14
-14
IDisputeGame.sol
...ages/contracts-bedrock/contracts/dispute/IDisputeGame.sol
+4
-1
IDisputeGameFactory.sol
...ntracts-bedrock/contracts/dispute/IDisputeGameFactory.sol
+2
-1
IFaultDisputeGame.sol
...contracts-bedrock/contracts/dispute/IFaultDisputeGame.sol
+7
-2
DisputeTypes.sol
...es/contracts-bedrock/contracts/libraries/DisputeTypes.sol
+2
-2
SafeCall.sol
packages/contracts-bedrock/contracts/libraries/SafeCall.sol
+28
-0
BondManager.t.sol
packages/contracts-bedrock/contracts/test/BondManager.t.sol
+450
-0
SafeCall.t.sol
packages/contracts-bedrock/contracts/test/SafeCall.t.sol
+39
-0
message-utils.ts
packages/sdk/src/utils/message-utils.ts
+37
-4
No files found.
op-chain-ops/crossdomain/migrate.go
View file @
86773832
...
...
@@ -19,6 +19,17 @@ var (
errLegacyStorageSlotNotFound
=
errors
.
New
(
"cannot find storage slot"
)
)
// Constants used by `CrossDomainMessenger.baseGas`
var
(
RelayConstantOverhead
uint64
=
200
_000
RelayPerByteDataCost
uint64
=
params
.
TxDataNonZeroGasEIP2028
MinGasDynamicOverheadNumerator
uint64
=
64
MinGasDynamicOverheadDenominator
uint64
=
63
RelayCallOverhead
uint64
=
40
_000
RelayReservedGas
uint64
=
40
_000
RelayGasCheckBuffer
uint64
=
5
_000
)
// MigrateWithdrawals will migrate a list of pending withdrawals given a StateDB.
func
MigrateWithdrawals
(
withdrawals
SafeFilteredWithdrawals
,
...
...
@@ -112,16 +123,38 @@ func MigrateWithdrawalGasLimit(data []byte, chainID *big.Int) uint64 {
// Compute the upper bound on the gas limit. This could be more
// accurate if individual 0 bytes and non zero bytes were accounted
// for.
dataCost
:=
uint64
(
len
(
data
))
*
params
.
TxDataNonZeroGasEIP2028
dataCost
:=
uint64
(
len
(
data
))
*
RelayPerByteDataCost
// Goerli has a lower gas limit than other chains.
overhead
:=
uint64
(
200
_000
)
if
chainID
.
Cmp
(
big
.
NewInt
(
420
))
!=
0
{
overhead
=
1
_000_000
var
overhead
uint64
if
chainID
.
Cmp
(
big
.
NewInt
(
420
))
==
0
{
overhead
=
uint64
(
200
_000
)
}
else
{
// Mimic `baseGas` from `CrossDomainMessenger.sol`
overhead
=
uint64
(
// Constant overhead
RelayConstantOverhead
+
// Dynamic overhead (EIP-150)
// We use a constant 1 million gas limit due to the overhead of simulating all migrated withdrawal
// transactions during the migration. This is a conservative estimate, and if a withdrawal
// uses more than the minimum gas limit, it will fail and need to be replayed with a higher
// gas limit.
(
MinGasDynamicOverheadNumerator
*
1
_000_000
)
/
MinGasDynamicOverheadDenominator
+
// Gas reserved for the worst-case cost of 3/5 of the `CALL` opcode's dynamic gas
// factors. (Conservative)
RelayCallOverhead
+
// Relay reserved gas (to ensure execution of `relayMessage` completes after the
// subcontext finishes executing) (Conservative)
RelayReservedGas
+
// Gas reserved for the execution between the `hasMinGas` check and the `CALL`
// opcode. (Conservative)
RelayGasCheckBuffer
,
)
}
// Set the outer gas limit. This cannot be zero
// Set the outer
minimum
gas limit. This cannot be zero
gasLimit
:=
dataCost
+
overhead
// Cap the gas limit to be 25 million to prevent creating withdrawals
// that go over the block gas limit.
if
gasLimit
>
25
_000_000
{
...
...
op-node/node/node.go
View file @
86773832
...
...
@@ -378,12 +378,21 @@ func (n *OpNode) RequestL2Range(ctx context.Context, start, end eth.L2BlockRef)
return
n
.
rpcSync
.
RequestL2Range
(
ctx
,
start
,
end
)
}
if
n
.
p2pNode
!=
nil
&&
n
.
p2pNode
.
AltSyncEnabled
()
{
if
unixTimeStale
(
start
.
Time
,
12
*
time
.
Hour
)
{
n
.
log
.
Debug
(
"ignoring request to sync L2 range, timestamp is too old for p2p"
,
"start"
,
start
,
"end"
,
end
,
"start_time"
,
start
.
Time
)
return
nil
}
return
n
.
p2pNode
.
RequestL2Range
(
ctx
,
start
,
end
)
}
n
.
log
.
Debug
(
"ignoring request to sync L2 range, no sync method available"
,
"start"
,
start
,
"end"
,
end
)
return
nil
}
// unixTimeStale returns true if the unix timestamp is before the current time minus the supplied duration.
func
unixTimeStale
(
timestamp
uint64
,
duration
time
.
Duration
)
bool
{
return
time
.
Unix
(
int64
(
timestamp
),
0
)
.
Before
(
time
.
Now
()
.
Add
(
-
1
*
duration
))
}
func
(
n
*
OpNode
)
P2P
()
p2p
.
Node
{
return
n
.
p2pNode
}
...
...
op-node/node/node_test.go
0 → 100644
View file @
86773832
package
node
import
(
"testing"
"time"
"github.com/stretchr/testify/require"
)
func
TestUnixTimeStale
(
t
*
testing
.
T
)
{
require
.
True
(
t
,
unixTimeStale
(
1
_600_000_000
,
1
*
time
.
Hour
))
require
.
False
(
t
,
unixTimeStale
(
uint64
(
time
.
Now
()
.
Unix
()),
1
*
time
.
Hour
))
}
packages/contracts-bedrock/contracts/dispute/BondManager.sol
0 → 100644
View file @
86773832
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.15;
import { GameType } from "../libraries/DisputeTypes.sol";
import { GameStatus } from "../libraries/DisputeTypes.sol";
import { SafeCall } from "../libraries/SafeCall.sol";
import { IDisputeGame } from "./IDisputeGame.sol";
import { IDisputeGameFactory } from "./IDisputeGameFactory.sol";
/**
* @title BondManager
* @notice The Bond Manager serves as an escrow for permissionless output proposal bonds.
*/
contract BondManager {
// The Bond Type
struct Bond {
address owner;
uint256 expiration;
bytes32 id;
uint256 amount;
}
/**
* @notice Mapping from bondId to bond.
*/
mapping(bytes32 => Bond) public bonds;
/**
* @notice BondPosted is emitted when a bond is posted.
* @param bondId is the id of the bond.
* @param owner is the address that owns the bond.
* @param expiration is the time at which the bond expires.
* @param amount is the amount of the bond.
*/
event BondPosted(bytes32 bondId, address owner, uint256 expiration, uint256 amount);
/**
* @notice BondSeized is emitted when a bond is seized.
* @param bondId is the id of the bond.
* @param owner is the address that owns the bond.
* @param seizer is the address that seized the bond.
* @param amount is the amount of the bond.
*/
event BondSeized(bytes32 bondId, address owner, address seizer, uint256 amount);
/**
* @notice BondReclaimed is emitted when a bond is reclaimed by the owner.
* @param bondId is the id of the bond.
* @param claiment is the address that reclaimed the bond.
* @param amount is the amount of the bond.
*/
event BondReclaimed(bytes32 bondId, address claiment, uint256 amount);
/**
* @notice The permissioned dispute game factory.
* @dev Used to verify the status of bonds.
*/
IDisputeGameFactory public immutable DISPUTE_GAME_FACTORY;
/**
* @notice Instantiates the bond maanger with the registered dispute game factory.
* @param _disputeGameFactory is the dispute game factory.
*/
constructor(IDisputeGameFactory _disputeGameFactory) {
DISPUTE_GAME_FACTORY = _disputeGameFactory;
}
/**
* @notice Post a bond with a given id and owner.
* @dev This function will revert if the provided bondId is already in use.
* @param _bondId is the id of the bond.
* @param _bondOwner is the address that owns the bond.
* @param _minClaimHold is the minimum amount of time the owner
* must wait before reclaiming their bond.
*/
function post(
bytes32 _bondId,
address _bondOwner,
uint256 _minClaimHold
) external payable {
require(bonds[_bondId].owner == address(0), "BondManager: BondId already posted.");
require(_bondOwner != address(0), "BondManager: Owner cannot be the zero address.");
require(msg.value > 0, "BondManager: Value must be non-zero.");
uint256 expiration = _minClaimHold + block.timestamp;
bonds[_bondId] = Bond({
owner: _bondOwner,
expiration: expiration,
id: _bondId,
amount: msg.value
});
emit BondPosted(_bondId, _bondOwner, expiration, msg.value);
}
/**
* @notice Seizes the bond with the given id.
* @dev This function will revert if there is no bond at the given id.
* @param _bondId is the id of the bond.
*/
function seize(bytes32 _bondId) external {
Bond memory b = bonds[_bondId];
require(b.owner != address(0), "BondManager: The bond does not exist.");
require(b.expiration >= block.timestamp, "BondManager: Bond expired.");
IDisputeGame caller = IDisputeGame(msg.sender);
IDisputeGame game = DISPUTE_GAME_FACTORY.games(
GameType.ATTESTATION,
caller.rootClaim(),
caller.extraData()
);
require(msg.sender == address(game), "BondManager: Unauthorized seizure.");
require(game.status() == GameStatus.CHALLENGER_WINS, "BondManager: Game incomplete.");
delete bonds[_bondId];
emit BondSeized(_bondId, b.owner, msg.sender, b.amount);
bool success = SafeCall.send(payable(msg.sender), gasleft(), b.amount);
require(success, "BondManager: Failed to send Ether.");
}
/**
* @notice Seizes the bond with the given id and distributes it to recipients.
* @dev This function will revert if there is no bond at the given id.
* @param _bondId is the id of the bond.
* @param _claimRecipients is a set of addresses to split the bond amongst.
*/
function seizeAndSplit(bytes32 _bondId, address[] calldata _claimRecipients) external {
Bond memory b = bonds[_bondId];
require(b.owner != address(0), "BondManager: The bond does not exist.");
require(b.expiration >= block.timestamp, "BondManager: Bond expired.");
IDisputeGame caller = IDisputeGame(msg.sender);
IDisputeGame game = DISPUTE_GAME_FACTORY.games(
GameType.ATTESTATION,
caller.rootClaim(),
caller.extraData()
);
require(msg.sender == address(game), "BondManager: Unauthorized seizure.");
require(game.status() == GameStatus.CHALLENGER_WINS, "BondManager: Game incomplete.");
delete bonds[_bondId];
emit BondSeized(_bondId, b.owner, msg.sender, b.amount);
uint256 len = _claimRecipients.length;
uint256 proportionalAmount = b.amount / len;
for (uint256 i = 0; i < len; i++) {
bool success = SafeCall.send(
payable(_claimRecipients[i]),
gasleft() / len,
proportionalAmount
);
require(success, "BondManager: Failed to send Ether.");
}
}
/**
* @notice Reclaims the bond of the bond owner.
* @dev This function will revert if there is no bond at the given id.
* @param _bondId is the id of the bond.
*/
function reclaim(bytes32 _bondId) external {
Bond memory b = bonds[_bondId];
require(b.owner == msg.sender, "BondManager: Unauthorized claimant.");
require(b.expiration <= block.timestamp, "BondManager: Bond isn't claimable yet.");
delete bonds[_bondId];
emit BondReclaimed(_bondId, msg.sender, b.amount);
bool success = SafeCall.send(payable(msg.sender), gasleft(), b.amount);
require(success, "BondManager: Failed to send Ether.");
}
}
packages/contracts-bedrock/contracts/dispute/DisputeGameFactory.sol
View file @
86773832
...
...
@@ -12,7 +12,6 @@ import { NoImplementation } from "../libraries/DisputeErrors.sol";
import { GameAlreadyExists } from "../libraries/DisputeErrors.sol";
import { IDisputeGame } from "./IDisputeGame.sol";
import { IBondManager } from "./IBondManager.sol";
import { IDisputeGameFactory } from "./IDisputeGameFactory.sol";
/**
...
...
packages/contracts-bedrock/contracts/dispute/IBondManager.sol
View file @
86773832
//
/
SPDX-License-Identifier: MIT
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.15;
/**
...
...
@@ -9,36 +9,36 @@ interface IBondManager {
/**
* @notice Post a bond with a given id and owner.
* @dev This function will revert if the provided bondId is already in use.
* @param bondId is the id of the bond.
* @param
o
wner is the address that owns the bond.
* @param minClaimHold is the minimum amount of time the owner
* @param
_
bondId is the id of the bond.
* @param
_bondO
wner is the address that owns the bond.
* @param
_
minClaimHold is the minimum amount of time the owner
* must wait before reclaiming their bond.
*/
function post(
bytes32 bondId,
address
o
wner,
uint256 minClaimHold
bytes32
_
bondId,
address
_bondO
wner,
uint256
_
minClaimHold
) external payable;
/**
* @notice Seizes the bond with the given id.
* @dev This function will revert if there is no bond at the given id.
* @param bondId is the id of the bond.
* @param
_
bondId is the id of the bond.
*/
function seize(bytes32 bondId) external;
function seize(bytes32
_
bondId) external;
/**
* @notice Seizes the bond with the given id and distributes it to recipients.
* @dev This function will revert if there is no bond at the given id.
* @param bondId is the id of the bond.
* @param
r
ecipients is a set of addresses to split the bond amongst.
* @param
_
bondId is the id of the bond.
* @param
_claimR
ecipients is a set of addresses to split the bond amongst.
*/
function seizeAndSplit(bytes32
bondId, address[] calldata r
ecipients) external;
function seizeAndSplit(bytes32
_bondId, address[] calldata _claimR
ecipients) external;
/**
* @notice Reclaims the bond of the bond owner.
* @dev This function will revert if there is no bond at the given id.
* @param bondId is the id of the bond.
* @param
_
bondId is the id of the bond.
*/
function reclaim(bytes32 bondId) external;
function reclaim(bytes32
_
bondId) external;
}
packages/contracts-bedrock/contracts/dispute/IDisputeGame.sol
View file @
86773832
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.15;
import { Claim, GameType, GameStatus, Timestamp } from "../libraries/DisputeTypes.sol";
import { Claim } from "../libraries/DisputeTypes.sol";
import { GameType } from "../libraries/DisputeTypes.sol";
import { GameStatus } from "../libraries/DisputeTypes.sol";
import { Timestamp } from "../libraries/DisputeTypes.sol";
import { IVersioned } from "./IVersioned.sol";
import { IBondManager } from "./IBondManager.sol";
...
...
packages/contracts-bedrock/contracts/dispute/IDisputeGameFactory.sol
View file @
86773832
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.15;
import { Claim, GameType } from "../libraries/DisputeTypes.sol";
import { Claim } from "../libraries/DisputeTypes.sol";
import { GameType } from "../libraries/DisputeTypes.sol";
import { IDisputeGame } from "./IDisputeGame.sol";
...
...
packages/contracts-bedrock/contracts/dispute/IFaultDisputeGame.sol
View file @
86773832
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.15;
import { Claim, ClaimHash, Clock, Bond, Position, Timestamp } from "../libraries/DisputeTypes.sol";
import { Clock } from "../libraries/DisputeTypes.sol";
import { Claim } from "../libraries/DisputeTypes.sol";
import { Position } from "../libraries/DisputeTypes.sol";
import { Timestamp } from "../libraries/DisputeTypes.sol";
import { ClaimHash } from "../libraries/DisputeTypes.sol";
import { BondAmount } from "../libraries/DisputeTypes.sol";
import { IDisputeGame } from "./IDisputeGame.sol";
...
...
@@ -60,7 +65,7 @@ interface IFaultDisputeGame is IDisputeGame {
* @param claimHash The unique ClaimHash
* @return bond The Bond associated with the ClaimHash
*/
function bonds(ClaimHash claimHash) external view returns (Bond bond);
function bonds(ClaimHash claimHash) external view returns (Bond
Amount
bond);
/**
* @notice Maps a unique ClaimHash its chess clock.
...
...
packages/contracts-bedrock/contracts/libraries/DisputeTypes.sol
View file @
86773832
...
...
@@ -18,9 +18,9 @@ type Claim is bytes32;
type ClaimHash is bytes32;
/**
* @notice A bond represents the amount of collateral that a user has locked up in a claim.
* @notice A bond
amount
represents the amount of collateral that a user has locked up in a claim.
*/
type Bond is uint256;
type Bond
Amount
is uint256;
/**
* @notice A dedicated timestamp type.
...
...
packages/contracts-bedrock/contracts/libraries/SafeCall.sol
View file @
86773832
...
...
@@ -6,6 +6,34 @@ pragma solidity 0.8.15;
* @notice Perform low level safe calls
*/
library SafeCall {
/**
* @notice Performs a low level call without copying any returndata.
* @dev Passes no calldata to the call context.
*
* @param _target Address to call
* @param _gas Amount of gas to pass to the call
* @param _value Amount of value to pass to the call
*/
function send(
address _target,
uint256 _gas,
uint256 _value
) internal returns (bool) {
bool _success;
assembly {
_success := call(
_gas, // gas
_target, // recipient
_value, // ether value
0, // inloc
0, // inlen
0, // outloc
0 // outlen
)
}
return _success;
}
/**
* @notice Perform a low level call without copying any returndata
*
...
...
packages/contracts-bedrock/contracts/test/BondManager.t.sol
0 → 100644
View file @
86773832
This diff is collapsed.
Click to expand it.
packages/contracts-bedrock/contracts/test/SafeCall.t.sol
View file @
86773832
...
...
@@ -5,6 +5,45 @@ import { CommonTest } from "./CommonTest.t.sol";
import { SafeCall } from "../libraries/SafeCall.sol";
contract SafeCall_Test is CommonTest {
function testFuzz_send_succeeds(
address from,
address to,
uint256 gas,
uint64 value
) external {
vm.assume(from.balance == 0);
vm.assume(to.balance == 0);
// no precompiles (mainnet)
assumeNoPrecompiles(to, 1);
// don't call the vm
vm.assume(to != address(vm));
vm.assume(from != address(vm));
// don't call the console
vm.assume(to != address(0x000000000000000000636F6e736F6c652e6c6f67));
// don't call the create2 deployer
vm.assume(to != address(0x4e59b44847b379578588920cA78FbF26c0B4956C));
// don't call the ffi interface
vm.assume(to != address(0x5615dEB798BB3E4dFa0139dFa1b3D433Cc23b72f));
assertEq(from.balance, 0, "from balance is 0");
vm.deal(from, value);
assertEq(from.balance, value, "from balance not dealt");
uint256[2] memory balancesBefore = [from.balance, to.balance];
vm.expectCall(to, value, bytes(""));
vm.prank(from);
bool success = SafeCall.send(to, gas, value);
assertTrue(success, "send not successful");
if (from == to) {
assertEq(from.balance, balancesBefore[0], "Self-send did not change balance");
} else {
assertEq(from.balance, balancesBefore[0] - value, "from balance not drained");
assertEq(to.balance, balancesBefore[1] + value, "to balance received");
}
}
function testFuzz_call_succeeds(
address from,
address to,
...
...
packages/sdk/src/utils/message-utils.ts
View file @
86773832
...
...
@@ -5,6 +5,15 @@ import { LowLevelMessage } from '../interfaces'
const
{
hexDataLength
}
=
utils
// Constants used by `CrossDomainMessenger.baseGas`
const
RELAY_CONSTANT_OVERHEAD
=
BigNumber
.
from
(
200
_000
)
const
RELAY_PER_BYTE_DATA_COST
=
BigNumber
.
from
(
16
)
const
MIN_GAS_DYNAMIC_OVERHEAD_NUMERATOR
=
BigNumber
.
from
(
64
)
const
MIN_GAS_DYNAMIC_OVERHEAD_DENOMINATOR
=
BigNumber
.
from
(
63
)
const
RELAY_CALL_OVERHEAD
=
BigNumber
.
from
(
40
_000
)
const
RELAY_RESERVED_GAS
=
BigNumber
.
from
(
40
_000
)
const
RELAY_GAS_CHECK_BUFFER
=
BigNumber
.
from
(
5
_000
)
/**
* Utility for hashing a LowLevelMessage object.
*
...
...
@@ -46,11 +55,35 @@ export const migratedWithdrawalGasLimit = (
chainID
:
number
):
BigNumber
=>
{
// Compute the gas limit and cap at 25 million
const
dataCost
=
BigNumber
.
from
(
hexDataLength
(
data
)).
mul
(
16
)
let
overhead
=
200
_000
if
(
chainID
!==
420
)
{
overhead
=
1
_000_000
const
dataCost
=
BigNumber
.
from
(
hexDataLength
(
data
)).
mul
(
RELAY_PER_BYTE_DATA_COST
)
let
overhead
:
BigNumber
if
(
chainID
===
420
)
{
overhead
=
BigNumber
.
from
(
200
_000
)
}
else
{
// Dynamic overhead (EIP-150)
// We use a constant 1 million gas limit due to the overhead of simulating all migrated withdrawal
// transactions during the migration. This is a conservative estimate, and if a withdrawal
// uses more than the minimum gas limit, it will fail and need to be replayed with a higher
// gas limit.
const
dynamicOverhead
=
MIN_GAS_DYNAMIC_OVERHEAD_NUMERATOR
.
mul
(
1
_000_000
).
div
(
MIN_GAS_DYNAMIC_OVERHEAD_DENOMINATOR
)
// Constant overhead
overhead
=
RELAY_CONSTANT_OVERHEAD
.
add
(
dynamicOverhead
)
.
add
(
RELAY_CALL_OVERHEAD
)
// Gas reserved for the worst-case cost of 3/5 of the `CALL` opcode's dynamic gas
// factors. (Conservative)
// Relay reserved gas (to ensure execution of `relayMessage` completes after the
// subcontext finishes executing) (Conservative)
.
add
(
RELAY_RESERVED_GAS
)
// Gas reserved for the execution between the `hasMinGas` check and the `CALL`
// opcode. (Conservative)
.
add
(
RELAY_GAS_CHECK_BUFFER
)
}
let
minGasLimit
=
dataCost
.
add
(
overhead
)
if
(
minGasLimit
.
gt
(
25
_000_000
))
{
minGasLimit
=
BigNumber
.
from
(
25
_000_000
)
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment