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
971f6fcd
Unverified
Commit
971f6fcd
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/moar-consensus-metrics
parents
2b7e5008
915b275a
Changes
20
Expand all
Hide whitespace changes
Inline
Side-by-side
Showing
20 changed files
with
972 additions
and
32 deletions
+972
-32
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
config.go
proxyd/config.go
+1
-0
consensus_poller.go
proxyd/consensus_poller.go
+36
-2
example.config.toml
proxyd/example.config.toml
+2
-0
consensus_test.go
proxyd/integration_tests/consensus_test.go
+109
-0
consensus.toml
proxyd/integration_tests/testdata/consensus.toml
+1
-0
proxyd.go
proxyd/proxyd.go
+3
-0
No files found.
op-chain-ops/crossdomain/migrate.go
View file @
971f6fcd
...
@@ -19,6 +19,17 @@ var (
...
@@ -19,6 +19,17 @@ var (
errLegacyStorageSlotNotFound
=
errors
.
New
(
"cannot find storage slot"
)
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.
// MigrateWithdrawals will migrate a list of pending withdrawals given a StateDB.
func
MigrateWithdrawals
(
func
MigrateWithdrawals
(
withdrawals
SafeFilteredWithdrawals
,
withdrawals
SafeFilteredWithdrawals
,
...
@@ -112,16 +123,38 @@ func MigrateWithdrawalGasLimit(data []byte, chainID *big.Int) uint64 {
...
@@ -112,16 +123,38 @@ func MigrateWithdrawalGasLimit(data []byte, chainID *big.Int) uint64 {
// Compute the upper bound on the gas limit. This could be more
// Compute the upper bound on the gas limit. This could be more
// accurate if individual 0 bytes and non zero bytes were accounted
// accurate if individual 0 bytes and non zero bytes were accounted
// for.
// for.
dataCost
:=
uint64
(
len
(
data
))
*
params
.
TxDataNonZeroGasEIP2028
dataCost
:=
uint64
(
len
(
data
))
*
RelayPerByteDataCost
// Goerli has a lower gas limit than other chains.
// Goerli has a lower gas limit than other chains.
overhead
:=
uint64
(
200
_000
)
var
overhead
uint64
if
chainID
.
Cmp
(
big
.
NewInt
(
420
))
!=
0
{
if
chainID
.
Cmp
(
big
.
NewInt
(
420
))
==
0
{
overhead
=
1
_000_000
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
gasLimit
:=
dataCost
+
overhead
// Cap the gas limit to be 25 million to prevent creating withdrawals
// Cap the gas limit to be 25 million to prevent creating withdrawals
// that go over the block gas limit.
// that go over the block gas limit.
if
gasLimit
>
25
_000_000
{
if
gasLimit
>
25
_000_000
{
...
...
op-node/node/node.go
View file @
971f6fcd
...
@@ -378,12 +378,21 @@ func (n *OpNode) RequestL2Range(ctx context.Context, start, end eth.L2BlockRef)
...
@@ -378,12 +378,21 @@ func (n *OpNode) RequestL2Range(ctx context.Context, start, end eth.L2BlockRef)
return
n
.
rpcSync
.
RequestL2Range
(
ctx
,
start
,
end
)
return
n
.
rpcSync
.
RequestL2Range
(
ctx
,
start
,
end
)
}
}
if
n
.
p2pNode
!=
nil
&&
n
.
p2pNode
.
AltSyncEnabled
()
{
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
)
return
n
.
p2pNode
.
RequestL2Range
(
ctx
,
start
,
end
)
}
}
n
.
log
.
Debug
(
"ignoring request to sync L2 range, no sync method available"
,
"start"
,
start
,
"end"
,
end
)
n
.
log
.
Debug
(
"ignoring request to sync L2 range, no sync method available"
,
"start"
,
start
,
"end"
,
end
)
return
nil
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
{
func
(
n
*
OpNode
)
P2P
()
p2p
.
Node
{
return
n
.
p2pNode
return
n
.
p2pNode
}
}
...
...
op-node/node/node_test.go
0 → 100644
View file @
971f6fcd
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 @
971f6fcd
// 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 @
971f6fcd
...
@@ -12,7 +12,6 @@ import { NoImplementation } from "../libraries/DisputeErrors.sol";
...
@@ -12,7 +12,6 @@ import { NoImplementation } from "../libraries/DisputeErrors.sol";
import { GameAlreadyExists } from "../libraries/DisputeErrors.sol";
import { GameAlreadyExists } from "../libraries/DisputeErrors.sol";
import { IDisputeGame } from "./IDisputeGame.sol";
import { IDisputeGame } from "./IDisputeGame.sol";
import { IBondManager } from "./IBondManager.sol";
import { IDisputeGameFactory } from "./IDisputeGameFactory.sol";
import { IDisputeGameFactory } from "./IDisputeGameFactory.sol";
/**
/**
...
...
packages/contracts-bedrock/contracts/dispute/IBondManager.sol
View file @
971f6fcd
//
/
SPDX-License-Identifier: MIT
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.15;
pragma solidity ^0.8.15;
/**
/**
...
@@ -9,36 +9,36 @@ interface IBondManager {
...
@@ -9,36 +9,36 @@ interface IBondManager {
/**
/**
* @notice Post a bond with a given id and owner.
* @notice Post a bond with a given id and owner.
* @dev This function will revert if the provided bondId is already in use.
* @dev This function will revert if the provided bondId is already in use.
* @param bondId is the id of the bond.
* @param
_
bondId is the id of the bond.
* @param
o
wner is the address that owns the bond.
* @param
_bondO
wner is the address that owns the bond.
* @param minClaimHold is the minimum amount of time the owner
* @param
_
minClaimHold is the minimum amount of time the owner
* must wait before reclaiming their bond.
* must wait before reclaiming their bond.
*/
*/
function post(
function post(
bytes32 bondId,
bytes32
_
bondId,
address
o
wner,
address
_bondO
wner,
uint256 minClaimHold
uint256
_
minClaimHold
) external payable;
) external payable;
/**
/**
* @notice Seizes the bond with the given id.
* @notice Seizes the bond with the given id.
* @dev This function will revert if there is no bond at 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.
* @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.
* @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.
* @param
r
ecipients is a set of addresses to split the bond amongst.
* @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.
* @notice Reclaims the bond of the bond owner.
* @dev This function will revert if there is no bond at 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 reclaim(bytes32 bondId) external;
function reclaim(bytes32
_
bondId) external;
}
}
packages/contracts-bedrock/contracts/dispute/IDisputeGame.sol
View file @
971f6fcd
// SPDX-License-Identifier: MIT
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.15;
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 { IVersioned } from "./IVersioned.sol";
import { IBondManager } from "./IBondManager.sol";
import { IBondManager } from "./IBondManager.sol";
...
...
packages/contracts-bedrock/contracts/dispute/IDisputeGameFactory.sol
View file @
971f6fcd
// SPDX-License-Identifier: MIT
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.15;
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";
import { IDisputeGame } from "./IDisputeGame.sol";
...
...
packages/contracts-bedrock/contracts/dispute/IFaultDisputeGame.sol
View file @
971f6fcd
// SPDX-License-Identifier: MIT
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.15;
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";
import { IDisputeGame } from "./IDisputeGame.sol";
...
@@ -60,7 +65,7 @@ interface IFaultDisputeGame is IDisputeGame {
...
@@ -60,7 +65,7 @@ interface IFaultDisputeGame is IDisputeGame {
* @param claimHash The unique ClaimHash
* @param claimHash The unique ClaimHash
* @return bond The Bond associated with the 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.
* @notice Maps a unique ClaimHash its chess clock.
...
...
packages/contracts-bedrock/contracts/libraries/DisputeTypes.sol
View file @
971f6fcd
...
@@ -18,9 +18,9 @@ type Claim is bytes32;
...
@@ -18,9 +18,9 @@ type Claim is bytes32;
type ClaimHash 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.
* @notice A dedicated timestamp type.
...
...
packages/contracts-bedrock/contracts/libraries/SafeCall.sol
View file @
971f6fcd
...
@@ -6,6 +6,34 @@ pragma solidity 0.8.15;
...
@@ -6,6 +6,34 @@ pragma solidity 0.8.15;
* @notice Perform low level safe calls
* @notice Perform low level safe calls
*/
*/
library SafeCall {
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
* @notice Perform a low level call without copying any returndata
*
*
...
...
packages/contracts-bedrock/contracts/test/BondManager.t.sol
0 → 100644
View file @
971f6fcd
This diff is collapsed.
Click to expand it.
packages/contracts-bedrock/contracts/test/SafeCall.t.sol
View file @
971f6fcd
...
@@ -5,6 +5,45 @@ import { CommonTest } from "./CommonTest.t.sol";
...
@@ -5,6 +5,45 @@ import { CommonTest } from "./CommonTest.t.sol";
import { SafeCall } from "../libraries/SafeCall.sol";
import { SafeCall } from "../libraries/SafeCall.sol";
contract SafeCall_Test is CommonTest {
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(
function testFuzz_call_succeeds(
address from,
address from,
address to,
address to,
...
...
packages/sdk/src/utils/message-utils.ts
View file @
971f6fcd
...
@@ -5,6 +5,15 @@ import { LowLevelMessage } from '../interfaces'
...
@@ -5,6 +5,15 @@ import { LowLevelMessage } from '../interfaces'
const
{
hexDataLength
}
=
utils
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.
* Utility for hashing a LowLevelMessage object.
*
*
...
@@ -46,11 +55,35 @@ export const migratedWithdrawalGasLimit = (
...
@@ -46,11 +55,35 @@ export const migratedWithdrawalGasLimit = (
chainID
:
number
chainID
:
number
):
BigNumber
=>
{
):
BigNumber
=>
{
// Compute the gas limit and cap at 25 million
// Compute the gas limit and cap at 25 million
const
dataCost
=
BigNumber
.
from
(
hexDataLength
(
data
)).
mul
(
16
)
const
dataCost
=
BigNumber
.
from
(
hexDataLength
(
data
)).
mul
(
let
overhead
=
200
_000
RELAY_PER_BYTE_DATA_COST
if
(
chainID
!==
420
)
{
)
overhead
=
1
_000_000
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
)
let
minGasLimit
=
dataCost
.
add
(
overhead
)
if
(
minGasLimit
.
gt
(
25
_000_000
))
{
if
(
minGasLimit
.
gt
(
25
_000_000
))
{
minGasLimit
=
BigNumber
.
from
(
25
_000_000
)
minGasLimit
=
BigNumber
.
from
(
25
_000_000
)
...
...
proxyd/config.go
View file @
971f6fcd
...
@@ -105,6 +105,7 @@ type BackendGroupConfig struct {
...
@@ -105,6 +105,7 @@ type BackendGroupConfig struct {
ConsensusBanPeriod
TOMLDuration
`toml:"consensus_ban_period"`
ConsensusBanPeriod
TOMLDuration
`toml:"consensus_ban_period"`
ConsensusMaxUpdateThreshold
TOMLDuration
`toml:"consensus_max_update_threshold"`
ConsensusMaxUpdateThreshold
TOMLDuration
`toml:"consensus_max_update_threshold"`
ConsensusMaxBlockLag
uint64
`toml:"consensus_max_block_lag"`
ConsensusMinPeerCount
int
`toml:"consensus_min_peer_count"`
ConsensusMinPeerCount
int
`toml:"consensus_min_peer_count"`
}
}
...
...
proxyd/consensus_poller.go
View file @
971f6fcd
...
@@ -35,6 +35,7 @@ type ConsensusPoller struct {
...
@@ -35,6 +35,7 @@ type ConsensusPoller struct {
banPeriod
time
.
Duration
banPeriod
time
.
Duration
maxUpdateThreshold
time
.
Duration
maxUpdateThreshold
time
.
Duration
maxBlockLag
uint64
}
}
type
backendState
struct
{
type
backendState
struct
{
...
@@ -160,6 +161,12 @@ func WithMaxUpdateThreshold(maxUpdateThreshold time.Duration) ConsensusOpt {
...
@@ -160,6 +161,12 @@ func WithMaxUpdateThreshold(maxUpdateThreshold time.Duration) ConsensusOpt {
}
}
}
}
func
WithMaxBlockLag
(
maxBlockLag
uint64
)
ConsensusOpt
{
return
func
(
cp
*
ConsensusPoller
)
{
cp
.
maxBlockLag
=
maxBlockLag
}
}
func
WithMinPeerCount
(
minPeerCount
uint64
)
ConsensusOpt
{
func
WithMinPeerCount
(
minPeerCount
uint64
)
ConsensusOpt
{
return
func
(
cp
*
ConsensusPoller
)
{
return
func
(
cp
*
ConsensusPoller
)
{
cp
.
minPeerCount
=
minPeerCount
cp
.
minPeerCount
=
minPeerCount
...
@@ -181,6 +188,7 @@ func NewConsensusPoller(bg *BackendGroup, opts ...ConsensusOpt) *ConsensusPoller
...
@@ -181,6 +188,7 @@ func NewConsensusPoller(bg *BackendGroup, opts ...ConsensusOpt) *ConsensusPoller
banPeriod
:
5
*
time
.
Minute
,
banPeriod
:
5
*
time
.
Minute
,
maxUpdateThreshold
:
30
*
time
.
Second
,
maxUpdateThreshold
:
30
*
time
.
Second
,
maxBlockLag
:
50
,
minPeerCount
:
3
,
minPeerCount
:
3
,
}
}
...
@@ -264,11 +272,29 @@ func (cp *ConsensusPoller) UpdateBackend(ctx context.Context, be *Backend) {
...
@@ -264,11 +272,29 @@ func (cp *ConsensusPoller) UpdateBackend(ctx context.Context, be *Backend) {
// UpdateBackendGroupConsensus resolves the current group consensus based on the state of the backends
// UpdateBackendGroupConsensus resolves the current group consensus based on the state of the backends
func
(
cp
*
ConsensusPoller
)
UpdateBackendGroupConsensus
(
ctx
context
.
Context
)
{
func
(
cp
*
ConsensusPoller
)
UpdateBackendGroupConsensus
(
ctx
context
.
Context
)
{
var
highestBlock
hexutil
.
Uint64
var
lowestBlock
hexutil
.
Uint64
var
lowestBlock
hexutil
.
Uint64
var
lowestBlockHash
string
var
lowestBlockHash
string
currentConsensusBlockNumber
:=
cp
.
GetConsensusBlockNumber
()
currentConsensusBlockNumber
:=
cp
.
GetConsensusBlockNumber
()
// find the highest block, in order to use it defining the highest non-lagging ancestor block
for
_
,
be
:=
range
cp
.
backendGroup
.
Backends
{
peerCount
,
backendLatestBlockNumber
,
_
,
lastUpdate
,
_
:=
cp
.
getBackendState
(
be
)
if
!
be
.
skipPeerCountCheck
&&
peerCount
<
cp
.
minPeerCount
{
continue
}
if
lastUpdate
.
Add
(
cp
.
maxUpdateThreshold
)
.
Before
(
time
.
Now
())
{
continue
}
if
backendLatestBlockNumber
>
highestBlock
{
highestBlock
=
backendLatestBlockNumber
}
}
// find the highest common ancestor block
for
_
,
be
:=
range
cp
.
backendGroup
.
Backends
{
for
_
,
be
:=
range
cp
.
backendGroup
.
Backends
{
peerCount
,
backendLatestBlockNumber
,
backendLatestBlockHash
,
lastUpdate
,
_
:=
cp
.
getBackendState
(
be
)
peerCount
,
backendLatestBlockNumber
,
backendLatestBlockHash
,
lastUpdate
,
_
:=
cp
.
getBackendState
(
be
)
...
@@ -279,6 +305,11 @@ func (cp *ConsensusPoller) UpdateBackendGroupConsensus(ctx context.Context) {
...
@@ -279,6 +305,11 @@ func (cp *ConsensusPoller) UpdateBackendGroupConsensus(ctx context.Context) {
continue
continue
}
}
// check if backend is lagging behind the highest block
if
backendLatestBlockNumber
<
highestBlock
&&
uint64
(
highestBlock
-
backendLatestBlockNumber
)
>
cp
.
maxBlockLag
{
continue
}
if
lowestBlock
==
0
||
backendLatestBlockNumber
<
lowestBlock
{
if
lowestBlock
==
0
||
backendLatestBlockNumber
<
lowestBlock
{
lowestBlock
=
backendLatestBlockNumber
lowestBlock
=
backendLatestBlockNumber
lowestBlockHash
=
backendLatestBlockHash
lowestBlockHash
=
backendLatestBlockHash
...
@@ -317,12 +348,15 @@ func (cp *ConsensusPoller) UpdateBackendGroupConsensus(ctx context.Context) {
...
@@ -317,12 +348,15 @@ func (cp *ConsensusPoller) UpdateBackendGroupConsensus(ctx context.Context) {
- not banned
- not banned
- with minimum peer count
- with minimum peer count
- updated recently
- updated recently
- not lagging
*/
*/
peerCount
,
_
,
_
,
lastUpdate
,
bannedUntil
:=
cp
.
getBackendState
(
be
)
peerCount
,
latestBlockNumber
,
_
,
lastUpdate
,
bannedUntil
:=
cp
.
getBackendState
(
be
)
notUpdated
:=
lastUpdate
.
Add
(
cp
.
maxUpdateThreshold
)
.
Before
(
time
.
Now
())
notUpdated
:=
lastUpdate
.
Add
(
cp
.
maxUpdateThreshold
)
.
Before
(
time
.
Now
())
isBanned
:=
time
.
Now
()
.
Before
(
bannedUntil
)
isBanned
:=
time
.
Now
()
.
Before
(
bannedUntil
)
notEnoughPeers
:=
!
be
.
skipPeerCountCheck
&&
peerCount
<
cp
.
minPeerCount
notEnoughPeers
:=
!
be
.
skipPeerCountCheck
&&
peerCount
<
cp
.
minPeerCount
if
!
be
.
IsHealthy
()
||
be
.
IsRateLimited
()
||
!
be
.
Online
()
||
notUpdated
||
isBanned
||
notEnoughPeers
{
lagging
:=
latestBlockNumber
<
proposedBlock
if
!
be
.
IsHealthy
()
||
be
.
IsRateLimited
()
||
!
be
.
Online
()
||
notUpdated
||
isBanned
||
notEnoughPeers
||
lagging
{
filteredBackendsNames
=
append
(
filteredBackendsNames
,
be
.
Name
)
filteredBackendsNames
=
append
(
filteredBackendsNames
,
be
.
Name
)
continue
continue
}
}
...
...
proxyd/example.config.toml
View file @
971f6fcd
...
@@ -93,6 +93,8 @@ backends = ["infura"]
...
@@ -93,6 +93,8 @@ backends = ["infura"]
# consensus_ban_period = "1m"
# consensus_ban_period = "1m"
# Maximum delay for update the backend, default 30s
# Maximum delay for update the backend, default 30s
# consensus_max_update_threshold = "20s"
# consensus_max_update_threshold = "20s"
# Maximum block lag, default 50
# consensus_max_block_lag = 10
# Minimum peer count, default 3
# Minimum peer count, default 3
# consensus_min_peer_count = 4
# consensus_min_peer_count = 4
...
...
proxyd/integration_tests/consensus_test.go
View file @
971f6fcd
...
@@ -97,6 +97,115 @@ func TestConsensus(t *testing.T) {
...
@@ -97,6 +97,115 @@ func TestConsensus(t *testing.T) {
require
.
Equal
(
t
,
1
,
len
(
consensusGroup
))
require
.
Equal
(
t
,
1
,
len
(
consensusGroup
))
})
})
t
.
Run
(
"prevent using a backend lagging behind"
,
func
(
t
*
testing
.
T
)
{
h1
.
ResetOverrides
()
h2
.
ResetOverrides
()
bg
.
Consensus
.
Unban
()
h1
.
AddOverride
(
&
ms
.
MethodTemplate
{
Method
:
"eth_getBlockByNumber"
,
Block
:
"latest"
,
Response
:
buildGetBlockResponse
(
"0x1"
,
"hash1"
),
})
h2
.
AddOverride
(
&
ms
.
MethodTemplate
{
Method
:
"eth_getBlockByNumber"
,
Block
:
"latest"
,
Response
:
buildGetBlockResponse
(
"0x100"
,
"hash0x100"
),
})
h2
.
AddOverride
(
&
ms
.
MethodTemplate
{
Method
:
"eth_getBlockByNumber"
,
Block
:
"0x100"
,
Response
:
buildGetBlockResponse
(
"0x100"
,
"hash0x100"
),
})
for
_
,
be
:=
range
bg
.
Backends
{
bg
.
Consensus
.
UpdateBackend
(
ctx
,
be
)
}
bg
.
Consensus
.
UpdateBackendGroupConsensus
(
ctx
)
// since we ignored node1, the consensus should be at 0x100
require
.
Equal
(
t
,
"0x100"
,
bg
.
Consensus
.
GetConsensusBlockNumber
()
.
String
())
consensusGroup
:=
bg
.
Consensus
.
GetConsensusGroup
()
be
:=
backend
(
bg
,
"node1"
)
require
.
NotNil
(
t
,
be
)
require
.
NotContains
(
t
,
consensusGroup
,
be
)
require
.
Equal
(
t
,
1
,
len
(
consensusGroup
))
})
t
.
Run
(
"prevent using a backend lagging behind - at limit"
,
func
(
t
*
testing
.
T
)
{
h1
.
ResetOverrides
()
h2
.
ResetOverrides
()
bg
.
Consensus
.
Unban
()
h1
.
AddOverride
(
&
ms
.
MethodTemplate
{
Method
:
"eth_getBlockByNumber"
,
Block
:
"latest"
,
Response
:
buildGetBlockResponse
(
"0x1"
,
"hash1"
),
})
// 0x1 + 50 = 0x33
h2
.
AddOverride
(
&
ms
.
MethodTemplate
{
Method
:
"eth_getBlockByNumber"
,
Block
:
"latest"
,
Response
:
buildGetBlockResponse
(
"0x33"
,
"hash0x100"
),
})
h2
.
AddOverride
(
&
ms
.
MethodTemplate
{
Method
:
"eth_getBlockByNumber"
,
Block
:
"0x100"
,
Response
:
buildGetBlockResponse
(
"0x33"
,
"hash0x100"
),
})
for
_
,
be
:=
range
bg
.
Backends
{
bg
.
Consensus
.
UpdateBackend
(
ctx
,
be
)
}
bg
.
Consensus
.
UpdateBackendGroupConsensus
(
ctx
)
// since we ignored node1, the consensus should be at 0x100
require
.
Equal
(
t
,
"0x1"
,
bg
.
Consensus
.
GetConsensusBlockNumber
()
.
String
())
consensusGroup
:=
bg
.
Consensus
.
GetConsensusGroup
()
require
.
Equal
(
t
,
2
,
len
(
consensusGroup
))
})
t
.
Run
(
"prevent using a backend lagging behind - one before limit"
,
func
(
t
*
testing
.
T
)
{
h1
.
ResetOverrides
()
h2
.
ResetOverrides
()
bg
.
Consensus
.
Unban
()
h1
.
AddOverride
(
&
ms
.
MethodTemplate
{
Method
:
"eth_getBlockByNumber"
,
Block
:
"latest"
,
Response
:
buildGetBlockResponse
(
"0x1"
,
"hash1"
),
})
// 0x1 + 49 = 0x32
h2
.
AddOverride
(
&
ms
.
MethodTemplate
{
Method
:
"eth_getBlockByNumber"
,
Block
:
"latest"
,
Response
:
buildGetBlockResponse
(
"0x32"
,
"hash0x100"
),
})
h2
.
AddOverride
(
&
ms
.
MethodTemplate
{
Method
:
"eth_getBlockByNumber"
,
Block
:
"0x100"
,
Response
:
buildGetBlockResponse
(
"0x32"
,
"hash0x100"
),
})
for
_
,
be
:=
range
bg
.
Backends
{
bg
.
Consensus
.
UpdateBackend
(
ctx
,
be
)
}
bg
.
Consensus
.
UpdateBackendGroupConsensus
(
ctx
)
require
.
Equal
(
t
,
"0x1"
,
bg
.
Consensus
.
GetConsensusBlockNumber
()
.
String
())
consensusGroup
:=
bg
.
Consensus
.
GetConsensusGroup
()
require
.
Equal
(
t
,
2
,
len
(
consensusGroup
))
})
t
.
Run
(
"prevent using a backend not in sync"
,
func
(
t
*
testing
.
T
)
{
t
.
Run
(
"prevent using a backend not in sync"
,
func
(
t
*
testing
.
T
)
{
h1
.
ResetOverrides
()
h1
.
ResetOverrides
()
h2
.
ResetOverrides
()
h2
.
ResetOverrides
()
...
...
proxyd/integration_tests/testdata/consensus.toml
View file @
971f6fcd
...
@@ -18,6 +18,7 @@ consensus_aware = true
...
@@ -18,6 +18,7 @@ consensus_aware = true
consensus_handler
=
"noop"
# allow more control over the consensus poller for tests
consensus_handler
=
"noop"
# allow more control over the consensus poller for tests
consensus_ban_period
=
"1m"
consensus_ban_period
=
"1m"
consensus_max_update_threshold
=
"2m"
consensus_max_update_threshold
=
"2m"
consensus_max_block_lag
=
50
consensus_min_peer_count
=
4
consensus_min_peer_count
=
4
[rpc_method_mappings]
[rpc_method_mappings]
...
...
proxyd/proxyd.go
View file @
971f6fcd
...
@@ -328,6 +328,9 @@ func Start(config *Config) (*Server, func(), error) {
...
@@ -328,6 +328,9 @@ func Start(config *Config) (*Server, func(), error) {
if
bgcfg
.
ConsensusMaxUpdateThreshold
>
0
{
if
bgcfg
.
ConsensusMaxUpdateThreshold
>
0
{
copts
=
append
(
copts
,
WithMaxUpdateThreshold
(
time
.
Duration
(
bgcfg
.
ConsensusMaxUpdateThreshold
)))
copts
=
append
(
copts
,
WithMaxUpdateThreshold
(
time
.
Duration
(
bgcfg
.
ConsensusMaxUpdateThreshold
)))
}
}
if
bgcfg
.
ConsensusMaxBlockLag
>
0
{
copts
=
append
(
copts
,
WithMaxBlockLag
(
bgcfg
.
ConsensusMaxBlockLag
))
}
if
bgcfg
.
ConsensusMinPeerCount
>
0
{
if
bgcfg
.
ConsensusMinPeerCount
>
0
{
copts
=
append
(
copts
,
WithMinPeerCount
(
uint64
(
bgcfg
.
ConsensusMinPeerCount
)))
copts
=
append
(
copts
,
WithMinPeerCount
(
uint64
(
bgcfg
.
ConsensusMinPeerCount
)))
}
}
...
...
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