Skip to content

Commit

Permalink
refactor(bridge): use deterministic message status slot (w/o mapping) (
Browse files Browse the repository at this point in the history
  • Loading branch information
dantaik authored Jan 5, 2023
1 parent a8e82d5 commit 915f3fe
Show file tree
Hide file tree
Showing 15 changed files with 347 additions and 210 deletions.
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

0 comments on commit 915f3fe

Please sign in to comment.