Skip to content

Commit 062b35a

Browse files
authored
Merge pull request #21 from datachainlab/impl-initiator
feat: impl initiator
2 parents 5ac4183 + 9603c02 commit 062b35a

File tree

18 files changed

+401
-38
lines changed

18 files changed

+401
-38
lines changed

src/core/CrossModule.sol

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,16 +13,30 @@ import {Packet} from "@hyperledger-labs/yui-ibc-solidity/contracts/core/04-chann
1313
import "./PacketHandler.sol";
1414
import "./IBCKeeper.sol";
1515

16-
abstract contract CrossModule is AccessControl, IIBCModule, IBCKeeper, PacketHandler {
16+
import {Initiator} from "./Initiator.sol";
17+
import {TxAuthManager} from "./TxAuthManager.sol";
18+
import {TxManager} from "./TxManager.sol";
19+
import {TxRunner} from "./TxRunner.sol";
20+
21+
abstract contract CrossModule is
22+
AccessControl,
23+
IIBCModule,
24+
IBCKeeper,
25+
PacketHandler,
26+
Initiator,
27+
TxAuthManager,
28+
TxManager,
29+
TxRunner
30+
{
1731
bytes32 public constant IBC_ROLE = keccak256("IBC_ROLE");
1832

19-
constructor(IIBCHandler ibcHandler_) IBCKeeper(ibcHandler_) {
33+
constructor(IIBCHandler ibcHandler_) Initiator() IBCKeeper(ibcHandler_) {
2034
_grantRole(IBC_ROLE, address(ibcHandler_));
2135
}
2236

23-
function supportsInterface(bytes4 interfaceId) public view virtual override(AccessControl, IERC165) returns (bool) {
24-
return interfaceId == type(IIBCModule).interfaceId || interfaceId == type(IIBCModuleInitializer).interfaceId
25-
|| super.supportsInterface(interfaceId);
37+
function supportsInterface(bytes4 interfaceID) public view virtual override(AccessControl, IERC165) returns (bool) {
38+
return interfaceID == type(IIBCModule).interfaceId || interfaceID == type(IIBCModuleInitializer).interfaceId
39+
|| super.supportsInterface(interfaceID);
2640
}
2741

2842
// function initiateTx() external {}

src/core/CrossStore.sol

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
// SPDX-License-Identifier: Apache-2.0
2+
pragma solidity ^0.8.20;
3+
4+
import {MsgInitiateTx, MsgInitiateTxResponse} from "../proto/cross/core/initiator/Initiator.sol";
5+
import {Account} from "../proto/cross/core/auth/Auth.sol";
6+
import {CoordinatorState} from "src/proto/cross/core/atomic/simple/AtomicSimple.sol";
7+
8+
abstract contract CrossStore {
9+
// keccak256(abi.encode(uint256(keccak256("cross.core.auth")) - 1)) & ~bytes32(uint256(0xff))
10+
bytes32 internal constant AUTH_STORAGE_LOCATION =
11+
hex"93e3b8eb4220ad7cd6d04c9032dc8d35824a37bf01220f3aeab27e9ece04f300";
12+
13+
// keccak256(abi.encode(uint256(keccak256("cross.core.tx")) - 1)) & ~bytes32(uint256(0xff))
14+
bytes32 internal constant TX_STORAGE_LOCATION =
15+
hex"2af14e14eac421b9203c410963a611c3e22a3e7dd2f462f3872422d801fd5a00";
16+
17+
// keccak256(abi.encode(uint256(keccak256("cross.core.coord")) - 1)) & ~bytes32(uint256(0xff))
18+
bytes32 internal constant COORD_STORAGE_LOCATION =
19+
hex"ef61b39f0bec0016a7c6e65e3ee8d2ea1b2327afbd737de3be2c6827c42f9600";
20+
21+
struct AuthStorage {
22+
mapping(bytes32 => mapping(bytes32 => bool)) remaining;
23+
mapping(bytes32 => uint256) remainingCount;
24+
mapping(bytes32 => bool) authInitialized;
25+
mapping(bytes32 => Account.Data[]) requiredAccounts;
26+
}
27+
28+
struct TxStorage {
29+
mapping(bytes32 => MsgInitiateTx.Data) txMsg;
30+
mapping(bytes32 => MsgInitiateTxResponse.InitiateTxStatus) txStatus;
31+
}
32+
33+
struct CoordEntry {
34+
bool exists;
35+
CoordinatorState.Data data;
36+
}
37+
38+
struct CoordStorage {
39+
mapping(bytes32 => CoordEntry) states;
40+
}
41+
42+
function _getAuthStorage() internal pure returns (AuthStorage storage $) {
43+
// solhint-disable-next-line no-inline-assembly
44+
assembly { $.slot := AUTH_STORAGE_LOCATION }
45+
}
46+
47+
function _getTxStorage() internal pure returns (TxStorage storage $) {
48+
// solhint-disable-next-line no-inline-assembly
49+
assembly { $.slot := TX_STORAGE_LOCATION }
50+
}
51+
52+
function _getCoordStorage() internal pure returns (CoordStorage storage $) {
53+
// solhint-disable-next-line no-inline-assembly
54+
assembly { $.slot := COORD_STORAGE_LOCATION }
55+
}
56+
}

src/core/IAuthenticator.sol

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,11 @@ import {
1212
} from "../proto/cross/core/auth/Auth.sol";
1313

1414
interface IAuthenticator {
15-
event TxSigned(address indexed signer, bytes32 indexed txId, AuthType.AuthMode method);
15+
error AuthStateAlreadyInitialized(bytes32 txID);
16+
error IDNotFound(bytes32 txID);
17+
error AuthAlreadyCompleted(bytes32 txID);
18+
19+
event TxSigned(address indexed signer, bytes32 indexed txID, AuthType.AuthMode method);
1620

1721
function signTx(MsgSignTx.Data calldata msg_) external returns (MsgSignTxResponse.Data memory);
1822

src/core/IContractModule.sol

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ interface IContractModule {
2222

2323
// CrossContext is a context in cross-chain transaction
2424
struct CrossContext {
25-
bytes txId;
25+
bytes txID;
2626
uint8 txIndex;
2727
Account.Data[] signers;
2828
}

src/core/ICoordinator.sol

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ import {
77
} from "src/proto/cross/core/atomic/simple/AtomicSimple.sol";
88

99
interface ICoordinator {
10+
error CoordinatorStateNotFound(bytes32 txID);
11+
1012
function coordinatorState(QueryCoordinatorStateRequest.Data calldata req)
1113
external
1214
view

src/core/IInitiator.sol

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,13 @@ pragma solidity ^0.8.20;
44
import {MsgInitiateTx, MsgInitiateTxResponse, QuerySelfXCCResponse} from "../proto/cross/core/initiator/Initiator.sol";
55

66
interface IInitiator {
7-
event TxInitiated(bytes txId, address indexed proposer);
7+
error UnexpectedChainID(bytes32 expectedHash, bytes32 gotHash);
8+
error MessageTimeoutHeight(uint256 blockNumber, uint64 timeoutVersionHeight);
9+
error MessageTimeoutTimestamp(uint256 blockTimestamp, uint64 timeoutTimestamp);
10+
error TxIDAlreadyExists(bytes32 txIDHash);
11+
error SelfXCCNotImplemented();
12+
13+
event TxInitiated(bytes txID, address indexed proposer);
814

915
function initiateTx(MsgInitiateTx.Data calldata msg_) external returns (MsgInitiateTxResponse.Data memory resp);
1016

src/core/Initiator.sol

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
// SPDX-License-Identifier: Apache-2.0
2+
pragma solidity ^0.8.20;
3+
4+
import {IInitiator} from "./IInitiator.sol";
5+
import {TxAuthManagerBase} from "./TxAuthManagerBase.sol";
6+
import {TxManagerBase} from "./TxManagerBase.sol";
7+
import {Strings} from "@openzeppelin/contracts/utils/Strings.sol";
8+
9+
import {MsgInitiateTx, MsgInitiateTxResponse, QuerySelfXCCResponse} from "../proto/cross/core/initiator/Initiator.sol";
10+
import {Account} from "../proto/cross/core/auth/Auth.sol";
11+
12+
abstract contract Initiator is IInitiator, TxAuthManagerBase, TxManagerBase {
13+
bytes32 public immutable CHAIN_ID_HASH;
14+
15+
constructor() {
16+
CHAIN_ID_HASH = keccak256(bytes(Strings.toString(block.chainid)));
17+
}
18+
19+
function initiateTx(MsgInitiateTx.Data calldata msg_)
20+
external
21+
override
22+
returns (MsgInitiateTxResponse.Data memory resp)
23+
{
24+
// chain_id check
25+
bytes32 got = keccak256(bytes(msg_.chain_id));
26+
if (got != CHAIN_ID_HASH) revert IInitiator.UnexpectedChainID(CHAIN_ID_HASH, got);
27+
28+
// timeouts
29+
uint64 rh = msg_.timeout_height.revision_height;
30+
if (rh != 0 && (block.number + 1 > uint256(rh))) {
31+
revert IInitiator.MessageTimeoutHeight(block.number, rh);
32+
}
33+
// slither-disable-next-line timestamp
34+
if (msg_.timeout_timestamp > 0 && (block.timestamp + 1 > msg_.timeout_timestamp)) {
35+
revert IInitiator.MessageTimeoutTimestamp(block.timestamp, msg_.timeout_timestamp);
36+
}
37+
38+
// generate txID
39+
bytes32 txIDHash = sha256(MsgInitiateTx.encode(msg_));
40+
bytes memory txID = abi.encodePacked(txIDHash);
41+
if (isTxRecorded(txIDHash)) revert IInitiator.TxIDAlreadyExists(txIDHash);
42+
43+
// persist as PENDING
44+
createTx(txIDHash, msg_);
45+
46+
// auth init & sign
47+
Account.Data[] memory required = _getRequiredAccounts(msg_);
48+
initAuthState(txIDHash, required);
49+
bool completed = sign(txIDHash, msg_.signers);
50+
51+
emit TxInitiated(txID, msg.sender);
52+
53+
if (completed) {
54+
runTxIfCompleted(txIDHash);
55+
return MsgInitiateTxResponse.Data({
56+
txID: txID, status: MsgInitiateTxResponse.InitiateTxStatus.INITIATE_TX_STATUS_VERIFIED
57+
});
58+
}
59+
return MsgInitiateTxResponse.Data({
60+
txID: txID, status: MsgInitiateTxResponse.InitiateTxStatus.INITIATE_TX_STATUS_PENDING
61+
});
62+
}
63+
64+
function selfXCC() external view virtual override returns (QuerySelfXCCResponse.Data memory) {
65+
revert IInitiator.SelfXCCNotImplemented();
66+
}
67+
68+
function _getRequiredAccounts(MsgInitiateTx.Data calldata msg_) internal pure returns (Account.Data[] memory out) {
69+
uint256 upper = 0;
70+
for (uint256 i = 0; i < msg_.contract_transactions.length; ++i) {
71+
upper += msg_.contract_transactions[i].signers.length;
72+
}
73+
out = new Account.Data[](upper);
74+
uint256 n = 0;
75+
76+
for (uint256 i = 0; i < msg_.contract_transactions.length; ++i) {
77+
Account.Data[] memory rs = msg_.contract_transactions[i].signers;
78+
for (uint256 j = 0; j < rs.length; ++j) {
79+
out[n] = rs[j];
80+
++n;
81+
}
82+
}
83+
return out;
84+
}
85+
}

src/core/TxAtomicSimple.sol

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ abstract contract TxAtomicSimple is IBCKeeper, PacketHandler, ContractRegistry {
2020
error UnexpectedTypeURL();
2121
error NotImplemented();
2222

23-
event OnContractCall(bytes indexed txId, uint8 indexed txIndex, bool indexed success, bytes ret);
23+
event OnContractCall(bytes indexed txID, uint8 indexed txIndex, bool indexed success, bytes ret);
2424

2525
function handlePacket(Packet memory packet) internal virtual override returns (bytes memory acknowledgement) {
2626
IContractModule module = getModule(packet);

src/core/TxAuthManager.sol

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
// SPDX-License-Identifier: Apache-2.0
2+
pragma solidity ^0.8.20;
3+
4+
import {TxAuthManagerBase} from "./TxAuthManagerBase.sol";
5+
import {CrossStore} from "./CrossStore.sol";
6+
import {Account, TxAuthState} from "../proto/cross/core/auth/Auth.sol";
7+
8+
abstract contract TxAuthManager is TxAuthManagerBase, CrossStore {
9+
function initAuthState(bytes32 txID, Account.Data[] memory signers) internal virtual override {
10+
CrossStore.AuthStorage storage s = _getAuthStorage();
11+
if (s.authInitialized[txID]) revert AuthStateAlreadyInitialized(txID);
12+
_setStateFromRemainingList(s, txID, signers);
13+
s.authInitialized[txID] = true;
14+
}
15+
16+
function isCompletedAuth(bytes32 txID) internal view virtual override returns (bool) {
17+
CrossStore.AuthStorage storage s = _getAuthStorage();
18+
if (!s.authInitialized[txID]) revert IDNotFound(txID);
19+
return s.remainingCount[txID] == 0;
20+
}
21+
22+
function sign(bytes32 txID, Account.Data[] memory signers) internal virtual override returns (bool) {
23+
CrossStore.AuthStorage storage s = _getAuthStorage();
24+
if (!s.authInitialized[txID]) revert IDNotFound(txID);
25+
if (s.remainingCount[txID] == 0) revert AuthAlreadyCompleted(txID);
26+
27+
for (uint256 i = 0; i < signers.length; ++i) {
28+
bytes32 key = _accountKey(signers[i]);
29+
if (s.remaining[txID][key]) {
30+
s.remaining[txID][key] = false;
31+
--s.remainingCount[txID];
32+
if (s.remainingCount[txID] == 0) return true;
33+
}
34+
}
35+
return s.remainingCount[txID] == 0;
36+
}
37+
38+
function getAuthState(bytes32 txID) internal view virtual override returns (TxAuthState.Data memory) {
39+
CrossStore.AuthStorage storage s = _getAuthStorage();
40+
if (!s.authInitialized[txID]) revert IDNotFound(txID);
41+
Account.Data[] memory remains = _getRemainingSigners(s, txID);
42+
return TxAuthState.Data({remaining_signers: remains});
43+
}
44+
45+
function _getRemainingSigners(CrossStore.AuthStorage storage s, bytes32 txID)
46+
internal
47+
view
48+
returns (Account.Data[] memory out)
49+
{
50+
uint256 total = s.requiredAccounts[txID].length;
51+
uint256 need = s.remainingCount[txID];
52+
out = new Account.Data[](need);
53+
uint256 p = 0;
54+
for (uint256 i = 0; i < total; ++i) {
55+
Account.Data memory acc = s.requiredAccounts[txID][i];
56+
if (s.remaining[txID][_accountKey(acc)]) {
57+
out[p] = acc;
58+
++p;
59+
if (p == need) break;
60+
}
61+
}
62+
}
63+
64+
function _setStateFromRemainingList(CrossStore.AuthStorage storage s, bytes32 txID, Account.Data[] memory list)
65+
internal
66+
{
67+
for (uint256 i = 0; i < list.length; ++i) {
68+
bytes32 key = _accountKey(list[i]);
69+
if (!s.remaining[txID][key]) {
70+
s.remaining[txID][key] = true;
71+
++s.remainingCount[txID];
72+
s.requiredAccounts[txID].push(list[i]);
73+
}
74+
}
75+
}
76+
77+
function _accountKey(Account.Data memory a) internal pure returns (bytes32) {
78+
return keccak256(abi.encode(a.id, a.auth_type));
79+
}
80+
}

src/core/TxAuthManagerBase.sol

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
// SPDX-License-Identifier: Apache-2.0
2+
pragma solidity ^0.8.20;
3+
4+
import {Account, TxAuthState} from "../proto/cross/core/auth/Auth.sol";
5+
6+
abstract contract TxAuthManagerBase {
7+
error AuthStateAlreadyInitialized(bytes32 txID);
8+
error IDNotFound(bytes32 txID);
9+
error AuthAlreadyCompleted(bytes32 txID);
10+
11+
function initAuthState(bytes32 txID, Account.Data[] memory signers) internal virtual;
12+
function isCompletedAuth(bytes32 txID) internal view virtual returns (bool);
13+
function sign(bytes32 txID, Account.Data[] memory signers) internal virtual returns (bool);
14+
function getAuthState(bytes32 txID) internal view virtual returns (TxAuthState.Data memory);
15+
}

0 commit comments

Comments
 (0)