Skip to content

eth/vm/logic/arithmetic: handle exp(X,0) == 1. #1217

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Aug 28, 2018

Conversation

veox
Copy link
Contributor

@veox veox commented Aug 28, 2018

What was wrong?

0^X calc was short-circuited. Y^0 == 1, but this wasn't tested for.

Caught by exp8.json test (not yet tracked in tests fixture on master).

Semi-accidentally caught in PR #1181, which updated the JSON test fixtures to a newer version, which does contain the exp8.json test. The failure got lost among others.

Can be seen in py3{5,6}-vm CI tests: for the EVM bytecode 0x600060000a600055, which is:

000000: PUSH1 0x00
000002: PUSH1 0x00
000004: EXP
000005: PUSH1 0x00
000007: SSTORE

... there was a 15000 gas use delta (to that of expected): as if the storage was (re-)set to 0, not 1. Mentioned CI runs:

How was it fixed?

  • An additional short-circuit check for exponent == 0 was added.
  • A regular test (as compared to ethereum/tests common tests) was added.

Cute Animal Picture

put a cute animal picture link inside the parentheses

Source: not sure; via @ano at peepeth

@voith
Copy link
Contributor

voith commented Aug 28, 2018

I have an unrelated question to this PR:
I was trying to understand this line:

result = pow(base, exponent, constants.UINT_256_CEILING)

In my understanding mod=constants.UINT_256_CEILING is used to prevent an overflow.
I was trying to see what would happen when base would be negative.

In [0]: pow(-1, 3)
Out[0]: -1

In [1]: pow(-1, 3, 2**256)
Out[1]: 115792089237316195423570985008687907853269984665640564039457584007913129639935

is this intended behaviour?

Copy link
Member

@pipermerriam pipermerriam left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@veox
Copy link
Contributor Author

veox commented Aug 28, 2018

@voith I think so?..

The yellowpaper is a bit terse on this, but it does specify some signed arithmetic operations (e.g. SDIV in addition to DIV (division, /), or SGT in addition to GT (greater than, >); but not SEXP in addition to EXP).

It could be assumed that the stack values are to be interpreted as unsigned integers, but I haven't found this stated explicitly anywhere. (EDIT: Didn't look extra-thoroughly.)

There is a section on precompile 5 ("arbitrary-precision exponentiation under modulo"), for which one could assume the logic follows that of EXP. For this precompile, inputs are non-negative integers.

@pipermerriam
Copy link
Member

@voith I am pretty sure @veox is correct that arguments are treated as unsigned integers.

@veox
Copy link
Contributor Author

veox commented Aug 28, 2018

@pipermerriam Should be able to...

@voith
Copy link
Contributor

voith commented Aug 28, 2018

For this precompile, inputs are non-negative integers.

Yeah, just tested some solidity code. solidity doesn't allow exponentiation when the base is negative!

@veox
Copy link
Contributor Author

veox commented Aug 28, 2018

@pipermerriam See commit e7b0af9: tests 0^0==1, 0^1==0. Is this enough? Too much?..


EDIT: If OK, should be fine to squash-and-merge on your end. ;)

(HomesteadVM, 0, 1, 0,),
(HomesteadVM, 0, 0, 1,),
(FrontierVM, 0, 1, 0,),
(FrontierVM, 0, 0, 1,),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Side note: looks like it would be valuable to have a parametrized fixture in conftest that returns all mainnet VMs. Then this would reduce to two cases, and we wouldn't have to remember to update the test on every new fork, like:

@pytest.mark.parametrize(
    'base, exponent, expected',
    (
        (0, 1, 0,),
        (0, 0, 1,),
    )
)
def test_exp(mainnet_vm, base, exponent, expected):
    computation = prepare_computation(mainnet_vm)
    computation.stack_push(exponent)
    computation.stack_push(base)
    computation.opcodes[opcode_values.EXP](computation)
    result = computation.stack_pop(type_hint=constants.UINT256)
    assert result == expected

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Lets wait for the chain-builder to land as it will make this a lot easier to do

Copy link
Member

@pipermerriam pipermerriam Aug 30, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

chain builder has landed so this should be pretty easy now

@pipermerriam pipermerriam merged commit 0c12ccf into ethereum:master Aug 28, 2018
@veox veox deleted the fix-exp8-consensus-failure branch August 28, 2018 21:05
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants