Skip to content
This repository has been archived by the owner on Nov 6, 2020. It is now read-only.

EIP198 and built-in activation #4926

Merged
merged 3 commits into from
Mar 21, 2017
Merged

EIP198 and built-in activation #4926

merged 3 commits into from
Mar 21, 2017

Conversation

rphmeier
Copy link
Contributor

Implements EIP198 with exponentiation by squaring.

Changes to built-in declaration: optional activate_at field, defaults to 0. Built-ins won't be available from the Engine until activated. Replaces the builtin_cost, execute_builtin, and is_builtin of engine with a single builtin(&Address, block_number) -> Option<&Builtin>, reducing the amount of lookups done.

Could use a few more test cases, although I expect the consensus tests will cover this in detail.

num::BigUint is known to be much slower than GMP. ramp is a faster pure-Rust alternative, but requires nightly. If this calculation starts to become a bottleneck, we can move to GMP.

@rphmeier rphmeier added A0-pleasereview 🤓 Pull request needs code review. M4-core ⛓ Core client code / Rust. labels Mar 15, 2017
@rphmeier rphmeier requested a review from arkpar March 15, 2017 18:52
@rphmeier rphmeier mentioned this pull request Mar 15, 2017
12 tasks
@@ -189,6 +189,7 @@
"0000000000000000000000000000000000000002": { "builtin": { "name": "sha256", "pricing": { "linear": { "base": 60, "word": 12 } } } },
"0000000000000000000000000000000000000003": { "builtin": { "name": "ripemd160", "pricing": { "linear": { "base": 600, "word": 120 } } } },
"0000000000000000000000000000000000000004": { "builtin": { "name": "identity", "pricing": { "linear": { "base": 15, "word": 3 } } } },
"0000000000000000000000000000000000000005": { "builtin": { "name": "modexp", "activate_at": "0x7fffffffffffff", "pricing": { "modexp": { "divisor": 20 } } } },
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Unfortunately, the pricing scheme for modexp isn't a simple function of the size like the others, so we have to create a special pricer which can't really be reused.

// prefer to fail rather than silently break consensus.
if !builtin.is_active(self.info.number) {
let msg = format!("Engine returned unactivated built-in at {}", params.code_address);
return Err(evm::Error::Internal(msg));
Copy link
Collaborator

Choose a reason for hiding this comment

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

State checkpoint is not discarded nor reverted here.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Nice catch. It should be done with a Drop guard to prevent mistakes like that.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Although, looking closer, this branch already indicates a consensus failure of some kind.


// read lengths as usize.
// ignoring the first 24 bytes might technically lead us to fall out of consensus,
// but so would running out of addressable memory!
Copy link
Collaborator

Choose a reason for hiding this comment

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

Why impose such artificial limitation? Is it part of the spec?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yeah, the spec favors 32 bytes over 8 to encode the lengths because that's the native EVM word size and would require much less fiddling with bits to pass arguments, although really nobody will be able to pass lengths anywhere near that size.

I'd argue that this is a physical rather than an artificial limitation: I don't think a machine will exist for a long time which can handle 2^256-bit numbers.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Sure, we can't handle such memory now as well that's why I see no point in this limit at all. Why 8 and not 7?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

two reasons: it makes decoding the size much easier with the byteorder library, and it's an amount of memory actually addressable on 64-bit machines.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Right, sorry. Makes sense!


// calculate modexp: exponentiation by squaring.
fn modexp(mut base: BigUint, mut exp: BigUint, modulus: BigUint) -> BigUint {
if base == BigUint::zero() || modulus <= BigUint::one() { return BigUint::zero() }
Copy link
Collaborator

Choose a reason for hiding this comment

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

0**0 will return 0, there is a comment in original EIP suggesting that it should be 1 for consistency with existing evm EXP opcode.

if base == BigUint::zero() || modulus <= BigUint::one() { return BigUint::zero() }

let mut result = BigUint::one();
base = base % &modulus;
Copy link
Collaborator

Choose a reason for hiding this comment

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

This may lead to base == 0 and we sould short circuit this case as well.

@rphmeier
Copy link
Contributor Author

@tomusdrw Grumbles addressed; I changed the detected consensus failure in executive (which should never happen) to panic instead of issue an error which would be silently swallowed.

(true, false) => return BigUint::zero(), // 0^n % m, n>0
(false, false) if modulus <= BigUint::one() => return BigUint::zero(), // a^b % 1 = 0.
_ => {}
}

let mut result = BigUint::one();
base = base % &modulus;
Copy link
Collaborator

Choose a reason for hiding this comment

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

You could do this line at the very beginning.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

At the very least you have to check if the modulus is zero beforehand or the code will panic.

@gavofyork
Copy link
Contributor

looks reasonable in design. haven't reviewed algo yet, but tests should cover that.

@debris
Copy link
Collaborator

debris commented Mar 17, 2017

merged with master to trigger the CI build

@arkpar arkpar added A8-looksgood 🦄 Pull request is reviewed well. and removed A0-pleasereview 🤓 Pull request needs code review. labels Mar 21, 2017
@NikVolf NikVolf merged commit 797a3e1 into master Mar 21, 2017
@NikVolf NikVolf deleted the eip198 branch March 21, 2017 16:36
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
A8-looksgood 🦄 Pull request is reviewed well. M4-core ⛓ Core client code / Rust.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants