Skip to content
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions deployments.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,6 @@ debug_mode = true
[31337.address]
ibc_handler = "0xaa43d337145E8930d01cb4E60Abf6595C692921E"
mock_cross_contract = "0xff77D90D6aA12db33d3Ba50A34fB25401f6e4c4F"
cross_simple_module = "0x2F5703804E29F4252FA9405B8D357220d11b3bd9"
mock_client = "0xaE1C9125BbcF63bf51294C4D15CBD472782E330D"
cross_simple_module = "0xa7f733a4fEA1071f58114b203F57444969b86524"
mock_client = "0x87d7778dbc81251D5A0D78DFD8a0C359887E98C9"
deployer = "0xa89F47C6b463f74d87572b058427dA0A13ec5425"
2 changes: 1 addition & 1 deletion pkg/consts/contract.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import (

const (
IBCHandlerAddress = "0xaa43d337145E8930d01cb4E60Abf6595C692921E"
CrossSimpleModuleAddress = "0x2F5703804E29F4252FA9405B8D357220d11b3bd9"
CrossSimpleModuleAddress = "0xa7f733a4fEA1071f58114b203F57444969b86524"
)

type contractConfig struct{}
Expand Down
403 changes: 379 additions & 24 deletions pkg/contract/crosssimplemodule/crosssimplemodule.go

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion pkg/testing/chain_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ func (chain *Chain) findEventOnContractCall(ctx context.Context, txID []byte) (*

for iter.Next() {
ev := iter.Event
if ev.TxId == idHash {
if ev.TxID == idHash {
return ev, nil
}
}
Expand Down
52 changes: 50 additions & 2 deletions pkg/testing/cross_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ func (suite *CrossTestSuite) TestRecvPacket() {
suite.Require().NoError(err)
suite.Require().True(event.Success)
suite.Require().Equal(event.Ret, successMsg)
suite.Require().Equal(event.TxId, crypto.Keccak256Hash(txID))
suite.Require().Equal(event.TxID, crypto.Keccak256Hash(txID))
suite.Require().Equal(event.TxIndex, uint8(1))
}

Expand Down Expand Up @@ -97,7 +97,7 @@ func (suite *CrossTestSuite) TestRecvPacket() {
suite.Require().NoError(err)
suite.Require().False(event.Success)
suite.Require().Empty(event.Ret)
suite.Require().Equal(event.TxId, crypto.Keccak256Hash(txID))
suite.Require().Equal(event.TxID, crypto.Keccak256Hash(txID))
suite.Require().Equal(event.TxIndex, uint8(1))
}
}
Expand Down Expand Up @@ -144,6 +144,54 @@ func (suite *CrossTestSuite) createPacket(txID []byte, callInfo []byte) packets.
return packets.NewPacketData(nil, pdc)
}

func (suite *CrossTestSuite) TestInitiateTx_ShouldFailBecauseTxRunNotImplemented() {
ctx := context.Background()
opts := suite.chain.LegacyTxOpts(ctx, 0)

xcc, err := xcctypes.PackCrossChainChannel(&xcctypes.ChannelInfo{})
suite.Require().NoError(err)

authTypeBinding := crosssimplemodule.AuthTypeData{
Mode: uint8(authtypes.AuthMode_AUTH_MODE_CHANNEL),
Option: crosssimplemodule.GoogleProtobufAnyData{
TypeUrl: xcc.TypeUrl,
Value: xcc.Value,
},
}
signerBinding := crosssimplemodule.AccountData{
Id: opts.From.Bytes(),
AuthType: authTypeBinding,
}

ctBinding := crosssimplemodule.ContractTransactionData{
CrossChainChannel: crosssimplemodule.GoogleProtobufAnyData{
TypeUrl: xcc.TypeUrl,
Value: xcc.Value,
},
Signers: []crosssimplemodule.AccountData{signerBinding},
CallInfo: []byte("dummy call info"),
}

msg := crosssimplemodule.MsgInitiateTxData{
ChainId: fmt.Sprintf("%d", suite.chain.chainID),
Nonce: 0,
CommitProtocol: 0,
ContractTransactions: []crosssimplemodule.ContractTransactionData{ctBinding},
Signers: []crosssimplemodule.AccountData{signerBinding},
TimeoutHeight: crosssimplemodule.IbcCoreClientV1HeightData{},
TimeoutTimestamp: uint64(time.Now().Add(5 * time.Minute).Unix()),
}

err = suite.chain.TxSyncIfNoError(ctx)(
suite.chain.CrossSimpleModule.InitiateTx(opts, msg),
)

suite.Require().Error(err, "transaction should have reverted, but it succeeded")
suite.T().Logf("Received expected error from TxSync: %s", err.Error())

suite.Require().Contains(err.Error(), "failed to call transaction", "Error message should indicate a failed receipt")
}

func TestChainTestSuite(t *testing.T) {
suite.Run(t, new(CrossTestSuite))
}
12 changes: 11 additions & 1 deletion script/DeployAll.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ import {
// === App ===
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Deploy sample

import {IContractModule} from "src/core/IContractModule.sol";
import {CrossSimpleModule} from "src/core/CrossSimpleModule.sol";
import {TxAuthManager} from "src/core/TxAuthManager.sol";
import {TxManager} from "src/core/TxManager.sol";
import {MockClient} from "@hyperledger-labs/yui-ibc-solidity/contracts/clients/mock/MockClient.sol";
import {MockCrossContract} from "src/example/MockCrossContract.sol";

Expand Down Expand Up @@ -69,7 +71,15 @@ contract DeployAll is Script, Config {
MockCrossContract app = new MockCrossContract();
console2.log(" MockCrossContract:", address(app));

CrossSimpleModule module = new CrossSimpleModule(handler, IContractModule(address(app)), debugMode);
TxAuthManager txAuthManager = new TxAuthManager();
console2.log(" TxAuthManager:", address(txAuthManager));

TxManager txManager = new TxManager();
console2.log(" TxManager:", address(txManager));

CrossSimpleModule module = new CrossSimpleModule(
handler, address(txAuthManager), address(txManager), IContractModule(address(app)), debugMode
);
console2.log(" CrossSimpleModule:", address(module));

MockClient mclient = new MockClient(address(handler));
Expand Down
16 changes: 9 additions & 7 deletions src/core/CrossModule.sol
Original file line number Diff line number Diff line change
Expand Up @@ -14,23 +14,25 @@ import "./PacketHandler.sol";
import "./IBCKeeper.sol";

import {Initiator} from "./Initiator.sol";
import {TxAuthManager} from "./TxAuthManager.sol";
import {TxManager} from "./TxManager.sol";
import {TxRunner} from "./TxRunner.sol";
import {DelegatedLogicHandler} from "./DelegatedLogicHandler.sol";
import {CrossStore} from "./CrossStore.sol";

abstract contract CrossModule is
AccessControl,
IIBCModule,
IBCKeeper,
PacketHandler,
CrossStore,
Initiator,
TxAuthManager,
TxManager,
TxRunner
DelegatedLogicHandler
{
bytes32 public constant IBC_ROLE = keccak256("IBC_ROLE");

constructor(IIBCHandler ibcHandler_) Initiator() IBCKeeper(ibcHandler_) {
constructor(IIBCHandler ibcHandler_, address txAuthManager_, address txManager_)
Initiator()
IBCKeeper(ibcHandler_)
DelegatedLogicHandler(txAuthManager_, txManager_)
{
_grantRole(IBC_ROLE, address(ibcHandler_));
}

Expand Down
8 changes: 7 additions & 1 deletion src/core/CrossSimpleModule.sol
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,13 @@ import "./IContractModule.sol";
import {IIBCHandler} from "@hyperledger-labs/yui-ibc-solidity/contracts/core/25-handler/IIBCHandler.sol";

contract CrossSimpleModule is CrossModule, SimpleContractRegistry, TxAtomicSimple {
constructor(IIBCHandler ibcHandler_, IContractModule module, bool debugMode) CrossModule(ibcHandler_) {
constructor(
IIBCHandler ibcHandler_,
address txAuthManager_,
address txManager_,
IContractModule module,
bool debugMode
) CrossModule(ibcHandler_, txAuthManager_, txManager_) {
if (debugMode) {
_grantRole(IBC_ROLE, _msgSender());
}
Expand Down
70 changes: 70 additions & 0 deletions src/core/DelegatedLogicHandler.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
// SPDX-License-Identifier: Apache-2.0
// solhint-disable avoid-low-level-calls
pragma solidity ^0.8.20;

import {TxAuthManagerBase} from "./TxAuthManagerBase.sol";
import {TxManagerBase} from "./TxManagerBase.sol";
import {ITxAuthManager} from "./ITxAuthManager.sol";
import {ITxManager} from "./ITxManager.sol";

import {MsgInitiateTx} from "../proto/cross/core/initiator/Initiator.sol";
import {Account, TxAuthState} from "../proto/cross/core/auth/Auth.sol";

abstract contract DelegatedLogicHandler is TxAuthManagerBase, TxManagerBase {
address public immutable TX_AUTH_MANAGER;
address public immutable TX_MANAGER;

error DelegateCallAuthFailed();
error DelegateCallTxFailed();
error StaticCallAuthFailed();
error StaticCallTxFailed();

constructor(address txAuthManager_, address txManager_) {
TX_AUTH_MANAGER = txAuthManager_;
TX_MANAGER = txManager_;
}

function _initAuthState(bytes32 txID, Account.Data[] memory signers) internal virtual override {
(bool success,) =
TX_AUTH_MANAGER.delegatecall(abi.encodeWithSelector(ITxAuthManager.initAuthState.selector, txID, signers));
if (!success) revert DelegateCallAuthFailed();
}

function _sign(bytes32 txID, Account.Data[] memory signers) internal virtual override returns (bool) {
(bool success, bytes memory ret) =
TX_AUTH_MANAGER.delegatecall(abi.encodeWithSelector(ITxAuthManager.sign.selector, txID, signers));
if (!success) revert DelegateCallAuthFailed();
return abi.decode(ret, (bool));
}

function _isCompletedAuth(bytes32 txID) internal virtual override returns (bool) {
(bool success, bytes memory ret) =
TX_AUTH_MANAGER.delegatecall(abi.encodeWithSelector(ITxAuthManager.isCompletedAuth.selector, txID));
if (!success) revert DelegateCallAuthFailed();
return abi.decode(ret, (bool));
}

function _getAuthState(bytes32 txID) internal virtual override returns (TxAuthState.Data memory) {
(bool success, bytes memory ret) =
TX_AUTH_MANAGER.delegatecall(abi.encodeWithSelector(ITxAuthManager.getAuthState.selector, txID));
if (!success) revert DelegateCallAuthFailed();
return abi.decode(ret, (TxAuthState.Data));
}

function _createTx(bytes32 txID, MsgInitiateTx.Data calldata src) internal virtual override {
(bool success,) = TX_MANAGER.delegatecall(abi.encodeWithSelector(ITxManager.createTx.selector, txID, src));
if (!success) revert DelegateCallTxFailed();
}

function _runTxIfCompleted(bytes32 txID) internal virtual override {
(bool success,) = TX_MANAGER.delegatecall(abi.encodeWithSelector(ITxManager.runTxIfCompleted.selector, txID));
if (!success) revert DelegateCallTxFailed();
}

function _isTxRecorded(bytes32 txID) internal virtual override returns (bool) {
(bool success, bytes memory ret) =
TX_MANAGER.delegatecall(abi.encodeWithSelector(ITxManager.isTxRecorded.selector, txID));
if (!success) revert DelegateCallTxFailed();
return abi.decode(ret, (bool));
}
}
11 changes: 11 additions & 0 deletions src/core/ITxAuthManager.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.20;

import {Account, TxAuthState} from "../proto/cross/core/auth/Auth.sol";

interface ITxAuthManager {
function initAuthState(bytes32 txID, Account.Data[] calldata signers) external;
function isCompletedAuth(bytes32 txID) external returns (bool);
function sign(bytes32 txID, Account.Data[] calldata signers) external returns (bool);
function getAuthState(bytes32 txID) external returns (TxAuthState.Data memory);
}
10 changes: 10 additions & 0 deletions src/core/ITxManager.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.20;

import {MsgInitiateTx} from "../proto/cross/core/initiator/Initiator.sol";

interface ITxManager {
function createTx(bytes32 txID, MsgInitiateTx.Data calldata src) external;
function runTxIfCompleted(bytes32 txID) external;
function isTxRecorded(bytes32 txID) external returns (bool);
}
15 changes: 9 additions & 6 deletions src/core/Initiator.sol
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,9 @@ import {Strings} from "@openzeppelin/contracts/utils/Strings.sol";
import {MsgInitiateTx, MsgInitiateTxResponse, QuerySelfXCCResponse} from "../proto/cross/core/initiator/Initiator.sol";
import {Account} from "../proto/cross/core/auth/Auth.sol";

abstract contract Initiator is IInitiator, TxAuthManagerBase, TxManagerBase {
import "@openzeppelin/contracts/utils/ReentrancyGuard.sol";

abstract contract Initiator is IInitiator, TxAuthManagerBase, TxManagerBase, ReentrancyGuard {
bytes32 public immutable CHAIN_ID_HASH;

constructor() {
Expand All @@ -19,6 +21,7 @@ abstract contract Initiator is IInitiator, TxAuthManagerBase, TxManagerBase {
function initiateTx(MsgInitiateTx.Data calldata msg_)
external
override
nonReentrant
returns (MsgInitiateTxResponse.Data memory resp)
{
// chain_id check
Expand All @@ -38,20 +41,20 @@ abstract contract Initiator is IInitiator, TxAuthManagerBase, TxManagerBase {
// generate txID
bytes32 txIDHash = sha256(MsgInitiateTx.encode(msg_));
bytes memory txID = abi.encodePacked(txIDHash);
if (isTxRecorded(txIDHash)) revert IInitiator.TxIDAlreadyExists(txIDHash);
if (_isTxRecorded(txIDHash)) revert IInitiator.TxIDAlreadyExists(txIDHash);

// persist as PENDING
createTx(txIDHash, msg_);
_createTx(txIDHash, msg_);

// auth init & sign
Account.Data[] memory required = _getRequiredAccounts(msg_);
initAuthState(txIDHash, required);
bool completed = sign(txIDHash, msg_.signers);
_initAuthState(txIDHash, required);
bool completed = _sign(txIDHash, msg_.signers);

emit TxInitiated(txID, msg.sender);

if (completed) {
runTxIfCompleted(txIDHash);
_runTxIfCompleted(txIDHash);
return MsgInitiateTxResponse.Data({
txID: txID, status: MsgInitiateTxResponse.InitiateTxStatus.INITIATE_TX_STATUS_VERIFIED
});
Expand Down
27 changes: 22 additions & 5 deletions src/core/TxAuthManager.sol
Original file line number Diff line number Diff line change
Expand Up @@ -4,22 +4,39 @@ pragma solidity ^0.8.20;
import {TxAuthManagerBase} from "./TxAuthManagerBase.sol";
import {CrossStore} from "./CrossStore.sol";
import {Account, TxAuthState} from "../proto/cross/core/auth/Auth.sol";
import {ITxAuthManager} from "./ITxAuthManager.sol";

abstract contract TxAuthManager is TxAuthManagerBase, CrossStore {
function initAuthState(bytes32 txID, Account.Data[] memory signers) internal virtual override {
contract TxAuthManager is TxAuthManagerBase, CrossStore, ITxAuthManager {
function initAuthState(bytes32 txID, Account.Data[] calldata signers) external override {
_initAuthState(txID, signers);
}

function isCompletedAuth(bytes32 txID) external override returns (bool) {
return _isCompletedAuth(txID);
}

function sign(bytes32 txID, Account.Data[] calldata signers) external override returns (bool) {
return _sign(txID, signers);
}

function getAuthState(bytes32 txID) external override returns (TxAuthState.Data memory) {
return _getAuthState(txID);
}

function _initAuthState(bytes32 txID, Account.Data[] memory signers) internal virtual override {
CrossStore.AuthStorage storage s = _getAuthStorage();
if (s.authInitialized[txID]) revert AuthStateAlreadyInitialized(txID);
_setStateFromRemainingList(s, txID, signers);
s.authInitialized[txID] = true;
}

function isCompletedAuth(bytes32 txID) internal view virtual override returns (bool) {
function _isCompletedAuth(bytes32 txID) internal virtual override returns (bool) {
CrossStore.AuthStorage storage s = _getAuthStorage();
if (!s.authInitialized[txID]) revert IDNotFound(txID);
return s.remainingCount[txID] == 0;
}

function sign(bytes32 txID, Account.Data[] memory signers) internal virtual override returns (bool) {
function _sign(bytes32 txID, Account.Data[] memory signers) internal virtual override returns (bool) {
CrossStore.AuthStorage storage s = _getAuthStorage();
if (!s.authInitialized[txID]) revert IDNotFound(txID);
if (s.remainingCount[txID] == 0) revert AuthAlreadyCompleted(txID);
Expand All @@ -35,7 +52,7 @@ abstract contract TxAuthManager is TxAuthManagerBase, CrossStore {
return s.remainingCount[txID] == 0;
}

function getAuthState(bytes32 txID) internal view virtual override returns (TxAuthState.Data memory) {
function _getAuthState(bytes32 txID) internal virtual override returns (TxAuthState.Data memory) {
CrossStore.AuthStorage storage s = _getAuthStorage();
if (!s.authInitialized[txID]) revert IDNotFound(txID);
Account.Data[] memory remains = _getRemainingSigners(s, txID);
Expand Down
8 changes: 4 additions & 4 deletions src/core/TxAuthManagerBase.sol
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ abstract contract TxAuthManagerBase {
error IDNotFound(bytes32 txID);
error AuthAlreadyCompleted(bytes32 txID);

function initAuthState(bytes32 txID, Account.Data[] memory signers) internal virtual;
function isCompletedAuth(bytes32 txID) internal view virtual returns (bool);
function sign(bytes32 txID, Account.Data[] memory signers) internal virtual returns (bool);
function getAuthState(bytes32 txID) internal view virtual returns (TxAuthState.Data memory);
function _initAuthState(bytes32 txID, Account.Data[] memory signers) internal virtual;
function _isCompletedAuth(bytes32 txID) internal virtual returns (bool);
function _sign(bytes32 txID, Account.Data[] memory signers) internal virtual returns (bool);
function _getAuthState(bytes32 txID) internal virtual returns (TxAuthState.Data memory);
}
Loading
Loading