ethereum.forks.amsterdam.vm.instructions.arithmetic

Ethereum Virtual Machine (EVM) Arithmetic Instructions.

.. contents:: Table of Contents :backlinks: none :local:

Introduction

Implementations of the EVM Arithmetic instructions.

add

Adds the top two elements of the stack together, and pushes the result back on the stack.

Parameters

evm : The current EVM frame.

def add(evm: Evm) -> None:
29
    <snip>
39
    # STACK
40
    x = pop(evm.stack)
41
    y = pop(evm.stack)
42
43
    # GAS
44
    charge_gas(evm, GasCosts.OPCODE_ADD)
45
46
    # OPERATION
47
    result = x.wrapping_add(y)
48
49
    push(evm.stack, result)
50
51
    # PROGRAM COUNTER
52
    evm.pc += Uint(1)

sub

Subtracts the top two elements of the stack, and pushes the result back on the stack.

Parameters

evm : The current EVM frame.

def sub(evm: Evm) -> None:
56
    <snip>
66
    # STACK
67
    x = pop(evm.stack)
68
    y = pop(evm.stack)
69
70
    # GAS
71
    charge_gas(evm, GasCosts.OPCODE_SUB)
72
73
    # OPERATION
74
    result = x.wrapping_sub(y)
75
76
    push(evm.stack, result)
77
78
    # PROGRAM COUNTER
79
    evm.pc += Uint(1)

mul

Multiplies the top two elements of the stack, and pushes the result back on the stack.

Parameters

evm : The current EVM frame.

def mul(evm: Evm) -> None:
83
    <snip>
93
    # STACK
94
    x = pop(evm.stack)
95
    y = pop(evm.stack)
96
97
    # GAS
98
    charge_gas(evm, GasCosts.OPCODE_MUL)
99
100
    # OPERATION
101
    result = x.wrapping_mul(y)
102
103
    push(evm.stack, result)
104
105
    # PROGRAM COUNTER
106
    evm.pc += Uint(1)

div

Integer division of the top two elements of the stack. Pushes the result back on the stack.

Parameters

evm : The current EVM frame.

def div(evm: Evm) -> None:
110
    <snip>
120
    # STACK
121
    dividend = pop(evm.stack)
122
    divisor = pop(evm.stack)
123
124
    # GAS
125
    charge_gas(evm, GasCosts.OPCODE_DIV)
126
127
    # OPERATION
128
    if divisor == 0:
129
        quotient = U256(0)
130
    else:
131
        quotient = dividend // divisor
132
133
    push(evm.stack, quotient)
134
135
    # PROGRAM COUNTER
136
    evm.pc += Uint(1)

U255_CEIL_VALUE

139
U255_CEIL_VALUE = 2**255

sdiv

Signed integer division of the top two elements of the stack. Pushes the result back on the stack.

Parameters

evm : The current EVM frame.

def sdiv(evm: Evm) -> None:
143
    <snip>
153
    # STACK
154
    dividend = pop(evm.stack).to_signed()
155
    divisor = pop(evm.stack).to_signed()
156
157
    # GAS
158
    charge_gas(evm, GasCosts.OPCODE_SDIV)
159
160
    # OPERATION
161
    if divisor == 0:
162
        quotient = 0
163
    elif dividend == -U255_CEIL_VALUE and divisor == -1:
164
        quotient = -U255_CEIL_VALUE
165
    else:
166
        sign = get_sign(dividend * divisor)
167
        quotient = sign * (abs(dividend) // abs(divisor))
168
169
    push(evm.stack, U256.from_signed(quotient))
170
171
    # PROGRAM COUNTER
172
    evm.pc += Uint(1)

mod

Modulo remainder of the top two elements of the stack. Pushes the result back on the stack.

Parameters

evm : The current EVM frame.

def mod(evm: Evm) -> None:
176
    <snip>
186
    # STACK
187
    x = pop(evm.stack)
188
    y = pop(evm.stack)
189
190
    # GAS
191
    charge_gas(evm, GasCosts.OPCODE_MOD)
192
193
    # OPERATION
194
    if y == 0:
195
        remainder = U256(0)
196
    else:
197
        remainder = x % y
198
199
    push(evm.stack, remainder)
200
201
    # PROGRAM COUNTER
202
    evm.pc += Uint(1)

smod

Signed modulo remainder of the top two elements of the stack. Pushes the result back on the stack.

Parameters

evm : The current EVM frame.

def smod(evm: Evm) -> None:
206
    <snip>
216
    # STACK
217
    x = pop(evm.stack).to_signed()
218
    y = pop(evm.stack).to_signed()
219
220
    # GAS
221
    charge_gas(evm, GasCosts.OPCODE_SMOD)
222
223
    # OPERATION
224
    if y == 0:
225
        remainder = 0
226
    else:
227
        remainder = get_sign(x) * (abs(x) % abs(y))
228
229
    push(evm.stack, U256.from_signed(remainder))
230
231
    # PROGRAM COUNTER
232
    evm.pc += Uint(1)

addmod

Modulo addition of the top 2 elements with the 3rd element. Pushes the result back on the stack.

Parameters

evm : The current EVM frame.

def addmod(evm: Evm) -> None:
236
    <snip>
246
    # STACK
247
    x = Uint(pop(evm.stack))
248
    y = Uint(pop(evm.stack))
249
    z = Uint(pop(evm.stack))
250
251
    # GAS
252
    charge_gas(evm, GasCosts.OPCODE_ADDMOD)
253
254
    # OPERATION
255
    if z == 0:
256
        result = U256(0)
257
    else:
258
        result = U256((x + y) % z)
259
260
    push(evm.stack, result)
261
262
    # PROGRAM COUNTER
263
    evm.pc += Uint(1)

mulmod

Modulo multiplication of the top 2 elements with the 3rd element. Pushes the result back on the stack.

Parameters

evm : The current EVM frame.

def mulmod(evm: Evm) -> None:
267
    <snip>
277
    # STACK
278
    x = Uint(pop(evm.stack))
279
    y = Uint(pop(evm.stack))
280
    z = Uint(pop(evm.stack))
281
282
    # GAS
283
    charge_gas(evm, GasCosts.OPCODE_MULMOD)
284
285
    # OPERATION
286
    if z == 0:
287
        result = U256(0)
288
    else:
289
        result = U256((x * y) % z)
290
291
    push(evm.stack, result)
292
293
    # PROGRAM COUNTER
294
    evm.pc += Uint(1)

exp

Exponential operation of the top 2 elements. Pushes the result back on the stack.

Parameters

evm : The current EVM frame.

def exp(evm: Evm) -> None:
298
    <snip>
308
    # STACK
309
    base = Uint(pop(evm.stack))
310
    exponent = Uint(pop(evm.stack))
311
312
    # GAS
313
    # This is equivalent to 1 + floor(log(y, 256)). But in python the log
314
    # function is inaccurate leading to wrong results.
315
    exponent_bits = exponent.bit_length()
316
    exponent_bytes = (exponent_bits + Uint(7)) // Uint(8)
317
    charge_gas(
318
        evm,
319
        ExecutionGas(
320
            GasCosts.OPCODE_EXP_BASE
321
            + GasCosts.OPCODE_EXP_PER_BYTE * exponent_bytes
322
        ),
323
    )
324
325
    # OPERATION
326
    result = U256(pow(base, exponent, Uint(U256.MAX_VALUE) + Uint(1)))
327
328
    push(evm.stack, result)
329
330
    # PROGRAM COUNTER
331
    evm.pc += Uint(1)

signextend

Sign extend operation. In other words, extend a signed number which fits in N bytes to 32 bytes.

Parameters

evm : The current EVM frame.

def signextend(evm: Evm) -> None:
335
    <snip>
345
    # STACK
346
    byte_num = pop(evm.stack)
347
    value = pop(evm.stack)
348
349
    # GAS
350
    charge_gas(evm, GasCosts.OPCODE_SIGNEXTEND)
351
352
    # OPERATION
353
    if byte_num > U256(31):
354
        # Can't extend any further
355
        result = value
356
    else:
357
        # U256(0).to_be_bytes() gives b'' instead of b'\x00'.
358
        value_bytes = Bytes(value.to_be_bytes32())
359
        # Now among the obtained value bytes, consider only
360
        # N `least significant bytes`, where N is `byte_num + 1`.
361
        value_bytes = value_bytes[31 - int(byte_num) :]
362
        sign_bit = value_bytes[0] >> 7
363
        if sign_bit == 0:
364
            result = U256.from_be_bytes(value_bytes)
365
        else:
366
            num_bytes_prepend = U256(32) - (byte_num + U256(1))
367
            result = U256.from_be_bytes(
368
                bytearray([0xFF] * num_bytes_prepend) + value_bytes
369
            )
370
371
    push(evm.stack, result)
372
373
    # PROGRAM COUNTER
374
    evm.pc += Uint(1)