From c052df366b116d3dd77d951aa623905b61315d71 Mon Sep 17 00:00:00 2001 From: CedarMist <134699267+CedarMist@users.noreply.github.com> Date: Mon, 16 Oct 2023 13:12:23 +0100 Subject: [PATCH 1/3] contracts: add gaspad and gasused precompiles --- contracts/contracts/Sapphire.sol | 30 ++++++++++++++++++++++++ contracts/contracts/tests/Gas.sol | 27 +++++++++++++++++++++ contracts/test/gas.ts | 39 +++++++++++++++++++++++++++++++ 3 files changed, 96 insertions(+) create mode 100644 contracts/contracts/tests/Gas.sol create mode 100644 contracts/test/gas.ts diff --git a/contracts/contracts/Sapphire.sol b/contracts/contracts/Sapphire.sol index 5ee4aed9..d1d55691 100644 --- a/contracts/contracts/Sapphire.sol +++ b/contracts/contracts/Sapphire.sol @@ -23,6 +23,10 @@ library Sapphire { 0x0100000000000000000000000000000000000007; address internal constant CURVE25519_PUBLIC_KEY = 0x0100000000000000000000000000000000000008; + address internal constant GAS_USED = + 0x0100000000000000000000000000000000000009; + address internal constant GAS_PAD = + 0x010000000000000000000000000000000000000a; // Oasis-specific, general precompiles address internal constant SHA512_256 = @@ -225,6 +229,32 @@ library Sapphire { require(success, "verify: failed"); return abi.decode(v, (bool)); } + + /** + * @dev Set the current transactions gas usage to a specific amount + * @param toAmount Gas usage will be set to this amount + * @custom:see @oasisprotocol/oasis-sdk :: precompile/gas.rs :: call_pad_gas + * + * Will cause a reversion if the current usage is more than the amount + */ + function gaspad(uint128 toAmount) internal view + { + (bool success,) = GAS_PAD.staticcall( + abi.encode(toAmount) + ); + require(success, "verify: failed"); + } + + /** + * @dev Returns the amount of gas currently used by the transaction + * @custom:see @oasisprotocol/oasis-sdk :: precompile/gas.rs :: call_gas_used + */ + function gasused() internal view returns (uint64) + { + (bool success, bytes memory v) = GAS_USED.staticcall(""); + require(success, "gasused: failed"); + return abi.decode(v, (uint64)); + } } /** diff --git a/contracts/contracts/tests/Gas.sol b/contracts/contracts/tests/Gas.sol new file mode 100644 index 00000000..b1473799 --- /dev/null +++ b/contracts/contracts/tests/Gas.sol @@ -0,0 +1,27 @@ +// SPDX-License-Identifier: Apache-2.0 + +pragma solidity ^0.8.0; + +import {Sapphire} from "../Sapphire.sol"; + +contract GasTests { + bytes32 tmp; + + function testConstantTime(uint useGas, uint128 padGasAmount) + external + { + if( useGas == 1 ) + { + bytes32 x; + + for( uint i = 0; i < 100; i++ ) + { + x = keccak256(abi.encodePacked(x, tmp)); + } + + tmp = x; + } + + Sapphire.gaspad(padGasAmount); + } +} diff --git a/contracts/test/gas.ts b/contracts/test/gas.ts new file mode 100644 index 00000000..a7bdf5c3 --- /dev/null +++ b/contracts/test/gas.ts @@ -0,0 +1,39 @@ +// SPDX-License-Identifier: Apache-2.0 + +import { expect } from 'chai'; +import { ethers } from 'hardhat'; +import { GasTests__factory } from '../typechain-types/factories/contracts/tests/Gas.sol'; +import { GasTests } from '../typechain-types/contracts/tests/Gas.sol/GasTests'; + +describe('Gas Padding', function () { + let contract: GasTests; + + before(async () => { + const factory = (await ethers.getContractFactory( + 'GasTests', + )) as GasTests__factory; + contract = await factory.deploy(); + }); + + it('Gas Padding works as Expected', async () => { + const expectedGas = 122735; + + let tx = await contract.testConstantTime(1, 100000); + let receipt = await tx.wait(); + expect(receipt.cumulativeGasUsed).eq(expectedGas); + + tx = await contract.testConstantTime(2, 100000); + receipt = await tx.wait(); + expect(receipt.cumulativeGasUsed).eq(expectedGas); + + tx = await contract.testConstantTime(1, 100000); + receipt = await tx.wait(); + expect(receipt.cumulativeGasUsed).eq(expectedGas); + + // Note: calldata isn't included in gas padding + // Thus when the value is 0 it will use 4 gas instead of 16 gas + tx = await contract.testConstantTime(0, 100000); + receipt = await tx.wait(); + expect(receipt.cumulativeGasUsed).eq(expectedGas - 12); + }); +}); From e7813835640c755ebb132ef45b69fe950f5eb556 Mon Sep 17 00:00:00 2001 From: CedarMist <134699267+CedarMist@users.noreply.github.com> Date: Mon, 16 Oct 2023 13:21:27 +0100 Subject: [PATCH 2/3] contracts: formatting --- contracts/contracts/Sapphire.sol | 10 ++---- contracts/contracts/tests/Gas.sol | 10 ++---- contracts/test/gas.ts | 60 +++++++++++++++---------------- 3 files changed, 36 insertions(+), 44 deletions(-) diff --git a/contracts/contracts/Sapphire.sol b/contracts/contracts/Sapphire.sol index d1d55691..5a64b636 100644 --- a/contracts/contracts/Sapphire.sol +++ b/contracts/contracts/Sapphire.sol @@ -237,11 +237,8 @@ library Sapphire { * * Will cause a reversion if the current usage is more than the amount */ - function gaspad(uint128 toAmount) internal view - { - (bool success,) = GAS_PAD.staticcall( - abi.encode(toAmount) - ); + function gaspad(uint128 toAmount) internal view { + (bool success, ) = GAS_PAD.staticcall(abi.encode(toAmount)); require(success, "verify: failed"); } @@ -249,8 +246,7 @@ library Sapphire { * @dev Returns the amount of gas currently used by the transaction * @custom:see @oasisprotocol/oasis-sdk :: precompile/gas.rs :: call_gas_used */ - function gasused() internal view returns (uint64) - { + function gasused() internal view returns (uint64) { (bool success, bytes memory v) = GAS_USED.staticcall(""); require(success, "gasused: failed"); return abi.decode(v, (uint64)); diff --git a/contracts/contracts/tests/Gas.sol b/contracts/contracts/tests/Gas.sol index b1473799..dc81c6bb 100644 --- a/contracts/contracts/tests/Gas.sol +++ b/contracts/contracts/tests/Gas.sol @@ -7,15 +7,11 @@ import {Sapphire} from "../Sapphire.sol"; contract GasTests { bytes32 tmp; - function testConstantTime(uint useGas, uint128 padGasAmount) - external - { - if( useGas == 1 ) - { + function testConstantTime(uint256 useGas, uint128 padGasAmount) external { + if (useGas == 1) { bytes32 x; - for( uint i = 0; i < 100; i++ ) - { + for (uint256 i = 0; i < 100; i++) { x = keccak256(abi.encodePacked(x, tmp)); } diff --git a/contracts/test/gas.ts b/contracts/test/gas.ts index a7bdf5c3..fe175350 100644 --- a/contracts/test/gas.ts +++ b/contracts/test/gas.ts @@ -6,34 +6,34 @@ import { GasTests__factory } from '../typechain-types/factories/contracts/tests/ import { GasTests } from '../typechain-types/contracts/tests/Gas.sol/GasTests'; describe('Gas Padding', function () { - let contract: GasTests; - - before(async () => { - const factory = (await ethers.getContractFactory( - 'GasTests', - )) as GasTests__factory; - contract = await factory.deploy(); - }); - - it('Gas Padding works as Expected', async () => { - const expectedGas = 122735; - - let tx = await contract.testConstantTime(1, 100000); - let receipt = await tx.wait(); - expect(receipt.cumulativeGasUsed).eq(expectedGas); - - tx = await contract.testConstantTime(2, 100000); - receipt = await tx.wait(); - expect(receipt.cumulativeGasUsed).eq(expectedGas); - - tx = await contract.testConstantTime(1, 100000); - receipt = await tx.wait(); - expect(receipt.cumulativeGasUsed).eq(expectedGas); - - // Note: calldata isn't included in gas padding - // Thus when the value is 0 it will use 4 gas instead of 16 gas - tx = await contract.testConstantTime(0, 100000); - receipt = await tx.wait(); - expect(receipt.cumulativeGasUsed).eq(expectedGas - 12); - }); + let contract: GasTests; + + before(async () => { + const factory = (await ethers.getContractFactory( + 'GasTests', + )) as GasTests__factory; + contract = await factory.deploy(); + }); + + it('Gas Padding works as Expected', async () => { + const expectedGas = 122735; + + let tx = await contract.testConstantTime(1, 100000); + let receipt = await tx.wait(); + expect(receipt.cumulativeGasUsed).eq(expectedGas); + + tx = await contract.testConstantTime(2, 100000); + receipt = await tx.wait(); + expect(receipt.cumulativeGasUsed).eq(expectedGas); + + tx = await contract.testConstantTime(1, 100000); + receipt = await tx.wait(); + expect(receipt.cumulativeGasUsed).eq(expectedGas); + + // Note: calldata isn't included in gas padding + // Thus when the value is 0 it will use 4 gas instead of 16 gas + tx = await contract.testConstantTime(0, 100000); + receipt = await tx.wait(); + expect(receipt.cumulativeGasUsed).eq(expectedGas - 12); + }); }); From 5d11ac87d69121bad48078ce24e3eec44f3a4968 Mon Sep 17 00:00:00 2001 From: CedarMist <134699267+CedarMist@users.noreply.github.com> Date: Mon, 30 Oct 2023 18:30:50 +0000 Subject: [PATCH 3/3] contracts: changed gas padding naming to match rust code --- contracts/contracts/Sapphire.sol | 8 ++++---- contracts/contracts/tests/Gas.sol | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/contracts/contracts/Sapphire.sol b/contracts/contracts/Sapphire.sol index 5a64b636..1099cc4c 100644 --- a/contracts/contracts/Sapphire.sol +++ b/contracts/contracts/Sapphire.sol @@ -25,7 +25,7 @@ library Sapphire { 0x0100000000000000000000000000000000000008; address internal constant GAS_USED = 0x0100000000000000000000000000000000000009; - address internal constant GAS_PAD = + address internal constant PAD_GAS = 0x010000000000000000000000000000000000000a; // Oasis-specific, general precompiles @@ -237,8 +237,8 @@ library Sapphire { * * Will cause a reversion if the current usage is more than the amount */ - function gaspad(uint128 toAmount) internal view { - (bool success, ) = GAS_PAD.staticcall(abi.encode(toAmount)); + function padGas(uint128 toAmount) internal view { + (bool success, ) = PAD_GAS.staticcall(abi.encode(toAmount)); require(success, "verify: failed"); } @@ -246,7 +246,7 @@ library Sapphire { * @dev Returns the amount of gas currently used by the transaction * @custom:see @oasisprotocol/oasis-sdk :: precompile/gas.rs :: call_gas_used */ - function gasused() internal view returns (uint64) { + function gasUsed() internal view returns (uint64) { (bool success, bytes memory v) = GAS_USED.staticcall(""); require(success, "gasused: failed"); return abi.decode(v, (uint64)); diff --git a/contracts/contracts/tests/Gas.sol b/contracts/contracts/tests/Gas.sol index dc81c6bb..cdcedf62 100644 --- a/contracts/contracts/tests/Gas.sol +++ b/contracts/contracts/tests/Gas.sol @@ -18,6 +18,6 @@ contract GasTests { tmp = x; } - Sapphire.gaspad(padGasAmount); + Sapphire.padGas(padGasAmount); } }