Skip to content

Test Point Evaluation Precompile Gas

Documentation for tests/cancun/eip4844_blobs/test_point_evaluation_precompile_gas.py.

Generate fixtures for these test cases for Cancun with:

Cancun only:

fill -v tests/cancun/eip4844_blobs/test_point_evaluation_precompile_gas.py --fork=Cancun --evm-bin=/path/to/evm-tool-dev-version
For all forks up to and including Cancun:
fill -v tests/cancun/eip4844_blobs/test_point_evaluation_precompile_gas.py --until=Cancun --evm-bin=/path/to/evm-tool-dev-version

Tests gas usage on point evaluation precompile for EIP-4844: Shard Blob Transactions

Test gas usage on point evaluation precompile for EIP-4844: Shard Blob Transactions.

Spec dataclass

Parameters from the EIP-4844 specifications as defined at https://eips.ethereum.org/EIPS/eip-4844#parameters

If the parameter is not currently used within the tests, it is commented out.

Source code in tests/cancun/eip4844_blobs/spec.py
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
@dataclass(frozen=True)
class Spec:
    """
    Parameters from the EIP-4844 specifications as defined at
    https://eips.ethereum.org/EIPS/eip-4844#parameters

    If the parameter is not currently used within the tests, it is commented
    out.
    """

    BLOB_TX_TYPE = 0x03
    FIELD_ELEMENTS_PER_BLOB = 4096
    BLS_MODULUS = 0x73EDA753299D7D483339D80809A1D80553BDA402FFFE5BFEFFFFFFFF00000001
    BLOB_COMMITMENT_VERSION_KZG = 1
    POINT_EVALUATION_PRECOMPILE_ADDRESS = 20
    POINT_EVALUATION_PRECOMPILE_GAS = 50_000
    MAX_DATA_GAS_PER_BLOCK = 786432
    TARGET_DATA_GAS_PER_BLOCK = 393216
    MIN_DATA_GASPRICE = 1
    DATA_GASPRICE_UPDATE_FRACTION = 3338477
    # MAX_VERSIONED_HASHES_LIST_SIZE = 2**24
    # MAX_CALLDATA_SIZE = 2**24
    # MAX_ACCESS_LIST_SIZE = 2**24
    # MAX_ACCESS_LIST_STORAGE_KEYS = 2**24
    # MAX_TX_WRAP_COMMITMENTS = 2**12
    # LIMIT_BLOBS_PER_TX = 2**12
    DATA_GAS_PER_BLOB = 2**17
    HASH_OPCODE_BYTE = 0x49
    HASH_GAS_COST = 3

    @classmethod
    def kzg_to_versioned_hash(
        cls,
        kzg_commitment: bytes | int,  # 48 bytes
        blob_commitment_version_kzg: Optional[bytes | int] = None,
    ) -> bytes:
        """
        Calculates the versioned hash for a given KZG commitment.
        """
        if blob_commitment_version_kzg is None:
            blob_commitment_version_kzg = cls.BLOB_COMMITMENT_VERSION_KZG
        if isinstance(kzg_commitment, int):
            kzg_commitment = kzg_commitment.to_bytes(48, "big")
        if isinstance(blob_commitment_version_kzg, int):
            blob_commitment_version_kzg = blob_commitment_version_kzg.to_bytes(1, "big")
        return blob_commitment_version_kzg + sha256(kzg_commitment).digest()[1:]

    @classmethod
    def fake_exponential(cls, factor: int, numerator: int, denominator: int) -> int:
        """
        Used to calculate the data gas cost.
        """
        i = 1
        output = 0
        numerator_accumulator = factor * denominator
        while numerator_accumulator > 0:
            output += numerator_accumulator
            numerator_accumulator = (numerator_accumulator * numerator) // (denominator * i)
            i += 1
        return output // denominator

    @classmethod
    def calc_excess_data_gas(cls, parent: BlockHeaderDataGasFields) -> int:
        """
        Calculate the excess data gas for a block given the excess data gas
        and data gas used from the parent block header.
        """
        if parent.excess_data_gas + parent.data_gas_used < cls.TARGET_DATA_GAS_PER_BLOCK:
            return 0
        else:
            return parent.excess_data_gas + parent.data_gas_used - cls.TARGET_DATA_GAS_PER_BLOCK

    # Note: Currently unused.
    # @classmethod
    # def get_total_data_gas(cls, tx: Transaction) -> int:
    #     """
    #     Calculate the total data gas for a transaction.
    #     """
    #     if tx.blob_versioned_hashes is None:
    #         return 0
    #     return cls.DATA_GAS_PER_BLOB * len(tx.blob_versioned_hashes)

    @classmethod
    def get_data_gasprice(cls, *, excess_data_gas: int) -> int:
        """
        Calculate the data gas price from the excess.
        """
        return cls.fake_exponential(
            cls.MIN_DATA_GASPRICE,
            excess_data_gas,
            cls.DATA_GASPRICE_UPDATE_FRACTION,
        )

test_point_evaluation_precompile_gas_usage(blockchain_test, pre, tx, post)

Test point evaluation precompile gas usage under different call contexts and gas limits:

  • Test using all call types (CALL, DELEGATECALL, CALLCODE, STATICCALL)
  • Test using different gas limits (exact gas, insufficient gas, extra gas)
  • Test using correct and incorrect proofs
Source code in tests/cancun/eip4844_blobs/test_point_evaluation_precompile_gas.py
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
@pytest.mark.parametrize(
    "call_type",
    [Op.CALL, Op.DELEGATECALL, Op.CALLCODE, Op.STATICCALL],
)
@pytest.mark.parametrize(
    "call_gas",
    [
        Spec.POINT_EVALUATION_PRECOMPILE_GAS,
        Spec.POINT_EVALUATION_PRECOMPILE_GAS - 1,
        Spec.POINT_EVALUATION_PRECOMPILE_GAS + 1,
    ],
    ids=["exact_gas", "insufficient_gas", "extra_gas"],
)
@pytest.mark.parametrize("proof", ["correct", "incorrect"])
@pytest.mark.valid_from("Cancun")
def test_point_evaluation_precompile_gas_usage(
    blockchain_test: BlockchainTestFiller,
    pre: Dict,
    tx: Transaction,
    post: Dict,
):
    """
    Test point evaluation precompile gas usage under different call contexts and gas limits:

    - Test using all call types (CALL, DELEGATECALL, CALLCODE, STATICCALL)
    - Test using different gas limits (exact gas, insufficient gas, extra gas)
    - Test using correct and incorrect proofs
    """
    blockchain_test(
        pre=pre,
        post=post,
        blocks=[Block(txs=[tx])],
    )