Skip to content
Closed
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 = "0xaE1C9125BbcF63bf51294C4D15CBD472782E330D"
mock_client = "0xa7f733a4fEA1071f58114b203F57444969b86524"
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 = "0xaE1C9125BbcF63bf51294C4D15CBD472782E330D"
)

type contractConfig struct{}
Expand Down
1 change: 1 addition & 0 deletions proto/cross/core/auth/Auth.proto
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ message MsgExtSignTx {

bytes txID = 1 [(gogoproto.casttype) = "github.com/datachainlab/cross/x/core/types.TxID"];
repeated Account signers = 2 [(gogoproto.nullable) = false];
repeated bytes signatures = 3;
}

message MsgExtSignTxResponse {
Expand Down
14 changes: 13 additions & 1 deletion script/DeployAll.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,10 @@ import {
// === App ===
import {IContractModule} from "src/core/IContractModule.sol";
import {CrossSimpleModule} from "src/core/CrossSimpleModule.sol";
import {IAuthExtensionVerifier} from "src/core/IAuthExtensionVerifier.sol";
import {MockClient} from "@hyperledger-labs/yui-ibc-solidity/contracts/clients/mock/MockClient.sol";
import {MockCrossContract} from "src/example/MockCrossContract.sol";
import {SampleEcrecoverVerifier} from "src/example/SampleEcrecoverVerifier.sol";

import {IIBCModuleInitializer} from "@hyperledger-labs/yui-ibc-solidity/contracts/core/26-router/IIBCModule.sol";
import {ILightClient} from "@hyperledger-labs/yui-ibc-solidity/contracts/core/02-client/ILightClient.sol";
Expand Down Expand Up @@ -69,7 +71,17 @@ contract DeployAll is Script, Config {
MockCrossContract app = new MockCrossContract();
console2.log(" MockCrossContract:", address(app));

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

string[] memory typeUrls = new string[](1);
typeUrls[0] = "/verifier.ecrecover";

IAuthExtensionVerifier[] memory verifiers = new IAuthExtensionVerifier[](1);
verifiers[0] = IAuthExtensionVerifier(verifier);

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

MockClient mclient = new MockClient(address(handler));
Expand Down
23 changes: 14 additions & 9 deletions src/core/Authenticator.sol
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
pragma solidity ^0.8.20;

import {IAuthenticator} from "./IAuthenticator.sol";
import {TxAuthManagerBase} from "./TxAuthManagerBase.sol";
import {TxAuthManagerBase} from "./TxAuthManager.sol";
import {TxManagerBase} from "./TxManagerBase.sol";

import {
Expand Down Expand Up @@ -33,14 +33,19 @@ abstract contract Authenticator is IAuthenticator, TxAuthManagerBase, TxManagerB
return MsgSignTxResponse.Data({tx_auth_completed: completed, log: ""});
}

function extSignTx(
MsgExtSignTx.Data calldata /*msg_*/
)
external
override
returns (MsgExtSignTxResponse.Data memory)
{
revert IAuthenticator.ExtSignTxNotImplemented();
function extSignTx(MsgExtSignTx.Data calldata msg_) external override returns (MsgExtSignTxResponse.Data memory) {
bytes32 txIDHash = sha256(msg_.txID);

_verifySignatures(txIDHash, msg_.signers, msg_.signatures);

bool completed = sign(txIDHash, msg_.signers);
if (completed) {
runTxIfCompleted(txIDHash);
}

emit TxSigned(msg.sender, txIDHash, AuthType.AuthMode.AUTH_MODE_EXTENSION);

return MsgExtSignTxResponse.Data({x: true});
}

function txAuthState(
Expand Down
7 changes: 6 additions & 1 deletion src/core/CrossModule.sol
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import {Authenticator} from "./Authenticator.sol";
import {TxAuthManager} from "./TxAuthManager.sol";
import {TxManager} from "./TxManager.sol";
import {TxRunner} from "./TxRunner.sol";
import {IAuthExtensionVerifier} from "./IAuthExtensionVerifier.sol";

abstract contract CrossModule is
AccessControl,
Expand All @@ -32,7 +33,11 @@ abstract contract CrossModule is
{
bytes32 public constant IBC_ROLE = keccak256("IBC_ROLE");

constructor(IIBCHandler ibcHandler_) Initiator() IBCKeeper(ibcHandler_) {
constructor(IIBCHandler ibcHandler_, string[] memory typeUrls, IAuthExtensionVerifier[] memory verifiers)
Initiator()
IBCKeeper(ibcHandler_)
TxAuthManager(typeUrls, verifiers)
{
_grantRole(IBC_ROLE, address(ibcHandler_));
}

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

contract CrossSimpleModule is CrossModule, SimpleContractRegistry, TxAtomicSimple {
constructor(IIBCHandler ibcHandler_, IContractModule module, bool debugMode) CrossModule(ibcHandler_) {
constructor(
IIBCHandler ibcHandler_,
IContractModule module,
string[] memory typeUrls,
IAuthExtensionVerifier[] memory verifiers,
bool debugMode
) CrossModule(ibcHandler_, typeUrls, verifiers) {
if (debugMode) {
_grantRole(IBC_ROLE, _msgSender());
}
Expand Down
2 changes: 2 additions & 0 deletions src/core/CrossStore.sol
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.20;

import {IAuthExtensionVerifier} from "./IAuthExtensionVerifier.sol";
import {MsgInitiateTx, MsgInitiateTxResponse} from "../proto/cross/core/initiator/Initiator.sol";
import {Account} from "../proto/cross/core/auth/Auth.sol";
import {CoordinatorState} from "src/proto/cross/core/atomic/simple/AtomicSimple.sol";
Expand All @@ -19,6 +20,7 @@ abstract contract CrossStore {
hex"ef61b39f0bec0016a7c6e65e3ee8d2ea1b2327afbd737de3be2c6827c42f9600";

struct AuthStorage {
mapping(string => IAuthExtensionVerifier) authVerifiers;
mapping(bytes32 => mapping(bytes32 => bool)) remaining;
mapping(bytes32 => uint256) remainingCount;
mapping(bytes32 => bool) authInitialized;
Expand Down
11 changes: 11 additions & 0 deletions src/core/IAuthExtensionVerifier.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} from "../proto/cross/core/auth/Auth.sol";

interface IAuthExtensionVerifier {
function verify(bytes32 txIDHash, Account.Data calldata signer, bytes calldata signature)
external
view
returns (bool isValid);
}
68 changes: 67 additions & 1 deletion src/core/TxAuthManager.sol
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,29 @@ 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 {Account, TxAuthState, AuthType} from "../proto/cross/core/auth/Auth.sol";
import {IAuthExtensionVerifier} from "./IAuthExtensionVerifier.sol";

abstract contract TxAuthManager is TxAuthManagerBase, CrossStore {
uint256 private constant MAX_SIGNERS_PER_TX = 32;

constructor(string[] memory typeUrls, IAuthExtensionVerifier[] memory verifiers) {
if (typeUrls.length != verifiers.length) revert ArrayLengthMismatch();

CrossStore.AuthStorage storage s = _getAuthStorage();

for (uint256 i = 0; i < typeUrls.length; ++i) {
string memory typeUrl = typeUrls[i];
IAuthExtensionVerifier verifier = verifiers[i];

if (bytes(typeUrl).length == 0) revert EmptyTypeUrl();
if (address(verifier) == address(0)) revert ZeroAddressVerifier();

s.authVerifiers[typeUrl] = verifier;
emit VerifierRegistered(typeUrl, address(verifier));
}
}

function initAuthState(bytes32 txID, Account.Data[] memory signers) internal virtual override {
CrossStore.AuthStorage storage s = _getAuthStorage();
if (s.authInitialized[txID]) revert AuthStateAlreadyInitialized(txID);
Expand Down Expand Up @@ -61,6 +81,52 @@ abstract contract TxAuthManager is TxAuthManagerBase, CrossStore {
}
}

function _verifySignatures(bytes32 txIDHash, Account.Data[] calldata signers, bytes[] calldata signatures)
internal
view
virtual
override
{
uint256 len = signers.length;
if (len != signatures.length) {
revert SignerCountMismatch(len, signatures.length);
}

if (len > MAX_SIGNERS_PER_TX) {
revert TooManySigners(len, MAX_SIGNERS_PER_TX);
}

CrossStore.AuthStorage storage s = _getAuthStorage();

// slither-disable-start calls-loop
for (uint256 i = 0; i < len; ++i) {
Account.Data calldata signer = signers[i];

if (signer.auth_type.mode != AuthType.AuthMode.AUTH_MODE_EXTENSION) {
revert AuthModeMismatch();
}

string calldata typeUrl = signer.auth_type.option.type_url;
IAuthExtensionVerifier verifier = s.authVerifiers[typeUrl];
if (address(verifier) == address(0)) {
revert VerifierNotFound(typeUrl);
}

bytes memory callData =
abi.encodeWithSelector(IAuthExtensionVerifier.verify.selector, txIDHash, signer, signatures[i]);

(bool ok, bytes memory ret) = address(verifier).staticcall(callData);
if (!ok) {
revert VerifierStaticCallFailed(typeUrl);
}
bool verified = abi.decode(ret, (bool));
if (!verified) {
revert VerifierReturnedFalse(txIDHash, typeUrl);
}
}
// slither-disable-end calls-loop
}

function _setStateFromRemainingList(CrossStore.AuthStorage storage s, bytes32 txID, Account.Data[] memory list)
internal
{
Expand Down
16 changes: 16 additions & 0 deletions src/core/TxAuthManagerBase.sol
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,25 @@ abstract contract TxAuthManagerBase {
error AuthStateAlreadyInitialized(bytes32 txID);
error IDNotFound(bytes32 txID);
error AuthAlreadyCompleted(bytes32 txID);
error SignerCountMismatch(uint256 signerCount, uint256 signatureCount);
error AuthModeMismatch();
error VerifierNotFound(string typeUrl);
error SignatureVerificationFailed(bytes32 txID);
error ArrayLengthMismatch();
error EmptyTypeUrl();
error ZeroAddressVerifier();
error VerifierStaticCallFailed(string typeUrl);
error VerifierReturnedFalse(bytes32 txIDHash, string typeUrl);
error TooManySigners(uint256 got, uint256 maxAllowed);

event VerifierRegistered(string typeUrl, address indexed verifier);

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 _verifySignatures(bytes32 txIDHash, Account.Data[] calldata signers, bytes[] calldata signatures)
internal
view
virtual;
}
23 changes: 23 additions & 0 deletions src/example/SampleEcrecoverVerifier.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.20;

import {IAuthExtensionVerifier} from "../core/IAuthExtensionVerifier.sol";
import {Account} from "../proto/cross/core/auth/Auth.sol";
import {ECDSA} from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol";
import {MessageHashUtils} from "@openzeppelin/contracts/utils/cryptography/MessageHashUtils.sol";

contract SampleEcrecoverVerifier is IAuthExtensionVerifier {
function verify(bytes32 txIdHash, Account.Data calldata signer, bytes calldata signature)
external
view
override
returns (bool isValid)
{
if (signer.id.length != 20) return false;
if (signature.length != 65) return false;
address signerAddress = address(bytes20(signer.id));
bytes32 messageHash = MessageHashUtils.toEthSignedMessageHash(txIdHash);
address recoveredAddress = ECDSA.recover(messageHash, signature);
return recoveredAddress != address(0) && recoveredAddress == signerAddress;
}
}
Loading