Trait ContextStatefulPrecompile

pub trait ContextStatefulPrecompile<DB>: Sync + Send
where DB: Database,
{ // Required method fn call( &self, bytes: &Bytes, gas_limit: u64, evmctx: &mut InnerEvmContext<DB>, ) -> Result<PrecompileOutput, PrecompileErrors>; }
Expand description

Context aware stateful precompile trait. It is used to create a arc precompile in ContextPrecompile.

Required Methods§

fn call( &self, bytes: &Bytes, gas_limit: u64, evmctx: &mut InnerEvmContext<DB>, ) -> Result<PrecompileOutput, PrecompileErrors>

Implementors§

§

impl<DB> ContextStatefulPrecompile<DB> for RngPrecompile
where DB: Database,

§RNG Precompile

§Input Encoding

The input to this precompile is encoded as follows:

FieldBytesDescription
requested_output_len4Big-endian uint32 specifying how many random bytes to generate
persnRemaining bytes used as personalization data (must be non-empty)
§Overview

We interpret the input as a [u8] slice of bytes used as personalization for the RNG entropy.

Using the pers bytes, the block rng transcript, and the block VRF key, we produce a leaf RNG that implements the RngCore interface and query it for bytes.

§Gas Cost
§Pricing Fundamental Operations

The RNG precompile uses Merlin transcripts that rely on the Strobe128 hash function. Strobe uses the keccak256 sponge, which has an EVM opcode cost of g=30+6×ceil(input size/32). We have a more complex initialization than SHA, so we price a base cost of 100 gas. However, Strobe128 is designed for 128-bit security (instead of SHA3’s 256-bit security), which allows it to work faster. The dominating cost for the keccak256 sponges is the keccak256 permutation. For SHA3, you permute once per 136 bytes of data absorbed. Ethereum simplifies this cost calculation as 6 gas per 32-byte word absorbed. Strobe128, on the other hand, can absorb/squeeze 166 bytes before it needs to run the keccak256 permutation. 136 / 166 * 6 ≈ 4.9, which we round up to 5 gas, instead of 6 gas per word.

The transcripts also use points on the Ristretto group for Curve25519 and require scalar multiplications. Scalar multiplication is optimized through the use of the Montgomery ladder for Curve25519, so this should be as fast or faster than a Secp256k1 scalar multiplication. Benchmarks by XRLP support this: https://xrpl.org/blog/2014/curves-with-a-twist We bound the cost at that of ecrecover, which performs 3 secp256k1 scalar multiplications, a point addition, plus some other computation. Charging the same amount as ecrecover (3000 gas) is very conservative but allows us to lower the cost later on.

§Pricing RNG Operations

The cost of initializing the leaf_rng comes from:

  • The Root RNG initialization requires a running hash of the transcript. The Root RNG
    is initialized by adding 13 bytes to the transcript and then keying the rng
    (essentially hashing) using Strobe128.

  • (optional) If personalization bytes are provided, the RNG is seeded with
    those pers bytes

  • Each leaf RNG requires forking the root_rng, which involves adding
    a 32-byte tx_hash and label (2 bytes) per transaction. Then a separate
    VRF hash function is used that performs a single EC scalar multiplication

  • The leaf RNG is initialized, which involves keying the RNG based on 32 random bytes
    from the parent RNG.

Filling bytes once the RNG is initialized:

  • Filling bytes occurs by squeezing the keccak sponge. As described above,
    take inspiration from Ethereum and charge 5 gas per 32-byte word to account for the
    cheaper Strobe parameters.

To calculate the base init cost of the RNG precompile, we get:

  • 100 gas from setting up Strobe128
  • (13 + len(pers) + 32 + 2 + 32) * 5 = 395 + 5 * len(pers) gas for hashing init bytes
  • 3000 gas for the EC scalar multiplication

We add a 50% buffer to our gas calculations (which may be lowered in the future).

RNG_INIT_BASE = round(100 + 395 + 3000) = 3500
fill_cost     = ceil(fill_len / 32) * 5