Skip to content

Fix padding and gas cost computation issues in precompiles#961

Merged
sorpaas merged 9 commits intopolkadot-evm:masterfrom
moonbeam-foundation:jeremy-support-padding-in-bn-precompiles
Jan 16, 2023
Merged

Fix padding and gas cost computation issues in precompiles#961
sorpaas merged 9 commits intopolkadot-evm:masterfrom
moonbeam-foundation:jeremy-support-padding-in-bn-precompiles

Conversation

@nanocryk
Copy link
Copy Markdown
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
Comment thread frame/evm/precompile/modexp/src/lib.rs Outdated
@nanocryk nanocryk changed the title Pad input of precompiles if too short Fix padding and gas cost computation issues in precompiles Jan 6, 2023
Comment thread frame/evm/precompile/modexp/src/lib.rs
Comment thread frame/evm/precompile/modexp/src/lib.rs Outdated
Comment thread frame/evm/precompile/bn128/src/lib.rs Outdated
});
}
let mut buf = [0u8; 32];
let mut input_iter = input[start_inx..].iter().copied();
Copy link
Copy Markdown
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.

Comment thread frame/evm/precompile/bn128/src/lib.rs Outdated
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
Copy Markdown
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.

Comment thread frame/evm/precompile/modexp/src/lib.rs Outdated
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
Copy Markdown
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
Copy Markdown
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
Copy Markdown
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

Comment thread frame/evm/precompile/modexp/src/lib.rs Outdated
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
Copy Markdown
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
Copy Markdown
Collaborator

@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