Skip to content

Commit

Permalink
port timechain#1544 to here (#45)
Browse files Browse the repository at this point in the history
Co-authored-by: David Craven <[email protected]>
  • Loading branch information
agryaznov and dvc94ch authored Feb 26, 2025
1 parent 62ce628 commit b8b5d47
Show file tree
Hide file tree
Showing 18 changed files with 125 additions and 63 deletions.
2 changes: 1 addition & 1 deletion foundry.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ solc = '0.8.28'
# - https://ethereumclassic.org/knowledge/history
evm_version = 'shanghai'
optimizer = true
optimizer_runs = 200000
optimizer_runs = 500000

###############
# EVM options #
Expand Down
8 changes: 0 additions & 8 deletions remappings.txt

This file was deleted.

2 changes: 1 addition & 1 deletion src/GatewayProxy.sol
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
pragma solidity >=0.8.0;

import {ERC1967} from "./utils/ERC1967.sol";
import {Context, CreateKind, IUniversalFactory} from "@universal-factory/IUniversalFactory.sol";
import {Context, CreateKind, IUniversalFactory} from "../lib/universal-factory/src/IUniversalFactory.sol";

contract GatewayProxy {
/**
Expand Down
15 changes: 9 additions & 6 deletions src/Primitives.sol
Original file line number Diff line number Diff line change
Expand Up @@ -281,7 +281,7 @@ library PrimitiveUtils {
bytes memory onGmpReceived = callback.callback;
bytes32 dataHash;
assembly ("memory-safe") {
let offset := add(onGmpReceived, 0xa4)
let offset := add(onGmpReceived, 0xc4)
dataHash := keccak256(add(offset, 0x20), mload(offset))
}
callback.eip712hash = bytes32(GMP_VERSION);
Expand Down Expand Up @@ -332,9 +332,10 @@ library PrimitiveUtils {
// | 0x0124..0x0144 <- onGmpReceived.id
// | 0x0144..0x0164 <- onGmpReceived.network
// | 0x0164..0x0184 <- onGmpReceived.source
// | 0x0184..0x01a4 <- onGmpReceived.data.offset
// | 0x01a4..0x01c4 <- onGmpReceived.data.length
// | 0x01c4........ <- onGmpReceived.data
// | 0x0184..0x01a4 <- onGmpReceived.nonce
// | 0x01a4..0x01c4 <- onGmpReceived.data.offset
// | 0x01c4..0x01e4 <- onGmpReceived.data.length
// | 0x01e4........ <- onGmpReceived.data
if (isCalldata) {
GmpMessage calldata m = _intoCalldataPointer(message);
callback.source = m.source;
Expand All @@ -345,10 +346,11 @@ library PrimitiveUtils {
callback.nonce = m.nonce;
bytes calldata data = m.data;
callback.callback = abi.encodeWithSignature(
"onGmpReceived(bytes32,uint128,bytes32,bytes)",
"onGmpReceived(bytes32,uint128,bytes32,uint64,bytes)",
callback.eip712hash,
callback.srcNetwork,
callback.source,
callback.nonce,
data
);
} else {
Expand All @@ -360,10 +362,11 @@ library PrimitiveUtils {
callback.gasLimit = m.gasLimit;
callback.nonce = m.nonce;
callback.callback = abi.encodeWithSignature(
"onGmpReceived(bytes32,uint128,bytes32,bytes)",
"onGmpReceived(bytes32,uint128,bytes32,uint64,bytes)",
callback.eip712hash,
callback.srcNetwork,
callback.source,
callback.nonce,
m.data
);
}
Expand Down
2 changes: 1 addition & 1 deletion src/interfaces/IGmpReceiver.sol
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ interface IGmpReceiver {
* @param payload The message payload with no specified format
* @return 32 byte result which will be stored together with GMP message
*/
function onGmpReceived(bytes32 id, uint128 network, bytes32 source, bytes calldata payload)
function onGmpReceived(bytes32 id, uint128 network, bytes32 source, uint64 nonce, bytes calldata payload)
external
payable
returns (bytes32);
Expand Down
4 changes: 2 additions & 2 deletions src/utils/GasUtils.sol
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ library GasUtils {
/**
* @dev Base cost of the `IExecutor.execute` method.
*/
uint256 internal constant EXECUTION_BASE_COST = EXECUTION_SELECTOR_OVERHEAD + 46960;
uint256 internal constant EXECUTION_BASE_COST = EXECUTION_SELECTOR_OVERHEAD + 46960 + 38;

/**
* @dev Base cost of the `IGateway.submitMessage` method.
Expand Down Expand Up @@ -347,7 +347,7 @@ library GasUtils {
uint256 gas = _toWord(messageSize) * 3;
gas = gas.saturatingAdd(_toWord(messageSize) * 6);
gas = gas.saturatingAdd(gasUsed);
uint256 memoryExpansion = messageSize.align32() + 0x80 + 0x0120 + 164 + 0x40;
uint256 memoryExpansion = messageSize.align32() + 0x80 + 0x0120 + 164 + 0x40 + 32;
{
// Selector + Signature + GmpMessage
uint256 words = messageSize.align32().saturatingAdd(388 + 31) >> 5;
Expand Down
5 changes: 3 additions & 2 deletions test/Batching.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ pragma solidity >=0.8.0;

import {Test, console, Vm} from "forge-std/Test.sol";
import {VmSafe} from "forge-std/Vm.sol";
import {FactoryUtils} from "@universal-factory/FactoryUtils.sol";
import {IUniversalFactory} from "@universal-factory/IUniversalFactory.sol";
import {FactoryUtils} from "../lib/universal-factory/src/FactoryUtils.sol";
import {IUniversalFactory} from "../lib/universal-factory/src/IUniversalFactory.sol";
import {TestUtils, SigningKey, SigningUtils} from "./TestUtils.sol";
import {GasSpender} from "./utils/GasSpender.sol";
import {BaseTest} from "./utils/BaseTest.sol";
Expand Down Expand Up @@ -161,6 +161,7 @@ contract Batching is BaseTest {
0x0000000000000000000000000000000000000000000000000000000000000000,
1,
0x0000000000000000000000000000000000000000000000000000000000000000,
0,
abi.encode(gasToWaste)
)
);
Expand Down
26 changes: 13 additions & 13 deletions test/GasUtils.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ contract GasUtilsTest is BaseTest {
dest: gmpReceiver,
destNetwork: DEST_NETWORK_ID,
gasLimit: gasLimit,
nonce: 0,
nonce: 1,
data: data
});

Expand Down Expand Up @@ -221,21 +221,21 @@ contract GasUtilsTest is BaseTest {

function test_gasUtils() external pure {
uint256 baseCost = GasUtils.EXECUTION_BASE_COST;
assertEq(GasUtils.estimateGas(0, 0, 0), 31528 + baseCost);
assertEq(GasUtils.estimateGas(0, 33, 0), 31901 + baseCost);
assertEq(GasUtils.estimateGas(33, 0, 0), 32561 + baseCost);
assertEq(GasUtils.estimateGas(20, 13, 0), 32301 + baseCost);
assertEq(GasUtils.estimateGas(0, 0, 0), 31531 + baseCost);
assertEq(GasUtils.estimateGas(0, 33, 0), 31904 + baseCost);
assertEq(GasUtils.estimateGas(33, 0, 0), 32564 + baseCost);
assertEq(GasUtils.estimateGas(20, 13, 0), 32304 + baseCost);

UFloat9x56 one = UFloatMath.ONE;
assertEq(GasUtils.estimateWeiCost(one, 0, 0, 0, 0), 31528 + baseCost);
assertEq(GasUtils.estimateWeiCost(one, 0, 0, 33, 0), 31901 + baseCost);
assertEq(GasUtils.estimateWeiCost(one, 0, 33, 0, 0), 32561 + baseCost);
assertEq(GasUtils.estimateWeiCost(one, 0, 20, 13, 0), 32301 + baseCost);
assertEq(GasUtils.estimateWeiCost(one, 0, 0, 0, 0), 31531 + baseCost);
assertEq(GasUtils.estimateWeiCost(one, 0, 0, 33, 0), 31904 + baseCost);
assertEq(GasUtils.estimateWeiCost(one, 0, 33, 0, 0), 32564 + baseCost);
assertEq(GasUtils.estimateWeiCost(one, 0, 20, 13, 0), 32304 + baseCost);

UFloat9x56 two = UFloat9x56.wrap(0x8080000000000000);
assertEq(GasUtils.estimateWeiCost(two, 0, 0, 0, 0), (31528 + baseCost) * 2);
assertEq(GasUtils.estimateWeiCost(two, 0, 0, 33, 0), (31901 + baseCost) * 2);
assertEq(GasUtils.estimateWeiCost(two, 0, 33, 0, 0), (32561 + baseCost) * 2);
assertEq(GasUtils.estimateWeiCost(two, 0, 20, 13, 0), (32301 + baseCost) * 2);
assertEq(GasUtils.estimateWeiCost(two, 0, 0, 0, 0), (31531 + baseCost) * 2);
assertEq(GasUtils.estimateWeiCost(two, 0, 0, 33, 0), (31904 + baseCost) * 2);
assertEq(GasUtils.estimateWeiCost(two, 0, 33, 0, 0), (32564 + baseCost) * 2);
assertEq(GasUtils.estimateWeiCost(two, 0, 20, 13, 0), (32304 + baseCost) * 2);
}
}
5 changes: 3 additions & 2 deletions test/Gateway.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -328,21 +328,22 @@ contract GatewayTest is BaseTest {
0x0000000000000000000000000000000000000000000000000000000000000000,
1,
0x0000000000000000000000000000000000000000000000000000000000000000,
0,
abi.encode(uint256(1234))
)
);
// Calling the receiver contract directly to make the address warm
address sender = TestUtils.createTestAccount(10 ether);
(uint256 gasUsed,, bytes memory output) =
TestUtils.executeCall(sender, address(receiver), 23_318, 0, testEncodedCall);
TestUtils.executeCall(sender, address(receiver), 23_318 + 128, 0, testEncodedCall);
assertEq(gasUsed, 1234);
assertEq(output.length, 32);
}

function test_estimateMessageCost() external {
vm.txGasPrice(1);
uint256 cost = gateway.estimateMessageCost(DEST_NETWORK_ID, 96, 100000);
assertEq(cost, GasUtils.EXECUTION_BASE_COST + 133821);
assertEq(cost, GasUtils.EXECUTION_BASE_COST + 133824);
}

function test_checkPayloadSize() external {
Expand Down
4 changes: 2 additions & 2 deletions test/GatewayProxy.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@

pragma solidity >=0.8.0;

import {IUniversalFactory} from "@universal-factory/IUniversalFactory.sol";
import {FactoryUtils} from "@universal-factory/FactoryUtils.sol";
import {IUniversalFactory} from "../lib/universal-factory/src/IUniversalFactory.sol";
import {FactoryUtils} from "../lib/universal-factory/src/FactoryUtils.sol";
import {Test, console} from "forge-std/Test.sol";
import {VmSafe} from "forge-std/Vm.sol";
import {TestUtils, SigningKey, SigningUtils} from "./TestUtils.sol";
Expand Down
2 changes: 1 addition & 1 deletion test/GmpTestTools.sol
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import {GatewayProxy} from "../src/GatewayProxy.sol";
import {IGateway} from "../src/interfaces/IGateway.sol";
import {BranchlessMath} from "../src/utils/BranchlessMath.sol";
import {GmpMessage, TssKey, Network, Signature, GmpSender, PrimitiveUtils} from "../src/Primitives.sol";
import {IUniversalFactory} from "@universal-factory/IUniversalFactory.sol";
import {IUniversalFactory} from "../lib/universal-factory/src/IUniversalFactory.sol";

library GmpTestTools {
/**
Expand Down
4 changes: 2 additions & 2 deletions test/MockERC20.sol
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

pragma solidity >=0.8.0;

import {ERC20} from "@solmate/tokens/ERC20.sol";
import {ERC20} from "../lib/solmate/src/tokens/ERC20.sol";
import {IGmpReceiver} from "../src/interfaces/IGmpReceiver.sol";
import {IGateway} from "../src/interfaces/IGateway.sol";

Expand Down Expand Up @@ -73,7 +73,7 @@ contract MockERC20 is ERC20, IGmpReceiver {
);
}

function onGmpReceived(bytes32 id, uint128 network, bytes32 sender, bytes calldata data)
function onGmpReceived(bytes32 id, uint128 network, bytes32 sender, uint64, bytes calldata data)
external
payable
returns (bytes32)
Expand Down
8 changes: 4 additions & 4 deletions test/TestUtils.sol
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,11 @@ pragma solidity >=0.8.0;

import {VmSafe, Vm} from "forge-std/Vm.sol";
import {console} from "forge-std/console.sol";
import {Schnorr} from "@frost-evm/Schnorr.sol";
import {SECP256K1} from "@frost-evm/SECP256K1.sol";
import {Schnorr} from "../lib/frost-evm/sol/Schnorr.sol";
import {SECP256K1} from "../lib/frost-evm/sol/SECP256K1.sol";
import {BranchlessMath} from "../src/utils/BranchlessMath.sol";
import {IUniversalFactory} from "@universal-factory/IUniversalFactory.sol";
import {FactoryUtils} from "@universal-factory/FactoryUtils.sol";
import {IUniversalFactory} from "../lib/universal-factory/src/IUniversalFactory.sol";
import {FactoryUtils} from "../lib/universal-factory/src/FactoryUtils.sol";
import {IGateway} from "../src/interfaces/IGateway.sol";
import {Gateway, GatewayEIP712} from "../src/Gateway.sol";
import {GatewayProxy} from "../src/GatewayProxy.sol";
Expand Down
6 changes: 3 additions & 3 deletions test/utils/BaseTest.sol
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@

pragma solidity >=0.8.0;

import {IUniversalFactory} from "@universal-factory/IUniversalFactory.sol";
import {FactoryUtils} from "@universal-factory/FactoryUtils.sol";
import {Interpreter} from "@evm-interpreter/Interpreter.sol";
import {IUniversalFactory} from "../../lib/universal-factory/src/IUniversalFactory.sol";
import {FactoryUtils} from "../../lib/universal-factory/src/FactoryUtils.sol";
import {Interpreter} from "../../lib/evm-interpreter/src/Interpreter.sol";
import {Test, console, Vm} from "forge-std/Test.sol";
import {VmSafe} from "forge-std/Vm.sol";

Expand Down
6 changes: 3 additions & 3 deletions test/utils/GasSpender.sol
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ contract GasSpender is IGmpReceiver {
// 0x07 0x3d RETURNDATASIZE
// 0x08 0x6020 PUSH1 0x20
// 0x0a 0x91 SWAP2
// 0x0b 0x6064 PUSH1 0x64
// 0x0b 0x6084 PUSH1 0x84
// 0x0d 0x35 CALLDATALOAD -- Load the payload offset from the calldata
// 0x0e 0x6024 PUSH1 0x24
// 0x10 0x01 ADD
Expand Down Expand Up @@ -61,7 +61,7 @@ contract GasSpender is IGmpReceiver {
// `=>0x3a 0x5b JUMPDEST
// 0x3b 0xf3 RETURN
bytes private constant BYTECODE =
hex"5a600201803d523d60209160643560240135146018575bfd5b60365a116018575a604903565b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5bf3";
hex"5a600201803d523d60209160843560240135146018575bfd5b60365a116018575a604903565b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5bf3";

constructor() payable {
bytes memory bytecode = BYTECODE;
Expand All @@ -70,7 +70,7 @@ contract GasSpender is IGmpReceiver {
}
}

function onGmpReceived(bytes32, uint128, bytes32, bytes calldata payload) external payable returns (bytes32) {
function onGmpReceived(bytes32, uint128, bytes32, uint64, bytes calldata payload) external payable returns (bytes32) {
unchecked {
// OBS: This is just an example on how this contract works, the actual code is implemented directly in
// low level EVM, as defined in the `BYTECODE` constant.
Expand Down
1 change: 1 addition & 0 deletions test/utils/GasSpender.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ contract GasSpenderTest is BaseTest {
0x0000000000000000000000000000000000000000000000000000000000000000,
1,
0x0000000000000000000000000000000000000000000000000000000000000000,
0,
abi.encode(gasToWaste)
)
);
Expand Down
31 changes: 19 additions & 12 deletions test/utils/GmpProxy.sol
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {ERC1967} from "../../src/utils/ERC1967.sol";
import {IGmpReceiver} from "../../src/interfaces/IGmpReceiver.sol";
import {IGateway} from "../../src/interfaces/IGateway.sol";
import {BranchlessMath} from "../../src/utils/BranchlessMath.sol";
import {console} from "forge-std/console.sol";

contract GmpProxy is IGmpReceiver {
using BranchlessMath for uint256;
Expand All @@ -31,21 +32,27 @@ contract GmpProxy is IGmpReceiver {
NETWORK_ID = GATEWAY.networkId();
}

function sendMessage(GmpMessage calldata message) external payable {
function sendMessage(GmpMessage calldata message) external payable returns (bytes32) {
uint256 value = address(this).balance.min(msg.value);
GATEWAY.submitMessage{value: value}(message.dest, message.destNetwork, message.gasLimit, message.data);
return GATEWAY.submitMessage{value: value}(message.dest, message.destNetwork, message.gasLimit, message.data);
}

function estimateMessageCost(uint256 messageSize, uint256 gasLimit) external view returns (uint256) {
return GATEWAY.estimateMessageCost(NETWORK_ID, messageSize, gasLimit);
}

function onGmpReceived(bytes32 id, uint128, bytes32, bytes calldata payload) external payable returns (bytes32) {
// For testing purpose
// we keep the original struct in payload so we dont depend on OnGmpReceived call since it doesnt provide everything.
(GmpMessage memory message) = abi.decode(payload, (GmpMessage));
message.data = payload;

function onGmpReceived(bytes32 id, uint128 srcNetwork, bytes32 src, uint64 nonce, bytes calldata payload) external payable returns (bytes32) {
// when estimating gas an insane amount of gas is provided
uint256 gasLimit = gasleft();
// this is the constant added to gasLimit
unchecked { console.log(300_000 - gasLimit); }
uint64 msgGasLimit;
unchecked { msgGasLimit = uint64(gasLimit + 579); }
GmpMessage memory message = GmpMessage({
source: src,
srcNetwork: uint16(srcNetwork),
dest: address(this),
destNetwork: NETWORK_ID,
gasLimit: msgGasLimit,
nonce: nonce,
data: payload
});
emit MessageReceived(message);
return id;
}
Expand Down
57 changes: 57 additions & 0 deletions test/utils/GmpProxy.t.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
// SPDX-License-Identifier: MIT

pragma solidity >=0.8.0;

import {Test, console, Vm} from "forge-std/Test.sol";
import {IGateway} from "../../src/interfaces/IGateway.sol";
import {IGmpReceiver} from "../../src/interfaces/IGmpReceiver.sol";
import {GmpProxy} from "./GmpProxy.sol";

contract MockGateway is IGateway {
uint16 _networkId;

constructor(uint16 network) {
_networkId = network;
}

function networkId() external view returns (uint16) {
return _networkId;
}

function estimateMessageCost(
uint16,
uint256,
uint256
) external pure returns (uint256) {
return 0;
}

function submitMessage(
address,
uint16,
uint256,
bytes calldata
) external payable returns (bytes32) {
return 0x0;
}
}

contract GmpProxyTest is Test {
MockGateway gateway;
GmpProxy proxy;

function setUp() external {
gateway = new MockGateway(1);
proxy = new GmpProxy(address(gateway));
}

function test_onGmpReceived() external {
proxy.onGmpReceived{gas: 300000}(
0x0,
0,
0x0,
0,
abi.encode(0x0)
);
}
}

0 comments on commit b8b5d47

Please sign in to comment.