Skip to content
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

Fix padding and gas cost computation issues in precompiles #961

Conversation

nanocryk
Copy link
Contributor

@nanocryk nanocryk commented Jan 4, 2023

Precompiles modexp(5), ecadd(6) and ecmul(7) have inconsistencies compared to the yellowpaper and geth implementation.

  • in geth (link 1, link 2) ecadd and ecmul pad the input with zeros when it is too short. In Frontier it currently reverts instead.
  • in geth (link) and the yellowpaper, modexp also pad the input.
  • modexp gas cost computation is wrongly implemented in Frontier, due to some confusing behavior of Python & operator in the EIP2565. The correct behavior, also expressed in the yellowpaper, is to take the first 32 bytes of the exp input, while in Rust (exponent & (2**256 - 1)) will keep the 32 last bytes. To avoid any confusion, I do like in the yellowpaper by looking directly at the first 32 bytes in the input, which is what is done in geth.

@nanocryk nanocryk requested a review from sorpaas as a code owner January 4, 2023 10:26
@nanocryk nanocryk changed the title Pad input of BnCurve precompiles if too short Pad input of precompiles if too short Jan 6, 2023
@nanocryk nanocryk changed the title Pad input of precompiles if too short Fix padding and gas cost computation issues in precompiles Jan 6, 2023
});
}
let mut buf = [0u8; 32];
let mut input_iter = input[start_inx..].iter().copied();
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
let mut input_iter = input[start_inx..].iter().copied();
buf[..input.len()].copy_from_slice(&input[..]);

It also needs a check above that the input length is <= 32.

let mut px_buf = [0u8; 32];
let mut py_buf = [0u8; 32];
let mut input_iter = input[start_inx..].iter().copied();
px_buf.fill_with(|| input_iter.next().unwrap_or(0));
Copy link
Member

Choose a reason for hiding this comment

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

Same as above, please use copy_with_slice.

let mut mod_len_buf = [0u8; 32];

let mut input_iter = input.iter().copied();
base_len_buf.fill_with(|| input_iter.next().unwrap_or(0));
Copy link
Member

Choose a reason for hiding this comment

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

Also same as above, use copy_with_slice.

if mod_len == 0 {
return Ok(PrecompileOutput {
exit_status: ExitSucceed::Returned,
output: vec![],
Copy link
Member

Choose a reason for hiding this comment

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

Is this actually right? EIP-198 states:

returns an output (BASE**EXPONENT) % MODULUS as a byte array with the same length as the modulus.

So if I'm to understand the spec, this should be a number of zeros, instead of an empty array.

Also is this special case actually needed? L166 already has:

if base_len == 0 && mod_len == 0 {

Copy link
Contributor Author

@nanocryk nanocryk Jan 9, 2023

Choose a reason for hiding this comment

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

Base len is encoded before mod len, so a truncated input could have non-zero base len but zero mod len.

As the EIP states that it returns a byte array with the same length as the modulus, then if mod len is 0 the output should be zero len too.

I'm not too familiar with Go but this seems to return zero len array:
https://github.com/ethereum/go-ethereum/blob/v1.10.26/core/vm/contracts.go#L386

let base_start = 96; // previous 3 32-byte fields
let base = BigUint::from_bytes_be(&input[base_start..base_start + base_len]);
let mut base_buf = vec![0u8; base_len];
base_buf.fill_with(|| input_iter.next().unwrap_or(0));
Copy link
Member

Choose a reason for hiding this comment

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

Same as above, please use copy_with_slice.

@crystalin
Copy link
Contributor

@sorpaas can we get an additional review from you?

@sorpaas sorpaas merged commit 76ad4c8 into polkadot-evm:master Jan 16, 2023
@nanocryk nanocryk deleted the jeremy-support-padding-in-bn-precompiles branch January 17, 2023 11:14
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