Skip to content

Commit

Permalink
Merge pull request #205 from oasisprotocol/CedarMist/gaspad
Browse files Browse the repository at this point in the history
contracts: add gaspad and gasused precompiles
  • Loading branch information
CedarMist authored Oct 31, 2023
2 parents fcd5102 + 5d11ac8 commit bcecd07
Show file tree
Hide file tree
Showing 3 changed files with 88 additions and 0 deletions.
26 changes: 26 additions & 0 deletions contracts/contracts/Sapphire.sol
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,10 @@ library Sapphire {
0x0100000000000000000000000000000000000007;
address internal constant CURVE25519_PUBLIC_KEY =
0x0100000000000000000000000000000000000008;
address internal constant GAS_USED =
0x0100000000000000000000000000000000000009;
address internal constant PAD_GAS =
0x010000000000000000000000000000000000000a;

// Oasis-specific, general precompiles
address internal constant SHA512_256 =
Expand Down Expand Up @@ -225,6 +229,28 @@ 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 padGas(uint128 toAmount) internal view {
(bool success, ) = PAD_GAS.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));
}
}

/**
Expand Down
23 changes: 23 additions & 0 deletions contracts/contracts/tests/Gas.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.0;

import {Sapphire} from "../Sapphire.sol";

contract GasTests {
bytes32 tmp;

function testConstantTime(uint256 useGas, uint128 padGasAmount) external {
if (useGas == 1) {
bytes32 x;

for (uint256 i = 0; i < 100; i++) {
x = keccak256(abi.encodePacked(x, tmp));
}

tmp = x;
}

Sapphire.padGas(padGasAmount);
}
}
39 changes: 39 additions & 0 deletions contracts/test/gas.ts
Original file line number Diff line number Diff line change
@@ -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);
});
});

0 comments on commit bcecd07

Please sign in to comment.