Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

refactor(bridge): use deterministic message status slot (w/o mapping) #6861

Merged
merged 18 commits into from
Jan 5, 2023
7 changes: 4 additions & 3 deletions packages/protocol/contracts/bridge/Bridge.sol
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import "./libs/LibBridgeProcess.sol";
import "./libs/LibBridgeRetry.sol";
import "./libs/LibBridgeSend.sol";
import "./libs/LibBridgeSignal.sol";
import "./libs/LibBridgeStatus.sol";

/**
* Bridge contract which is deployed on both L1 and L2. Mostly a thin wrapper
Expand All @@ -39,7 +40,7 @@ contract Bridge is EssentialContract, IBridge {

event MessageStatusChanged(
bytes32 indexed signal,
LibBridgeData.MessageStatus status
LibBridgeStatus.MessageStatus status
);

event DestChainEnabled(uint256 indexed chainId, bool enabled);
Expand Down Expand Up @@ -159,8 +160,8 @@ contract Bridge is EssentialContract, IBridge {

function getMessageStatus(
bytes32 signal
) public view virtual returns (LibBridgeData.MessageStatus) {
return state.messageStatus[signal];
) public view virtual returns (LibBridgeStatus.MessageStatus) {
return LibBridgeStatus.getMessageStatus(signal);
}

function context() public view returns (Context memory) {
Expand Down
47 changes: 1 addition & 46 deletions packages/protocol/contracts/bridge/libs/LibBridgeData.sol
Original file line number Diff line number Diff line change
Expand Up @@ -19,69 +19,24 @@ import "../IBridge.sol";
* @author dantaik <[email protected]>
*/
library LibBridgeData {
/*********************
* Structs *
*********************/
enum MessageStatus {
NEW,
RETRIABLE,
DONE,
FAILED
}

struct State {
// chainId => isEnabled
mapping(uint256 => bool) destChains;
// message hash => status
mapping(bytes32 => MessageStatus) messageStatus;
uint256 nextMessageId;
IBridge.Context ctx; // 3 slots
uint256[44] __gap;
uint256[45] __gap;
}

/*********************
* Constants *
*********************/

// TODO: figure out this value
bytes32 internal constant SIGNAL_PLACEHOLDER = bytes32(uint256(1));
uint256 internal constant CHAINID_PLACEHOLDER = type(uint256).max;
address internal constant SRC_CHAIN_SENDER_PLACEHOLDER =
0x0000000000000000000000000000000000000001;

/*********************
* Events *
*********************/

// Note: These events must match the ones defined in Bridge.sol.
event MessageSent(bytes32 indexed signal, IBridge.Message message);

event MessageStatusChanged(bytes32 indexed signal, MessageStatus status);

event DestChainEnabled(uint256 indexed chainId, bool enabled);

/*********************
* Internal Functions*
*********************/

/**
* @dev If messageStatus is same as in the messageStatus mapping,
* does nothing.
* @param state The current bridge state.
* @param signal The messageHash of the message.
* @param status The status of the message.
*/
function updateMessageStatus(
State storage state,
bytes32 signal,
MessageStatus status
) internal {
if (state.messageStatus[signal] != status) {
state.messageStatus[signal] = status;
emit LibBridgeData.MessageStatusChanged(signal, status);
}
}

/**
* @dev Hashes messages and returns the hash signed with
* "TAIKO_BRIDGE_MESSAGE" for verification.
Expand Down
14 changes: 8 additions & 6 deletions packages/protocol/contracts/bridge/libs/LibBridgeProcess.sol
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import "../EtherVault.sol";
import "./LibBridgeData.sol";
import "./LibBridgeInvoke.sol";
import "./LibBridgeSignal.sol";
import "./LibBridgeStatus.sol";

/**
* Process bridge messages on the destination chain.
Expand Down Expand Up @@ -55,7 +56,8 @@ library LibBridgeProcess {
// LibBridgeRetry.sol
bytes32 signal = message.hashMessage();
require(
state.messageStatus[signal] == LibBridgeData.MessageStatus.NEW,
LibBridgeStatus.getMessageStatus(signal) ==
LibBridgeStatus.MessageStatus.NEW,
"B:status"
);
// Message must have been "received" on the destChain (current chain)
Expand Down Expand Up @@ -83,13 +85,13 @@ library LibBridgeProcess {
// will actually consume the Ether.
message.owner.sendEther(message.depositValue);

LibBridgeData.MessageStatus status;
LibBridgeStatus.MessageStatus status;
uint256 refundAmount;

if (message.to == address(this) || message.to == address(0)) {
// For these two special addresses, the call will not be actually
// invoked but will be marked DONE. The callValue will be refunded.
status = LibBridgeData.MessageStatus.DONE;
status = LibBridgeStatus.MessageStatus.DONE;
refundAmount = message.callValue;
} else {
uint256 gasLimit = msg.sender == message.owner
Expand All @@ -104,16 +106,16 @@ library LibBridgeProcess {
});

if (success) {
status = LibBridgeData.MessageStatus.DONE;
status = LibBridgeStatus.MessageStatus.DONE;
} else {
status = LibBridgeData.MessageStatus.RETRIABLE;
status = LibBridgeStatus.MessageStatus.RETRIABLE;
if (ethVault != address(0)) {
ethVault.sendEther(message.callValue);
}
}
}

state.updateMessageStatus(signal, status);
LibBridgeStatus.updateMessageStatus(signal, status);

address refundAddress = message.refundAddress == address(0)
? message.owner
Expand Down
14 changes: 9 additions & 5 deletions packages/protocol/contracts/bridge/libs/LibBridgeRetry.sol
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ pragma solidity ^0.8.9;
import "../EtherVault.sol";
import "./LibBridgeInvoke.sol";
import "./LibBridgeData.sol";
import "./LibBridgeStatus.sol";

/**
* Retry bridge messages.
Expand Down Expand Up @@ -50,8 +51,8 @@ library LibBridgeRetry {

bytes32 signal = message.hashMessage();
require(
state.messageStatus[signal] ==
LibBridgeData.MessageStatus.RETRIABLE,
LibBridgeStatus.getMessageStatus(signal) ==
LibBridgeStatus.MessageStatus.RETRIABLE,
"B:notFound"
);

Expand All @@ -69,11 +70,14 @@ library LibBridgeRetry {
gasLimit: gasleft()
})
) {
state.updateMessageStatus(signal, LibBridgeData.MessageStatus.DONE);
LibBridgeStatus.updateMessageStatus(
signal,
LibBridgeStatus.MessageStatus.DONE
);
} else if (isLastAttempt) {
state.updateMessageStatus(
LibBridgeStatus.updateMessageStatus(
signal,
LibBridgeData.MessageStatus.FAILED
LibBridgeStatus.MessageStatus.FAILED
);

address refundAddress = message.refundAddress == address(0)
Expand Down
29 changes: 14 additions & 15 deletions packages/protocol/contracts/bridge/libs/LibBridgeSignal.sol
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ import "../../common/AddressResolver.sol";
import "../../common/IHeaderSync.sol";
import "../../libs/LibBlockHeader.sol";
import "../../libs/LibTrieProof.sol";
import "./LibBridgeData.sol";
import "./LibBridgeStatus.sol";

/**
* Library for working with bridge signals.
Expand Down Expand Up @@ -43,9 +45,9 @@ library LibBridgeSignal {
address sender,
bytes32 signal
) internal onlyValidSenderAndSignal(sender, signal) {
bytes32 key = _key(sender, signal);
bytes32 k = _signalSlot(sender, signal);
assembly {
sstore(key, 1)
sstore(k, 1)
}
}

Expand All @@ -59,10 +61,10 @@ library LibBridgeSignal {
address sender,
bytes32 signal
) internal view onlyValidSenderAndSignal(sender, signal) returns (bool) {
bytes32 key = _key(sender, signal);
bytes32 k = _signalSlot(sender, signal);
uint256 v;
assembly {
v := sload(key)
v := sload(k)
}
return v == 1;
}
Expand All @@ -87,30 +89,27 @@ library LibBridgeSignal {
) internal view onlyValidSenderAndSignal(sender, signal) returns (bool) {
require(srcBridge != address(0), "B:srcBridge");

SignalProof memory mkp = abi.decode(proof, (SignalProof));
SignalProof memory sp = abi.decode(proof, (SignalProof));
LibTrieProof.verify({
stateRoot: mkp.header.stateRoot,
stateRoot: sp.header.stateRoot,
addr: srcBridge,
key: _key(sender, signal),
key: _signalSlot(sender, signal),
value: bytes32(uint256(1)),
mkproof: mkp.proof
mkproof: sp.proof
});
// get synced header hash of the header height specified in the proof
bytes32 syncedHeaderHash = IHeaderSync(resolver.resolve("taiko"))
.getSyncedHeader(mkp.header.height);
.getSyncedHeader(sp.header.height);
// check header hash specified in the proof matches the current chain
return
syncedHeaderHash != 0 &&
syncedHeaderHash == mkp.header.hashBlockHeader();
syncedHeaderHash == sp.header.hashBlockHeader();
}

/**
* Generate the storage key for a signal.
*/
function _key(
function _signalSlot(
address sender,
bytes32 signal
) private pure returns (bytes32) {
return keccak256(abi.encodePacked(sender, signal));
return keccak256(abi.encodePacked("SIGNAL", sender, signal));
}
}
62 changes: 62 additions & 0 deletions packages/protocol/contracts/bridge/libs/LibBridgeStatus.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
// SPDX-License-Identifier: MIT
//
// ╭━━━━╮╱╱╭╮╱╱╱╱╱╭╮╱╱╱╱╱╭╮
// ┃╭╮╭╮┃╱╱┃┃╱╱╱╱╱┃┃╱╱╱╱╱┃┃
// ╰╯┃┃┣┻━┳┫┃╭┳━━╮┃┃╱╱╭━━┫╰━┳━━╮
// ╱╱┃┃┃╭╮┣┫╰╯┫╭╮┃┃┃╱╭┫╭╮┃╭╮┃━━┫
// ╱╱┃┃┃╭╮┃┃╭╮┫╰╯┃┃╰━╯┃╭╮┃╰╯┣━━┃
// ╱╱╰╯╰╯╰┻┻╯╰┻━━╯╰━━━┻╯╰┻━━┻━━╯
pragma solidity ^0.8.9;

/**
* @author dantaik <[email protected]>
*/
library LibBridgeStatus {
enum MessageStatus {
NEW,
RETRIABLE,
DONE,
FAILED
}

event MessageStatusChanged(bytes32 indexed signal, MessageStatus status);

/**
* @dev If messageStatus is same as in the messageStatus mapping,
* does nothing.
* @param signal The messageHash of the message.
* @param status The status of the message.
*/
function updateMessageStatus(
bytes32 signal,
MessageStatus status
) internal {
if (getMessageStatus(signal) != status) {
_setMessageStatus(signal, status);
emit LibBridgeStatus.MessageStatusChanged(signal, status);
}
}

function getMessageStatus(
bytes32 signal
) internal view returns (MessageStatus) {
bytes32 k = _statusSlot(signal);
uint256 v;
assembly {
v := sload(k)
}
return MessageStatus(v);
}

function _setMessageStatus(bytes32 signal, MessageStatus status) private {
bytes32 k = _statusSlot(signal);
uint256 v = uint256(status);
assembly {
sstore(k, v)
}
}

function _statusSlot(bytes32 signal) private pure returns (bytes32) {
return keccak256(abi.encodePacked("MESSAGE_STATUS", signal));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,21 +9,20 @@
pragma solidity ^0.8.9;

import "../../../bridge/libs/LibBridgeData.sol";
import "../../../bridge/libs/LibBridgeStatus.sol";

contract TestLibBridgeData {
LibBridgeData.State public state;

function updateMessageStatus(
bytes32 signal,
LibBridgeData.MessageStatus status
LibBridgeStatus.MessageStatus status
) public {
LibBridgeData.updateMessageStatus(state, signal, status);
LibBridgeStatus.updateMessageStatus(signal, status);
}

function getMessageStatus(
bytes32 signal
) public view returns (LibBridgeData.MessageStatus) {
return state.messageStatus[signal];
) public view returns (LibBridgeStatus.MessageStatus) {
return LibBridgeStatus.getMessageStatus(signal);
}

function hashMessage(
Expand Down
4 changes: 2 additions & 2 deletions packages/protocol/docs/bridge/Bridge.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ uint256[50] __gap
### MessageStatusChanged

```solidity
event MessageStatusChanged(bytes32 signal, enum LibBridgeData.MessageStatus status)
event MessageStatusChanged(bytes32 signal, enum LibBridgeStatus.MessageStatus status)
```

### DestChainEnabled
Expand Down Expand Up @@ -120,7 +120,7 @@ by the specified sender.
### getMessageStatus

```solidity
function getMessageStatus(bytes32 signal) public view virtual returns (enum LibBridgeData.MessageStatus)
function getMessageStatus(bytes32 signal) public view virtual returns (enum LibBridgeStatus.MessageStatus)
```

### context
Expand Down
Loading