Skip to content

Commit

Permalink
contracts: add gaspad and gasused precompiles
Browse files Browse the repository at this point in the history
  • Loading branch information
CedarMist committed Oct 16, 2023
1 parent 5e712c1 commit a8484a8
Show file tree
Hide file tree
Showing 3 changed files with 96 additions and 0 deletions.
30 changes: 30 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 GAS_PAD =
0x010000000000000000000000000000000000000a;

// Oasis-specific, general precompiles
address internal constant SHA512_256 =
Expand Down Expand Up @@ -221,6 +225,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));
}
}

/**
Expand Down
27 changes: 27 additions & 0 deletions contracts/contracts/tests/Gas.sol
Original file line number Diff line number Diff line change
@@ -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);
}
}
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 a8484a8

Please sign in to comment.