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

Conversion of EVM stack to big integers #137

Closed
wants to merge 45 commits into from
Closed

Conversation

vkomenda
Copy link

No description provided.

5chdn and others added 30 commits February 21, 2019 11:25
* no-git for publish jobs, empty artifacts dir (openethereum#10393)

* no-git for publish jobs, empty artifacts dir

* fix syntax

* prettiness

* fix prettiness

* should get rid of git in publishing

* Fix to_pod storage trie value decoding (openethereum#10368)

* snap: reenable i386, arm64, armhf architecture publishing (openethereum#10386)

* snap: reenable i386, arm64, armhf architecture publishing

* gitlab: fix indent

* gitlab: fix yml syntax

* Linker for crosscomile

* fix target to linker

* new docker image

* fix lint, add build to this PR

* calc SHA3 using rhash

* add new images for i386, armhf

* show snap target & artifacts

* set CARGO_TARGET for publish snap

* move detect Version to publish snap

* rm libc6 dep from snap-template up pub-snap script

* clean up cargo config before add linker

* move linker config to docker images
* revert some changes, could be buggy (openethereum#10399)

* 10000 > 5000 (openethereum#10422)

addresses openethereum#10418

* fix panic when logging directory does not exist, closes openethereum#10420 (openethereum#10424)

* fix underflow in pip, closes openethereum#10419 (openethereum#10423)

* ci: clean up gitlab-ci.yml leftovers from previous merge (openethereum#10429)

* Update hardcoded headers for Foundation, Ropsten, Kovan and Classic (openethereum#10417)

* update foundation to #7262209

* update kovan to #10434561

* update ropsten to #5027841

* update classic to #7555073

* Update Ropsten headers to #5101569
* version: bump beta

* Implement parity_versionInfo & parity_setChain on LC; fix parity_setChain (openethereum#10312)

* Light client: implement parity_versionInfo RPC

* Light client: implement set_exit_handler & parity_setChain RPC

* parity_setChain RPC: return an error if failed (instead of `true`)

* Implement eth_subscribe('syncing') RPC for full node & light node

* Fix indentation

* Revert commit: Implement eth_subscribe('syncing')

* Revert change to Cr callback function

* CI publish to aws (openethereum#10446)

* move publish aws from gitlab.yml to gitlab scripts

* gitlab.yml cleaning
move publish AWS to gitlab scripts
remove dependencies from android build

* CI aws git checkout (openethereum#10451)

* Updating the CI system with the publication of releases and binary files on github

Signed-off-by: Denis S. Soldatov aka General-Beck <general.beck@gmail.com>

* move publish aws from gitlab.yml to gitlab scripts

Signed-off-by: Denis S. Soldatov aka General-Beck <general.beck@gmail.com>

* gitlab.yml cleaning
move publish AWS to gitlab scripts
remove dependencies from android build

Signed-off-by: Denis S. Soldatov aka General-Beck <general.beck@gmail.com>

* Revert "Updating the CI system with the publication of releases and binary files on github"

This reverts commit da87e06.

* remove no-git for aws

* microfix

* no need in no_git then

* Revert "CI aws git checkout (openethereum#10451)" (openethereum#10456)

* Revert "CI aws git checkout (openethereum#10451)"

This reverts commit 3e1d731.

* Update .gitlab-ci.yml

revert aws script with small fixes

* Delete publish-aws.sh

* Tests parallelized (openethereum#10452)

* tests splitted, phase 1

* typo

* fix wrong launch commands

* typos

* rearrangements

* use `nproc` function for threads

* use nproc for threads

* let theads be auto, build-andriod no more in regular run

* split val chain and cargo check

* renamed some files

* wrong phase

* check rust files before test jobs

* lint error

* rust files modivied var

* test except changes

* add rust_changes except

* lint error

* fixes

* .gitlab-ci.yml can't be excluded

* pipeline shouldn't start

* pipeline must go

* pipeline must go 2

* pipeline must go 3

* pipeline must go 4

* pipeline must go 5

* pipeline must go 6

* pipeline must go 7

* pipeline must not go 1

* pipeline must go 8

* avoid skippng tests yet, reintroducing them after the caching

* test theory

* parallelized cargo check with combusting helicopters

* less uploads

* alias for cargo checks

* nice template

* Ensure static validator set changes are recognized (openethereum#10467)
* version: bump beta

* Сaching through docker volume (openethereum#10477)

* _old codebase_ before docker update

* before docker update, testing runnr

* docker update, testing the caching

* distributed job cargo homes

* distributed job cargo homes 2

* distributed job cargo homes 3

* dockerfile with gitlab checkout, audit uses template

* dockerfile gets repo in volume

* change builds_dir

* trying docker cache for repo

* repo cached automatically

* after script is not concatenated

* check sccache non-cacheable reasons nature

* watch cache

* log sccache

* log sccache 2

* debug log sccache

* fix debug log sccache

* fix debug log sccache 2

* debug log cache 3

* debug log cache 3

* trace log all sccache

* test wo cargo cache

* test w removed cargo cache

* report non-cacheable reasons, cargo cache is back and empty

* report non-cacheable reasons, cargo cache is back and empty 2

* report non-cacheable reasons, cargo cache is back and empty 3

* wrap into after_script

* restore CI tags

`qa` -> `linux-docker`

* return to main runners, this will fail until config on runners And Dockerfile won't be updated

* typo fix CI lint

* return to docker tag

* fix win&mac build (openethereum#10486)

add CARGO_HOME:                      "${CI_PROJECT_DIR}/.cargo"

* fix(extract `timestamp_checked_add` as lib) (openethereum#10383)

* fix(extract `timestamp_checked_add` as lib)

* fix(whisper types): remove unused `EmptyTopics`

* fix(time-lib): feature-flag to use time-lib or std

This commit adds conditional compilation checks that falls back to `our time-lib` when
`time_checked_add` is not available in the standard library

Note, `time_checked_add` covers both `checked_add` and `checked_sub`

* fix(grumble): use cfg_attr to define rustc feature
* version: bump beta

* Add additional request tests (openethereum#10503)
* version: bump beta

* fix Sha3/keccak256 hash calculation for binaries (openethereum#10509)

openethereum#10495

* verbose flag for cpp tests (openethereum#10524)

* Initial support sccache for windows build (openethereum#10520)

* Initial support sccache for win build

* show sccache stats

* cache paths for shared runners

* sccache status is in the script.

* removed windows test for now

* ethcore: remove eth social and easthub chain configs (openethereum#10531)

* Fix max_gas (openethereum#10537)

Fix max_gas

* build android with cache, win fixes (openethereum#10546)

* build android with cache!

* windows fixes

* windows fixes 2

* windows fixes 3

* windows fixes 4

* windows should have sccache variables in env variables

* Update light client harcoded headers (openethereum#10547)

* kovan #10643457

* ropsten #5296129

* foundation #7460865

* classic #7747585

* indentation

* morden #3973121
* fix(rpc-types): replace uint and hash with `ethereum_types v0.4` (openethereum#10217)

* fix(rpc-types): remove uint and hash wrappers

* fix(tests)

* fix(cleanup)

* grumbles(rpc-api): revert `verify_signature`

* revert change of `U64` -> `u64`

* fix(cleanup after bad merge)

* chore(bump ethereum-types)

* fix(bad merge)

* feat(tests ethereum-types): add tests

* chore(update `ethereum-types` to 0.4.2)

* feat(tests for h256)

* chore(rpc): remove `ethbloom` import

Use re-export from `ethereum-types` instead

* fix(bad merge): remove `DefaultAccount` type

* doc(add TODO with issue link)

* chore(bump ethereum-types) (openethereum#10396)

Fixes a de-serialization bug in `ethereum-tyes`

* fix(light eth_gasPrice): ask network if not in cache (openethereum#10535)

* fix(light eth_gasPrice): ask N/W if not in cache

* fix(bad rebase)

* fix(light account response): update `tx_queue` (openethereum#10545)

* fix(bump dependencies) (openethereum#10540)

* cargo update -p log:0.4.5

* cargo update -p regex:1.0.5

* cargo update -p parking_lot

* cargo update -p serde_derive

* cargo update -p serde_json

* cargo update -p serde

* cargo update -p lazy_static

* cargo update -p num_cpus

* cargo update -p toml

# Conflicts:
#	Cargo.lock

* tx-pool: check transaction readiness before replacing (openethereum#10526)

* Update to vanilla tx pool error

* Prevent a non ready tx replacing a ready tx

* Make tests compile

* Test ready tx not replaced by future tx

* Transaction indirection

* Use StateReadiness to calculate Ready in `should_replace`

* Test existing txs from same sender are used to compute Readiness

* private-tx: Wire up ShouldReplace

* Revert "Use StateReadiness to calculate Ready in `should_replace`"

This reverts commit af9e69c

* Make replace generic so it works with private-tx

* Rename Replace and add missing docs

* ShouldReplace no longer mutable

* tx-pool: update to transaction-pool 2.0 from crates.io

* tx-pool: generic error type alias

* Exit early for first unmatching nonce

* Fix private-tx test, use existing write lock

* Use read lock for pool scoring

* fix openethereum#10390 (openethereum#10391)

* private-tx: replace error_chain (openethereum#10510)

* Update to vanilla tx pool error

* private-tx: remove error-chain, implement Error, derive Display

* private-tx: replace ErrorKind and bail!

* private-tx: add missing From impls and other compiler errors

* private-tx: use original tx-pool error

* Don't be silly cargo
* Add initial `engines::authority_round::randomness` module.
* Added `authority_round_random.json`, based on `pos-contracts@8d10a85ad87633a54a7444883b32687faf5b80bf`.
  The contract was compiled using solc `0.4.25+commit.59dbf8f1.Linux.g++`
  and formatted using [json_pp](https://packages.debian.org/stretch/libjson-pp-perl).
* Added phase skeleton for determination through constant contract calls.
* Factor out utility functions into `util` module.
* Add comments about adding txns to current block.
* Make calls service transactions i.e. zero gas price.

* call reportMalicious with 0 gas price

* call reportBenign with 0 gas price as well
`Engine::on_new_block` is called multiple times, so we need to
remember which round a random secret is for, and generate a new one
only in the following round. `on_block_new` also mustn't fail if
the same transaction is produced again.
* WIP: store the encrypted secret on chain

* Improve error handling

* Aura randomness: Fix error handling.

* Aura randomness: clean up and update comments.

* Aura randomness: fix existing commit hash check
It is now called by the new method `ValidatorSet::on_new_block()`.
Closes #71
These seem to be leftovers from debugging.
* Cleanup rand contract, add log for tx nonce error.

The contract seems to have a duplicate entry. This replaces it
with the generated ABI.

`Client::transact` sometimes fails with and `Old` error, meaning
the nonce is outdated. This will now just log an error instead of
making block creation fail. The `schedule_service_transaction`
method might get removed in the future anyway, if we make
`ValidatorSet::on_new_block` _return_ the transaction instead of
queueing it.

* Fix error handling in on_prepare_block.
* Push validator set txns directly onto block.

* Only call on_prepare_block on new blocks.
That solves the issue of the gas limit, where we'd otherwise pick
some arbitrary large value, and the chain ID, which now conforms
to EIP155 again.
@phahulin
Copy link

I'll try to fully resync POA Sokol with new binary and see if any errors come up

Copy link

@varasev varasev left a comment

Choose a reason for hiding this comment

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

I made google spreadsheet https://docs.google.com/spreadsheets/d/1Fa7pBG1vhwtPkQEBiFbjyWm_etWy2o1bVBHZYI3-zgA/edit?usp=sharing comparing the benchmark results for the commits:

It shows that the latest commit 0d3693a is much slower than the commit c159ba5 made before refactoring (for all the benchmark tests).

The commit 0d3693a is also slower than 967b29e.

So, the current refactoring gives us the opposite results (slowing down from 1.1 to 2.8 times).

@phahulin
Copy link

POA Core and xDai resynced without errors. In Sokol there was an error on block https://blockscout.com/poa/sokol/blocks/5021292/transactions (there's a single tx in that block).

2019-05-28 13:15:06 UTC Stage 5 block verification failed for #5021292 (0xff1a…258d)
Error: Error(Block(InvalidStateRoot(Mismatch { expected: 0x100be2e8b4d067add33a208b619801c7ba9a6427927612324214a15489b0cc3d, found: 0xf29cd92f062278f2149e8bba4e683b271f4bc73bfba3280ef9a84288a5b60613 })), State { next_error: None, backtrace: InternalBacktrace { backtrace: None } })
2019-05-28 13:15:06 UTC 
Bad block detected: Error(Block(InvalidStateRoot(Mismatch { expected: 0x100be2e8b4d067add33a208b619801c7ba9a6427927612324214a15489b0cc3d, found: 0xf29cd92f062278f2149e8bba4e683b271f4bc73bfba3280ef9a84288a5b60613 })), State { next_error: None, backtrace: InternalBacktrace { backtrace: None } })
RLP: f902eef9023da014542f792942c71956a9ff20500eeefc8b5dcacc541fa111882a09e22bf9e65ca01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d4934794d764da536506bd1af6e494a8cdd37959a7387acba0100be2e8b4d067add33a208b619801c7ba9a6427927612324214a15489b0cc3da029ee61e69790b8e34a4b40c00033d6fc7130b6349f2da66f1f79c091885e147fa027ad54967532e5e4c70407115a045b79491e1a1ddfe6c796182324cbc271a90fb901000000000000000000000000000000000000000000010000000000000000000000000000000000000000000400000020000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000001000000000000000200000000000001000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000000000000000000080080000000100000000000000000000000000000090fffffffffffffffffffffffffffffffe834c9e6c837a120082d4d7845bc0afac96d583010b088650617269747986312e32372e32826c69841259bcbcb841480895df40eaba0592bed5005f1645310036bde02aa07125c69ca83f6da68ff6189a2e04cdbe1fe089b390a99c0ccf35ae42fb027f4ef29749c26c0824d20bb501f8abf8a944843b9aca0082d620944f9895cfe49b8e648f8bae68519f059dd997a28280b844497d7551000000000000000000000000201406e518abe5e351a8717dd0f419616ca02fbc000000000000000000000000000000000000000000000000000000000000006481bda0151207ea78ce245710f085fa42ecfdc8000ae023f2e05c0598e23cc6bebdd20fa02dd54b69140cac8edd589764a1701410a014c30aa589a5cbae046e0eac7c111cc0
Header: Header { parent_hash: 0x14542f792942c71956a9ff20500eeefc8b5dcacc541fa111882a09e22bf9e65c, timestamp: 1539354540, number: 5021292, author: 0xd764da536506bd1af6e494a8cdd37959a7387acb, transactions_root: 0x29ee61e69790b8e34a4b40c00033d6fc7130b6349f2da66f1f79c091885e147f, uncles_hash: 0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347, extra_data: [213, 131, 1, 11, 8, 134, 80, 97, 114, 105, 116, 121, 134, 49, 46, 50, 55, 46, 50, 130, 108, 105], state_root: 0x100be2e8b4d067add33a208b619801c7ba9a6427927612324214a15489b0cc3d, receipts_root: 0x27ad54967532e5e4c70407115a045b79491e1a1ddfe6c796182324cbc271a90f, log_bloom: 0x00000000000000000000000000000000000000000100000000000000000000000000000000000000000004000000200000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000010000000000000002000000000000010000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000000000000000000800800000001000000000000000000000000000000, gas_used: 54487, gas_limit: 8000000, difficulty: 340282366920938463463374607431768211454, seal: [[132, 18, 89, 188, 188], [184, 65, 72, 8, 149, 223, 64, 234, 186, 5, 146, 190, 213, 0, 95, 22, 69, 49, 0, 54, 189, 224, 42, 160, 113, 37, 198, 156, 168, 63, 109, 166, 143, 246, 24, 154, 46, 4, 205, 190, 31, 224, 137, 179, 144, 169, 156, 12, 207, 53, 174, 66, 251, 2, 127, 78, 242, 151, 73, 194, 108, 8, 36, 210, 11, 181, 1]], hash: Some(0xff1acc51dab93d33969dc3160c777f6645f997a649b265046830282b4bb0258d) }
Uncles: 
Transactions:[Tx 0] UnverifiedTransaction { unsigned: Transaction { nonce: 68, gas_price: 1000000000, gas: 54816, action: Call(0x4f9895cfe49b8e648f8bae68519f059dd997a282), value: 0, data: [73, 125, 117, 81, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32, 20, 6, 229, 24, 171, 229, 227, 81, 168, 113, 125, 208, 244, 25, 97, 108, 160, 47, 188, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 100] }, v: 189, r: 9530427700988829602190235950132050505565850277403879439041744793130193768975, s: 20730937074926652319264621547119453737374562879435505647005372538798273728796, hash: 0x5121958fca2cc47adf8ce92b7d3646a9062c6c4b7903c1a38c77a7d4ed5a6dac }

Not sure if it's a real issue or just a bad block, I'll resync one more time.

@vkomenda
Copy link
Author

So is it this block on which resync failed? https://blockscout.com/poa/sokol/blocks/5021292/transactions

@phahulin
Copy link

Yes, it output the error message and stopped syncing.

@varasev
Copy link

varasev commented May 29, 2019

@phahulin This version is only for POSDAO (not for POA Networks) because it is based on aura-pos branch, so it is not compatible with POA Core, POA Sokol, Kovan, and xDai. Am I right, @vkomenda ?

@phahulin
Copy link

phahulin commented May 29, 2019

@varasev ah yes, you're right!
@vkomenda could you please create a branch with changes of this PR applied on top of upstream stable? Otherwise we won't have any running network to test them.

@varasev
Copy link

varasev commented May 29, 2019

could you please create a branch with changes of this PR applied on top of upstream stable?

I think, testing these changes makes no sense because they slow down the node's work: #137 (review)

We should test them when we see speed improvements in comparison with the stable branch of upstream.

@phahulin
Copy link

Ok, please ping me when it's ready

},
instructions::LOG0 | instructions::LOG1 | instructions::LOG2 | instructions::LOG3 | instructions::LOG4 => {
let no_of_topics = instruction.log_topics().expect("log_topics always return some for LOG* instructions; qed");
let log_gas = schedule.log_gas + schedule.log_topic_gas * no_of_topics;

let data_gas = overflowing!(Gas::from_u256(*stack.peek(1))?.overflow_mul(Gas::from(schedule.log_data_gas)));
let data_gas = overflowing!(Gas::from_u256(integer_to_u256(stack.peek(1)))?.overflow_mul(Gas::from(schedule.log_data_gas)));
Copy link
Collaborator

Choose a reason for hiding this comment

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

Here it might be possible to multiply first, and convert later.

Copy link
Author

Choose a reason for hiding this comment

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

This conversion disappears if Gas = Integer.

return Ok(Gas::from(0));
}

Gas::from_u256(overflowing!(offset.overflowing_add(*size)))
Gas::from_u256(overflowing!(integer_to_u256(offset).overflowing_add(integer_to_u256(size))))
Copy link
Collaborator

Choose a reason for hiding this comment

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

But even if CostType is still only implemented for U256 and usize, a Gas::from_integer could speed up some of the conversions in this file, at least for usize. And it would make the code less verbose.

if nel > 0 {
// Fill out the leading zeros.
self[offset..offset + nel].copy_from_slice(vec![0; nel].as_slice())
}
Copy link
Collaborator

Choose a reason for hiding this comment

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

This would avoid allocating a new vector of zeroes:

for byte in &mut self[offset..offset + nel] {
    *byte = 0;
}

@@ -343,7 +337,8 @@ impl<Cost: CostType> Interpreter<Cost> {
// Calculate gas cost
let requirements = self.gasometer.as_mut().expect(GASOMETER_PROOF).requirements(ext, instruction, info, &self.stack, self.mem.size())?;
if self.do_trace {
ext.trace_prepare_execute(self.reader.position - 1, opcode, requirements.gas_cost.as_u256(), Self::mem_written(instruction, &self.stack), Self::store_written(instruction, &self.stack));
let store_wr = Self::store_written(instruction, &self.stack).and_then(|(a, b)| Some((integer_to_u256(&a), integer_to_u256(&b))));
Copy link
Collaborator

Choose a reason for hiding this comment

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

and_then(|x| Some(f(x))) is equivalent to map(|x| f(x)).

ethcore/evm/src/interpreter/mod.rs Show resolved Hide resolved
} else {
value << (shift.as_u32() as usize)
value << shift.to_u32_wrapping()
Copy link
Collaborator

Choose a reason for hiding this comment

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

Does this need to be truncated to 256 bits?

let mut shifted = value >> shift;
if sign {
shifted = shifted | (U256::max_value() << (256 - shift));
shifted = shifted | (u256_max() << (256 - shift));
Copy link
Collaborator

Choose a reason for hiding this comment

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

This creates a number with more than 256 bits.
Would shifted = shifted | (((1 << shift) - 1) << (256 - shift)) be correct?
Or would it be easier to apply two's complement above (instead of explicitly taking the sign), and apply it here again?

let sign = n.get_bit(255);
let n_abs = if sign {
// The result of addition cannot overflow 256 bits because the bit 255 is 0 after ^.
(((Integer::from(1) << 256) - 1) ^ n) + 1
Copy link
Collaborator

Choose a reason for hiding this comment

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

2256 - 1 should probably also be a constant.
(Or would (Integer::from(1) << 256) - n be just as fast?)

let digits = n256.to_digits::<u8>(Order::MsfBe);
let len = digits.len();
r[SIZE - len .. SIZE].copy_from_slice(digits.as_slice());
r
Copy link
Collaborator

Choose a reason for hiding this comment

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

This could be deduplicated with integer_to_address, using a helper method with a size parameter.
Also, I wonder whether the allocation of n256 can be avoided by just using n.to_digits().as_slice() directly, and copying only the required number of bytes? That shouldn't be too wasteful, since we probably only call these methods with numbers that have at most 32 bytes?

Copy link
Author

Choose a reason for hiding this comment

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

I think you need to store the intermediate vector n.to_digits() anyway to refer to it as a slice but I'll try your suggestion.

#[inline]
fn address_to_u256(value: Address) -> U256 {
U256::from(&*H256::from(value))
fn apply_rug_first_signed_2<F>(f: F, a: Integer, b: Integer) -> Integer
Copy link
Collaborator

Choose a reason for hiding this comment

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

We need to convert the result back if it's negative, either in all the call sites or in this method.
(Also in apply_rug_signed_2.)

let (b, sign_b) = get_and_reset_sign(self.stack.pop_back());

// -2^255
let min = (U256::one() << 255) - U256::one();
Copy link
Author

@vkomenda vkomenda May 29, 2019

Choose a reason for hiding this comment

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

This old code is rather puzzling because the constant here is in fact the maximum signed 256-bit integer (2 ** 255) - 1, which is not what the comment claims it is (the precedence of the minus sign also requires extra parentheses in that comment). The minimum would have been just 1 << 255 equivalent to (-2) ** 255. Therefore I removed the old clause that checked whether a was the minimum integer because I think the implementation was incorrect. Moreover why would the minimum value of a need to be mapped to itself? There is no such behaviour in the Jello Paper.

Copy link
Collaborator

Choose a reason for hiding this comment

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

I agree, this is weird. And b == !U256::zero() is impossible anyway, isn't it? Because get_and_reset_sign can't return numbers greater than 2255?

What the special case was meant to handle somehow was probably the fact that the two's complement of -2255 is -2255, so in that case a would be 2255 (interpreted as unsigned). If you divide that by -1 (which is !U256::zero()) the result is not representable, but the Yellow Paper says in that case we should return -2255. But I think we'd do that anyway, even in the last else branch?

So probably the implementation is technically correct, because b == !U256::zero() is impossible anyway?

@vkomenda
Copy link
Author

I refactored the gasometer. Benchmarks report performance drop of 2-2.5 times with respect to the current aura-pos. This is in part because I had to move Integer to U256 conversion to the step function of the EVM interpreter when it reports remaining gas. To remove that and similar conversions it is required to refactor the VM submodule of ethcore. Whether that would increase performance back by more than 2.5 times, I'm not sure.

@varasev
Copy link

varasev commented May 31, 2019

As far as I understand, the main reason for slowness is U256 to Integer and Integer to U256 conversion operations, right?

@varasev
Copy link

varasev commented May 31, 2019

What if we just do the same for MUL opcode as for the MULMOD in the vk-mulmod branch? I mean instead

instructions::MUL => {
	let a = self.stack.pop_back();
	let b = self.stack.pop_back();
	self.stack.push(a.overflowing_mul(b).0);
},

try

instructions::MUL => {
	let a = self.stack.pop_back();
	let b = self.stack.pop_back();
	let a0 = Integer::from_digits(&a, Order::LsfLe);
	let b0 = Integer::from_digits(&b, Order::LsfLe);
	let r = a0 * b0;
	self.stack.push(
		U256::from_little_endian(r.to_digits::<u8>(Order::LsfLe).as_slice())
	);
},

Did you try to do this for the vk-mulmod branch and launch benchmarks for the MUL operation for different size of the operands a and b? What if it would make sense for the big operands?

@varasev
Copy link

varasev commented May 31, 2019

Another thought: what if we leave the rug for MULMOD and ADDMOD (because the rug works faster than U256 for these opcodes), but try to use the num_bigint crate for other math opcodes as Parity team did for the MULMOD here: https://github.com/paritytech/parity-ethereum/pull/10642/files#diff-f24406c9371a7a621c14519c77ce4bdd

For all these tries we need to launch benchmarks to see whether it makes sense and for which size of operands.

@vkomenda
Copy link
Author

vkomenda commented May 31, 2019

As far as I understand, the main reason for slowness is U256 to Integer and Integer to U256 conversion operations, right?

Yes. The places where the conversions happen matter. If any appear in the interpreter, it will run slower than if there weren't any conversions there. The place where conversions happen make difference to performance. Having conversions inside simple arithmetic opcode handlers (not complex like MULMOD) is enough to make these opcodes slower than the old U256 version. Currently though there are no conversions inside opcode handlers but there are some when the interpreter interacts with the VM. This interaction, as it happens, when the interpreter steps through an opcode. So, essentially we still do have conversions at every opcode. When the VM is refactored, we should see less conversions and performance should then increase compared to aura-pos and the upstream version.

@vkomenda
Copy link
Author

vkomenda commented Jun 4, 2019

I converted the VM in 967b29e and performance regressed even more. Now it's 2.1-3.1 times slower than in aura-pos. Using Integer everywhere turns out to be a naive solution. The upstream version has a better integrated solution with no dependency on rug and only MULMOD and ADDMOD being up to 2 times slower in uncommon cases. If we don't need further optimisation, I vote to adopt the upstream solution. In the other case, we need u64-valued counterparts of some methods that are now Integer-valued. Also we should have an EVM interpreter instance that performs internal gas computations using u64 instead of big integers. Instead of more instances we could try defining and using a type that wraps either an Integer or u64 and copy if the wrapped value is a u64.

@varasev
Copy link

varasev commented Jun 4, 2019

The upstream version has a better integrated solution with no dependency on rug and only MULMOD and ADDMOD being up to 2 times slower in uncommon cases.

I'd propose to leave our approach for MULMOD/ADDMOD with the rug, but try to use the upstream's num_bigint solution with other math opcodes as I suggested in the #137 (comment). Let's first do it for one opcode (e.g., MUL) and compare its performance with the U256. And then try to do that for the rest opcodes.

Also, I'd propose to benchmark this one #137 (comment) and see if it is really slower than the standard U256.

@vkomenda
Copy link
Author

vkomenda commented Jun 4, 2019

I'd propose to leave our approach for MULMOD/ADDMOD with the rug, but try to use the upstream's num_bigint solution with other math opcodes as I suggested in the #137 (comment)

I suggested not using rug because the dependency on it introduces problems with the Windows build. MULMOD and ADDMOD are only 1.2 times slower with num_bigint compared to rug in the case of small moduli. With large moduli that ratio increases to 2 but is still better than 10 compared to the old version.

Also, I'd propose to benchmark this one #137 (comment) and see if it is really slower than the standard U256.

Isn't this what I did in earlier commits? I used to convert from U256 inside opcode handlers. That was slower than the upstream version.

@varasev
Copy link

varasev commented Jun 4, 2019

Ok, that would be interesting to compare num_bigint approach for all the math opcodes with an old upstream version.

Also, the solution with improving the ethereum-types as @afck suggested could be implemented. I mean, we should try and benchmark different scenarios and choose the fastest one.

Isn't this what I did in earlier commits? I used to convert from U256 inside opcode handlers. That was slower than the upstream version.

Yeah, I just didn't see the benchmark results for that comparison (or don't remember where they are in this repo). Maybe I'll try to compare that myself when I have time.

@vkomenda
Copy link
Author

vkomenda commented Jun 4, 2019

I just didn't see the benchmark results for that comparison (or don't remember where they are in this repo)

I think you did benchmark that commit, 967b29e. I used a function apply_rug_truncated_2 which did the conversion and computation.

@ordian
Copy link

ordian commented Jul 17, 2019

FYI, the division algorithm in uint was improved (paritytech/parity-common#126) and integrated into parity-ethereum 2.6 (now in beta).

@varasev
Copy link

varasev commented Jul 18, 2019

@ordian Thank you for the info!

@vkomenda
Copy link
Author

Closing in favour of #179.

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.