mirror of
https://github.com/vacp2p/linea-monorepo.git
synced 2026-01-06 19:13:55 -05:00
[Feat] Merge linea-sequencer plugin repo into monorepo (#1041)
* fix: overflow for modexp arg (#489) * fix: overflow for modexp arg * fix: can be longer than 32 Bytes * Log ZkTracer counters for every produced block (#485) Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net> * fix(romLex): wrong stack arg for extcodecopy address (#498) * fix: lookup txndata <-> wcp (#488) Fixes #486 Signed-off-by: franklin.delehelle@consensys.net * fix: continuous tracing plugin start check (#500) Fixes #499 Signed-off-by: Daniel Lehrner <daniel.lehrner@consensys.net> Signed-off-by: franklin.delehelle@consensys.net * perf: cache tx-specific line counter (#497) Signed-off-by: franklin.delehelle@consensys.net * style: update name of prec limits to avoid confusion with old geth name (#506) * style: update name of prec limits to avoid confusion with old geth name * style: update module name * style: update name in .toml files * style: remove NB * [MINOR] Add javadoc (#507) Signed-off-by: Gabriel-Trintinalia <gabriel.trintinalia@consensys.net> * refactor: group RLPs modules, use retro-compatible module keys (#508) * feat: enable TxnData constraints (#502) Co-authored-by: Franklin Delehelle <franklin.delehelle@odena.eu> * feat: introduce a trivial assembler in `BytecodeCompiler` (#512) * ci: use Corset releases (#518) Directly download corset binary release instead of building it from scratch every time. This should spare 3-5 minutes per test CI cycle. * Perf/issue 520/update wcp module (#521) * perf: new spec * feat: constraint update * fix: debug * style: spotless * fix: constraint * fix: constraint * fix: constraint * fix: debug * perf: smart slt and sgt * docs: add reminder for value of slt and sgt + constraints update * fix: maxCT must take acc5 and acc6 as arg * style: rename + move stuff to tracing time * docs(copyright): fix copyright (#525) Signed-off-by: Gabriel-Trintinalia <gabriel.trintinalia@consensys.net> * perf: adaptive line count for ADD (#522) Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net> Co-authored-by: Franklin Delehelle <franklin.delehelle@odena.eu> Co-authored-by: Fabio Di Fabio <fabio.difabio@consensys.net> * perf: generalize line count caching (#527) Signed-off-by: franklin.delehelle@consensys.net * feat: implement EUC (#526) * feat: wip * feat(euc): module implem * refactor: convert to new module system * fix: constraints and .toml * style: spotless * fix: ras * fix: toml * feat: constraints --------- Co-authored-by: Franklin Delehelle <franklin.delehelle@odena.eu> * fix: invalid SStore gas computation (#532) * Upgrade Linea Besu to 24.1.0-SNAPSHOT (#535) Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net> * Add profitable transaction selector (#530) * Add profitable transaction selector Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net> * Set default min margin to 1.0 Co-authored-by: Julien Marchand <julien-marchand@users.noreply.github.com> * Pass TransactionSelectionContext to all methods of plugin selector Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net> * log each tx margin at trace level * Update arithmetization/src/main/java/net/consensys/linea/sequencer/txselection/selectors/ProfitableTransactionSelector.java Co-authored-by: Roman Vaseev <4833306+Filter94@users.noreply.github.com> * Add a test for verifying that a prev unprofitable tx is selected after a gas price bump Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net> * Enforce validation on tx selection parameters that must be positive Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net> * Acceptance test for profitable tx selector Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net> --------- Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net> Co-authored-by: Julien Marchand <julien-marchand@users.noreply.github.com> Co-authored-by: Roman Vaseev <4833306+Filter94@users.noreply.github.com> * fix: the hub should trace its transactions in the correct order (#538) * Fix CHANGELOG (#537) * Bump Linea Besu to 24.1.1-SNAPSHOT (#539) Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net> * refactor: add @EqualsAndHashCode annotations and remove corresponding methods (#541) Resolves: #540 Signed-off-by: Tsvetan Dimitrov <tsvetan.dimitrov@consensys.net> * fix: semantics of LinkedList (#544) * Update CHANGELOG (#545) Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net> * fix: stacked set multiple insertions in a single transaction (#548) * Update CHANGELOG from v0.1.4-test12 (#550) Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net> * fix: `StackedSet` semantics (#551) Co-authored-by: FlorianHuc <florian.huc@gmail.com> * Update changelog to v0.1.4-test13 (#552) Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net> * build: update Corset to 9.3.0 (#554) * Fix log of line counts in case of block limit reached + minor changes (#555) Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net> * Add the possibility to adjust the tx size in the profitability formula (#562) Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net> * Upgrade Linea Besu to 24.1.2-SNAPSHOT (#560) Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net> * tests: drop huge random tests (#563) Signed-off-by: franklin.delehelle@consensys.net * feat(modexp-data): implement MODEXP_DATA module (#547) Resolves: #288 Signed-off-by: Tsvetan Dimitrov <tsvetan.dimitrov@consensys.net> * feat: mechanics to capture conflations & replay them as test cases (#561) Signed-off-by: franklin.delehelle@consensys.net * perf(EUC): one less column (#570) * perf(EUC): one less column * feat(constraint): update * docs: Add basic plugins doc (#509) Signed-off-by: Gabriel-Trintinalia <gabriel.trintinalia@consensys.net> * Check upfront profitability + Unprofitable txs cache and retry limit (#565) Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net> * Avoid reprocessing txs that go over line count limit (#571) Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net> * Update CHANGELOG (#577) Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net> * Linea estimate gas endpoint (#585) Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net> Co-authored-by: Pedro Novais <1478752+jpnovais@users.noreply.github.com> * Fix for NoSuchElementException when not passing the gasPrice param (#587) Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net> * Update CHANGELOG for 0.1.4-test18-RC2 (#588) Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net> * Use compressed tx size also when selecting txs from block creation (#590) Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net> * Update CHANGELOG for release 0.1.4-test18-RC3 (#594) Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net> * fix: check that spilling and limits file contain all counted modules (#592) Fixes #511 Signed-off-by: franklin.delehelle@consensys.net * Avoid returing an estimated priority fee that is less than the min gas price (#598) Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net> * Get L2L1 settings from CLI options (#591) Signed-off-by franklin.delehelle@consensys.net Co-authored-by: Fabio Di Fabio <fabio.difabio@consensys.net> * feat: add a replay capture script (#600) Signed-off-by franklin.delehelle@consensys.net * Update CHANGELOG for v0.1.4-test18 (#599) Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net> * Upgrade Linea-Besu to 24.2.0-SNAPSHOT (#602) Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net> * move compress native into plugin repo (#604) * move compress native into plugin repo Signed-off-by: stefan.pingel@consensys.net <stefan.pingel@consensys.net> * spotless and add a missing header Signed-off-by: stefan.pingel@consensys.net <stefan.pingel@consensys.net> * some cleanup Signed-off-by: stefan.pingel@consensys.net <stefan.pingel@consensys.net> --------- Signed-off-by: stefan.pingel@consensys.net <stefan.pingel@consensys.net> * Add compression (#605) Signed-off-by: stefan.pingel@consensys.net <stefan.pingel@consensys.net> * make use of compression in profitability calculation Co-authored-by: Fabio Di Fabio <fabio.difabio@consensys.net> * Update for the new bad block manager (#607) Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net> * Update CHANGELOG for v0.1.4-test20 (#610) Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net> * fix: capture SSTORE-touched storage slots for correct gas computations (#606) * build: make the build script portable, explicit dependency on Go & GCC, test libcompress build (#621) Signed-off-by franklin.delehelle@consensys.net * Update after the refactor of transaction selection service (#626) Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net> * Use the right classloader to load the native library (#628) Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net> * Update CHANGELOG for v0.1.4-test21 (#630) Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net> * linea_estimateGas compatibility switch (#634) Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net> Co-authored-by: Sally MacFarlane <macfarla.github@gmail.com> * Refactor profitability options and calculator + gas price adjustment option (#638) Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net> * Update code to latest plugin API (#640) Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net> * Txpool profitability check (#603) Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net> * Fix price adjustment in profitability formula (#642) Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net> * Update CHANGELOG for v0.1.4-test22 (#641) Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net> * Build native lib for Windows (#645) Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net> * linea_estimateGas compatibility mode multiplier (#646) Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net> * Update CHANGELOG for v0.1.4-test23 (#647) Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net> * On Windows also build Linux native lib so it can run on WSL (#651) Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net> * Improve linea_estimateGas error response (#650) * Improve linea_estimateGas error response Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net> * Update arithmetization/src/main/java/net/consensys/linea/rpc/linea/LineaEstimateGas.java Co-authored-by: Sally MacFarlane <macfarla.github@gmail.com> --------- Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net> Co-authored-by: Sally MacFarlane <macfarla.github@gmail.com> * Update CHANGELOG for v0.1.4-test24 (#652) Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net> * Rename to TransactionPoolValidator to make it clear it only apply to txpool (#654) Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net> * feature: disable arithmetization tests (#4) Signed-off-by: Gabriel-Trintinalia <gabriel.trintinalia@consensys.net> * Extend Module Line Count Verification to `linea_estimateGas` RPC Method (#1) Signed-off-by: Gabriel-Trintinalia <gabriel.trintinalia@consensys.net> * In the txpool, reject a tx if its simulation fails (#2) Signed-off-by: Gabriel-Trintinalia <gabriel.trintinalia@consensys.net> Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net> Co-authored-by: Gabriel-Trintinalia <gabriel.trintinalia@consensys.net> * Update Linea Besu (#5) * feature: Add module limit verification to linea_estimateGas Signed-off-by: Gabriel-Trintinalia <gabriel.trintinalia@consensys.net> * feature: Add module limit verification to linea_estimateGas Signed-off-by: Gabriel-Trintinalia <gabriel.trintinalia@consensys.net> * feature: Add module limit verification to linea_estimateGas Signed-off-by: Gabriel-Trintinalia <gabriel.trintinalia@consensys.net> * feature: Add module limit verification to linea_estimateGas Signed-off-by: Gabriel-Trintinalia <gabriel.trintinalia@consensys.net> * feature: Add module limit verification to linea_estimateGas Signed-off-by: Gabriel-Trintinalia <gabriel.trintinalia@consensys.net> * feature: PR suggestion Signed-off-by: Gabriel-Trintinalia <gabriel.trintinalia@consensys.net> * feature: Add unit test Signed-off-by: Gabriel-Trintinalia <gabriel.trintinalia@consensys.net> * feature: Fix logging Signed-off-by: Gabriel-Trintinalia <gabriel.trintinalia@consensys.net> * Reject a tx, sent via eth_sendRawTransaction, if its simulation fails Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net> * remove unused method Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net> * Apply suggestions from code review Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net> * Fix test flakyness Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net> * Update Linea Besu version Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net> --------- Signed-off-by: Gabriel-Trintinalia <gabriel.trintinalia@consensys.net> Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net> Co-authored-by: Gabriel-Trintinalia <gabriel.trintinalia@consensys.net> * Build native lib for arm64 (#8) Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net> * Improve ZkTracer initialization time (#11) Improve ZkTracer initialization time by doing only once Opcodes and spilling loading from disk resources Signed-off-by: Ameziane H <ameziane.hamlat@consensys.net> * Add more log to txpool simulation validator (#12) Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net> * Update CHANGELOG for 0.1.4-test25 and 0.1.4-test26 (#9) Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net> * Calculate line count only once in linea_estimateGas (#13) Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net> * Update CHANGELOG for v0.1.4-test27 release (#14) Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net> * Extra data based pricing (#10) Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net> * Remove check that minGasPrice need to decrease to retry unprofitable tx (#17) Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net> * Update CHANGELOG for v0.1.4-test28 release (#16) * Update CHANGELOG for v0.1.4-test27 release Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net> * Update CHANGELOG for v0.1.4-test28 release Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net> * update CHANGELOG Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net> --------- Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net> * Update Java, Gradle and dependencies (#20) * Update Gradle and dependencies Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net> * Upgrade to Java 21 Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net> --------- Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net> * Update README.md (#21) Fixed typos in the documentation: - Changed "relating" to "related" for correct usage. - Corrected "build" to "built" for proper past tense. - Changed "than" to "then" for proper conditional usage. * Update besu to 24.6-develop-711dff7 (#18) * Update besu to 24.6-develop-711dff7 * update overridden traceEndTransaction methods * Remove arthimization code and include it as dependency (#15) Resolves: #3 Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net> Co-authored-by: Tsvetan Dimitrov <tsvetan.dimitrov@consensys.net> Co-authored-by: Gabriel-Trintinalia <gabriel.trintinalia@consensys.net> * Update Linea Besu dependecy to 24.6-develop-752aeff (#22) Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net> * Implement linea_setExtraData (#19) * Implement linea_setExtraData Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net> * Fix and test Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net> * Apply suggestions from code review Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net> * Apply suggestions from code review Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net> --------- Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net> * Set plugin-linea-tx-pool-simulation-check-api-enabled=false by default (#23) Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net> * Update documentation (#24) * Update documentation Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net> * minor edits --------- Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net> Co-authored-by: Sally MacFarlane <macfarla.github@gmail.com> * Re-add dependencies to the jar and change duplication strategy (#28) Re-add dependencies to the jar and change duplication strategy * Align linea_estimateGas behavior to geth (#25) Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net> * Update arithmetizationVersion to 0.1.5-rc3 (#29) Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net> * Remove leftover of the repo split (#27) Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net> * Update CHANGELOG for Release v0.1.5-test1 and fix artifact publishing (#32) Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net> * Include baseFee in the calculation of a profitable tx (#30) Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net> * update arithmetization to 0.1.5-rc4 (#36) * update arithmetization to 1.5.0-rc4 Signed-off-by: Sally MacFarlane <macfarla.github@gmail.com> * update module limits files Signed-off-by: Sally MacFarlane <macfarla.github@gmail.com> * ordering and add missing modules Signed-off-by: Sally MacFarlane <macfarla.github@gmail.com> * added missing modules blake_modexp_data and shakira_data and gas Signed-off-by: Sally MacFarlane <macfarla.github@gmail.com> * rename rlp txnrcpt Signed-off-by: Sally MacFarlane <macfarla.github@gmail.com> * Update module limit files and fix test Signed-off-by: Gabriel-Trintinalia <gabriel.trintinalia@consensys.net> --------- Signed-off-by: Sally MacFarlane <macfarla.github@gmail.com> Signed-off-by: Gabriel-Trintinalia <gabriel.trintinalia@consensys.net> Co-authored-by: Gabriel-Trintinalia <gabriel.trintinalia@consensys.net> * fix: patch inconsistencies in module line count limits (#41) Signed-off-by: Tsvetan Dimitrov <tsvetan.dimitrov@consensys.net> * fix typos (#40) * s/ConsenSys/Consensys (#37) Signed-off-by: Sally MacFarlane <macfarla.github@gmail.com> * Improve error log when setting pricing conf via extra data fails (#44) * Improve error log when setting pricing conf via extra data fails Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net> * Documentation using javadoc (#33) * Documentation using javadoc Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net> * Apply suggestions from code review * Update Javadoc Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net> --------- Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net> * Fix and enable unit tests in CI (#45) Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net> * Remove the check that profitable priority fee must be greater than minGasPrice (#49) Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net> * Option to disable setting minGasPrice via extra data (#50) Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net> * feat: bump linea-arithmetization version to 0.2.0-rc4 (#47) * feat: bump linea-arithmetization version to 0.2.0-rc1 * Set arithmetizationVersion=0.2.0-rc2 Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net> * Avoid including sequencer twice when running acceptance tests Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net> * Use shared options from arithmization and separate private options Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net> * Set arithmetizationVersion=0.2.0-rc3 Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net> * Set a value for plugin-linea-conflated-trace-generation-traces-output-path Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net> * Update to the latest version of the shared config Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net> * Set arithmetizationVersion=0.2.0-rc4 Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net> --------- Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net> Co-authored-by: Fabio Di Fabio <fabio.difabio@consensys.net> * Update CHANGELOG for release 0.2.0-rc4.1 (#52) Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net> * Set version to 0.2.0-SNAPSHOT (#53) Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net> * Change artithmetization dependency version to 0.2.0-rc5 (#55) Change artithmetization dependency version to 0.2.0-rc5 Signed-off-by: Ameziane H <ameziane.hamlat@consensys.net> * Set besuVersion=24.7-develop-f812936 and arithmetizationVersion=0.3.0-rc1 (#54) * Set besuVersion=24.7-develop-f812936 Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net> * Set arithmetizationVersion=0.3.0-rc1 Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net> --------- Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net> * Fix `linea_estimateGas` when called with `gasPrice` set (#58) * Fix `linea_estimateGas` when called with `gasPrice` set Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net> * Add test Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net> --------- Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net> * Update CHANGELOG for v0.3.0-rc1.1 (#59) Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net> * docs:extends userdocs (#57) * docs:extends userdocs * docs:extends userdocs * Update README.md repo name change & update of descriptions * Update README.md updated description * Update README.md updated description * Update README.md adds verbose description * Update README.md moving this repo to top * Update README.md moves this repo to top * Update README.md --------- Co-authored-by: Julien Marchand <julien-marchand@users.noreply.github.com> * docs: retires zk-EVM (#61) * Update linea besu version 24.7 develop c0029e6 (#62) * Set besuVersion=24.7-develop-f812936 Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net> * set besuVersion=24.7-develop-5622666 Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net> * Update Linea-Besu to version 24.7-develop-c0029e6 Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net> * Workaround fof DNSJava bug Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net> --------- Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net> * Gradle cleanup and simplification (#63) Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net> * Gradle: replace license check plugin (#65) Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net> * Improve linea_estimateGas error handling (#48) * improve linea-estimateGas error handling Signed-off-by: stefan.pingel@consensys.net <stefan.pingel@consensys.net> --------- Signed-off-by: stefan.pingel@consensys.net <stefan.pingel@consensys.net> Co-authored-by: Sally MacFarlane <macfarla.github@gmail.com> * Fix: postpone registration of plugin validator factory when blockchain service is fully inited (#67) Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net> * Update Linea-Besu and Tracer dependencies (#70) Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net> * Set besuVersion=24.9-delivery32 and arithmetizationVersion=0.6.0-rc1 (#71) * Set besuVersion=24.8-develop-915fcb01 and arithmetizationVersion=0.5.3-beta Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net> * Make it easier to reproduce and debug the failing ATs Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net> * fix ATs Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net> * Set besuVersion=24.9-delivery32 and arithmetizationVersion=0.6.0-rc1 Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net> * Align dependencies with Besu Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net> --------- Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net> * Split rpc methods into different plugins (#73) Signed-off-by: Gabriel-Trintinalia <gabriel.trintinalia@consensys.net> * Update CHANGELOG for release 0.6.0-rc1.1 (#74) Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net> * feat: Report discarded transactions to external service (#69) CLI option: --plugin-linea-rejected-tx-endpoint=<URI> This feature enables reporting of rejected transactions to an external server via JSON-RPC calls when the above CLI option is specified with a valid URI. * rename Hyperledger Besu to just Besu (#75) Signed-off-by: Sally MacFarlane <macfarla.github@gmail.com> * Log the transaction hash in the module limit warning (#76) Signed-off-by: Simon Dudley <simon.dudley@consensys.net> * Update arithmetization dependency version (#77) Signed-off-by: Ameziane H <ameziane.hamlat@consensys.net> * Update arithmetization dependency version (#79) Signed-off-by: Ameziane H <ameziane.hamlat@consensys.net> * Improve SimulationValidation Trace Logs (#78) - Trace log for transaction details when module over limit - Only print simulation disabled log when it is actually disabled Signed-off-by: Simon Dudley <simon.dudley@consensys.net> * Build macOS native lib (#80) Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net> * removed references to junit4 (#81) * removed references to junit4 Signed-off-by: Sally MacFarlane <macfarla.github@gmail.com> * formatting Signed-off-by: Sally MacFarlane <macfarla.github@gmail.com> * revert failing test Signed-off-by: Sally MacFarlane <macfarla.github@gmail.com> --------- Signed-off-by: Sally MacFarlane <macfarla.github@gmail.com> * update actions to node20 versions (#83) * update actions to node20 versions Signed-off-by: Sally MacFarlane <macfarla.github@gmail.com> * update gradlewrapper action Signed-off-by: Sally MacFarlane <macfarla.github@gmail.com> --------- Signed-off-by: Sally MacFarlane <macfarla.github@gmail.com> Co-authored-by: Fabio Di Fabio <fabio.difabio@consensys.net> * Update arithmetization dependency version (#84) * Update arithmetization dependency version to 0.6.0-rc6. * Fix build issues. Signed-off-by: Ameziane H <ameziane.hamlat@consensys.net> * Calculate the gas limit upper bound for the sender in linea_estimateGas (#86) Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net> * Fix a config key name clash with the tracer (#87) Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net> * Update tracer (#88) Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net> * Set besuVersion=24.9-delivery33 and arithmetizationVersion=0.7.0-rc1 (#89) Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net> * Set besuVersion=24.10-delivery34 and arithmetizationVersion=0.8.0-rc1 (#90) Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net> * Use required plugins in ATs to not register tracer plugins (#91) Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net> * feat: Report rejected transactions to an external service for tx pool validators used by LineaTransactionPoolValidatorPlugin (#85) * Extract reporting endpoint URI cli option * Linea Transaction Pool Validator to report rejected tx * Linea node type and refactored request builder * Update unit tests to use refactored generateSaveRejectedTxJsonRpc * Use transactionSelectionResult.toString in LineaTransactionSelector * Use Mutually exclusive = false for co-dep cli options * Add unit test for cli options. Change endpoint type to URL * JsonRpcManager - Use plugin identifier to create sub directories to store rpc calls * JsonRpcManager convert submitNewJsonRpcCall to do async chaining before calling submitJsonRpcCall * Add rej tx reporting to all tx pool validators * fix: Validate rejected-tx cli options without ArgGroup (#95) Removed PicoCli ArgGroup validation as it didn't work in Besu with config file parsing which uses DefaultValueProvider. Instead, validate rejected tx cli options in the toDomainObject method. Enforce the rule that node type is required when endpoint is specified. If only node type is specified, we need no custom validation as we are only enabling reporting when endpoint is specified. * Provide `chainId` to `createZkTracer(...)` (#93) * fix: provide chainId to createZkTracer(...) * fix: bumped arithmetization version to 0.8.0-rc2 * Make sure chainId is present and positive Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net> * Set arithmetizationVersion=0.8.0-rc4 Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net> --------- Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net> Co-authored-by: Fabio Di Fabio <fabio.difabio@consensys.net> * Fix for required chainId check (#96) Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net> * docs: Update plugins doc with reporting rejected tx cli options (#97) Update plugins doc with reporting rejected transaction cli options * added missing javadoc comments (#98) Signed-off-by: Sally MacFarlane <macfarla.github@gmail.com> * Rename minGasPrice to ethGasPrice from the extraData and track its value (#103) * Rename minGasPrice to ethGasPrice from the extraData. Signed-off-by: Ade Lucas <ade.lucas@consensys.net> * Expose metrics for profitability extra data field (#104) Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net> * Fix: MetricSystem is only available at start phase (#106) Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net> * Wait for the initial sync phase to complete before processing extra data (#105) Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net> * syntax suggestions (#100) Signed-off-by: Sally MacFarlane <macfarla.github@gmail.com> * log plugin name at registration; remove override of getName() method (#99) * log plugin name at registration; remove override of getName() method Signed-off-by: Sally MacFarlane <macfarla.github@gmail.com> * logging Signed-off-by: Sally MacFarlane <macfarla.github@gmail.com> --------- Signed-off-by: Sally MacFarlane <macfarla.github@gmail.com> * Accept txs that revert in eth_sendRawTransaction when simulation check is enabled (#108) Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net> * update linea-besu version (#110) Signed-off-by: Sally MacFarlane <macfarla.github@gmail.com> * feat: Rejected transaction reporting due to trace line limits (#111) - Rejected transaction reporting due to trace line limits - Remove other rejected transactions reporting - test fixes * Update Linea Besu and Tracer (#112) Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net> * 3D pricing metrics (#107) * Introduce profitablePriorityFeeCache Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net> * Add block listener and transaction selector handler - Introduce block listener in LineaTransactionSelectorPlugin to handle transaction profitability - Integrate transaction selector handler for processing transactions from the latest block - Extract profitability calculation into a separate method for clarity and maintainability Signed-off-by: Ade Lucas <ade.lucas@consensys.net> * Removed unused import Signed-off-by: Ade Lucas <ade.lucas@consensys.net> * Move profitability metrics during tx selection in a dedicated class Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net> * Improve the log of the future metric Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net> * Organize the profitability cache by block Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net> * Refctor to respect the requirement Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net> * Add block added listener and handler into LineaTransactionPoolValidatorPlugin - Implement: Node's perception on Profitability levels of TxPool's contents (lo, hi, avg of TransactionProfitabilityCalculator.profitablePriorityFeePerGas(transaction) / transaction.priorityFeePerGas). Signed-off-by: Ade Lucas <ade.lucas@consensys.net> * Add block added listener and handler into LineaTransactionPoolValidatorPlugin - Implement: Node's perception on Profitability levels of TxPool's contents (lo, hi, avg of TransactionProfitabilityCalculator.profitablePriorityFeePerGas(transaction) / transaction.priorityFeePerGas). Signed-off-by: Ade Lucas <ade.lucas@consensys.net> * Add missing import in ValidatorProfitabilityMetrics.java Signed-off-by: Ade Lucas <ade.lucas@consensys.net> * Use histogram and txpool get content api Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net> * Add min and max metrics to selection profitability metrics Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net> * Deleted ValidatorProfitabilityMetrics.java as part of 3D pricing metrics work Signed-off-by: Ade Lucas <ade.lucas@consensys.net> * Run './gradlew :sequencer:spotlessApply' to fix ProfitableTransactionSelectorTest.java Signed-off-by: Ade Lucas <ade.lucas@consensys.net> * Restore non-local dev settings for besuVersion in gradle.properties. Signed-off-by: Ade Lucas <ade.lucas@consensys.net> * Generalize histogram metrics in a single class and check for category enabled Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net> * Make profitability histogram metric buckets configurable Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net> * Update versions Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net> * Fix tests Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net> * Apply suggestions from code review Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net> * Update DEFAULT_PROFITABILITY_METRICS_BUCKETS Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net> * Update Linea Besu and Tracer Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net> --------- Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net> Signed-off-by: Ade Lucas <ade.lucas@consensys.net> Signed-off-by: Ade Lucas <37906710+cloudspores@users.noreply.github.com> Co-authored-by: Fabio Di Fabio <fabio.difabio@consensys.net> * Support state overrides in `linea_estimateGas` (#113) Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net> * Make `linea_estimateGas` to simulate tx on pending block (#116) Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net> * update to 24.12 besu (#117) * update to 24.12 besu Signed-off-by: Sally MacFarlane <macfarla.github@gmail.com> --------- Signed-off-by: Sally MacFarlane <macfarla.github@gmail.com> * add trivial change clause to contributing md (#119) Signed-off-by: Sally MacFarlane <macfarla.github@gmail.com> * Use Besu BOM (#121) Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net> * Workaround for overwritten Tracer MANIFEST.MF (#122) Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net> * Workaround for overwritten Tracer MANIFEST.MF (part 2) (#123) Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net> * Support for chainId and EIP-1559 fee market in linea_estimateGas (#94) Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net> * Update Linea Besu to delivery41 (#125) Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net> * Fix linea_estimateGas not allowing exceeding balance during simulation (#126) Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net> * log4j: replace status (deprecated) with level (#128) Signed-off-by: Sally MacFarlane <macfarla.github@gmail.com> * Fix the log of the profitability selector in the pre process phase (#129) Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net> * Set besuVersion=25.1-develop-921c726 and arithmetizationVersion=beta-v1.2.0-rc3 (#130) * Set besuVersion=25.1-develop-448d1a9 and arithmetizationVersion=beta-v1.2.0-rc2 Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net> * Get the nonce of the sender in `linea_estimateGas` if not provided Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net> * Use simulatePendingBlockHeader Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net> * Allow future nonce when simulating txs Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net> * update Linea Besu Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net> --------- Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net> * Remove uber jar (#124) Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net> * Fix release workflow (#137) Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net> * AT passing nonce to linea_estimateGas (#138) Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net> * update limits for blockhash, blockdata and blake prc (#133) Signed-off-by: F Bojarski <ceciestunepoubelle@protonmail.ch> Co-authored-by: Fabio Di Fabio <fabio.difabio@consensys.net> * Update Linea Besu to 25.2-delivery48 (#141) Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net> * Support stateful block tx selectors (#140) * Update Linea Besu to 25.2-delivery48 Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net> * Update tx selectors to use selector state Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net> * Adapt code to new TransactionEvaluationContext Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net> * Linea Besu to 25.2-delivery49 Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net> * Apply suggestions from code review Co-authored-by: Sally MacFarlane <macfarla.github@gmail.com> Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net> --------- Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net> Co-authored-by: Sally MacFarlane <macfarla.github@gmail.com> * Linea send bundle (#142) * initial commit for linea_sendBundle, bundle pool, and selector. outstanding work on creating a shared singleton bundle pool before we can smoke test the endpoint. Signed-off-by: garyschulte <garyschulte@gmail.com> * refactor a bit to use BlockTransactionSelector to inject transactions Signed-off-by: garyschulte <garyschulte@gmail.com> * missing spdx headers Signed-off-by: garyschulte <garyschulte@gmail.com> * fix mock Signed-off-by: garyschulte <garyschulte@gmail.com> * use record types for bundle params, wire up checks and pool rpc test Signed-off-by: garyschulte <garyschulte@gmail.com> * add linea_cancelBundle, wire up replacement by uuid Signed-off-by: garyschulte <garyschulte@gmail.com> * spdx again Signed-off-by: garyschulte <garyschulte@gmail.com> * builds against 25.1-bundles ; will fail build against 25.1-delivery45 Signed-off-by: garyschulte <garyschulte@gmail.com> * add blockAdded listener to cull bundle pool Signed-off-by: garyschulte <garyschulte@gmail.com> * add bundle block gas limits Signed-off-by: garyschulte <garyschulte@gmail.com> * spdx header again Signed-off-by: garyschulte <garyschulte@gmail.com> * resolve wiring issues, add bundle eval cache 'safety' Signed-off-by: garyschulte <garyschulte@gmail.com> * add bundle selector short circuit for non-bundle tx, fix minor serialization issues Signed-off-by: garyschulte <garyschulte@gmail.com> * Update tx selectors to use selector state Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net> * Adapt code to new TransactionEvaluationContext Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net> * First sendBundle ATs Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net> * Move shared services in AbstractLineaSharedPrivateOptionsPlugin also to simplify assume that the bundle pool is always available. Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net> * Use PendingTransaction::memorySize Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net> * ERC20 bundle AT Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net> * Adapt code to support bundle aware ZkTracer Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net> * Bundle failure ATs Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net> * Adapt to latest ZkTracer code Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net> * More bundle ATs Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net> * some more logs Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net> * Set arithmetizationVersion=beta-v1.3.0-rc1 Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net> * New tests and fixes Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net> --------- Signed-off-by: garyschulte <garyschulte@gmail.com> Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net> Co-authored-by: garyschulte <garyschulte@gmail.com> * Log extra data parsing at debug (#143) Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net> * Add check to detect and report invalid line counts (#144) Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net> * Set arithmetizationVersion=beta-v1.3-rc2 (#145) Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net> * besuVersion=25.2-delivery50 arithmetizationVersion=beta-v1.3-rc4 (#147) Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net> * Re-apply MANIFEST.MF workaround until we get rid of the uber jar everywhere (#149) Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net> * Fix ZkTracer commit and rollback of non bundle transactions (#150) Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net> * Simplify and optimize bundle and pending tx relation plus improve bundle gas limit check (#148) * Simplify and optimize bundle and pending tx relation plus improve bundle gas limit check Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net> * Apply suggestions from code review Co-authored-by: Sally MacFarlane <macfarla.github@gmail.com> Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net> * update code after rebasing Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net> * Apply suggestions from code review Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net> --------- Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net> Co-authored-by: Sally MacFarlane <macfarla.github@gmail.com> * Add acceptance test to verify bundle revert protection (#152) Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net> * Extend ATs (#153) * Test that txs targeting precompiles are not accepted Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net> * Add acceptance tests for excluded precompiles Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net> * ModExp ATs Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net> --------- Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net> * Use vanilla Besu from linea-besu-upstream (#155) * Use vanilla Besu from linea-besu-upstream * core test-support * Set arithmetizationVersion=beta-v2.1-rc2 (#156) Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net> * update link to besu contribution guidelines (#154) * update link to besu contribution guidelines Signed-off-by: Sally MacFarlane <macfarla.github@gmail.com> * updated link Signed-off-by: Sally MacFarlane <macfarla.github@gmail.com> --------- Signed-off-by: Sally MacFarlane <macfarla.github@gmail.com> * besuVersion=25.2.1-linea1 and arithmetizationVersion=beta-v2.1-rc3 (#159) Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net> * Fix release version (#161) Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net> * adjust log message (#162) Signed-off-by: Sally MacFarlane <macfarla.github@gmail.com> * Save and restore bundle pool (#157) Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net> * Set arithmetizationVersion=beta-v2.1-rc4 (#164) Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net> * Use vanilla Besu for local development (#165) Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net> * Extend parameters validation of bundles and reprocessing the same request (#168) Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net> * Froze the bundle pool when saving to disk, to avoid accepting and losing bundles (#169) * Froze the bundle pool when saving to disk, to avoid accepting and losing bundles Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net> * Apply suggestions from code review Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net> --------- Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net> * 166 ecpairing limits testing (#170) * Use JSON to save and restore bundles (#172) Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net> * Set besuVersion=25.3.0-linea2 and arithmetizationVersion=beta-v2.1-rc8 (#174) Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net> * remove reference to the fork (#175) Signed-off-by: Sally MacFarlane <macfarla.github@gmail.com> * Move reusable bundle related classes in a dedicated package (#173) Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net> * `ECADD`, `ECMUL`, `ECRECOVER` limits testing (#177) * added contract and deploy methods for testing other ecdata precompiles * added test for ecadd limit executing several calls per transaction * Added tests for ecrecover and ecmul * fixed parameters of the tests --------- Co-authored-by: Fabio Di Fabio <fabio.difabio@consensys.net> * besuVersion=25.3.0-linea3 and arithmetizationVersion=beta-v2.1-rc12 (#179) Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net> * Bundle store forward plugin (#178) Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net> * Fix registration of bundle CLI options (#180) Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net> * Fix forward bundle bad request and restore pool from disk (#183) * Fix forward bundle bad request * Enable restoring bundle pool from disk Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net> * added test for MODEXP limit and fixed some naming (#182) * Add options to configure bundle forward retry delay and call timeout (#184) * Fix forward bundle bad request Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net> * Enable restoring bundle pool from disk Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net> * Bump release version Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net> * Save bundle pool only if plugin enabled Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net> * Add forward bundles acceptance tests Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net> * Add configuration options for retry delay and call timeout Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net> * Fix test Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net> --------- Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net> * Run acceptance tests in parallel (#185) Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net> * Update Besu and bump release version (#188) Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net> * Set arithmetizationVersion=beta-v2.1-rc14 (#190) Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net> * Set besuVersion=25.4.1-linea1 and arithmetizationVersion=beta-v2.1-rc16 (#191) Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net> * Set besuVersion=25.5.0-linea1 and arithmetizationVersion=beta-v2.1-rc16.1 (#192) Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net> * use compressor for linea-monorepo (#193) * use compressor for linea-monorepo * remove unnecessary native libs build CI jobs * remove unnecessary static native libs loading * releaseVersion=v2.1-rc16.2.0 (#194) * releaseVersion=v2.1-rc16.2.0 * Update gradle.properties Co-authored-by: Fabio Di Fabio <fabio.difabio@consensys.net> Signed-off-by: Fluent Crafter <205769460+fluentcrafter@users.noreply.github.com> --------- Signed-off-by: Fluent Crafter <205769460+fluentcrafter@users.noreply.github.com> Co-authored-by: Fabio Di Fabio <fabio.difabio@consensys.net> * Set besuVersion=25.5.0-linea2 and arithmetizationVersion=beta-v2.1-rc16.2 (#196) Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net> * compressor: debug loading failures (#197) * compressor: debug loading failures * compressor: releaseVersion=v2.1-rc16.2.2 * compressor: update compressor and releaseVersion=v2.1-rc16.2.3 (#199) * compressor: update compressor and releaseVersion=v2.1-rc16.2.3 * compressor: update compressor and releaseVersion=v2.1-rc16.2.3 * compressor: update compressor (#200) * compressor: update compressor * releaseVersion=v2.1-rc16.2.4 * [Feat] Besu node running with Prague config for acceptance tests (#198) * working flexible path for genesis json template * added code from PragueAcceptanceTestHelper * fix comment * enabled engine api call to node in acceptance test * successful example test * strategy pattern; move engine API logic to EngineApiService class * removed duplicate code in LineaPluginTestBasePrague via inheritance, passing acceptance tests * successful refactor of createGetPayloadRequest * refactor of createNewPayloadRequest * refactor createForkChoiceRequest * refactor createForkChoiceRequest * refactor EngineApiService * refactor EngineApiService * write extra comments * more comments for EngineAPIService * wrote ExamplePragueTest * minor cleanup * some cleanup * refactor EngineApiService * minor cleanup * use EngineForkchoiceUpdatedParameter and EnginePayloadAttributesParameter from Besu * implement registerToHexStringSerializer * cleanup * make engineApiService private * remove unneeded code in createNewPayloadRequest * improve comment * edits to prague clique genesis file for pr comments * removed WaitUtils.sleep * set starting timestamp for prague config to 0 * added long timestampIncrement param to buildNewBlocks * remove besu types of engine api * remove state from EngineApiService * remove state from EngineApiService * remove hardcoded sleep time for Thread.sleep * migrated linea-sequencer yaml workflows, deleted gradlew binaries from linea-sequencer * removed gradlew wrapper for linea-sequencer project, merged settings.gradle, updated linea-sequencer-plugin-release to workflow_dispatch * centralized spotless config, replaced references to rootProject in linea-sequencer * centralize dependency managaement for de.undercouch.download * Revert "centralize dependency managaement for de.undercouch.download" This reverts commit 6d374706d8c8fed72c2c99beb5a1eb84ed4030a8. * removed common plugins * removed buildSrc from linea-sequencer * ./gradlew besu-plugins:linea-sequencer:build working * remove gradle.properties from linea-sequencer * removed duplicate test config * acceptance test can start for linea sequencer * disable parallel junit tests for linea-sequencer-plugin * downgrade spotless version * move spotless apply * centralized most linea-sequencer versions * version bumps * improve naming for linea-sequencer-plugin-testing * bump besu version to 25.5.0-linea2 * fix ci paths for linea-sequencer-plugin * centralize besu version used * cleanup * Call Besu `eth_estimateGas` internally for `linea_estimateGas` (#201) * Call Besu eth_estimateGas internally for linea_estimateGas Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net> * Go is not needed anymore Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net> --------- Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net> * edit for pr comments 1 --------- Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net> Signed-off-by: franklin.delehelle@consensys.net Signed-off-by: Daniel Lehrner <daniel.lehrner@consensys.net> Signed-off-by: Gabriel-Trintinalia <gabriel.trintinalia@consensys.net> Signed-off-by: Tsvetan Dimitrov <tsvetan.dimitrov@consensys.net> Signed-off-by: stefan.pingel@consensys.net <stefan.pingel@consensys.net> Signed-off-by: Ameziane H <ameziane.hamlat@consensys.net> Signed-off-by: Sally MacFarlane <macfarla.github@gmail.com> Signed-off-by: Simon Dudley <simon.dudley@consensys.net> Signed-off-by: Ade Lucas <ade.lucas@consensys.net> Signed-off-by: Ade Lucas <37906710+cloudspores@users.noreply.github.com> Signed-off-by: F Bojarski <ceciestunepoubelle@protonmail.ch> Signed-off-by: garyschulte <garyschulte@gmail.com> Signed-off-by: Fluent Crafter <205769460+fluentcrafter@users.noreply.github.com> Co-authored-by: letypequividelespoubelles <54240434+letypequividelespoubelles@users.noreply.github.com> Co-authored-by: Fabio Di Fabio <fabio.difabio@consensys.net> Co-authored-by: daniellehrner <daniel.lehrner@consensys.net> Co-authored-by: delehef <franklin.delehelle@consensys.net> Co-authored-by: Gabriel-Trintinalia <gabriel.trintinalia@consensys.net> Co-authored-by: Franklin Delehelle <franklin.delehelle@odena.eu> Co-authored-by: Julien Marchand <julien-marchand@users.noreply.github.com> Co-authored-by: Roman Vaseev <4833306+Filter94@users.noreply.github.com> Co-authored-by: Tsvetan Dimitrov <tsvetan.dimitrov@consensys.net> Co-authored-by: FlorianHuc <florian.huc@gmail.com> Co-authored-by: Pedro Novais <1478752+jpnovais@users.noreply.github.com> Co-authored-by: Stefan Pingel <16143240+pinges@users.noreply.github.com> Co-authored-by: Sally MacFarlane <macfarla.github@gmail.com> Co-authored-by: ahamlat <ameziane.hamlat@consensys.net> Co-authored-by: anonployed <164473726+anonployed@users.noreply.github.com> Co-authored-by: Feng Liyuan <darktemplar.f@gmail.com> Co-authored-by: m4sterbunny <harrie.bickle@consensys.net> Co-authored-by: Usman Saleem <usman@usmans.info> Co-authored-by: Simon Dudley <simon.dudley@consensys.net> Co-authored-by: Olivier Bégassat <38285177+OlivierBBB@users.noreply.github.com> Co-authored-by: Ade Lucas <37906710+cloudspores@users.noreply.github.com> Co-authored-by: garyschulte <garyschulte@gmail.com> Co-authored-by: Lorenzo Gentile <lorenzogentile404@gmail.com> Co-authored-by: Fluent Crafter <205769460+fluentcrafter@users.noreply.github.com>
This commit is contained in:
69
.github/workflows/linea-sequencer-plugin-release.yml
vendored
Normal file
69
.github/workflows/linea-sequencer-plugin-release.yml
vendored
Normal file
@@ -0,0 +1,69 @@
|
||||
name: Release
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
version:
|
||||
description: 'Version (must start with "v", e.g., "v1.2.3")'
|
||||
required: true
|
||||
type: string
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: gha-runner-scale-set-ubuntu-22.04-amd64-small
|
||||
steps:
|
||||
- name: Validate version
|
||||
run: |
|
||||
if [[ ! "${{ github.event.inputs.version }}" =~ ^v.+ ]]; then
|
||||
echo "Error: version must start with 'v'"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
- uses: actions/checkout@v4
|
||||
- name: Set up JDK 21
|
||||
uses: actions/setup-java@v4
|
||||
with:
|
||||
java-version: 21
|
||||
distribution: temurin
|
||||
cache: 'gradle'
|
||||
|
||||
- name: Build artifacts
|
||||
run: ./gradlew besu-plugins:linea-sequencer:artifacts -PreleaseVersion=${{ github.event.inputs.version }}
|
||||
env:
|
||||
JAVA_OPTS: -Xmx2g -Dorg.gradle.daemon=false
|
||||
|
||||
# TODO in later ticket - actions/create-release and actions/upload-release-asset are both archived
|
||||
# ncipollo/release-action@v1 is an actively-maintained alternative that can combine release creation and artifact upload
|
||||
- name: Draft Release
|
||||
id: create_release
|
||||
uses: actions/create-release@v1
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # This token is provided by Actions, you do not need to create your own token
|
||||
with:
|
||||
tag_name: ${{ github.ref }}
|
||||
release_name: Release ${{ github.ref }}
|
||||
body: 'Draft release of version ${{ github.event.inputs.version }}.'
|
||||
draft: true
|
||||
prerelease: false
|
||||
|
||||
- name: Upload Release Lib Asset
|
||||
id: upload-release-asset
|
||||
uses: actions/upload-release-asset@v1
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
upload_url: ${{ steps.create_release.outputs.upload_url }}
|
||||
asset_path: ./besu-plugins/linea-sequencer/sequencer/build/libs/linea-sequencer-${{ github.event.inputs.version }}.jar
|
||||
asset_name: linea-sequencer-${{ github.event.inputs.version }}.jar
|
||||
asset_content_type: application/octet-stream
|
||||
|
||||
- name: Upload Release Dist Asset
|
||||
id: upload-release-dist-asset
|
||||
uses: actions/upload-release-asset@v1
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
upload_url: ${{ steps.create_release.outputs.upload_url }}
|
||||
asset_path: ./besu-plugins/linea-sequencer/sequencer/build/distributions/linea-sequencer-${{ github.event.inputs.version }}.zip
|
||||
asset_name: linea-sequencer-${{ github.event.inputs.version }}.zip
|
||||
asset_content_type: application/octet-stream
|
||||
79
.github/workflows/linea-sequencer-plugin-testing.yml
vendored
Normal file
79
.github/workflows/linea-sequencer-plugin-testing.yml
vendored
Normal file
@@ -0,0 +1,79 @@
|
||||
name: linea-sequencer-plugin-testing
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
|
||||
concurrency:
|
||||
group: linea-sequencer-plugin-testing-${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: ${{ github.ref != 'refs/heads/main' }}
|
||||
|
||||
jobs:
|
||||
validation:
|
||||
name: "Gradlew Wrapper Checksum Validation"
|
||||
runs-on: gha-runner-scale-set-ubuntu-22.04-amd64-small
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: gradle/actions/wrapper-validation@v4
|
||||
|
||||
build:
|
||||
name: "Linea Sequencer Plugin Build"
|
||||
runs-on: gha-runner-scale-set-ubuntu-22.04-amd64-small
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Set up JDK 21
|
||||
uses: actions/setup-java@v4
|
||||
with:
|
||||
java-version: 21
|
||||
distribution: temurin
|
||||
|
||||
- name: Build and test
|
||||
run: ./gradlew besu-plugins:linea-sequencer:build
|
||||
env:
|
||||
JAVA_OPTS: -Xmx2g -Dorg.gradle.daemon=false
|
||||
|
||||
- name: Store distribution artifacts
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: distributions
|
||||
path: besu-plugins/linea-sequencer/sequencer/build/libs
|
||||
|
||||
acceptanceTest:
|
||||
name: "Linea Sequencer Plugin Acceptance Tests"
|
||||
runs-on: gha-runner-scale-set-ubuntu-22.04-amd64-large
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Set up JDK 21
|
||||
uses: actions/setup-java@v4
|
||||
with:
|
||||
java-version: 21
|
||||
distribution: temurin
|
||||
|
||||
- name: Run acceptance tests
|
||||
run: ./gradlew besu-plugins:linea-sequencer:acceptance-tests:acceptanceTests
|
||||
env:
|
||||
JAVA_OPTS: -Dorg.gradle.daemon=false
|
||||
|
||||
- name: Upload test report
|
||||
if: always()
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: acceptance-test-report
|
||||
path: besu-plugins/linea-sequencer/acceptance-tests/build/reports/tests/
|
||||
|
||||
spotless:
|
||||
name: "Linea Sequencer Plugin Spotless Check"
|
||||
runs-on: gha-runner-scale-set-ubuntu-22.04-amd64-small
|
||||
steps:
|
||||
- name: Checkout Repo
|
||||
uses: actions/checkout@v4
|
||||
- name: Set up Java
|
||||
uses: actions/setup-java@v4
|
||||
with:
|
||||
distribution: temurin
|
||||
java-version: 21
|
||||
- name: spotless
|
||||
run: ./gradlew --no-daemon --parallel clean besu-plugins:linea-sequencer:spotlessCheck
|
||||
5
.github/workflows/main.yml
vendored
5
.github/workflows/main.yml
vendored
@@ -24,6 +24,7 @@ jobs:
|
||||
contracts-excluding-local-deployment-artifacts: ${{ steps.exclusion-filter.outputs.contracts-excluding-local-deployment-artifacts }}
|
||||
contracts-excluding-local-deployment-artifacts-count: ${{ steps.exclusion-filter.outputs.contracts-excluding-local-deployment-artifacts_count }}
|
||||
smart-contracts: ${{ steps.filter.outputs.smart-contracts }}
|
||||
linea-sequencer-plugin: ${{ steps.filter.outputs.linea-sequencer-plugin }}
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
@@ -105,6 +106,9 @@ jobs:
|
||||
- 'prover/**'
|
||||
- '.github/workflows/main.yml'
|
||||
- '.github/workflows/run-smc-tests.yml'
|
||||
linea-sequencer-plugin:
|
||||
- 'besu-plugins/linea-sequencer/**'
|
||||
- '.github/workflows/linea-sequencer-plugin-testing.yml'
|
||||
|
||||
- name: Filter out commit changes
|
||||
uses: dorny/paths-filter@v3
|
||||
@@ -147,6 +151,7 @@ jobs:
|
||||
with:
|
||||
commit_tag: ${{ needs.store-image-name-and-tags.outputs.commit_tag }}
|
||||
coordinator_changed: ${{ needs.filter-commit-changes.outputs.coordinator }}
|
||||
linea_sequencer_changed: ${{ needs.filter-commit-changes.outputs.linea-sequencer-plugin }}
|
||||
postman_changed: ${{ needs.filter-commit-changes.outputs.postman }}
|
||||
prover_changed: ${{ needs.filter-commit-changes.outputs.prover }}
|
||||
smart_contracts_changed: ${{ needs.filter-commit-changes.outputs.smart-contracts }}
|
||||
|
||||
9
.github/workflows/testing.yml
vendored
9
.github/workflows/testing.yml
vendored
@@ -24,6 +24,10 @@ on:
|
||||
smart_contracts_changed:
|
||||
required: true
|
||||
type: string
|
||||
linea_sequencer_changed:
|
||||
required: true
|
||||
type: string
|
||||
|
||||
jobs:
|
||||
# Cache for pulling Docker images is disabled as we empirically found that this
|
||||
# (retrieving cache and loading Docker images) actually increased test time-to-completion
|
||||
@@ -63,6 +67,11 @@ jobs:
|
||||
commit_tag: ${{ inputs.commit_tag }}
|
||||
secrets: inherit
|
||||
|
||||
linea-sequencer:
|
||||
uses: ./.github/workflows/linea-sequencer-plugin-testing.yml
|
||||
if: ${{ inputs.linea_sequencer_changed == 'true' }}
|
||||
secrets: inherit
|
||||
|
||||
# If all jobs are skipped, the workflow will still succeed.
|
||||
always_succeed:
|
||||
runs-on: ubuntu-24.04
|
||||
|
||||
34
besu-plugins/linea-sequencer/.gitignore
vendored
Normal file
34
besu-plugins/linea-sequencer/.gitignore
vendored
Normal file
@@ -0,0 +1,34 @@
|
||||
*.bak
|
||||
*.swp
|
||||
*.tmp
|
||||
*~.nib
|
||||
*.iml
|
||||
*.launch
|
||||
*.swp
|
||||
*.log
|
||||
*.out
|
||||
nohup.out
|
||||
.classpath
|
||||
.DS_Store
|
||||
.externalToolBuilders/
|
||||
.gradle/
|
||||
.idea/
|
||||
.loadpath
|
||||
.metadata
|
||||
.prefs
|
||||
.project
|
||||
.recommenders/
|
||||
.settings
|
||||
.springBeans
|
||||
.vertx
|
||||
.java-version
|
||||
./bin
|
||||
local.properties
|
||||
target/
|
||||
tmp/
|
||||
build/
|
||||
out/
|
||||
site/
|
||||
/.direnv/
|
||||
/.envrc
|
||||
acceptance-tests/bin/
|
||||
220
besu-plugins/linea-sequencer/CHANGELOG.md
Normal file
220
besu-plugins/linea-sequencer/CHANGELOG.md
Normal file
@@ -0,0 +1,220 @@
|
||||
# Changelog
|
||||
|
||||
## Next release
|
||||
* feat: Report rejected transactions only due to trace limit overflows to an external service.
|
||||
* feat: Report rejected transactions to an external service for validators used by LineaTransactionPoolValidatorPlugin [#85](https://github.com/Consensys/linea-sequencer/pull/85)
|
||||
* feat: Report rejected transactions to an external service for LineaTransactionSelector used by LineaTransactionSelectorPlugin [#69](https://github.com/Consensys/linea-sequencer/pull/69)
|
||||
|
||||
## 0.6.0-rc1.1
|
||||
* bump linea-arithmetization version to 0.6.0-rc1 [#71](https://github.com/Consensys/linea-sequencer/pull/71)
|
||||
* bump Linea-Besu version to 24.9-delivery32 [#71](https://github.com/Consensys/linea-sequencer/pull/71)
|
||||
|
||||
## 0.3.0-rc2.1
|
||||
* bump linea-arithmetization version to 0.3.0-rc2 [#62](https://github.com/Consensys/linea-sequencer/pull/62)
|
||||
* bump Linea-Besu version to 24.7-develop-c0029e6 (delivery-28) [#62](https://github.com/Consensys/linea-sequencer/pull/62)
|
||||
|
||||
## 0.3.0-rc1.1
|
||||
* bump linea-arithmetization version to 0.3.0-rc1 [#54](https://github.com/Consensys/linea-sequencer/pull/54)
|
||||
* bump Linea-Besu version to 24.7-develop-f812936 (delivery-27) [#54](https://github.com/Consensys/linea-sequencer/pull/54)
|
||||
* Fix linea_estimateGas when called with gasPrice set [#58](https://github.com/Consensys/linea-sequencer/pull/58)
|
||||
|
||||
## 0.2.0-rc5.1
|
||||
* bump linea-arithmetization version to 0.2.0-rc5 [#55](https://github.com/Consensys/linea-sequencer/pull/55)
|
||||
|
||||
## 0.2.0-rc4.1
|
||||
* feat: bump linea-arithmetization version to 0.2.0-rc4 [#47](https://github.com/Consensys/linea-sequencer/pull/47)
|
||||
* Option to disable setting minGasPrice via extra data [#50](https://github.com/Consensys/linea-sequencer/pull/50)
|
||||
* Remove the check that profitable priority fee must be greater than minGasPrice [#49](https://github.com/Consensys/linea-sequencer/pull/49)
|
||||
* Fix and enable unit tests in CI [#45](https://github.com/Consensys/linea-sequencer/pull/45)
|
||||
* Documentation using javadoc [#33](https://github.com/Consensys/linea-sequencer/pull/33)
|
||||
* Improve error log when setting pricing conf via extra data fails [#44](https://github.com/Consensys/linea-sequencer/pull/44)
|
||||
|
||||
## 0.1.5-test1
|
||||
First release of the new series that uses on the ZkTracer as dependency from `linea-arithmetization` repo
|
||||
* arithmetizationVersion=0.1.5-rc3 [#29](https://github.com/Consensys/linea-sequencer/pull/29)
|
||||
* Align linea_estimateGas behavior to geth [#25](https://github.com/Consensys/linea-sequencer/pull/25)
|
||||
* Implement linea_setExtraData [#19](https://github.com/Consensys/linea-sequencer/pull/19)
|
||||
* Set plugin-linea-tx-pool-simulation-check-api-enabled=false by default [#23](https://github.com/Consensys/linea-sequencer/pull/23)
|
||||
|
||||
## 0.1.4-test28
|
||||
Test pre-release 28 from [temp/issue-248/count-stack-only](https://github.com/Consensys/besu-sequencer-plugins/tree/temp/issue-248/count-stack-only)
|
||||
* Extra data based pricing [#10](https://github.com/Consensys/linea-sequencer/pull/10)
|
||||
* Remove check that minGasPrice need to decrease to retry unprofitable tx [#17](https://github.com/Consensys/linea-sequencer/pull/17)
|
||||
|
||||
## 0.1.4-test27
|
||||
Test pre-release 27 from [temp/issue-248/count-stack-only](https://github.com/Consensys/besu-sequencer-plugins/tree/temp/issue-248/count-stack-only)
|
||||
* Calculate line count only once in linea_estimateGas [#13](https://github.com/Consensys/linea-sequencer/pull/13)
|
||||
|
||||
## 0.1.4-test26
|
||||
Test pre-release 26 from [temp/issue-248/count-stack-only](https://github.com/Consensys/besu-sequencer-plugins/tree/temp/issue-248/count-stack-only)
|
||||
* Improve ZkTracer initialization time [#11](https://github.com/Consensys/linea-sequencer/pull/11)
|
||||
* Add more log to txpool simulation validator [#12](https://github.com/Consensys/linea-sequencer/pull/12)
|
||||
|
||||
## 0.1.4-test25
|
||||
Test pre-release 25 from [temp/issue-248/count-stack-only](https://github.com/Consensys/besu-sequencer-plugins/tree/temp/issue-248/count-stack-only)
|
||||
* Extend Module Line Count Verification to linea_estimateGas RPC Method [#1](https://github.com/Consensys/linea-sequencer/pull/1)
|
||||
* In the txpool, reject a tx if its simulation fails [#2](https://github.com/Consensys/linea-sequencer/pull/2)
|
||||
|
||||
## 0.1.4-test24
|
||||
Test pre-release 24 from [temp/issue-248/count-stack-only](https://github.com/Consensys/besu-sequencer-plugins/tree/temp/issue-248/count-stack-only)
|
||||
* Improve linea_estimateGas error response [#650](https://github.com/Consensys/besu-sequencer-plugins/pull/650)
|
||||
* On Windows also build Linux native lib so it can run on WSL [#651](https://github.com/Consensys/besu-sequencer-plugins/pull/651)
|
||||
|
||||
## 0.1.4-test23
|
||||
Test pre-release 23 from [temp/issue-248/count-stack-only](https://github.com/Consensys/besu-sequencer-plugins/tree/temp/issue-248/count-stack-only)
|
||||
* linea_estimateGas compatibility mode multiplier https://github.com/Consensys/besu-sequencer-plugins/pull/646
|
||||
|
||||
## 0.1.4-test22
|
||||
Test pre-release 22 from [temp/issue-248/count-stack-only](https://github.com/Consensys/besu-sequencer-plugins/tree/temp/issue-248/count-stack-only)
|
||||
* linea_estimateGas compatibility switch https://github.com/Consensys/besu-sequencer-plugins/pull/634
|
||||
* Update profitability formula with gas price adjustment option https://github.com/Consensys/besu-sequencer-plugins/pull/638
|
||||
* Update code to latest plugin API https://github.com/Consensys/besu-sequencer-plugins/pull/640
|
||||
* Txpool profitability check https://github.com/Consensys/besu-sequencer-plugins/pull/603
|
||||
* Fix price adjustment in profitability formula https://github.com/Consensys/besu-sequencer-plugins/pull/642
|
||||
|
||||
## 0.1.4-test21
|
||||
Test pre-release 21 from [temp/issue-248/count-stack-only](https://github.com/Consensys/besu-sequencer-plugins/tree/temp/issue-248/count-stack-only)
|
||||
* fix: capture SSTORE-touched storage slots for correct gas computations [#606](https://github.com/Consensys/besu-sequencer-plugins/pull/606)
|
||||
* build: make the build script portable, explicit dependency on Go & GCC, test libcompress build [#621](https://github.com/Consensys/besu-sequencer-plugins/pull/621)
|
||||
* Update after the refactor of transaction selection service [#626](https://github.com/Consensys/besu-sequencer-plugins/pull/626)
|
||||
* Use the right classloader to load the native library [#628](https://github.com/Consensys/besu-sequencer-plugins/pull/628)
|
||||
|
||||
## 0.1.4-test20
|
||||
Test pre-release 20 from [temp/issue-248/count-stack-only](https://github.com/Consensys/besu-sequencer-plugins/tree/temp/issue-248/count-stack-only)
|
||||
* Get L2L1 settings from CLI options [#591](https://github.com/Consensys/besu-sequencer-plugins/pull/591)
|
||||
* feat: add a replay capture script [#600](https://github.com/Consensys/besu-sequencer-plugins/pull/600)
|
||||
* move compress native into plugin repo [#604](https://github.com/Consensys/besu-sequencer-plugins/pull/604)
|
||||
* Add compression [#605](https://github.com/Consensys/besu-sequencer-plugins/pull/605)
|
||||
* Update for the new bad block manager [#607](https://github.com/Consensys/besu-sequencer-plugins/pull/607)
|
||||
|
||||
## 0.1.4-test19
|
||||
Test pre-release 19 from [temp/issue-248/count-stack-only](https://github.com/Consensys/besu-sequencer-plugins/tree/temp/issue-248/count-stack-only)
|
||||
* Avoid returning an estimated priority fee that is less than the min gas price [#598](https://github.com/Consensys/besu-sequencer-plugins/pull/598)
|
||||
|
||||
## 0.1.4-test18
|
||||
Test pre-release 18 from [temp/issue-248/count-stack-only](https://github.com/Consensys/besu-sequencer-plugins/tree/temp/issue-248/count-stack-only)
|
||||
* fix: check that spilling and limits file contain all counted modules [#592](https://github.com/Consensys/besu-sequencer-plugins/pull/592)
|
||||
|
||||
## 0.1.4-test18-RC3
|
||||
Test pre-release 18-RC3 from [temp/issue-248/count-stack-only](https://github.com/Consensys/besu-sequencer-plugins/tree/temp/issue-248/count-stack-only)
|
||||
* Use compressed tx size also when selecting txs from block creation [#590](https://github.com/Consensys/besu-sequencer-plugins/pull/590)
|
||||
|
||||
## 0.1.4-test18-RC2
|
||||
Test pre-release 18-RC2 from [temp/issue-248/count-stack-only](https://github.com/Consensys/besu-sequencer-plugins/tree/temp/issue-248/count-stack-only)
|
||||
* Fix linea_estimateGas reports Internal error when value or gas price is missing [#587](https://github.com/Consensys/besu-sequencer-plugins/pull/587)
|
||||
|
||||
## 0.1.4-test18-RC1
|
||||
Test pre-release 18-RC1 from [temp/issue-248/count-stack-only](https://github.com/Consensys/besu-sequencer-plugins/tree/temp/issue-248/count-stack-only)
|
||||
* Linea estimate gas endpoint [#585](https://github.com/Consensys/besu-sequencer-plugins/pull/585)
|
||||
|
||||
## 0.1.4-test17
|
||||
Test pre-release 17 from [temp/issue-248/count-stack-only](https://github.com/Consensys/besu-sequencer-plugins/tree/temp/issue-248/count-stack-only)
|
||||
* tests: drop huge random tests [#563](https://github.com/Consensys/besu-sequencer-plugins/pull/563)
|
||||
* feat(modexp-data): implement MODEXP_DATA module [#547](https://github.com/Consensys/besu-sequencer-plugins/pull/547)
|
||||
* feat: mechanics to capture conflations & replay them as test cases [#561](https://github.com/Consensys/besu-sequencer-plugins/pull/561)
|
||||
* perf(EUC): one less column [#570](https://github.com/Consensys/besu-sequencer-plugins/pull/570)
|
||||
* docs: Add basic plugins doc [#509](https://github.com/Consensys/besu-sequencer-plugins/pull/509)
|
||||
* Check upfront profitability + Unprofitable txs cache and retry limit [#565](https://github.com/Consensys/besu-sequencer-plugins/pull/565)
|
||||
* Avoid reprocessing txs that go over line count limit [#571](https://github.com/Consensys/besu-sequencer-plugins/pull/571)
|
||||
|
||||
## 0.1.4-test16
|
||||
Test pre-release 16 from [temp/issue-248/count-stack-only](https://github.com/Consensys/besu-sequencer-plugins/tree/temp/issue-248/count-stack-only)
|
||||
* fix: bug-compatibility with Geth
|
||||
* fix: PubHash 16 factor
|
||||
|
||||
Full changeset https://github.com/Consensys/besu-sequencer-plugins/compare/v0.1.4-test15...v0.1.4-test16
|
||||
|
||||
## 0.1.4-test15
|
||||
release rebase off of main
|
||||
* add option to adjust the tx size used to calculate the profitability of a tx during block creation(#562)[https://github.com/Consensys/besu-sequencer-plugins/pull/562]
|
||||
|
||||
## 0.1.4-test14
|
||||
release rebase off of main
|
||||
Test pre-release 14 from [temp/issue-248/count-stack-only](https://github.com/Consensys/besu-sequencer-plugins/tree/temp/issue-248/count-stack-only)
|
||||
* Fix log of line counts in case of block limit reached + minor changes [#555](https://github.com/Consensys/besu-sequencer-plugins/pull/555)
|
||||
* build: update Corset to 9.3.0 [#554](https://github.com/Consensys/besu-sequencer-plugins/pull/554)
|
||||
|
||||
## 0.1.4-test13
|
||||
Test pre-release 13 from [temp/issue-248/count-stack-only](https://github.com/Consensys/besu-sequencer-plugins/tree/temp/issue-248/count-stack-only)
|
||||
* fix stackedSet [c3f226775f24508b93a758e4226a51ae386d76a5](https://github.com/Consensys/besu-sequencer-plugins/commit/c3f226775f24508b93a758e4226a51ae386d76a5)
|
||||
|
||||
## 0.1.4-test12
|
||||
Test pre-release 12 from [temp/issue-248/count-stack-only](https://github.com/Consensys/besu-sequencer-plugins/tree/temp/issue-248/count-stack-only)
|
||||
* fix: stacked set multiple insertions in a single transaction (#548)
|
||||
|
||||
## 0.1.4-test11
|
||||
Test pre-release 11 from [temp/issue-248/count-stack-only](https://github.com/Consensys/besu-sequencer-plugins/tree/temp/issue-248/count-stack-only)
|
||||
* same as 0.1.4-test10
|
||||
|
||||
## 0.1.4-test10
|
||||
Test pre-release 10 from [temp/issue-248/count-stack-only](https://github.com/Consensys/besu-sequencer-plugins/tree/temp/issue-248/count-stack-only)
|
||||
* fix: semantics of LinkedList (#544)
|
||||
* refactor: add @EqualsAndHashCode annotations and remove corresponding methods (#541)
|
||||
|
||||
## 0.1.4-test9
|
||||
Test pre-release 9 from [temp/issue-248/count-stack-only](https://github.com/Consensys/besu-sequencer-plugins/tree/temp/issue-248/count-stack-only)
|
||||
* Bump Linea Besu to 24.1.1-SNAPSHOT
|
||||
|
||||
## 0.1.4-test8
|
||||
Test pre-release 8 from [temp/issue-248/count-stack-only](https://github.com/Consensys/besu-sequencer-plugins/tree/temp/issue-248/count-stack-only)
|
||||
* Add profitable transaction selector [#530](https://github.com/Consensys/besu-sequencer-plugins/pull/530)
|
||||
* temp: geth-compatibily hacks [820918a](https://github.com/Consensys/besu-sequencer-plugins/commit/820918a39e8d394e73b8de85a46391ffe7d314b1)
|
||||
|
||||
## 0.1.4-test7
|
||||
Test pre-release 7 from [temp/issue-248/count-stack-only](https://github.com/Consensys/besu-sequencer-plugins/tree/temp/issue-248/count-stack-only)
|
||||
* fix: invalid SStore gas computation [#532](https://github.com/Consensys/besu-sequencer-plugins/pull/532)
|
||||
|
||||
## 0.1.4-test6
|
||||
Test pre-release 6, fix: [make precompile counters work](https://github.com/Consensys/besu-sequencer-plugins/commit/10f03ead5207746f253703a328f13988ed9b9305)
|
||||
* feat: implement fake hashdata/info [Franklin Delehelle]
|
||||
* temp: geth-compatibily hacks [Franklin Delehelle]
|
||||
* refactor: group RLPs modules, use retro-compatible module keys [#508](https://github.com/Consensys/besu-sequencer-plugins/pull/508)
|
||||
* [MINOR] Add javadoc [#507](https://github.com/Consensys/besu-sequencer-plugins/pull/507)
|
||||
* style: update name of prec limits to avoid confusion with old geth name [#506](https://github.com/Consensys/besu-sequencer-plugins/pull/506)
|
||||
* perf: cache tx-specific line counter [#497](https://github.com/Consensys/besu-sequencer-plugins/pull/497)
|
||||
* fix: continuous tracing plugin start check [#500](https://github.com/Consensys/besu-sequencer-plugins/pull/500)
|
||||
* fix: lookup txndata <-> wcp [#488](https://github.com/Consensys/besu-sequencer-plugins/pull/488)
|
||||
* fix(romLex): wrong stack arg for extcodecopy address [#498](https://github.com/Consensys/besu-sequencer-plugins/pull/498)
|
||||
|
||||
## 0.1.4-test3
|
||||
Test pre-release 3 from [temp/issue-248/count-stack-only](https://github.com/Consensys/besu-sequencer-plugins/tree/temp/issue-248/count-stack-only)
|
||||
* Log ZkTracer counters for every produced block [#485](https://github.com/Consensys/besu-sequencer-plugins/pull/485)
|
||||
* fix: overflow for modexp arg [#489](https://github.com/Consensys/besu-sequencer-plugins/pull/489)
|
||||
* bin reimplementation [#473](https://github.com/Consensys/besu-sequencer-plugins/pull/473)
|
||||
* applyMavenExclusions=false [#477](https://github.com/Consensys/besu-sequencer-plugins/pull/477)
|
||||
|
||||
## 0.1.4-test2
|
||||
Testing pre-release from branch test-release/v0.1.4-test2
|
||||
|
||||
* revert make loginfo counts closer to Geth
|
||||
* head: disable stp & txndata
|
||||
|
||||
## 0.1.4-test
|
||||
Temporary line counting release for testnet.
|
||||
|
||||
* count stack temporary impl: make loginfo counts closer to Geth [temp/issue-248/count-stack-only](https://github.com/Consensys/besu-sequencer-plugins/tree/temp/issue-248/count-stack-only)
|
||||
--
|
||||
* fix: `Bytes.toUnsignedInteger` [#484](https://github.com/Consensys/besu-sequencer-plugins/pull/484)
|
||||
* perf: delay computations at trace time [#483](https://github.com/Consensys/besu-sequencer-plugins/pull/483)
|
||||
|
||||
## 0.1.3
|
||||
- perf: improve `StackedSet` performances [#466](https://github.com/Consensys/besu-sequencer-plugins/pull/466)
|
||||
- feat: implement L1 block & Keccak limits [#445](https://github.com/Consensys/besu-sequencer-plugins/pull/445)
|
||||
- feat: partially implement EC_DATA [#475](https://github.com/Consensys/besu-sequencer-plugins/pull/475)
|
||||
- fix: ensure trace files are always deleted [#462](https://github.com/Consensys/besu-sequencer-plugins/pull/462)
|
||||
|
||||
|
||||
## 0.1.2
|
||||
Release 8 for 23.10.4-SNAPSHOT of linea-besu
|
||||
- changed default file name to toml [#476](https://github.com/Consensys/besu-sequencer-plugins/pull/476)
|
||||
- feat: implement `BIN` counting [#471](https://github.com/Consensys/besu-sequencer-plugins/pull/471)
|
||||
- Upgrade Linea Besu to 23.10.4-SNAPSHOT [#469](https://github.com/Consensys/besu-sequencer-plugins/pull/469)
|
||||
- fix: incorrect address comparison [#470](https://github.com/Consensys/besu-sequencer-plugins/pull/470)
|
||||
- fix: line count discrepancy [#468](https://github.com/Consensys/besu-sequencer-plugins/pull/468)
|
||||
|
||||
## 0.1.1
|
||||
Release for 23.10.3-SNAPSHOT of linea-besu
|
||||
|
||||
## 0.1.0
|
||||
- Initial build of besu-sequencer-plugins
|
||||
- uses 23.10.3-SNAPSHOT as linea-besu version
|
||||
15
besu-plugins/linea-sequencer/LICENSE
Normal file
15
besu-plugins/linea-sequencer/LICENSE
Normal file
@@ -0,0 +1,15 @@
|
||||
# License
|
||||
|
||||
Copyright 2024 Consensys Software Inc.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
65
besu-plugins/linea-sequencer/README.md
Normal file
65
besu-plugins/linea-sequencer/README.md
Normal file
@@ -0,0 +1,65 @@
|
||||
# Besu Plugins related to tracer and sequencer functionality
|
||||
|
||||
This repository hosts the implementation of the sequencer, the component of the Linea stack responsible for ordering transactions and building blocks, as well as executing them. It provides a set of [Besu](https://github.com/hyperledger/besu):Linea plugins.
|
||||
|
||||
It serves developers by making the Linea tech stack open source under
|
||||
the [Apache 2.0 license](LICENSE).
|
||||
|
||||
## What is Linea?
|
||||
|
||||
[Linea](https://linea.build) is a developer-ready layer 2 network scaling Ethereum. It's secured with a zero-knowledge rollup, built on lattice-based cryptography, and powered by [Consensys](https://consensys.io).
|
||||
|
||||
## Get started
|
||||
|
||||
If you already have an understanding of the tech stack, use our [Quickstart](docs/quickstart.md) guide.
|
||||
|
||||
### Looking for Plugins?
|
||||
|
||||
Discover [existing plugins](docs/plugins.md) and understand the [plugin release process](docs/plugin-release.md).
|
||||
|
||||
## Looking for the Linea code?
|
||||
|
||||
Linea's stack is made up of multiple repositories, these include:
|
||||
- This repo, [linea-monorepo](https://github.com/Consensys/linea-monorepo): main repository for the Linea stack & network
|
||||
- [besu](https://github.com/hyperledger/besu): Besu client
|
||||
- [linea-tracer](https://github.com/Consensys/linea-tracer): Linea-Besu plugin which produces the traces that the constraint system applies and that serve as inputs to the prover
|
||||
- [linea-constraints](https://github.com/Consensys/linea-constraints): Implementation of the constraint system from the specification
|
||||
- [linea-specification](https://github.com/Consensys/linea-specification): Specification of the constraint system defining Linea's zkEVM
|
||||
|
||||
Linea abstracts away the complexity of this technical architecture to allow developers to:
|
||||
|
||||
- [Bridge tokens](https://docs.linea.build/developers/guides/bridge)
|
||||
- [Deploy a contract](https://docs.linea.build/developers/quickstart/deploy-smart-contract)
|
||||
- [Run a node](https://docs.linea.build/developers/guides/run-a-node)
|
||||
|
||||
... and more.
|
||||
|
||||
## How to contribute
|
||||
|
||||
Contributions are welcome!
|
||||
|
||||
### Guidelines for Non-Code and other Trivial Contributions
|
||||
Please keep in mind that we do not accept non-code contributions like fixing comments, typos or some other trivial fixes. Although we appreciate the extra help, managing lots of these small contributions is unfeasible, and puts extra pressure in our continuous delivery systems (running all tests, etc). Feel free to open an issue pointing any of those errors, and we will batch them into a single change.
|
||||
|
||||
1. [Create an issue](https://github.com/Consensys/linea-monorepo/issues).
|
||||
> If the proposed update requires input, also tag us for discussion.
|
||||
2. Submit the update as a pull request from your [fork of this repo](https://github.com/Consensys/linea-monorepo/fork), and tag us for review.
|
||||
> Include the issue number in the pull request description and (optionally) in the branch name.
|
||||
|
||||
Consider starting with a ["good first issue"](https://github.com/ConsenSys/linea-monorepo/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22).
|
||||
|
||||
Before contributing, ensure you're familiar with:
|
||||
|
||||
- Our [Linea contribution guide](https://github.com/Consensys/linea-monorepo/blob/main/docs/contribute.md)
|
||||
- Our [Linea code of conduct](https://github.com/Consensys/linea-monorepo/blob/main/docs/code-of-conduct.md)
|
||||
- The [Besu contribution guide](https://github.com/hyperledger/besu/blob/main/CONTRIBUTING.md), for Besu:Linea related contributions
|
||||
- Our [Security policy](https://github.com/Consensys/linea-monorepo/blob/main/docs/security.md)
|
||||
|
||||
|
||||
### Useful links
|
||||
|
||||
- [Linea docs](https://docs.linea.build)
|
||||
- [Linea blog](https://linea.mirror.xyz)
|
||||
- [Support](https://support.linea.build)
|
||||
- [Discord](https://discord.gg/linea)
|
||||
- [X](https://x.com/LineaBuild)
|
||||
86
besu-plugins/linea-sequencer/acceptance-tests/build.gradle
Normal file
86
besu-plugins/linea-sequencer/acceptance-tests/build.gradle
Normal file
@@ -0,0 +1,86 @@
|
||||
import java.time.LocalTime
|
||||
|
||||
/*
|
||||
* Copyright Consensys Software Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
|
||||
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations under the License.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
plugins {
|
||||
alias(libs.plugins.web3j)
|
||||
alias(libs.plugins.web3jSolidity)
|
||||
alias(libs.plugins.lombok)
|
||||
alias(libs.plugins.gradleVersions)
|
||||
alias(libs.plugins.dependencyManagement)
|
||||
alias(libs.plugins.download)
|
||||
}
|
||||
|
||||
def lineaSequencerProject = project(lineaSequencerProjectPath)
|
||||
apply from: lineaSequencerProject.file("gradle/java.gradle")
|
||||
apply from: lineaSequencerProject.file("gradle/dependency-management.gradle")
|
||||
apply from: lineaSequencerProject.file('gradle/common-dependencies.gradle')
|
||||
apply from: lineaSequencerProject.file("gradle/lint.gradle")
|
||||
|
||||
web3j {
|
||||
generatedPackageName = 'linea.plugin.acc.test.tests.web3j.generated'
|
||||
}
|
||||
|
||||
solidity {
|
||||
resolvePackages = false
|
||||
optimizeRuns = 1
|
||||
// TODO: remove the forced version, when DEV network is upgraded to support latest forks
|
||||
version '0.8.19'
|
||||
}
|
||||
|
||||
processTestResources.dependsOn("${lineaSequencerProjectPath}:acceptance-tests:generateTestContractWrappers")
|
||||
|
||||
tasks.register('acceptanceTests', Test) {
|
||||
description = 'Runs acceptance tests.'
|
||||
|
||||
inputs.property("integration.date", LocalTime.now()) // so it runs on every invocation
|
||||
|
||||
useJUnitPlatform {
|
||||
includeTags("AcceptanceTest")
|
||||
}
|
||||
|
||||
maxParallelForks Runtime.runtime.availableProcessors()
|
||||
systemProperties["junit.jupiter.execution.parallel.enabled"] = false
|
||||
}
|
||||
|
||||
dependencies {
|
||||
annotationProcessor 'org.apache.logging.log4j:log4j-core'
|
||||
|
||||
implementation 'org.apache.logging.log4j:log4j-core'
|
||||
|
||||
testImplementation project("${lineaSequencerProjectPath}:sequencer")
|
||||
|
||||
testImplementation "${besuArtifactGroup}:besu-datatypes"
|
||||
testImplementation "${besuArtifactGroup}.internal:clique"
|
||||
testImplementation "${besuArtifactGroup}.internal:api"
|
||||
testImplementation "${besuArtifactGroup}.internal:core"
|
||||
testImplementation "${besuArtifactGroup}.internal:dsl"
|
||||
testImplementation "${besuArtifactGroup}.internal:eth"
|
||||
testImplementation "${besuArtifactGroup}.internal:metrics-core"
|
||||
testImplementation "${besuArtifactGroup}.internal:services"
|
||||
testImplementation group: "${besuArtifactGroup}.internal", name: "core", classifier: "test-support"
|
||||
|
||||
testImplementation 'net.consensys.linea.zktracer:arithmetization'
|
||||
|
||||
testImplementation 'org.awaitility:awaitility'
|
||||
}
|
||||
|
||||
// Do not run acceptance tests with ./gradlew test, only with ./gradlew acceptanceTests
|
||||
test.enabled = false
|
||||
|
||||
jar {
|
||||
enabled = false
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
* Copyright Consensys Software Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
|
||||
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations under the License.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
package linea.plugin.acc.test.utils;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.Serializable;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
import lombok.SneakyThrows;
|
||||
import org.apache.logging.log4j.core.Appender;
|
||||
import org.apache.logging.log4j.core.Core;
|
||||
import org.apache.logging.log4j.core.Layout;
|
||||
import org.apache.logging.log4j.core.LogEvent;
|
||||
import org.apache.logging.log4j.core.appender.AbstractAppender;
|
||||
import org.apache.logging.log4j.core.config.Property;
|
||||
import org.apache.logging.log4j.core.config.plugins.Plugin;
|
||||
import org.apache.logging.log4j.core.config.plugins.PluginAttribute;
|
||||
import org.apache.logging.log4j.core.config.plugins.PluginElement;
|
||||
import org.apache.logging.log4j.core.config.plugins.PluginFactory;
|
||||
|
||||
@Plugin(name = "Memory", category = Core.CATEGORY_NAME, elementType = Appender.ELEMENT_TYPE)
|
||||
public class MemoryAppender extends AbstractAppender {
|
||||
private static final ByteArrayOutputStream collectingOutput = new ByteArrayOutputStream();
|
||||
|
||||
protected MemoryAppender(String name, Layout<? extends Serializable> layout) {
|
||||
super(name, null, layout, true, Property.EMPTY_ARRAY);
|
||||
}
|
||||
|
||||
@PluginFactory
|
||||
public static MemoryAppender createAppender(
|
||||
@PluginAttribute("name") String name,
|
||||
@PluginElement("Layout") Layout<? extends Serializable> layout) {
|
||||
return new MemoryAppender(name, layout);
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
@Override
|
||||
public void append(LogEvent event) {
|
||||
collectingOutput.write(getLayout().toByteArray(event));
|
||||
}
|
||||
|
||||
public static void reset() {
|
||||
collectingOutput.reset();
|
||||
}
|
||||
|
||||
public static String getLog() {
|
||||
return collectingOutput.toString(StandardCharsets.UTF_8);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,103 @@
|
||||
/*
|
||||
* Copyright Consensys Software Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
|
||||
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations under the License.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
package linea.plugin.acc.test;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.util.List;
|
||||
|
||||
import org.hyperledger.besu.tests.acceptance.dsl.account.Accounts;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.web3j.crypto.Credentials;
|
||||
import org.web3j.protocol.Web3j;
|
||||
import org.web3j.tx.RawTransactionManager;
|
||||
import org.web3j.tx.TransactionManager;
|
||||
|
||||
/** This class tests the block gas limit functionality of the plugin. */
|
||||
public class BlockGasLimitTest extends LineaPluginTestBase {
|
||||
|
||||
private static final BigInteger GAS_PRICE = BigInteger.TEN.pow(9);
|
||||
private static final BigInteger VALUE = BigInteger.TWO;
|
||||
|
||||
@Override
|
||||
public List<String> getTestCliOptions() {
|
||||
return new TestCommandLineOptionsBuilder()
|
||||
.set("--plugin-linea-max-block-gas=", "300000")
|
||||
.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
@BeforeEach
|
||||
public void setup() throws Exception {
|
||||
super.setup();
|
||||
minerNode.execute(minerTransactions.minerStop());
|
||||
}
|
||||
|
||||
/**
|
||||
* if we have a list of transactions [t_0.3, t_0.3, t_0.66, t_0.4], just two blocks are created,
|
||||
* where t_x fills X% of a limit.
|
||||
*/
|
||||
@Test
|
||||
public void multipleBlocksFilledRespectingUserBlockGasLimit() throws Exception {
|
||||
final Web3j web3j = minerNode.nodeRequests().eth();
|
||||
final Credentials credentials1 = Credentials.create(Accounts.GENESIS_ACCOUNT_ONE_PRIVATE_KEY);
|
||||
TransactionManager txManager1 = new RawTransactionManager(web3j, credentials1, CHAIN_ID);
|
||||
final Credentials credentials2 = Credentials.create(Accounts.GENESIS_ACCOUNT_TWO_PRIVATE_KEY);
|
||||
TransactionManager txManager2 = new RawTransactionManager(web3j, credentials2, CHAIN_ID);
|
||||
|
||||
final var tx100kGas1 =
|
||||
txManager1.sendTransaction(
|
||||
GAS_PRICE,
|
||||
BigInteger.valueOf(MAX_TX_GAS_LIMIT).divide(BigInteger.TEN),
|
||||
accounts.getSecondaryBenefactor().getAddress(),
|
||||
"a".repeat(10000),
|
||||
VALUE);
|
||||
|
||||
final var tx100kGas2 =
|
||||
txManager1.sendTransaction(
|
||||
GAS_PRICE.multiply(BigInteger.TWO),
|
||||
BigInteger.valueOf(MAX_TX_GAS_LIMIT).divide(BigInteger.TEN),
|
||||
accounts.getSecondaryBenefactor().getAddress(),
|
||||
"b".repeat(10000),
|
||||
VALUE);
|
||||
|
||||
final var tx200kGas =
|
||||
txManager2.sendTransaction(
|
||||
GAS_PRICE.multiply(BigInteger.TEN),
|
||||
BigInteger.valueOf(MAX_TX_GAS_LIMIT).divide(BigInteger.TEN),
|
||||
accounts.getPrimaryBenefactor().getAddress(),
|
||||
"c".repeat(20000),
|
||||
VALUE);
|
||||
|
||||
final var tx125kGas =
|
||||
txManager1.sendTransaction(
|
||||
GAS_PRICE.multiply(BigInteger.TWO),
|
||||
BigInteger.valueOf(MAX_TX_GAS_LIMIT).divide(BigInteger.TEN),
|
||||
accounts.getSecondaryBenefactor().getAddress(),
|
||||
"d".repeat(12500),
|
||||
VALUE);
|
||||
|
||||
startMining();
|
||||
|
||||
assertTransactionsMinedInSameBlock(
|
||||
web3j, List.of(tx100kGas1.getTransactionHash(), tx200kGas.getTransactionHash()));
|
||||
assertTransactionsMinedInSameBlock(
|
||||
web3j, List.of(tx100kGas2.getTransactionHash(), tx125kGas.getTransactionHash()));
|
||||
}
|
||||
|
||||
private void startMining() {
|
||||
minerNode.execute(minerTransactions.minerStart());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,564 @@
|
||||
/*
|
||||
* Copyright Consensys Software Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
|
||||
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations under the License.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
package linea.plugin.acc.test;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.BiFunction;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import linea.plugin.acc.test.tests.web3j.generated.EcAdd;
|
||||
import linea.plugin.acc.test.tests.web3j.generated.EcMul;
|
||||
import linea.plugin.acc.test.tests.web3j.generated.EcPairing;
|
||||
import linea.plugin.acc.test.tests.web3j.generated.EcRecover;
|
||||
import net.consensys.linea.config.LineaTracerConfiguration;
|
||||
import net.consensys.linea.sequencer.modulelimit.ModuleLineCountValidator;
|
||||
import org.apache.tuweni.bytes.Bytes;
|
||||
import org.hyperledger.besu.datatypes.Hash;
|
||||
import org.hyperledger.besu.tests.acceptance.dsl.account.Account;
|
||||
import org.hyperledger.besu.tests.acceptance.dsl.node.configuration.genesis.GenesisConfigurationFactory;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.Arguments;
|
||||
import org.junit.jupiter.params.provider.MethodSource;
|
||||
import org.web3j.protocol.Web3j;
|
||||
import org.web3j.protocol.core.methods.response.EthSendTransaction;
|
||||
import org.web3j.utils.Numeric;
|
||||
|
||||
public class EcDataLimitsTest extends LineaPluginTestBase {
|
||||
|
||||
@Override
|
||||
public List<String> getTestCliOptions() {
|
||||
return new TestCommandLineOptionsBuilder()
|
||||
// disable line count validation to accept excluded precompile txs in the txpool
|
||||
.set("--plugin-linea-tx-pool-simulation-check-api-enabled=", "false")
|
||||
// set the module limits file
|
||||
.set("--plugin-linea-module-limit-file-path=", getResourcePath("/moduleLimits.toml"))
|
||||
.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected GenesisConfigurationFactory.CliqueOptions getCliqueOptions() {
|
||||
return new GenesisConfigurationFactory.CliqueOptions(
|
||||
BLOCK_PERIOD_SECONDS + 1,
|
||||
GenesisConfigurationFactory.CliqueOptions.DEFAULT.epochLength(),
|
||||
false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the EcPairing limits, that are the number of times a certain circuit may be invoked in a
|
||||
* single block.
|
||||
*
|
||||
* @param nTransactions the number of transactions to try to include in the same block. The last
|
||||
* one is not supposed to fit as it exceeds the limit, thus it is included in the next block
|
||||
* @param input a function that generates the input data for each transaction
|
||||
* @param target the expected string to be found in the blocks log
|
||||
* @throws Exception if an error occurs during the test
|
||||
*/
|
||||
@ParameterizedTest
|
||||
@MethodSource("ecPairingLimitsTestSource")
|
||||
public void ecPairingLimitsTest(
|
||||
int nTransactions, BiFunction<Integer, Integer, String> input, String target)
|
||||
throws Exception {
|
||||
|
||||
// Deploy the EcPairing contract
|
||||
final EcPairing ecPairing = deployEcPairing();
|
||||
|
||||
// Create an account to send the transactions
|
||||
Account ecPairingSender = accounts.createAccount("ecPairingSender");
|
||||
|
||||
// Fund the account using secondary benefactor
|
||||
String fundTxHash =
|
||||
accountTransactions
|
||||
.createTransfer(accounts.getSecondaryBenefactor(), ecPairingSender, 1, BigInteger.ZERO)
|
||||
.execute(minerNode.nodeRequests())
|
||||
.toHexString();
|
||||
// Verify that the transaction for transferring funds was successful
|
||||
minerNode.verify(eth.expectSuccessfulTransactionReceipt(fundTxHash));
|
||||
|
||||
String[] txHashes = new String[nTransactions];
|
||||
for (int i = 0; i < nTransactions; i++) {
|
||||
// With decreasing nonce we force the transactions to be included in the same block
|
||||
// i = 0 , 1 , ..., nTransactions - 1
|
||||
// nonce = nTransactions - 1, nTransactions - 2, ..., 0
|
||||
int nonce = nTransactions - 1 - i;
|
||||
|
||||
// Craft the transaction data
|
||||
final byte[] encodedCallEcPairing =
|
||||
encodedCallEcPairing(
|
||||
ecPairing,
|
||||
ecPairingSender,
|
||||
nonce,
|
||||
Bytes.fromHexString(input.apply(i, nTransactions)));
|
||||
|
||||
// Send the transaction
|
||||
final Web3j web3j = minerNode.nodeRequests().eth();
|
||||
final EthSendTransaction resp =
|
||||
web3j.ethSendRawTransaction(Numeric.toHexString(encodedCallEcPairing)).send();
|
||||
|
||||
// Store the transaction hash
|
||||
txHashes[nonce] = resp.getTransactionHash();
|
||||
}
|
||||
|
||||
// Transfer used as sentry to ensure a new block is mined
|
||||
final Hash transferTxHash =
|
||||
accountTransactions
|
||||
.createTransfer(
|
||||
accounts.getPrimaryBenefactor(),
|
||||
accounts.getSecondaryBenefactor(),
|
||||
1,
|
||||
BigInteger.ONE) // nonce is 1 as primary benefactor also deploys the contract
|
||||
.execute(minerNode.nodeRequests());
|
||||
// Wait for the sentry to be mined
|
||||
minerNode.verify(eth.expectSuccessfulTransactionReceipt(transferTxHash.toHexString()));
|
||||
|
||||
// Assert that all the transactions involving the EcPairing precompile, but the last one, were
|
||||
// included in the same block
|
||||
assertTransactionsMinedInSameBlock(
|
||||
minerNode.nodeRequests().eth(), Arrays.asList(txHashes).subList(0, nTransactions - 1));
|
||||
|
||||
// Assert that the last transaction was included in another block
|
||||
assertTransactionsMinedInSeparateBlocks(
|
||||
minerNode.nodeRequests().eth(), List.of(txHashes[0], txHashes[nTransactions - 1]));
|
||||
|
||||
// Assert that the target string is contained in the blocks log
|
||||
final String blockLog = getAndResetLog();
|
||||
assertThat(blockLog).contains(target);
|
||||
}
|
||||
|
||||
private static Stream<Arguments> ecPairingLimitsTestSource() {
|
||||
Map<String, Integer> moduleLimits =
|
||||
ModuleLineCountValidator.createLimitModules(
|
||||
new LineaTracerConfiguration(getResourcePath("/moduleLimits.toml")));
|
||||
final int PRECOMPILE_ECPAIRING_FINAL_EXPONENTIATIONS =
|
||||
moduleLimits.get("PRECOMPILE_ECPAIRING_FINAL_EXPONENTIATIONS");
|
||||
final int PRECOMPILE_ECPAIRING_MILLER_LOOPS =
|
||||
moduleLimits.get("PRECOMPILE_ECPAIRING_MILLER_LOOPS");
|
||||
final int PRECOMPILE_ECPAIRING_G2_MEMBERSHIP_CALLS =
|
||||
moduleLimits.get("PRECOMPILE_ECPAIRING_G2_MEMBERSHIP_CALLS");
|
||||
|
||||
/*
|
||||
Structure of the input:
|
||||
Ax + Ay
|
||||
BxIm + BxRe
|
||||
ByIm + ByRe
|
||||
*/
|
||||
|
||||
// Valid pair requiring 1 Miller Loop and 1 final exponentiation
|
||||
final String nonTrivial =
|
||||
"01395d002b3ca9180fb924650ef0656ead838fd027d487fed681de0d674c30da097c3a9a072f9c85edf7a36812f8ee05e2cc73140749dcd7d29ceb34a8412188"
|
||||
+ "2bd3295ff81c577fe772543783411c36f463676d9692ca4250588fbad0b44dc707d8d8329e62324af8091e3a4ffe5a57cb8664d1f5f6838c55261177118e9313"
|
||||
+ "230f1851ba0d3d7d36c8603c7118c86bd2b6a7a1610c4af9e907cb702beff1d812843e703009c1c1a2f1088dcf4d91e9ed43189aa6327cae9a68be22a1aee5cb";
|
||||
|
||||
// Valid pair requiring 1 G2 membership test
|
||||
final String leftTrivialValid =
|
||||
"00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"
|
||||
+ "266152e278e5dab4e14f0d93a3e54550d08dc30ef4fe911257bd3e313864b85922cebabf989f812c0a6e67362bcb83d55c6378a4f500ecc8a6a5518b3d1695e0"
|
||||
+ "070a5a339edbbb67c35d0d44b3ffff6b5803b198af7645c892f6af2fa8abf6f2117f82e731f61e688908fa2c831c6a1c7775e6f9cfd49e06d1d24d3d13e5936a";
|
||||
|
||||
List<Arguments> arguments = new ArrayList<>();
|
||||
|
||||
/*
|
||||
This test case produces PRECOMPILE_ECPAIRING_FINAL_EXPONENTIATIONS + 1 transactions performing one ECPAIRING.
|
||||
All ECPAIRING are well-formed and none of the points is ever the point at infinity,
|
||||
leading in total to:
|
||||
- 1 Miller loops
|
||||
- 0 G2 membership tests
|
||||
- 1 final exponentiations
|
||||
per pairing.
|
||||
|
||||
In total:
|
||||
- PRECOMPILE_ECPAIRING_FINAL_EXPONENTIATIONS + 1 Miller loops (< PRECOMPILE_ECPAIRING_MILLER_LOOPS)
|
||||
- 0 G2 membership tests
|
||||
- PRECOMPILE_ECPAIRING_FINAL_EXPONENTIATIONS + 1 final exponentiations
|
||||
|
||||
This final transaction exceeds the PRECOMPILE_ECPAIRING_FINAL_EXPONENTIATIONS and must be included in the next block.
|
||||
*/
|
||||
arguments.add(
|
||||
Arguments.of(
|
||||
PRECOMPILE_ECPAIRING_FINAL_EXPONENTIATIONS
|
||||
+ 1, // 1 final exponentiation per transaction
|
||||
(BiFunction<Integer, Integer, String>)
|
||||
(i, nTransactions) -> nonTrivial, // 1 pair per transaction
|
||||
"Cumulated line count for module PRECOMPILE_ECPAIRING_FINAL_EXPONENTIATIONS="
|
||||
+ (PRECOMPILE_ECPAIRING_FINAL_EXPONENTIATIONS + 1)
|
||||
+ " is above the limit "
|
||||
+ PRECOMPILE_ECPAIRING_FINAL_EXPONENTIATIONS
|
||||
+ ", stopping selection"));
|
||||
|
||||
final int nPairsPerTransaction = 8;
|
||||
|
||||
/*
|
||||
This test case produces PRECOMPILE_ECPAIRING_MILLER_LOOPS / nPairsPerTransaction + 1
|
||||
transactions each performing nPairsPerTransaction ECPAIRING's
|
||||
except the last transaction which requires only 1.
|
||||
All ECPAIRING are well-formed and none of the points is ever the point at infinity,
|
||||
leading in total to:
|
||||
- 1 Miller loops
|
||||
- 0 G2 membership tests
|
||||
- 1 final exponentiations
|
||||
per pairing.
|
||||
|
||||
In total:
|
||||
- PRECOMPILE_ECPAIRING_MILLER_LOOPS + 1 Miller loops
|
||||
- 0 G2 membership tests
|
||||
- PRECOMPILE_ECPAIRING_MILLER_LOOPS / nPairsPerTransaction + 1 final exponentiations (< PRECOMPILE_ECPAIRING_FINAL_EXPONENTIATIONS)
|
||||
|
||||
This final transaction exceeds the PRECOMPILE_ECPAIRING_MILLER_LOOPS and must be included in the next block.
|
||||
*/
|
||||
arguments.add(
|
||||
Arguments.of(
|
||||
PRECOMPILE_ECPAIRING_MILLER_LOOPS / nPairsPerTransaction + 1,
|
||||
// nPairsPerTransaction Miller Loops per transaction except the last one which has 1
|
||||
(BiFunction<Integer, Integer, String>)
|
||||
(i, nTransactions) ->
|
||||
nonTrivial.repeat(i < nTransactions - 1 ? nPairsPerTransaction : 1),
|
||||
// nPairsPerTransaction pairs per transaction except the last one which has 1
|
||||
"Cumulated line count for module PRECOMPILE_ECPAIRING_MILLER_LOOPS="
|
||||
+ (PRECOMPILE_ECPAIRING_MILLER_LOOPS + 1)
|
||||
+ " is above the limit "
|
||||
+ PRECOMPILE_ECPAIRING_MILLER_LOOPS
|
||||
+ ", stopping selection"));
|
||||
|
||||
/*
|
||||
This test case produces PRECOMPILE_ECPAIRING_G2_MEMBERSHIP_CALLS / nPairsPerTransaction + 1
|
||||
transactions each performing nPairsPerTransaction ECPAIRING's
|
||||
except the last transaction which requires only 1.
|
||||
All ECPAIRING are well-formed and the small point is always the point at infinity
|
||||
leading to:
|
||||
- 0 Miller loops
|
||||
- 1 G2 membership tests
|
||||
- 0 final exponentiations
|
||||
per pairing.
|
||||
|
||||
In total:
|
||||
- 0 Miller loops
|
||||
- PRECOMPILE_ECPAIRING_G2_MEMBERSHIP_CALLS + 1 G2 membership tests
|
||||
- 0 final exponentiations
|
||||
|
||||
This final transaction exceeds the PRECOMPILE_ECPAIRING_G2_MEMBERSHIP_CALLS and must be included in the next block.
|
||||
*/
|
||||
arguments.add(
|
||||
Arguments.of(
|
||||
PRECOMPILE_ECPAIRING_G2_MEMBERSHIP_CALLS / nPairsPerTransaction + 1,
|
||||
// nPairsPerTransaction g2 membership test per transaction except the last one which has
|
||||
// 1
|
||||
(BiFunction<Integer, Integer, String>)
|
||||
(i, nTransactions) ->
|
||||
leftTrivialValid.repeat(i < nTransactions - 1 ? nPairsPerTransaction : 1),
|
||||
// nPairsPerTransaction pairs per transaction except the last one which has 1
|
||||
"Cumulated line count for module PRECOMPILE_ECPAIRING_G2_MEMBERSHIP_CALLS="
|
||||
+ (PRECOMPILE_ECPAIRING_G2_MEMBERSHIP_CALLS + 1)
|
||||
+ " is above the limit "
|
||||
+ PRECOMPILE_ECPAIRING_G2_MEMBERSHIP_CALLS
|
||||
+ ", stopping selection"));
|
||||
|
||||
/*
|
||||
Description of the test cases:
|
||||
|
||||
- This method defines 3 test cases.
|
||||
- Each test case is defined by a tuple (nTransactions, input, target). See the test method signature for more details.
|
||||
- Each test case goal is crossing a limit independently. Specifically:
|
||||
* The first test case crosses the limit of final exponentiations.
|
||||
* The second test case crosses the limit of Miller loops.
|
||||
* The third test case crosses the limit of G2 membership tests.
|
||||
Note that while the first two test cases requires 2 circuits, the limits are crossed independently.
|
||||
- Each test cases generates at least two blocks, one for the transactions that fit in the limit and another for the
|
||||
transaction that exceeds the limit.
|
||||
- Due to how the corresponding test is structured, we observe exactly 4 blocks:
|
||||
* 1 including 1 transaction to deploy the EcPairing contract calling the precompile
|
||||
* 1 including 1 transaction to fund ecPairing sender
|
||||
* 1 including all the transactions that fit in the limit and the sentry transaction
|
||||
* 1 including the transaction that exceeds the limit
|
||||
*/
|
||||
return arguments.stream();
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the EcAdd PRECOMPILE_ECADD_EFFECTIVE_CALLS limit, that is the number of times the
|
||||
* corresponding circuit may be invoked in a single block.
|
||||
*/
|
||||
@Test
|
||||
public void ecAddLimitTest() throws Exception {
|
||||
Map<String, Integer> moduleLimits =
|
||||
ModuleLineCountValidator.createLimitModules(
|
||||
new LineaTracerConfiguration(getResourcePath("/moduleLimits.toml")));
|
||||
final int PRECOMPILE_ECADD_EFFECTIVE_CALLS =
|
||||
moduleLimits.get("PRECOMPILE_ECADD_EFFECTIVE_CALLS");
|
||||
|
||||
/*
|
||||
* nTransactions: the number of transactions to try to include in the same block. The last
|
||||
* one is not supposed to fit as it exceeds the limit, thus it is included in the next block. Note
|
||||
* that in this specific test more than one call to the ECADD precompile is executed within the
|
||||
* same transaction to reach the limit with a smaller number of transaction
|
||||
*
|
||||
* input: input data for each transaction
|
||||
* target: the expected string to be found in the blocks log
|
||||
*/
|
||||
final int callsPerTransaction = 32;
|
||||
final int nTransactions = PRECOMPILE_ECADD_EFFECTIVE_CALLS / callsPerTransaction + 1;
|
||||
final String input =
|
||||
"070375d4eec4f22aa3ad39cb40ccd73d2dbab6de316e75f81dc2948a996795d5041b98f07f44aa55ce8bd97e32cacf55f1e42229d540d5e7a767d1138a5da656185f6f5cf93c8afa0461a948c2da7c403b6f8477c488155dfa8d2da1c62517b813d83d7a51eb18fdb51225873c87d44f883e770ce2ca56c305d02d6cb99ca5b8";
|
||||
final String target =
|
||||
"Cumulated line count for module PRECOMPILE_ECADD_EFFECTIVE_CALLS="
|
||||
+ (PRECOMPILE_ECADD_EFFECTIVE_CALLS + callsPerTransaction)
|
||||
+ " is above the limit "
|
||||
+ PRECOMPILE_ECADD_EFFECTIVE_CALLS
|
||||
+ ", stopping selection";
|
||||
|
||||
// Deploy the EcAdd contract
|
||||
final EcAdd ecAdd = deployEcAdd();
|
||||
|
||||
// Create an account to send the transactions
|
||||
Account ecAddSender = accounts.createAccount("ecAddSender");
|
||||
|
||||
// Fund the account using secondary benefactor
|
||||
String fundTxHash =
|
||||
accountTransactions
|
||||
.createTransfer(accounts.getSecondaryBenefactor(), ecAddSender, 1, BigInteger.ZERO)
|
||||
.execute(minerNode.nodeRequests())
|
||||
.toHexString();
|
||||
// Verify that the transaction for transferring funds was successful
|
||||
minerNode.verify(eth.expectSuccessfulTransactionReceipt(fundTxHash));
|
||||
|
||||
String[] txHashes = new String[nTransactions];
|
||||
for (int i = 0; i < nTransactions; i++) {
|
||||
// With decreasing nonce we force the transactions to be included in the same block
|
||||
// i = 0 , 1 , ..., nTransactions - 1
|
||||
// nonce = nTransactions - 1, nTransactions - 2, ..., 0
|
||||
int nonce = nTransactions - 1 - i;
|
||||
|
||||
// Craft the transaction data
|
||||
final byte[] encodedCallEcAdd =
|
||||
encodedCallEcAdd(ecAdd, ecAddSender, nonce, Bytes.fromHexString(input));
|
||||
|
||||
// Send the transaction
|
||||
final Web3j web3j = minerNode.nodeRequests().eth();
|
||||
final EthSendTransaction resp =
|
||||
web3j.ethSendRawTransaction(Numeric.toHexString(encodedCallEcAdd)).send();
|
||||
|
||||
// Store the transaction hash
|
||||
txHashes[nonce] = resp.getTransactionHash();
|
||||
}
|
||||
|
||||
// Transfer used as sentry to ensure a new block is mined
|
||||
final Hash transferTxHash =
|
||||
accountTransactions
|
||||
.createTransfer(
|
||||
accounts.getPrimaryBenefactor(),
|
||||
accounts.getSecondaryBenefactor(),
|
||||
1,
|
||||
BigInteger.ONE) // nonce is 1 as primary benefactor also deploys the contract
|
||||
.execute(minerNode.nodeRequests());
|
||||
// Wait for the sentry to be mined
|
||||
minerNode.verify(eth.expectSuccessfulTransactionReceipt(transferTxHash.toHexString()));
|
||||
|
||||
// Assert that all the transactions involving the EcPairing precompile, but the last one, were
|
||||
// included in the same block
|
||||
assertTransactionsMinedInSameBlock(
|
||||
minerNode.nodeRequests().eth(), Arrays.asList(txHashes).subList(0, nTransactions - 1));
|
||||
|
||||
// Assert that the last transaction was included in another block
|
||||
assertTransactionsMinedInSeparateBlocks(
|
||||
minerNode.nodeRequests().eth(), List.of(txHashes[0], txHashes[nTransactions - 1]));
|
||||
|
||||
// Assert that the target string is contained in the blocks log
|
||||
final String blockLog = getAndResetLog();
|
||||
assertThat(blockLog).contains(target);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the EcMul PRECOMPILE_ECMUL_EFFECTIVE_CALLS limit, that is the number of times the
|
||||
* corresponding circuit may be invoked in a single block.
|
||||
*/
|
||||
@Test
|
||||
public void ecMulLimitTest() throws Exception {
|
||||
Map<String, Integer> moduleLimits =
|
||||
ModuleLineCountValidator.createLimitModules(
|
||||
new LineaTracerConfiguration(getResourcePath("/moduleLimits.toml")));
|
||||
final int PRECOMPILE_ECMUL_EFFECTIVE_CALLS =
|
||||
moduleLimits.get("PRECOMPILE_ECMUL_EFFECTIVE_CALLS");
|
||||
|
||||
/*
|
||||
* nTransactions: the number of transactions to try to include in the same block. The last
|
||||
* one is not supposed to fit as it exceeds the limit, thus it is included in the next block
|
||||
* input: input data for each transaction
|
||||
* target: the expected string to be found in the blocks log
|
||||
*/
|
||||
final int nTransactions = PRECOMPILE_ECMUL_EFFECTIVE_CALLS + 1;
|
||||
final String input =
|
||||
"030644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd315ed738c0e0a7c92e7845f96b2ae9c0a68a6a449e3538fc7ff3ebf7a5a18a2c40000000000000000000000000000000000000000000000000000000000000001";
|
||||
final String target =
|
||||
"Cumulated line count for module PRECOMPILE_ECMUL_EFFECTIVE_CALLS="
|
||||
+ (PRECOMPILE_ECMUL_EFFECTIVE_CALLS + 1)
|
||||
+ " is above the limit "
|
||||
+ PRECOMPILE_ECMUL_EFFECTIVE_CALLS
|
||||
+ ", stopping selection";
|
||||
|
||||
// Deploy the EcMul contract
|
||||
final EcMul ecMul = deployEcMul();
|
||||
|
||||
// Create an account to send the transactions
|
||||
Account ecMulSender = accounts.createAccount("ecMulSender");
|
||||
|
||||
// Fund the account using secondary benefactor
|
||||
String fundTxHash =
|
||||
accountTransactions
|
||||
.createTransfer(accounts.getSecondaryBenefactor(), ecMulSender, 1, BigInteger.ZERO)
|
||||
.execute(minerNode.nodeRequests())
|
||||
.toHexString();
|
||||
// Verify that the transaction for transferring funds was successful
|
||||
minerNode.verify(eth.expectSuccessfulTransactionReceipt(fundTxHash));
|
||||
|
||||
String[] txHashes = new String[nTransactions];
|
||||
for (int i = 0; i < nTransactions; i++) {
|
||||
// With decreasing nonce we force the transactions to be included in the same block
|
||||
// i = 0 , 1 , ..., nTransactions - 1
|
||||
// nonce = nTransactions - 1, nTransactions - 2, ..., 0
|
||||
int nonce = nTransactions - 1 - i;
|
||||
|
||||
// Craft the transaction data
|
||||
final byte[] encodedCallEcMul =
|
||||
encodedCallEcMul(ecMul, ecMulSender, nonce, Bytes.fromHexString(input));
|
||||
|
||||
// Send the transaction
|
||||
final Web3j web3j = minerNode.nodeRequests().eth();
|
||||
final EthSendTransaction resp =
|
||||
web3j.ethSendRawTransaction(Numeric.toHexString(encodedCallEcMul)).send();
|
||||
|
||||
// Store the transaction hash
|
||||
txHashes[nonce] = resp.getTransactionHash();
|
||||
}
|
||||
|
||||
// Transfer used as sentry to ensure a new block is mined
|
||||
final Hash transferTxHash =
|
||||
accountTransactions
|
||||
.createTransfer(
|
||||
accounts.getPrimaryBenefactor(),
|
||||
accounts.getSecondaryBenefactor(),
|
||||
1,
|
||||
BigInteger.ONE) // nonce is 1 as primary benefactor also deploys the contract
|
||||
.execute(minerNode.nodeRequests());
|
||||
// Wait for the sentry to be mined
|
||||
minerNode.verify(eth.expectSuccessfulTransactionReceipt(transferTxHash.toHexString()));
|
||||
|
||||
// Assert that all the transactions involving the EcPairing precompile, but the last one, were
|
||||
// included in the same block
|
||||
assertTransactionsMinedInSameBlock(
|
||||
minerNode.nodeRequests().eth(), Arrays.asList(txHashes).subList(0, nTransactions - 1));
|
||||
|
||||
// Assert that the last transaction was included in another block
|
||||
assertTransactionsMinedInSeparateBlocks(
|
||||
minerNode.nodeRequests().eth(), List.of(txHashes[0], txHashes[nTransactions - 1]));
|
||||
|
||||
// Assert that the target string is contained in the blocks log
|
||||
final String blockLog = getAndResetLog();
|
||||
assertThat(blockLog).contains(target);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the EcRecover PRECOMPILE_ECRECOVER_EFFECTIVE_CALLS limit, that is the number of times the
|
||||
* corresponding circuit may be invoked in a single block.
|
||||
*/
|
||||
@Test
|
||||
public void ecRecoverLimitTest() throws Exception {
|
||||
Map<String, Integer> moduleLimits =
|
||||
ModuleLineCountValidator.createLimitModules(
|
||||
new LineaTracerConfiguration(getResourcePath("/moduleLimits.toml")));
|
||||
final int PRECOMPILE_ECRECOVER_EFFECTIVE_CALLS =
|
||||
moduleLimits.get("PRECOMPILE_ECRECOVER_EFFECTIVE_CALLS");
|
||||
|
||||
/*
|
||||
* nTransactions: the number of transactions to try to include in the same block. The last
|
||||
* one is not supposed to fit as it exceeds the limit, thus it is included in the next block
|
||||
* input: input data for each transaction
|
||||
* target: the expected string to be found in the blocks log
|
||||
*/
|
||||
final int nTransactions = PRECOMPILE_ECRECOVER_EFFECTIVE_CALLS + 1;
|
||||
final String input =
|
||||
"279d94621558f755796898fc4bd36b6d407cae77537865afe523b79c74cc680b000000000000000000000000000000000000000000000000000000000000001bc2ff96feed8749a5ad1c0714f950b5ac939d8acedbedcbc2949614ab8af063121feecd50adc6273fdd5d11c6da18c8cfe14e2787f5a90af7c7c1328e7d0a2c42";
|
||||
final String target =
|
||||
"Cumulated line count for module PRECOMPILE_ECRECOVER_EFFECTIVE_CALLS="
|
||||
+ (PRECOMPILE_ECRECOVER_EFFECTIVE_CALLS + 1)
|
||||
+ " is above the limit "
|
||||
+ PRECOMPILE_ECRECOVER_EFFECTIVE_CALLS
|
||||
+ ", stopping selection";
|
||||
|
||||
// Deploy the EcRecover contract
|
||||
final EcRecover ecRecover = deployEcRecover();
|
||||
|
||||
// Create an account to send the transactions
|
||||
Account ecRecoverSender = accounts.createAccount("ecRecoverSender");
|
||||
|
||||
// Fund the account using secondary benefactor
|
||||
String fundTxHash =
|
||||
accountTransactions
|
||||
.createTransfer(accounts.getSecondaryBenefactor(), ecRecoverSender, 1, BigInteger.ZERO)
|
||||
.execute(minerNode.nodeRequests())
|
||||
.toHexString();
|
||||
// Verify that the transaction for transferring funds was successful
|
||||
minerNode.verify(eth.expectSuccessfulTransactionReceipt(fundTxHash));
|
||||
|
||||
String[] txHashes = new String[nTransactions];
|
||||
for (int i = 0; i < nTransactions; i++) {
|
||||
// With decreasing nonce we force the transactions to be included in the same block
|
||||
// i = 0 , 1 , ..., nTransactions - 1
|
||||
// nonce = nTransactions - 1, nTransactions - 2, ..., 0
|
||||
int nonce = nTransactions - 1 - i;
|
||||
|
||||
// Craft the transaction data
|
||||
final byte[] encodedCallEcRecover =
|
||||
encodedCallEcRecover(ecRecover, ecRecoverSender, nonce, Bytes.fromHexString(input));
|
||||
|
||||
// Send the transaction
|
||||
final Web3j web3j = minerNode.nodeRequests().eth();
|
||||
final EthSendTransaction resp =
|
||||
web3j.ethSendRawTransaction(Numeric.toHexString(encodedCallEcRecover)).send();
|
||||
|
||||
// Store the transaction hash
|
||||
txHashes[nonce] = resp.getTransactionHash();
|
||||
}
|
||||
|
||||
// Transfer used as sentry to ensure a new block is mined
|
||||
final Hash transferTxHash =
|
||||
accountTransactions
|
||||
.createTransfer(
|
||||
accounts.getPrimaryBenefactor(),
|
||||
accounts.getSecondaryBenefactor(),
|
||||
1,
|
||||
BigInteger.ONE) // nonce is 1 as primary benefactor also deploys the contract
|
||||
.execute(minerNode.nodeRequests());
|
||||
// Wait for the sentry to be mined
|
||||
minerNode.verify(eth.expectSuccessfulTransactionReceipt(transferTxHash.toHexString()));
|
||||
|
||||
// Assert that all the transactions involving the EcPairing precompile, but the last one, were
|
||||
// included in the same block
|
||||
assertTransactionsMinedInSameBlock(
|
||||
minerNode.nodeRequests().eth(), Arrays.asList(txHashes).subList(0, nTransactions - 1));
|
||||
|
||||
// Assert that the last transaction was included in another block
|
||||
assertTransactionsMinedInSeparateBlocks(
|
||||
minerNode.nodeRequests().eth(), List.of(txHashes[0], txHashes[nTransactions - 1]));
|
||||
|
||||
// Assert that the target string is contained in the blocks log
|
||||
final String blockLog = getAndResetLog();
|
||||
assertThat(blockLog).contains(target);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
/*
|
||||
* Copyright Consensys Software Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
|
||||
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations under the License.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
package linea.plugin.acc.test;
|
||||
|
||||
import java.math.BigInteger;
|
||||
|
||||
import org.hyperledger.besu.tests.acceptance.dsl.account.Accounts;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.web3j.crypto.Credentials;
|
||||
import org.web3j.protocol.Web3j;
|
||||
import org.web3j.tx.RawTransactionManager;
|
||||
import org.web3j.tx.TransactionManager;
|
||||
import org.web3j.tx.gas.DefaultGasProvider;
|
||||
|
||||
/**
|
||||
* Example test using Besu node configured for Prague. Note that block building must be triggered
|
||||
* explicitly through `this.buildNewBlock()`
|
||||
*/
|
||||
public class ExamplePragueTest extends LineaPluginTestBasePrague {
|
||||
private static final BigInteger GAS_PRICE = DefaultGasProvider.GAS_PRICE;
|
||||
private static final BigInteger GAS_LIMIT = DefaultGasProvider.GAS_LIMIT;
|
||||
private static final BigInteger VALUE = BigInteger.ZERO;
|
||||
private static final String DATA = "0x";
|
||||
|
||||
private Web3j web3j;
|
||||
private Credentials credentials;
|
||||
private TransactionManager txManager;
|
||||
private String recipient;
|
||||
|
||||
@Override
|
||||
@BeforeEach
|
||||
public void setup() throws Exception {
|
||||
super.setup();
|
||||
web3j = minerNode.nodeRequests().eth();
|
||||
credentials = Credentials.create(Accounts.GENESIS_ACCOUNT_ONE_PRIVATE_KEY);
|
||||
txManager = new RawTransactionManager(web3j, credentials, CHAIN_ID);
|
||||
recipient = accounts.getSecondaryBenefactor().getAddress();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void legacyTransactionsAreAccepted() throws Exception {
|
||||
// Act - Send a legacy transaction
|
||||
String txHash =
|
||||
txManager
|
||||
.sendTransaction(GAS_PRICE, GAS_LIMIT, recipient, DATA, VALUE)
|
||||
.getTransactionHash();
|
||||
|
||||
this.buildNewBlock();
|
||||
|
||||
// Assert
|
||||
minerNode.verify(eth.expectSuccessfulTransactionReceipt(txHash));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,234 @@
|
||||
/*
|
||||
* Copyright Consensys Software Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
|
||||
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations under the License.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
package linea.plugin.acc.test;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.math.BigInteger;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import linea.plugin.acc.test.tests.web3j.generated.ExcludedPrecompiles;
|
||||
import org.apache.tuweni.bytes.Bytes;
|
||||
import org.apache.tuweni.bytes.Bytes32;
|
||||
import org.hyperledger.besu.tests.acceptance.dsl.account.Account;
|
||||
import org.hyperledger.besu.tests.acceptance.dsl.account.Accounts;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.web3j.abi.datatypes.generated.Bytes8;
|
||||
import org.web3j.crypto.Credentials;
|
||||
import org.web3j.crypto.Hash;
|
||||
import org.web3j.crypto.RawTransaction;
|
||||
import org.web3j.crypto.TransactionEncoder;
|
||||
import org.web3j.protocol.Web3j;
|
||||
import org.web3j.protocol.core.methods.response.EthSendTransaction;
|
||||
import org.web3j.tx.gas.DefaultGasProvider;
|
||||
import org.web3j.utils.Numeric;
|
||||
|
||||
public class ExcludedPrecompilesTest extends LineaPluginTestBase {
|
||||
private static final BigInteger GAS_LIMIT = DefaultGasProvider.GAS_LIMIT;
|
||||
private static final BigInteger GAS_PRICE = DefaultGasProvider.GAS_PRICE;
|
||||
|
||||
@Override
|
||||
public List<String> getTestCliOptions() {
|
||||
return new TestCommandLineOptionsBuilder()
|
||||
// disable line count validation to accept excluded precompile txs in the txpool
|
||||
.set("--plugin-linea-tx-pool-simulation-check-api-enabled=", "false")
|
||||
.build();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void transactionsWithExcludedPrecompilesAreNotAccepted() throws Exception {
|
||||
final ExcludedPrecompiles excludedPrecompiles = deployExcludedPrecompiles();
|
||||
final Web3j web3j = minerNode.nodeRequests().eth();
|
||||
final String contractAddress = excludedPrecompiles.getContractAddress();
|
||||
|
||||
// fund a new account
|
||||
final var recipient = accounts.createAccount("recipient");
|
||||
final var txHashFundRecipient =
|
||||
accountTransactions
|
||||
.createTransfer(accounts.getPrimaryBenefactor(), recipient, 10, BigInteger.valueOf(1))
|
||||
.execute(minerNode.nodeRequests());
|
||||
minerNode.verify(eth.expectSuccessfulTransactionReceipt(txHashFundRecipient.toHexString()));
|
||||
|
||||
record InvalidCall(
|
||||
String senderPrivateKey, int nonce, String encodedContractCall, String expectedTraceLog) {}
|
||||
|
||||
final InvalidCall[] invalidCalls = {
|
||||
new InvalidCall(
|
||||
Accounts.GENESIS_ACCOUNT_ONE_PRIVATE_KEY,
|
||||
2,
|
||||
excludedPrecompiles
|
||||
.callRIPEMD160("I am not allowed here".getBytes(StandardCharsets.UTF_8))
|
||||
.encodeFunctionCall(),
|
||||
"Tx 0xe4648fd59d4289e59b112bf60931336440d306c85c2aac5a8b0c64ab35bc55b7 line count for module PRECOMPILE_RIPEMD_BLOCKS=1 is above the limit 0"),
|
||||
new InvalidCall(
|
||||
Accounts.GENESIS_ACCOUNT_TWO_PRIVATE_KEY,
|
||||
0,
|
||||
encodedCallBlake2F(excludedPrecompiles),
|
||||
"Tx 0x9f457b1b5244b03c54234f7f9e8225d4253135dd3c99a46dc527d115e7ea5dac line count for module PRECOMPILE_BLAKE_ROUNDS=12 is above the limit 0")
|
||||
};
|
||||
|
||||
final var invalidTxHashes =
|
||||
Arrays.stream(invalidCalls)
|
||||
.map(
|
||||
invalidCall -> {
|
||||
// this tx must not be accepted but not mined
|
||||
final RawTransaction txInvalid =
|
||||
RawTransaction.createTransaction(
|
||||
CHAIN_ID,
|
||||
BigInteger.valueOf(invalidCall.nonce),
|
||||
GAS_LIMIT.divide(BigInteger.TEN),
|
||||
contractAddress,
|
||||
BigInteger.ZERO,
|
||||
invalidCall.encodedContractCall,
|
||||
GAS_PRICE,
|
||||
GAS_PRICE);
|
||||
|
||||
final byte[] signedTxInvalid =
|
||||
TransactionEncoder.signMessage(
|
||||
txInvalid, Credentials.create(invalidCall.senderPrivateKey));
|
||||
|
||||
final EthSendTransaction signedTxInvalidResp;
|
||||
try {
|
||||
signedTxInvalidResp =
|
||||
web3j.ethSendRawTransaction(Numeric.toHexString(signedTxInvalid)).send();
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
||||
assertThat(signedTxInvalidResp.hasError()).isFalse();
|
||||
return signedTxInvalidResp.getTransactionHash();
|
||||
})
|
||||
.toList();
|
||||
|
||||
assertThat(getTxPoolContent()).hasSize(invalidTxHashes.size());
|
||||
|
||||
// transfer used as sentry to ensure a new block is mined without the invalid txs
|
||||
final var transferTxHash1 =
|
||||
accountTransactions
|
||||
.createTransfer(recipient, accounts.getSecondaryBenefactor(), 1)
|
||||
.execute(minerNode.nodeRequests());
|
||||
|
||||
// first sentry is mined and no tx of the bundle is mined
|
||||
minerNode.verify(eth.expectSuccessfulTransactionReceipt(transferTxHash1.toHexString()));
|
||||
Arrays.stream(invalidCalls)
|
||||
.forEach(
|
||||
invalidCall ->
|
||||
minerNode.verify(
|
||||
eth.expectNoTransactionReceipt(Hash.sha3(invalidCall.encodedContractCall))));
|
||||
|
||||
final String log = getLog();
|
||||
// verify trace log contains the exclusion cause
|
||||
Arrays.stream(invalidCalls)
|
||||
.forEach(invalidCall -> assertThat(log).contains(invalidCall.expectedTraceLog));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void invalidModExpCallsAreNotMined() throws Exception {
|
||||
final var modExp = deployModExp();
|
||||
|
||||
final var modExpSenders = new Account[3];
|
||||
final var foundTxHashes = new String[3];
|
||||
for (int i = 0; i < 3; i++) {
|
||||
modExpSenders[i] = accounts.createAccount("sender" + i);
|
||||
foundTxHashes[i] =
|
||||
accountTransactions
|
||||
.createTransfer(
|
||||
accounts.getSecondaryBenefactor(), modExpSenders[i], 1, BigInteger.valueOf(i))
|
||||
.execute(minerNode.nodeRequests())
|
||||
.toHexString();
|
||||
}
|
||||
Arrays.stream(foundTxHashes)
|
||||
.forEach(
|
||||
fundTxHash -> minerNode.verify(eth.expectSuccessfulTransactionReceipt(fundTxHash)));
|
||||
|
||||
final Bytes[][] invalidInputs = {
|
||||
{Bytes.fromHexString("0000000000000000000000000000000000000000000000000000000000000201")},
|
||||
{
|
||||
Bytes.fromHexString("00000000000000000000000000000000000000000000000000000000000003"),
|
||||
Bytes.fromHexString("ff")
|
||||
},
|
||||
{
|
||||
Bytes.fromHexString("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"),
|
||||
Bytes.fromHexString("00000000000000000000000000000000000000000000000000000000000003"),
|
||||
Bytes.fromHexString("ff")
|
||||
}
|
||||
};
|
||||
|
||||
for (int i = 0; i < invalidInputs.length; i++) {
|
||||
final var invalidCallTxHashes = new String[invalidInputs[i].length];
|
||||
for (int j = 0; j < invalidInputs[i].length; j++) {
|
||||
|
||||
// use always the same nonce since we expect this tx not to be mined
|
||||
final var mulmodOverflow =
|
||||
encodedCallModExp(modExp, modExpSenders[j], 0, invalidInputs[i][j]);
|
||||
|
||||
final Web3j web3j = minerNode.nodeRequests().eth();
|
||||
final EthSendTransaction resp =
|
||||
web3j.ethSendRawTransaction(Numeric.toHexString(mulmodOverflow)).send();
|
||||
invalidCallTxHashes[j] = resp.getTransactionHash();
|
||||
}
|
||||
|
||||
// transfer used as sentry to ensure a new block is mined without the invalid modexp call
|
||||
final var transferTxHash =
|
||||
accountTransactions
|
||||
.createTransfer(
|
||||
accounts.getPrimaryBenefactor(),
|
||||
accounts.getSecondaryBenefactor(),
|
||||
1,
|
||||
BigInteger.valueOf(i + 1))
|
||||
.execute(minerNode.nodeRequests());
|
||||
|
||||
// sentry is mined and the invalid modexp txs are not
|
||||
minerNode.verify(eth.expectSuccessfulTransactionReceipt(transferTxHash.toHexString()));
|
||||
final var blockLog = getAndResetLog();
|
||||
Arrays.stream(invalidCallTxHashes)
|
||||
.forEach(
|
||||
invalidCallTxHash -> {
|
||||
minerNode.verify(eth.expectNoTransactionReceipt(invalidCallTxHash));
|
||||
assertThat(blockLog)
|
||||
.contains(
|
||||
"Tx "
|
||||
+ invalidCallTxHash
|
||||
+ " line count for module PRECOMPILE_MODEXP_EFFECTIVE_CALLS=2147483647 is above the limit 10000, removing from the txpool");
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private String encodedCallBlake2F(final ExcludedPrecompiles excludedPrecompiles) {
|
||||
return excludedPrecompiles
|
||||
.callBlake2f(
|
||||
BigInteger.valueOf(12),
|
||||
List.of(
|
||||
Bytes32.fromHexString(
|
||||
"0x48c9bdf267e6096a3ba7ca8485ae67bb2bf894fe72f36e3cf1361d5f3af54fa5")
|
||||
.toArrayUnsafe(),
|
||||
Bytes32.fromHexString(
|
||||
"0xd182e6ad7f520e511f6c3e2b8c68059b6bbd41fbabd9831f79217e1319cde05b")
|
||||
.toArrayUnsafe()),
|
||||
List.of(
|
||||
Bytes32.fromHexString(
|
||||
"0x6162630000000000000000000000000000000000000000000000000000000000")
|
||||
.toArrayUnsafe(),
|
||||
Bytes32.ZERO.toArrayUnsafe(),
|
||||
Bytes32.ZERO.toArrayUnsafe(),
|
||||
Bytes32.ZERO.toArrayUnsafe()),
|
||||
List.of(Bytes8.DEFAULT.getValue(), Bytes8.DEFAULT.getValue()),
|
||||
true)
|
||||
.encodeFunctionCall();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,593 @@
|
||||
/*
|
||||
* Copyright Consensys Software Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
|
||||
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations under the License.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package linea.plugin.acc.test;
|
||||
|
||||
import static net.consensys.linea.metrics.LineaMetricCategory.PRICING_CONF;
|
||||
import static net.consensys.linea.metrics.LineaMetricCategory.SEQUENCER_PROFITABILITY;
|
||||
import static net.consensys.linea.metrics.LineaMetricCategory.TX_POOL_PROFITABILITY;
|
||||
import static org.assertj.core.api.Assertions.*;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.math.BigInteger;
|
||||
import java.net.URI;
|
||||
import java.net.http.HttpClient;
|
||||
import java.net.http.HttpRequest;
|
||||
import java.net.http.HttpResponse;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.fasterxml.jackson.databind.node.ObjectNode;
|
||||
import linea.plugin.acc.test.tests.web3j.generated.AcceptanceTestToken;
|
||||
import linea.plugin.acc.test.tests.web3j.generated.DummyAdder;
|
||||
import linea.plugin.acc.test.tests.web3j.generated.EcAdd;
|
||||
import linea.plugin.acc.test.tests.web3j.generated.EcMul;
|
||||
import linea.plugin.acc.test.tests.web3j.generated.EcPairing;
|
||||
import linea.plugin.acc.test.tests.web3j.generated.EcRecover;
|
||||
import linea.plugin.acc.test.tests.web3j.generated.ExcludedPrecompiles;
|
||||
import linea.plugin.acc.test.tests.web3j.generated.ModExp;
|
||||
import linea.plugin.acc.test.tests.web3j.generated.MulmodExecutor;
|
||||
import linea.plugin.acc.test.tests.web3j.generated.RevertExample;
|
||||
import linea.plugin.acc.test.tests.web3j.generated.SimpleStorage;
|
||||
import linea.plugin.acc.test.utils.MemoryAppender;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.RandomStringUtils;
|
||||
import org.apache.tuweni.bytes.Bytes;
|
||||
import org.apache.tuweni.bytes.Bytes32;
|
||||
import org.apache.tuweni.units.bigints.UInt32;
|
||||
import org.hyperledger.besu.datatypes.Address;
|
||||
import org.hyperledger.besu.datatypes.Hash;
|
||||
import org.hyperledger.besu.ethereum.eth.transactions.ImmutableTransactionPoolConfiguration;
|
||||
import org.hyperledger.besu.ethereum.eth.transactions.TransactionPoolConfiguration;
|
||||
import org.hyperledger.besu.metrics.prometheus.MetricsConfiguration;
|
||||
import org.hyperledger.besu.plugin.services.metrics.MetricCategory;
|
||||
import org.hyperledger.besu.tests.acceptance.dsl.AcceptanceTestBase;
|
||||
import org.hyperledger.besu.tests.acceptance.dsl.account.Account;
|
||||
import org.hyperledger.besu.tests.acceptance.dsl.account.Accounts;
|
||||
import org.hyperledger.besu.tests.acceptance.dsl.condition.txpool.TxPoolConditions;
|
||||
import org.hyperledger.besu.tests.acceptance.dsl.node.BesuNode;
|
||||
import org.hyperledger.besu.tests.acceptance.dsl.node.RunnableNode;
|
||||
import org.hyperledger.besu.tests.acceptance.dsl.node.configuration.BesuNodeConfigurationBuilder;
|
||||
import org.hyperledger.besu.tests.acceptance.dsl.node.configuration.NodeConfigurationFactory;
|
||||
import org.hyperledger.besu.tests.acceptance.dsl.node.configuration.genesis.GenesisConfigurationFactory;
|
||||
import org.hyperledger.besu.tests.acceptance.dsl.node.configuration.genesis.GenesisConfigurationFactory.CliqueOptions;
|
||||
import org.hyperledger.besu.tests.acceptance.dsl.transaction.txpool.TxPoolTransactions;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.web3j.crypto.Credentials;
|
||||
import org.web3j.crypto.RawTransaction;
|
||||
import org.web3j.crypto.TransactionEncoder;
|
||||
import org.web3j.protocol.Web3j;
|
||||
import org.web3j.protocol.core.RemoteCall;
|
||||
import org.web3j.protocol.core.methods.response.TransactionReceipt;
|
||||
import org.web3j.protocol.exceptions.TransactionException;
|
||||
import org.web3j.tx.RawTransactionManager;
|
||||
import org.web3j.tx.TransactionManager;
|
||||
import org.web3j.tx.gas.DefaultGasProvider;
|
||||
import org.web3j.tx.response.PollingTransactionReceiptProcessor;
|
||||
import org.web3j.tx.response.TransactionReceiptProcessor;
|
||||
|
||||
/** Base class for plugin tests. */
|
||||
@Slf4j
|
||||
public abstract class LineaPluginTestBase extends AcceptanceTestBase {
|
||||
public static final int MAX_CALLDATA_SIZE = 1188; // contract has a call data size of 1160
|
||||
public static final int MAX_TX_GAS_LIMIT = DefaultGasProvider.GAS_LIMIT.intValue();
|
||||
public static final long CHAIN_ID = 1337L;
|
||||
public static final int BLOCK_PERIOD_SECONDS = 5;
|
||||
public static final CliqueOptions DEFAULT_LINEA_CLIQUE_OPTIONS =
|
||||
new CliqueOptions(BLOCK_PERIOD_SECONDS, CliqueOptions.DEFAULT.epochLength(), false);
|
||||
protected static final HttpClient HTTP_CLIENT = HttpClient.newHttpClient();
|
||||
protected BesuNode minerNode;
|
||||
|
||||
@BeforeEach
|
||||
public void setup() throws Exception {
|
||||
minerNode =
|
||||
createCliqueNodeWithExtraCliOptionsAndRpcApis(
|
||||
"miner1", getCliqueOptions(), getTestCliOptions(), Set.of("LINEA", "MINER"), false);
|
||||
minerNode.setTransactionPoolConfiguration(
|
||||
ImmutableTransactionPoolConfiguration.builder()
|
||||
.from(TransactionPoolConfiguration.DEFAULT)
|
||||
.noLocalPriority(true)
|
||||
.build());
|
||||
cluster.start(minerNode);
|
||||
}
|
||||
|
||||
protected List<String> getTestCliOptions() {
|
||||
return new TestCommandLineOptionsBuilder().build();
|
||||
}
|
||||
|
||||
protected CliqueOptions getCliqueOptions() {
|
||||
return DEFAULT_LINEA_CLIQUE_OPTIONS;
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
public void stop() {
|
||||
cluster.stop();
|
||||
cluster.close();
|
||||
MemoryAppender.reset();
|
||||
}
|
||||
|
||||
protected Optional<Bytes32> maybeCustomGenesisExtraData() {
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
protected BesuNode createCliqueNodeWithExtraCliOptionsAndRpcApis(
|
||||
final String name,
|
||||
final CliqueOptions cliqueOptions,
|
||||
final List<String> extraCliOptions,
|
||||
final Set<String> extraRpcApis,
|
||||
final boolean isEngineRpcEnabled)
|
||||
throws IOException {
|
||||
final NodeConfigurationFactory node = new NodeConfigurationFactory();
|
||||
|
||||
final var nodeConfBuilder =
|
||||
new BesuNodeConfigurationBuilder()
|
||||
.name(name)
|
||||
.miningEnabled()
|
||||
.jsonRpcConfiguration(node.createJsonRpcWithCliqueEnabledConfig(extraRpcApis))
|
||||
.webSocketConfiguration(node.createWebSocketEnabledConfig())
|
||||
.inProcessRpcConfiguration(node.createInProcessRpcConfiguration(extraRpcApis))
|
||||
.devMode(false)
|
||||
.jsonRpcTxPool()
|
||||
.engineRpcEnabled(isEngineRpcEnabled)
|
||||
.genesisConfigProvider(
|
||||
validators -> Optional.of(provideGenesisConfig(validators, cliqueOptions)))
|
||||
.extraCLIOptions(extraCliOptions)
|
||||
.metricsConfiguration(
|
||||
MetricsConfiguration.builder()
|
||||
.enabled(true)
|
||||
.port(0)
|
||||
.metricCategories(
|
||||
Set.of(PRICING_CONF, SEQUENCER_PROFITABILITY, TX_POOL_PROFITABILITY))
|
||||
.build())
|
||||
.requestedPlugins(
|
||||
List.of(
|
||||
"LineaExtraDataPlugin",
|
||||
"LineaEstimateGasEndpointPlugin",
|
||||
"LineaSetExtraDataEndpointPlugin",
|
||||
"LineaTransactionPoolValidatorPlugin",
|
||||
"LineaTransactionSelectorPlugin",
|
||||
"LineaBundleEndpointsPlugin",
|
||||
"ForwardBundlesPlugin"));
|
||||
|
||||
return besu.create(nodeConfBuilder.build());
|
||||
}
|
||||
|
||||
protected String provideGenesisConfig(
|
||||
final Collection<? extends RunnableNode> validators, final CliqueOptions cliqueOptions) {
|
||||
final var genesis =
|
||||
GenesisConfigurationFactory.createCliqueGenesisConfig(validators, cliqueOptions).get();
|
||||
|
||||
return maybeCustomGenesisExtraData()
|
||||
.map(ed -> setGenesisCustomExtraData(genesis, ed))
|
||||
.orElse(genesis);
|
||||
}
|
||||
|
||||
protected String setGenesisCustomExtraData(final String genesis, final Bytes32 customExtraData) {
|
||||
final var om = new ObjectMapper();
|
||||
final ObjectNode root;
|
||||
try {
|
||||
root = (ObjectNode) om.readTree(genesis);
|
||||
} catch (JsonProcessingException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
final var existingExtraData = Bytes.fromHexString(root.get("extraData").asText());
|
||||
final var updatedExtraData = Bytes.concatenate(customExtraData, existingExtraData.slice(32));
|
||||
root.put("extraData", updatedExtraData.toHexString());
|
||||
return root.toPrettyString();
|
||||
}
|
||||
|
||||
protected void sendTransactionsWithGivenLengthPayload(
|
||||
final SimpleStorage simpleStorage,
|
||||
final List<String> accounts,
|
||||
final Web3j web3j,
|
||||
final int num) {
|
||||
final String contractAddress = simpleStorage.getContractAddress();
|
||||
final String txData =
|
||||
simpleStorage.set(RandomStringUtils.secure().nextAlphabetic(num)).encodeFunctionCall();
|
||||
final List<String> hashes = new ArrayList<>();
|
||||
accounts.forEach(
|
||||
a -> {
|
||||
final Credentials credentials = Credentials.create(a);
|
||||
TransactionManager txManager = new RawTransactionManager(web3j, credentials, CHAIN_ID);
|
||||
for (int i = 0; i < 5; i++) {
|
||||
try {
|
||||
hashes.add(
|
||||
txManager
|
||||
.sendTransaction(
|
||||
DefaultGasProvider.GAS_PRICE,
|
||||
DefaultGasProvider.GAS_LIMIT,
|
||||
contractAddress,
|
||||
txData,
|
||||
BigInteger.ZERO)
|
||||
.getTransactionHash());
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
assertTransactionsInCorrectBlocks(web3j, hashes, num);
|
||||
}
|
||||
|
||||
private void assertTransactionsInCorrectBlocks(Web3j web3j, List<String> hashes, int num) {
|
||||
final HashMap<Long, Integer> txMap = new HashMap<>();
|
||||
TransactionReceiptProcessor receiptProcessor = createReceiptProcessor(web3j);
|
||||
|
||||
// CallData for the transaction for empty String is 68 and grows in steps of 32 with (String
|
||||
// size / 32)
|
||||
final int maxTxs = MAX_CALLDATA_SIZE / (68 + ((num + 31) / 32) * 32);
|
||||
|
||||
// Wait for transaction to be mined and check that there are no more than maxTxs per block
|
||||
hashes.forEach(
|
||||
h -> {
|
||||
final TransactionReceipt transactionReceipt;
|
||||
try {
|
||||
transactionReceipt = receiptProcessor.waitForTransactionReceipt(h);
|
||||
} catch (IOException | TransactionException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
||||
final long blockNumber = transactionReceipt.getBlockNumber().longValue();
|
||||
txMap.compute(blockNumber, (b, n) -> n == null ? 1 : n + 1);
|
||||
|
||||
// make sure that no block contained more than maxTxs
|
||||
assertThat(txMap.get(blockNumber)).isLessThanOrEqualTo(maxTxs);
|
||||
});
|
||||
// make sure that at least one block has maxTxs
|
||||
assertThat(txMap).containsValue(maxTxs);
|
||||
}
|
||||
|
||||
protected SimpleStorage deploySimpleStorage() throws Exception {
|
||||
final Web3j web3j = minerNode.nodeRequests().eth();
|
||||
final Credentials credentials = Credentials.create(Accounts.GENESIS_ACCOUNT_ONE_PRIVATE_KEY);
|
||||
TransactionManager txManager =
|
||||
new RawTransactionManager(web3j, credentials, CHAIN_ID, createReceiptProcessor(web3j));
|
||||
|
||||
final RemoteCall<SimpleStorage> deploy =
|
||||
SimpleStorage.deploy(web3j, txManager, new DefaultGasProvider());
|
||||
return deploy.send();
|
||||
}
|
||||
|
||||
protected DummyAdder deployDummyAdder() throws Exception {
|
||||
final Web3j web3j = minerNode.nodeRequests().eth();
|
||||
final Credentials credentials = Credentials.create(Accounts.GENESIS_ACCOUNT_ONE_PRIVATE_KEY);
|
||||
TransactionManager txManager =
|
||||
new RawTransactionManager(web3j, credentials, CHAIN_ID, createReceiptProcessor(web3j));
|
||||
|
||||
final RemoteCall<DummyAdder> deploy =
|
||||
DummyAdder.deploy(web3j, txManager, new DefaultGasProvider());
|
||||
return deploy.send();
|
||||
}
|
||||
|
||||
protected MulmodExecutor deployMulmodExecutor() throws Exception {
|
||||
final Web3j web3j = minerNode.nodeRequests().eth();
|
||||
final Credentials credentials = Credentials.create(Accounts.GENESIS_ACCOUNT_ONE_PRIVATE_KEY);
|
||||
TransactionManager txManager =
|
||||
new RawTransactionManager(web3j, credentials, CHAIN_ID, createReceiptProcessor(web3j));
|
||||
|
||||
final RemoteCall<MulmodExecutor> deploy =
|
||||
MulmodExecutor.deploy(web3j, txManager, new DefaultGasProvider());
|
||||
return deploy.send();
|
||||
}
|
||||
|
||||
protected ModExp deployModExp() throws Exception {
|
||||
final Web3j web3j = minerNode.nodeRequests().eth();
|
||||
final Credentials credentials = Credentials.create(Accounts.GENESIS_ACCOUNT_ONE_PRIVATE_KEY);
|
||||
TransactionManager txManager =
|
||||
new RawTransactionManager(web3j, credentials, CHAIN_ID, createReceiptProcessor(web3j));
|
||||
|
||||
final RemoteCall<ModExp> deploy = ModExp.deploy(web3j, txManager, new DefaultGasProvider());
|
||||
return deploy.send();
|
||||
}
|
||||
|
||||
protected EcPairing deployEcPairing() throws Exception {
|
||||
final Web3j web3j = minerNode.nodeRequests().eth();
|
||||
final Credentials credentials = Credentials.create(Accounts.GENESIS_ACCOUNT_ONE_PRIVATE_KEY);
|
||||
TransactionManager txManager =
|
||||
new RawTransactionManager(web3j, credentials, CHAIN_ID, createReceiptProcessor(web3j));
|
||||
|
||||
final RemoteCall<EcPairing> deploy =
|
||||
EcPairing.deploy(web3j, txManager, new DefaultGasProvider());
|
||||
return deploy.send();
|
||||
}
|
||||
|
||||
protected EcAdd deployEcAdd() throws Exception {
|
||||
final Web3j web3j = minerNode.nodeRequests().eth();
|
||||
final Credentials credentials = Credentials.create(Accounts.GENESIS_ACCOUNT_ONE_PRIVATE_KEY);
|
||||
TransactionManager txManager =
|
||||
new RawTransactionManager(web3j, credentials, CHAIN_ID, createReceiptProcessor(web3j));
|
||||
|
||||
final RemoteCall<EcAdd> deploy = EcAdd.deploy(web3j, txManager, new DefaultGasProvider());
|
||||
return deploy.send();
|
||||
}
|
||||
|
||||
protected EcMul deployEcMul() throws Exception {
|
||||
final Web3j web3j = minerNode.nodeRequests().eth();
|
||||
final Credentials credentials = Credentials.create(Accounts.GENESIS_ACCOUNT_ONE_PRIVATE_KEY);
|
||||
TransactionManager txManager =
|
||||
new RawTransactionManager(web3j, credentials, CHAIN_ID, createReceiptProcessor(web3j));
|
||||
|
||||
final RemoteCall<EcMul> deploy = EcMul.deploy(web3j, txManager, new DefaultGasProvider());
|
||||
return deploy.send();
|
||||
}
|
||||
|
||||
protected EcRecover deployEcRecover() throws Exception {
|
||||
final Web3j web3j = minerNode.nodeRequests().eth();
|
||||
final Credentials credentials = Credentials.create(Accounts.GENESIS_ACCOUNT_ONE_PRIVATE_KEY);
|
||||
TransactionManager txManager =
|
||||
new RawTransactionManager(web3j, credentials, CHAIN_ID, createReceiptProcessor(web3j));
|
||||
|
||||
final RemoteCall<EcRecover> deploy =
|
||||
EcRecover.deploy(web3j, txManager, new DefaultGasProvider());
|
||||
return deploy.send();
|
||||
}
|
||||
|
||||
protected RevertExample deployRevertExample() throws Exception {
|
||||
final Web3j web3j = minerNode.nodeRequests().eth();
|
||||
final Credentials credentials = Credentials.create(Accounts.GENESIS_ACCOUNT_ONE_PRIVATE_KEY);
|
||||
TransactionManager txManager =
|
||||
new RawTransactionManager(web3j, credentials, CHAIN_ID, createReceiptProcessor(web3j));
|
||||
|
||||
final RemoteCall<RevertExample> deploy =
|
||||
RevertExample.deploy(web3j, txManager, new DefaultGasProvider());
|
||||
return deploy.send();
|
||||
}
|
||||
|
||||
protected AcceptanceTestToken deployAcceptanceTestToken() throws Exception {
|
||||
final Web3j web3j = minerNode.nodeRequests().eth();
|
||||
// 1000 AT tokens will be assigned to this account on deploy
|
||||
final Credentials credentials = accounts.getPrimaryBenefactor().web3jCredentialsOrThrow();
|
||||
TransactionManager txManager =
|
||||
new RawTransactionManager(web3j, credentials, CHAIN_ID, createReceiptProcessor(web3j));
|
||||
|
||||
final RemoteCall<AcceptanceTestToken> deploy =
|
||||
AcceptanceTestToken.deploy(web3j, txManager, new DefaultGasProvider());
|
||||
final var contract = deploy.send();
|
||||
final var balance = contract.balanceOf(accounts.getPrimaryBenefactor().getAddress()).send();
|
||||
assertThat(balance).isEqualTo(1000);
|
||||
return contract;
|
||||
}
|
||||
|
||||
protected ExcludedPrecompiles deployExcludedPrecompiles() throws Exception {
|
||||
final Web3j web3j = minerNode.nodeRequests().eth();
|
||||
final Credentials credentials = Credentials.create(Accounts.GENESIS_ACCOUNT_ONE_PRIVATE_KEY);
|
||||
TransactionManager txManager =
|
||||
new RawTransactionManager(web3j, credentials, CHAIN_ID, createReceiptProcessor(web3j));
|
||||
|
||||
final RemoteCall<ExcludedPrecompiles> deploy =
|
||||
ExcludedPrecompiles.deploy(web3j, txManager, new DefaultGasProvider());
|
||||
return deploy.send();
|
||||
}
|
||||
|
||||
public static String getResourcePath(String resource) {
|
||||
return Objects.requireNonNull(LineaPluginTestBase.class.getResource(resource)).getPath();
|
||||
}
|
||||
|
||||
protected void assertTransactionsMinedInSeparateBlocks(Web3j web3j, List<String> hashes)
|
||||
throws Exception {
|
||||
TransactionReceiptProcessor receiptProcessor = createReceiptProcessor(web3j);
|
||||
|
||||
final HashSet<Long> blockNumbers = new HashSet<>();
|
||||
for (String hash : hashes) {
|
||||
TransactionReceipt receipt = receiptProcessor.waitForTransactionReceipt(hash);
|
||||
assertThat(receipt).isNotNull();
|
||||
boolean isAdded = blockNumbers.add(receipt.getBlockNumber().longValue());
|
||||
assertThat(isAdded).isEqualTo(true);
|
||||
}
|
||||
}
|
||||
|
||||
protected void assertTransactionsMinedInSameBlock(Web3j web3j, List<String> hashes) {
|
||||
TransactionReceiptProcessor receiptProcessor = createReceiptProcessor(web3j);
|
||||
Set<Long> blockNumbers =
|
||||
hashes.stream()
|
||||
.map(
|
||||
hash -> {
|
||||
try {
|
||||
TransactionReceipt receipt = receiptProcessor.waitForTransactionReceipt(hash);
|
||||
assertThat(receipt).isNotNull();
|
||||
return receipt.getBlockNumber().longValue();
|
||||
} catch (IOException | TransactionException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
})
|
||||
.collect(Collectors.toSet());
|
||||
|
||||
assertThat(blockNumbers.size()).isEqualTo(1);
|
||||
}
|
||||
|
||||
protected void assertTransactionNotInThePool(String hash) {
|
||||
minerNode.verify(
|
||||
new TxPoolConditions(new TxPoolTransactions())
|
||||
.notInTransactionPool(Hash.fromHexString(hash)));
|
||||
}
|
||||
|
||||
protected List<Map<String, String>> getTxPoolContent() {
|
||||
return minerNode.execute(new TxPoolTransactions().getTxPoolContents());
|
||||
}
|
||||
|
||||
private TransactionReceiptProcessor createReceiptProcessor(Web3j web3j) {
|
||||
return new PollingTransactionReceiptProcessor(
|
||||
web3j,
|
||||
Math.max(1000, DEFAULT_LINEA_CLIQUE_OPTIONS.blockPeriodSeconds() * 1000 / 5),
|
||||
DEFAULT_LINEA_CLIQUE_OPTIONS.blockPeriodSeconds() * 3);
|
||||
}
|
||||
|
||||
protected String sendTransactionWithGivenLengthPayload(
|
||||
final String account, final Web3j web3j, final int num) throws IOException {
|
||||
String to = Address.fromHexString("fe3b557e8fb62b89f4916b721be55ceb828dbd73").toString();
|
||||
TransactionManager txManager = new RawTransactionManager(web3j, Credentials.create(account));
|
||||
|
||||
return txManager
|
||||
.sendTransaction(
|
||||
DefaultGasProvider.GAS_PRICE,
|
||||
BigInteger.valueOf(MAX_TX_GAS_LIMIT),
|
||||
to,
|
||||
RandomStringUtils.secure().nextAlphabetic(num),
|
||||
BigInteger.ZERO)
|
||||
.getTransactionHash();
|
||||
}
|
||||
|
||||
protected Bytes32 createExtraDataPricingField(
|
||||
final long fixedCostKWei, final long variableCostKWei, final long minGasPriceKWei) {
|
||||
final UInt32 fixed = UInt32.valueOf(BigInteger.valueOf(fixedCostKWei));
|
||||
final UInt32 variable = UInt32.valueOf(BigInteger.valueOf(variableCostKWei));
|
||||
final UInt32 min = UInt32.valueOf(BigInteger.valueOf(minGasPriceKWei));
|
||||
|
||||
return Bytes32.rightPad(
|
||||
Bytes.concatenate(Bytes.of((byte) 1), fixed.toBytes(), variable.toBytes(), min.toBytes()));
|
||||
}
|
||||
|
||||
protected double getMetricValue(
|
||||
final MetricCategory category,
|
||||
final String metricName,
|
||||
final List<Map.Entry<String, String>> labelValues)
|
||||
throws IOException, InterruptedException {
|
||||
|
||||
final var metricsReq =
|
||||
HttpRequest.newBuilder().GET().uri(URI.create(minerNode.metricsHttpUrl().get())).build();
|
||||
|
||||
final var respLines = HTTP_CLIENT.send(metricsReq, HttpResponse.BodyHandlers.ofLines());
|
||||
|
||||
final var searchString =
|
||||
category.getApplicationPrefix().orElse("")
|
||||
+ category.getName()
|
||||
+ "_"
|
||||
+ metricName
|
||||
+ labelValues.stream()
|
||||
.map(lv -> lv.getKey() + "=\"" + lv.getValue() + "\"")
|
||||
.collect(Collectors.joining(",", "{", "}"));
|
||||
|
||||
final var foundMetric =
|
||||
respLines.body().filter(line -> line.startsWith(searchString)).findFirst();
|
||||
|
||||
return foundMetric
|
||||
.map(line -> line.substring(searchString.length()).trim())
|
||||
.map(Double::valueOf)
|
||||
.orElse(Double.NaN);
|
||||
}
|
||||
|
||||
protected String getLog() {
|
||||
return MemoryAppender.getLog();
|
||||
}
|
||||
|
||||
protected String getAndResetLog() {
|
||||
final var log = MemoryAppender.getLog();
|
||||
MemoryAppender.reset();
|
||||
return log;
|
||||
}
|
||||
|
||||
protected byte[] encodedCallModExp(
|
||||
final ModExp modExp, final Account sender, final int nonce, final Bytes input) {
|
||||
final var modExpCalldata = modExp.callModExp(input.toArrayUnsafe()).encodeFunctionCall();
|
||||
|
||||
final var modExpCall =
|
||||
RawTransaction.createTransaction(
|
||||
CHAIN_ID,
|
||||
BigInteger.valueOf(nonce),
|
||||
DefaultGasProvider.GAS_LIMIT,
|
||||
modExp.getContractAddress(),
|
||||
BigInteger.ZERO,
|
||||
modExpCalldata,
|
||||
DefaultGasProvider.GAS_PRICE,
|
||||
DefaultGasProvider.GAS_PRICE.multiply(BigInteger.TEN).add(BigInteger.ONE));
|
||||
|
||||
return TransactionEncoder.signMessage(modExpCall, sender.web3jCredentialsOrThrow());
|
||||
}
|
||||
|
||||
protected byte[] encodedCallEcPairing(
|
||||
final EcPairing ecPairing, final Account sender, final int nonce, final Bytes input) {
|
||||
final var ecPairingCalldata =
|
||||
ecPairing.callEcPairing(input.toArrayUnsafe()).encodeFunctionCall();
|
||||
|
||||
final var ecPairingCall =
|
||||
RawTransaction.createTransaction(
|
||||
CHAIN_ID,
|
||||
BigInteger.valueOf(nonce),
|
||||
DefaultGasProvider.GAS_LIMIT,
|
||||
ecPairing.getContractAddress(),
|
||||
BigInteger.ZERO,
|
||||
ecPairingCalldata,
|
||||
DefaultGasProvider.GAS_PRICE,
|
||||
DefaultGasProvider.GAS_PRICE.multiply(BigInteger.TEN).add(BigInteger.ONE));
|
||||
|
||||
return TransactionEncoder.signMessage(ecPairingCall, sender.web3jCredentialsOrThrow());
|
||||
}
|
||||
|
||||
protected byte[] encodedCallEcAdd(
|
||||
final EcAdd ecAdd, final Account sender, final int nonce, final Bytes input) {
|
||||
final var ecAddCalldata = ecAdd.callEcAdd(input.toArrayUnsafe()).encodeFunctionCall();
|
||||
|
||||
final var ecAddCall =
|
||||
RawTransaction.createTransaction(
|
||||
CHAIN_ID,
|
||||
BigInteger.valueOf(nonce),
|
||||
DefaultGasProvider.GAS_LIMIT,
|
||||
ecAdd.getContractAddress(),
|
||||
BigInteger.ZERO,
|
||||
ecAddCalldata,
|
||||
DefaultGasProvider.GAS_PRICE,
|
||||
DefaultGasProvider.GAS_PRICE.multiply(BigInteger.TEN).add(BigInteger.ONE));
|
||||
|
||||
return TransactionEncoder.signMessage(ecAddCall, sender.web3jCredentialsOrThrow());
|
||||
}
|
||||
|
||||
protected byte[] encodedCallEcMul(
|
||||
final EcMul ecMul, final Account sender, final int nonce, final Bytes input) {
|
||||
final var ecMulCalldata = ecMul.callEcMul(input.toArrayUnsafe()).encodeFunctionCall();
|
||||
|
||||
final var ecMulCall =
|
||||
RawTransaction.createTransaction(
|
||||
CHAIN_ID,
|
||||
BigInteger.valueOf(nonce),
|
||||
DefaultGasProvider.GAS_LIMIT,
|
||||
ecMul.getContractAddress(),
|
||||
BigInteger.ZERO,
|
||||
ecMulCalldata,
|
||||
DefaultGasProvider.GAS_PRICE,
|
||||
DefaultGasProvider.GAS_PRICE.multiply(BigInteger.TEN).add(BigInteger.ONE));
|
||||
|
||||
return TransactionEncoder.signMessage(ecMulCall, sender.web3jCredentialsOrThrow());
|
||||
}
|
||||
|
||||
protected byte[] encodedCallEcRecover(
|
||||
final EcRecover ecRecover, final Account sender, final int nonce, final Bytes input) {
|
||||
final var ecRecoverCalldata =
|
||||
ecRecover.callEcRecover(input.toArrayUnsafe()).encodeFunctionCall();
|
||||
|
||||
final var ecRecoverCall =
|
||||
RawTransaction.createTransaction(
|
||||
CHAIN_ID,
|
||||
BigInteger.valueOf(nonce),
|
||||
DefaultGasProvider.GAS_LIMIT,
|
||||
ecRecover.getContractAddress(),
|
||||
BigInteger.ZERO,
|
||||
ecRecoverCalldata,
|
||||
DefaultGasProvider.GAS_PRICE,
|
||||
DefaultGasProvider.GAS_PRICE.multiply(BigInteger.TEN).add(BigInteger.ONE));
|
||||
|
||||
return TransactionEncoder.signMessage(ecRecoverCall, sender.web3jCredentialsOrThrow());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,104 @@
|
||||
/*
|
||||
* Copyright Consensys Software Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
|
||||
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations under the License.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package linea.plugin.acc.test;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.hyperledger.besu.consensus.clique.CliqueExtraData;
|
||||
import org.hyperledger.besu.datatypes.Address;
|
||||
import org.hyperledger.besu.ethereum.eth.transactions.ImmutableTransactionPoolConfiguration;
|
||||
import org.hyperledger.besu.ethereum.eth.transactions.TransactionPoolConfiguration;
|
||||
import org.hyperledger.besu.tests.acceptance.dsl.EngineAPIService;
|
||||
import org.hyperledger.besu.tests.acceptance.dsl.node.RunnableNode;
|
||||
import org.hyperledger.besu.tests.acceptance.dsl.node.configuration.genesis.GenesisConfigurationFactory;
|
||||
import org.hyperledger.besu.tests.acceptance.dsl.node.configuration.genesis.GenesisConfigurationFactory.CliqueOptions;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
|
||||
// This file initializes a Besu node configured for the Prague fork and makes it available to
|
||||
// acceptance tests.
|
||||
@Slf4j
|
||||
public abstract class LineaPluginTestBasePrague extends LineaPluginTestBase {
|
||||
private EngineAPIService engineApiService;
|
||||
private ObjectMapper mapper;
|
||||
private final String GENESIS_FILE_TEMPLATE_PATH = "/clique/clique-prague.json.tpl";
|
||||
|
||||
@BeforeEach
|
||||
@Override
|
||||
public void setup() throws Exception {
|
||||
minerNode =
|
||||
createCliqueNodeWithExtraCliOptionsAndRpcApis(
|
||||
"miner1", getCliqueOptions(), getTestCliOptions(), Set.of("LINEA", "MINER"), true);
|
||||
minerNode.setTransactionPoolConfiguration(
|
||||
ImmutableTransactionPoolConfiguration.builder()
|
||||
.from(TransactionPoolConfiguration.DEFAULT)
|
||||
.noLocalPriority(true)
|
||||
.build());
|
||||
cluster.start(minerNode);
|
||||
mapper = new ObjectMapper();
|
||||
this.engineApiService = new EngineAPIService(minerNode, ethTransactions, mapper);
|
||||
}
|
||||
|
||||
// Ideally GenesisConfigurationFactory.createCliqueGenesisConfig would support a custom genesis
|
||||
// file
|
||||
// path. We have resorted to inlining its logic here to allow a flexible genesis file path.
|
||||
@Override
|
||||
protected String provideGenesisConfig(
|
||||
final Collection<? extends RunnableNode> validators, final CliqueOptions cliqueOptions) {
|
||||
// Target state
|
||||
final String genesisTemplate =
|
||||
GenesisConfigurationFactory.readGenesisFile(GENESIS_FILE_TEMPLATE_PATH);
|
||||
final String hydratedGenesisTemplate =
|
||||
genesisTemplate
|
||||
.replace("%blockperiodseconds%", String.valueOf(cliqueOptions.blockPeriodSeconds()))
|
||||
.replace("%epochlength%", String.valueOf(cliqueOptions.epochLength()))
|
||||
.replace("%createemptyblocks%", String.valueOf(cliqueOptions.createEmptyBlocks()));
|
||||
|
||||
final List<Address> addresses =
|
||||
validators.stream().map(RunnableNode::getAddress).collect(Collectors.toList());
|
||||
final String extraDataString = CliqueExtraData.createGenesisExtraDataString(addresses);
|
||||
final String genesis = hydratedGenesisTemplate.replaceAll("%extraData%", extraDataString);
|
||||
|
||||
return maybeCustomGenesisExtraData()
|
||||
.map(ed -> setGenesisCustomExtraData(genesis, ed))
|
||||
.orElse(genesis);
|
||||
}
|
||||
|
||||
// No-arg override for simple test cases, we take sensible defaults from the genesis config
|
||||
protected void buildNewBlock() throws IOException, InterruptedException {
|
||||
var latestTimestamp = this.minerNode.execute(ethTransactions.block()).getTimestamp();
|
||||
var genesisConfigSerialized = this.minerNode.getGenesisConfig().get();
|
||||
JsonNode genesisConfig = mapper.readTree(genesisConfigSerialized);
|
||||
long defaultSlotTimeSeconds =
|
||||
genesisConfig.path("config").path("clique").path("blockperiodseconds").asLong();
|
||||
this.engineApiService.buildNewBlock(
|
||||
latestTimestamp.longValue() + defaultSlotTimeSeconds, defaultSlotTimeSeconds * 1000);
|
||||
}
|
||||
|
||||
// @param blockTimestampSeconds The Unix timestamp (in seconds) to assign to the new block.
|
||||
// @param blockBuildingTimeMs The duration (in milliseconds) allocated for the Besu node to
|
||||
// build the block.
|
||||
protected void buildNewBlock(long blockTimestampSeconds, long blockBuildingTimeMs)
|
||||
throws IOException, InterruptedException {
|
||||
this.engineApiService.buildNewBlock(blockTimestampSeconds, blockBuildingTimeMs);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,101 @@
|
||||
/*
|
||||
* Copyright Consensys Software Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
|
||||
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations under the License.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
package linea.plugin.acc.test;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.util.List;
|
||||
|
||||
import org.bouncycastle.crypto.digests.KeccakDigest;
|
||||
import org.hyperledger.besu.datatypes.Wei;
|
||||
import org.hyperledger.besu.tests.acceptance.dsl.account.Account;
|
||||
import org.hyperledger.besu.tests.acceptance.dsl.account.Accounts;
|
||||
import org.hyperledger.besu.tests.acceptance.dsl.transaction.account.TransferTransaction;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.web3j.crypto.Credentials;
|
||||
import org.web3j.protocol.Web3j;
|
||||
import org.web3j.tx.RawTransactionManager;
|
||||
import org.web3j.tx.TransactionManager;
|
||||
|
||||
public class ProfitableTransactionTest extends LineaPluginTestBase {
|
||||
private static final double MIN_MARGIN = 1.5;
|
||||
private static final Wei MIN_GAS_PRICE = Wei.of(1_000_000_000);
|
||||
|
||||
@Override
|
||||
public List<String> getTestCliOptions() {
|
||||
return new TestCommandLineOptionsBuilder()
|
||||
.set("--plugin-linea-min-margin=", String.valueOf(MIN_MARGIN))
|
||||
.build();
|
||||
}
|
||||
|
||||
@BeforeEach
|
||||
public void setMinGasPrice() {
|
||||
minerNode.getMiningParameters().setMinTransactionGasPrice(MIN_GAS_PRICE);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void transactionIsNotMinedWhenUnprofitable() throws Exception {
|
||||
|
||||
final Web3j web3j = minerNode.nodeRequests().eth();
|
||||
final Credentials credentials = Credentials.create(Accounts.GENESIS_ACCOUNT_ONE_PRIVATE_KEY);
|
||||
final TransactionManager txManager = new RawTransactionManager(web3j, credentials, CHAIN_ID);
|
||||
|
||||
final KeccakDigest keccakDigest = new KeccakDigest(256);
|
||||
final StringBuilder txData = new StringBuilder();
|
||||
txData.append("0x");
|
||||
for (int i = 0; i < 10; i++) {
|
||||
keccakDigest.update(new byte[] {(byte) i}, 0, 1);
|
||||
final byte[] out = new byte[32];
|
||||
keccakDigest.doFinal(out, 0);
|
||||
txData.append(new BigInteger(out));
|
||||
}
|
||||
|
||||
final var txUnprofitable =
|
||||
txManager.sendTransaction(
|
||||
MIN_GAS_PRICE.getAsBigInteger().divide(BigInteger.valueOf(100)),
|
||||
BigInteger.valueOf(MAX_TX_GAS_LIMIT / 2),
|
||||
credentials.getAddress(),
|
||||
txData.toString(),
|
||||
BigInteger.ZERO);
|
||||
|
||||
final Account sender = accounts.getSecondaryBenefactor();
|
||||
final Account recipient = accounts.createAccount("recipient");
|
||||
final TransferTransaction transferTx = accountTransactions.createTransfer(sender, recipient, 1);
|
||||
final var txHash = minerNode.execute(transferTx);
|
||||
|
||||
minerNode.verify(eth.expectSuccessfulTransactionReceipt(txHash.toHexString()));
|
||||
|
||||
// assert that tx below margin is not confirmed
|
||||
minerNode.verify(eth.expectNoTransactionReceipt(txUnprofitable.getTransactionHash()));
|
||||
}
|
||||
|
||||
/**
|
||||
* if we have a list of transactions [t_small, t_tooBig, t_small, ..., t_small] where t_tooBig is
|
||||
* too big to fit in a block, we have blocks created that contain all t_small transactions.
|
||||
*
|
||||
* @throws Exception if send transaction fails
|
||||
*/
|
||||
@Test
|
||||
public void transactionIsMinedWhenProfitable() {
|
||||
minerNode.getMiningParameters().setMinTransactionGasPrice(MIN_GAS_PRICE);
|
||||
final Account sender = accounts.getSecondaryBenefactor();
|
||||
final Account recipient = accounts.createAccount("recipient");
|
||||
|
||||
final TransferTransaction transferTx = accountTransactions.createTransfer(sender, recipient, 1);
|
||||
final var txHash = minerNode.execute(transferTx);
|
||||
|
||||
minerNode.verify(eth.expectSuccessfulTransactionReceipt(txHash.toHexString()));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
* Copyright Consensys Software Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
|
||||
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations under the License.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
package linea.plugin.acc.test;
|
||||
|
||||
import static linea.plugin.acc.test.LineaPluginTestBase.getResourcePath;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Properties;
|
||||
|
||||
import org.web3j.tx.gas.DefaultGasProvider;
|
||||
|
||||
/** This class is used to build a list of command line options for testing. */
|
||||
public class TestCommandLineOptionsBuilder {
|
||||
private final Properties cliOptions = new Properties();
|
||||
|
||||
private static final String MAX_VALUE = String.valueOf(Integer.MAX_VALUE);
|
||||
|
||||
public TestCommandLineOptionsBuilder() {
|
||||
cliOptions.setProperty("--plugin-linea-max-tx-calldata-size=", MAX_VALUE);
|
||||
cliOptions.setProperty("--plugin-linea-max-block-calldata-size=", MAX_VALUE);
|
||||
cliOptions.setProperty(
|
||||
"--plugin-linea-max-tx-gas-limit=", DefaultGasProvider.GAS_LIMIT.toString());
|
||||
cliOptions.setProperty("--plugin-linea-deny-list-path=", getResourcePath("/emptyDenyList.txt"));
|
||||
cliOptions.setProperty(
|
||||
"--plugin-linea-module-limit-file-path=", getResourcePath("/noModuleLimits.toml"));
|
||||
cliOptions.setProperty("--plugin-linea-max-block-gas=", MAX_VALUE);
|
||||
cliOptions.setProperty(
|
||||
"--plugin-linea-l1l2-bridge-contract=", "0x00000000000000000000000000000000DEADBEEF");
|
||||
cliOptions.setProperty("--plugin-linea-l1l2-bridge-topic=", "0x123456");
|
||||
}
|
||||
|
||||
public TestCommandLineOptionsBuilder set(String option, String value) {
|
||||
cliOptions.setProperty(option, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
public List<String> build() {
|
||||
List<String> optionsList = new ArrayList<>(cliOptions.size());
|
||||
for (String key : cliOptions.stringPropertyNames()) {
|
||||
optionsList.add(key + cliOptions.getProperty(key));
|
||||
}
|
||||
return optionsList;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,139 @@
|
||||
/*
|
||||
* Copyright Consensys Software Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
|
||||
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations under the License.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
package linea.plugin.acc.test;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import linea.plugin.acc.test.tests.web3j.generated.SimpleStorage;
|
||||
import org.hyperledger.besu.datatypes.Hash;
|
||||
import org.hyperledger.besu.tests.acceptance.dsl.account.Account;
|
||||
import org.hyperledger.besu.tests.acceptance.dsl.account.Accounts;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.web3j.crypto.Credentials;
|
||||
import org.web3j.protocol.Web3j;
|
||||
import org.web3j.tx.RawTransactionManager;
|
||||
import org.web3j.tx.TransactionManager;
|
||||
import org.web3j.tx.gas.DefaultGasProvider;
|
||||
|
||||
public class TransactionCallDataSizeLimitTest extends LineaPluginTestBase {
|
||||
|
||||
public static final int MAX_CALLDATA_SIZE = 1188; // contract has a call data size of 1160
|
||||
private static final BigInteger GAS_PRICE = DefaultGasProvider.GAS_PRICE;
|
||||
private static final BigInteger GAS_LIMIT = DefaultGasProvider.GAS_LIMIT;
|
||||
private static final BigInteger VALUE = BigInteger.ZERO;
|
||||
|
||||
@Override
|
||||
public List<String> getTestCliOptions() {
|
||||
return new TestCommandLineOptionsBuilder()
|
||||
.set("--plugin-linea-max-tx-calldata-size=", String.valueOf(MAX_CALLDATA_SIZE))
|
||||
.set("--plugin-linea-max-block-calldata-size=", String.valueOf(MAX_CALLDATA_SIZE))
|
||||
.build();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldMineTransactions() throws Exception {
|
||||
final SimpleStorage simpleStorage = deploySimpleStorage();
|
||||
|
||||
List<String> accounts =
|
||||
List.of(Accounts.GENESIS_ACCOUNT_ONE_PRIVATE_KEY, Accounts.GENESIS_ACCOUNT_TWO_PRIVATE_KEY);
|
||||
|
||||
final Web3j web3j = minerNode.nodeRequests().eth();
|
||||
final List<Integer> numCharactersInStringList = List.of(150, 200, 400);
|
||||
|
||||
numCharactersInStringList.forEach(
|
||||
num -> sendTransactionsWithGivenLengthPayload(simpleStorage, accounts, web3j, num));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void transactionIsMinedWhenNotTooBig() throws Exception {
|
||||
final SimpleStorage simpleStorage = deploySimpleStorage();
|
||||
final Web3j web3j = minerNode.nodeRequests().eth();
|
||||
final String contractAddress = simpleStorage.getContractAddress();
|
||||
final Credentials credentials = Credentials.create(Accounts.GENESIS_ACCOUNT_ONE_PRIVATE_KEY);
|
||||
TransactionManager txManager = new RawTransactionManager(web3j, credentials, CHAIN_ID);
|
||||
|
||||
final String txDataGood = simpleStorage.set("a".repeat(1200 - 80)).encodeFunctionCall();
|
||||
final String hashGood =
|
||||
txManager
|
||||
.sendTransaction(GAS_PRICE, GAS_LIMIT, contractAddress, txDataGood, VALUE)
|
||||
.getTransactionHash();
|
||||
|
||||
// make sure that a transaction that is not too big was mined
|
||||
minerNode.verify(eth.expectSuccessfulTransactionReceipt(hashGood));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void transactionIsNotMinedWhenTooBig() throws Exception {
|
||||
final SimpleStorage simpleStorage = deploySimpleStorage();
|
||||
final Web3j web3j = minerNode.nodeRequests().eth();
|
||||
final String contractAddress = simpleStorage.getContractAddress();
|
||||
final Credentials credentials = Credentials.create(Accounts.GENESIS_ACCOUNT_ONE_PRIVATE_KEY);
|
||||
TransactionManager txManager = new RawTransactionManager(web3j, credentials, CHAIN_ID);
|
||||
|
||||
final String txDataTooBig = simpleStorage.set("a".repeat(1200 - 79)).encodeFunctionCall();
|
||||
final var txTooBigResp =
|
||||
txManager.sendTransaction(GAS_PRICE, GAS_LIMIT, contractAddress, txDataTooBig, VALUE);
|
||||
|
||||
assertThat(txTooBigResp.hasError()).isTrue();
|
||||
assertThat(txTooBigResp.getError().getMessage())
|
||||
.isEqualTo("Calldata of transaction is greater than the allowed max of 1188");
|
||||
}
|
||||
|
||||
/**
|
||||
* if we have a list of transactions [t_small, t_tooBig, t_small, ..., t_small] where t_tooBig is
|
||||
* too big to fit in a block, we have blocks created that contain all t_small transactions.
|
||||
*
|
||||
* @throws Exception if send transaction fails
|
||||
*/
|
||||
@Test
|
||||
public void multipleSmallTxsMinedWhileTxTooBigNot() throws Exception {
|
||||
final SimpleStorage simpleStorage = deploySimpleStorage();
|
||||
|
||||
final Web3j web3j = minerNode.nodeRequests().eth();
|
||||
final String contractAddress = simpleStorage.getContractAddress();
|
||||
final Credentials credentials = Credentials.create(Accounts.GENESIS_ACCOUNT_ONE_PRIVATE_KEY);
|
||||
TransactionManager txManager = new RawTransactionManager(web3j, credentials, CHAIN_ID);
|
||||
|
||||
final Account smallCalldataSender = accounts.getSecondaryBenefactor();
|
||||
final Account recipient = accounts.createAccount("recipient");
|
||||
|
||||
final List<Hash> expectedConfirmedTxs = new ArrayList<>(4);
|
||||
|
||||
expectedConfirmedTxs.add(
|
||||
minerNode.execute(accountTransactions.createTransfer(smallCalldataSender, recipient, 1)));
|
||||
|
||||
final String txDataTooBig = simpleStorage.set("a".repeat(1200 - 79)).encodeFunctionCall();
|
||||
|
||||
final var txTooBigResp =
|
||||
txManager.sendTransaction(
|
||||
GAS_PRICE, BigInteger.valueOf(MAX_TX_GAS_LIMIT), contractAddress, txDataTooBig, VALUE);
|
||||
|
||||
expectedConfirmedTxs.addAll(
|
||||
minerNode.execute(
|
||||
accountTransactions.createIncrementalTransfers(smallCalldataSender, recipient, 3)));
|
||||
|
||||
assertThat(txTooBigResp.hasError()).isTrue();
|
||||
assertThat(txTooBigResp.getError().getMessage())
|
||||
.isEqualTo("Calldata of transaction is greater than the allowed max of 1188");
|
||||
|
||||
expectedConfirmedTxs.stream()
|
||||
.map(Hash::toHexString)
|
||||
.forEach(hash -> minerNode.verify(eth.expectSuccessfulTransactionReceipt(hash)));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,129 @@
|
||||
/*
|
||||
* Copyright Consensys Software Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
|
||||
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations under the License.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
package linea.plugin.acc.test;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import linea.plugin.acc.test.tests.web3j.generated.SimpleStorage;
|
||||
import org.hyperledger.besu.datatypes.Hash;
|
||||
import org.hyperledger.besu.tests.acceptance.dsl.account.Account;
|
||||
import org.hyperledger.besu.tests.acceptance.dsl.account.Accounts;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.web3j.crypto.Credentials;
|
||||
import org.web3j.protocol.Web3j;
|
||||
import org.web3j.tx.RawTransactionManager;
|
||||
import org.web3j.tx.TransactionManager;
|
||||
import org.web3j.tx.gas.DefaultGasProvider;
|
||||
|
||||
public class TransactionGasLimitTest extends LineaPluginTestBase {
|
||||
|
||||
public static final int MAX_TX_GAS_LIMIT = DefaultGasProvider.GAS_LIMIT.intValue();
|
||||
private static final BigInteger GAS_PRICE = DefaultGasProvider.GAS_PRICE;
|
||||
private static final BigInteger VALUE = BigInteger.ZERO;
|
||||
|
||||
@Override
|
||||
public List<String> getTestCliOptions() {
|
||||
return new TestCommandLineOptionsBuilder()
|
||||
.set("--plugin-linea-max-tx-gas-limit=", String.valueOf(MAX_TX_GAS_LIMIT))
|
||||
.build();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void transactionIsMinedWhenGasLimitIsNotExceeded() throws Exception {
|
||||
final SimpleStorage simpleStorage = deploySimpleStorage();
|
||||
|
||||
final Web3j web3j = minerNode.nodeRequests().eth();
|
||||
final String contractAddress = simpleStorage.getContractAddress();
|
||||
final Credentials credentials = Credentials.create(Accounts.GENESIS_ACCOUNT_ONE_PRIVATE_KEY);
|
||||
TransactionManager txManager = new RawTransactionManager(web3j, credentials, CHAIN_ID);
|
||||
|
||||
final String txData = simpleStorage.set("hello").encodeFunctionCall();
|
||||
|
||||
final String hashGood =
|
||||
txManager
|
||||
.sendTransaction(
|
||||
GAS_PRICE, BigInteger.valueOf(MAX_TX_GAS_LIMIT), contractAddress, txData, VALUE)
|
||||
.getTransactionHash();
|
||||
|
||||
// make sure that a transaction that is not too big was mined
|
||||
minerNode.verify(eth.expectSuccessfulTransactionReceipt(hashGood));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void transactionIsNotMinedWhenGasLimitIsExceeded() throws Exception {
|
||||
final SimpleStorage simpleStorage = deploySimpleStorage();
|
||||
|
||||
final Web3j web3j = minerNode.nodeRequests().eth();
|
||||
final String contractAddress = simpleStorage.getContractAddress();
|
||||
final Credentials credentials = Credentials.create(Accounts.GENESIS_ACCOUNT_ONE_PRIVATE_KEY);
|
||||
TransactionManager txManager = new RawTransactionManager(web3j, credentials, CHAIN_ID);
|
||||
|
||||
final String txData = simpleStorage.set("hello").encodeFunctionCall();
|
||||
|
||||
final var txTooBigResp =
|
||||
txManager.sendTransaction(
|
||||
GAS_PRICE, BigInteger.valueOf(MAX_TX_GAS_LIMIT + 1), contractAddress, txData, VALUE);
|
||||
|
||||
assertThat(txTooBigResp.hasError()).isTrue();
|
||||
assertThat(txTooBigResp.getError().getMessage())
|
||||
.isEqualTo("Gas limit of transaction is greater than the allowed max of 9000000");
|
||||
}
|
||||
|
||||
/**
|
||||
* if we have a list of transactions [t_small, t_tooBig, t_small, ..., t_small] where t_tooBig is
|
||||
* too big to fit in a block, we have blocks created that contain all t_small transactions.
|
||||
*
|
||||
* @throws Exception if send transaction fails
|
||||
*/
|
||||
@Test
|
||||
public void multipleSmallTxsMinedWhileTxTooBigNot() throws Exception {
|
||||
final SimpleStorage simpleStorage = deploySimpleStorage();
|
||||
|
||||
final Web3j web3j = minerNode.nodeRequests().eth();
|
||||
final String contractAddress = simpleStorage.getContractAddress();
|
||||
final Credentials credentials = Credentials.create(Accounts.GENESIS_ACCOUNT_ONE_PRIVATE_KEY);
|
||||
TransactionManager txManager = new RawTransactionManager(web3j, credentials, CHAIN_ID);
|
||||
|
||||
final Account lowGasSender = accounts.getSecondaryBenefactor();
|
||||
final Account recipient = accounts.createAccount("recipient");
|
||||
|
||||
final List<Hash> expectedConfirmedTxs = new ArrayList<>(4);
|
||||
|
||||
expectedConfirmedTxs.add(
|
||||
minerNode.execute(accountTransactions.createTransfer(lowGasSender, recipient, 1)));
|
||||
|
||||
final String txData = simpleStorage.set("too BIG").encodeFunctionCall();
|
||||
|
||||
final var txTooBigResp =
|
||||
txManager.sendTransaction(
|
||||
GAS_PRICE, BigInteger.valueOf(MAX_TX_GAS_LIMIT + 1), contractAddress, txData, VALUE);
|
||||
|
||||
expectedConfirmedTxs.addAll(
|
||||
minerNode.execute(
|
||||
accountTransactions.createIncrementalTransfers(lowGasSender, recipient, 3)));
|
||||
|
||||
assertThat(txTooBigResp.hasError()).isTrue();
|
||||
assertThat(txTooBigResp.getError().getMessage())
|
||||
.isEqualTo("Gas limit of transaction is greater than the allowed max of 9000000");
|
||||
|
||||
expectedConfirmedTxs.stream()
|
||||
.map(Hash::toHexString)
|
||||
.forEach(hash -> minerNode.verify(eth.expectSuccessfulTransactionReceipt(hash)));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
/*
|
||||
* Copyright Consensys Software Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
|
||||
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations under the License.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
package linea.plugin.acc.test;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatThrownBy;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.util.List;
|
||||
import java.util.stream.IntStream;
|
||||
|
||||
import org.hyperledger.besu.datatypes.Address;
|
||||
import org.hyperledger.besu.tests.acceptance.dsl.account.Accounts;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.web3j.crypto.Credentials;
|
||||
import org.web3j.protocol.Web3j;
|
||||
import org.web3j.protocol.core.methods.response.EthSendTransaction;
|
||||
import org.web3j.tx.RawTransactionManager;
|
||||
import org.web3j.utils.Convert;
|
||||
|
||||
public class TransactionPoolDenialTest extends LineaPluginTestBase {
|
||||
|
||||
private static final BigInteger GAS_PRICE = Convert.toWei("20", Convert.Unit.GWEI).toBigInteger();
|
||||
private static final BigInteger GAS_LIMIT = BigInteger.valueOf(210000);
|
||||
private static final BigInteger VALUE = BigInteger.ONE; // 1 wei
|
||||
|
||||
@Override
|
||||
public List<String> getTestCliOptions() {
|
||||
return new TestCommandLineOptionsBuilder()
|
||||
.set("--plugin-linea-deny-list-path=", getResourcePath("/denyList.txt"))
|
||||
.build();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void senderOnDenyListCannotAddTransactionToPool() throws Exception {
|
||||
final Credentials notDenied = Credentials.create(Accounts.GENESIS_ACCOUNT_ONE_PRIVATE_KEY);
|
||||
final Credentials denied = Credentials.create(Accounts.GENESIS_ACCOUNT_TWO_PRIVATE_KEY);
|
||||
final Web3j miner = minerNode.nodeRequests().eth();
|
||||
|
||||
RawTransactionManager transactionManager = new RawTransactionManager(miner, denied, CHAIN_ID);
|
||||
EthSendTransaction transactionResponse =
|
||||
transactionManager.sendTransaction(GAS_PRICE, GAS_LIMIT, notDenied.getAddress(), "", VALUE);
|
||||
|
||||
assertThat(transactionResponse.getTransactionHash()).isNull();
|
||||
assertThat(transactionResponse.getError().getMessage())
|
||||
.isEqualTo(
|
||||
"sender 0x627306090abab3a6e1400e9345bc60c78a8bef57 is blocked as appearing on the SDN or other legally prohibited list");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void transactionWithRecipientOnDenyListCannotBeAddedToPool() throws Exception {
|
||||
final Credentials notDenied = Credentials.create(Accounts.GENESIS_ACCOUNT_ONE_PRIVATE_KEY);
|
||||
final Credentials denied = Credentials.create(Accounts.GENESIS_ACCOUNT_TWO_PRIVATE_KEY);
|
||||
final Web3j miner = minerNode.nodeRequests().eth();
|
||||
|
||||
RawTransactionManager transactionManager =
|
||||
new RawTransactionManager(miner, notDenied, CHAIN_ID);
|
||||
EthSendTransaction transactionResponse =
|
||||
transactionManager.sendTransaction(GAS_PRICE, GAS_LIMIT, denied.getAddress(), "", VALUE);
|
||||
|
||||
assertThat(transactionResponse.getTransactionHash()).isNull();
|
||||
assertThat(transactionResponse.getError().getMessage())
|
||||
.isEqualTo(
|
||||
"recipient 0x627306090abab3a6e1400e9345bc60c78a8bef57 is blocked as appearing on the SDN or other legally prohibited list");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void transactionThatTargetPrecompileIsNotAccepted() {
|
||||
IntStream.rangeClosed(1, 9)
|
||||
.mapToObj(
|
||||
index ->
|
||||
accountTransactions.createTransfer(
|
||||
accounts.getPrimaryBenefactor(),
|
||||
accounts.createAccount(Address.precompiled(index)),
|
||||
1,
|
||||
BigInteger.valueOf(1)))
|
||||
.forEach(
|
||||
txWithPrecompileRecipient ->
|
||||
assertThatThrownBy(
|
||||
() -> txWithPrecompileRecipient.execute(minerNode.nodeRequests()))
|
||||
.hasMessage(
|
||||
"Error sending transaction: destination address is a precompile address and cannot receive transactions"));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
/*
|
||||
* Copyright Consensys Software Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
|
||||
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations under the License.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
package linea.plugin.acc.test;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import linea.plugin.acc.test.tests.web3j.generated.DummyAdder;
|
||||
import org.hyperledger.besu.datatypes.Hash;
|
||||
import org.hyperledger.besu.tests.acceptance.dsl.account.Account;
|
||||
import org.hyperledger.besu.tests.acceptance.dsl.account.Accounts;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.web3j.crypto.Credentials;
|
||||
import org.web3j.crypto.RawTransaction;
|
||||
import org.web3j.crypto.TransactionEncoder;
|
||||
import org.web3j.protocol.Web3j;
|
||||
import org.web3j.protocol.core.methods.response.EthSendTransaction;
|
||||
import org.web3j.tx.gas.DefaultGasProvider;
|
||||
import org.web3j.utils.Numeric;
|
||||
|
||||
public class TransactionTraceLimitOverflowTest extends LineaPluginTestBase {
|
||||
|
||||
private static final BigInteger GAS_LIMIT = DefaultGasProvider.GAS_LIMIT;
|
||||
private static final BigInteger VALUE = BigInteger.ZERO;
|
||||
private static final BigInteger GAS_PRICE = BigInteger.TEN.pow(11);
|
||||
|
||||
@Override
|
||||
public List<String> getTestCliOptions() {
|
||||
return new TestCommandLineOptionsBuilder()
|
||||
.set(
|
||||
"--plugin-linea-module-limit-file-path=",
|
||||
getResourcePath("/txOverflowModuleLimits.toml"))
|
||||
.set("--plugin-linea-tx-pool-simulation-check-api-enabled=", "false")
|
||||
.build();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void transactionOverModuleLineCountRemoved() throws Exception {
|
||||
final DummyAdder dummyAdder = deployDummyAdder();
|
||||
final Web3j web3j = minerNode.nodeRequests().eth();
|
||||
final String contractAddress = dummyAdder.getContractAddress();
|
||||
final String txData = dummyAdder.add(BigInteger.valueOf(100)).encodeFunctionCall();
|
||||
|
||||
// this tx will not be selected since it goes above the line count limit
|
||||
// but selection should go on and select the next one
|
||||
final RawTransaction txModuleLineCountTooBig =
|
||||
RawTransaction.createTransaction(
|
||||
CHAIN_ID,
|
||||
BigInteger.valueOf(1),
|
||||
GAS_LIMIT.divide(BigInteger.TEN),
|
||||
contractAddress,
|
||||
VALUE,
|
||||
txData,
|
||||
GAS_PRICE,
|
||||
GAS_PRICE.multiply(BigInteger.TEN).add(BigInteger.ONE));
|
||||
final byte[] signedTxContractInteraction =
|
||||
TransactionEncoder.signMessage(
|
||||
txModuleLineCountTooBig, Credentials.create(Accounts.GENESIS_ACCOUNT_ONE_PRIVATE_KEY));
|
||||
final EthSendTransaction signedTxContractInteractionResp =
|
||||
web3j.ethSendRawTransaction(Numeric.toHexString(signedTxContractInteraction)).send();
|
||||
|
||||
// these are under the line count limit and should be selected
|
||||
final Account fewLinesSender = accounts.getSecondaryBenefactor();
|
||||
final Account recipient = accounts.createAccount("recipient");
|
||||
final List<Hash> expectedConfirmedTxs = new ArrayList<>(4);
|
||||
|
||||
expectedConfirmedTxs.addAll(
|
||||
minerNode.execute(
|
||||
accountTransactions.createIncrementalTransfers(fewLinesSender, recipient, 4)));
|
||||
|
||||
expectedConfirmedTxs.stream()
|
||||
.map(Hash::toHexString)
|
||||
.forEach(hash -> minerNode.verify(eth.expectSuccessfulTransactionReceipt(hash)));
|
||||
|
||||
// assert that tx over line count limit is not confirmed and is removed from the pool
|
||||
minerNode.verify(
|
||||
eth.expectNoTransactionReceipt(signedTxContractInteractionResp.getTransactionHash()));
|
||||
assertTransactionNotInThePool(signedTxContractInteractionResp.getTransactionHash());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
/*
|
||||
* Copyright Consensys Software Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
|
||||
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations under the License.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
package linea.plugin.acc.test;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import linea.plugin.acc.test.tests.web3j.generated.DummyAdder;
|
||||
import org.hyperledger.besu.tests.acceptance.dsl.account.Accounts;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.web3j.crypto.Credentials;
|
||||
import org.web3j.crypto.RawTransaction;
|
||||
import org.web3j.crypto.TransactionEncoder;
|
||||
import org.web3j.protocol.Web3j;
|
||||
import org.web3j.protocol.core.methods.response.EthSendTransaction;
|
||||
import org.web3j.tx.gas.DefaultGasProvider;
|
||||
import org.web3j.utils.Numeric;
|
||||
|
||||
public class TransactionTraceLimitTest extends LineaPluginTestBase {
|
||||
|
||||
private static final BigInteger GAS_LIMIT = DefaultGasProvider.GAS_LIMIT;
|
||||
private static final BigInteger VALUE = BigInteger.ZERO;
|
||||
private static final BigInteger GAS_PRICE = BigInteger.TEN.pow(9);
|
||||
|
||||
@Override
|
||||
public List<String> getTestCliOptions() {
|
||||
return new TestCommandLineOptionsBuilder()
|
||||
.set("--plugin-linea-module-limit-file-path=", getResourcePath("/strictModuleLimits.toml"))
|
||||
.build();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void transactionsMinedInSeparateBlocksTest() throws Exception {
|
||||
final DummyAdder dummyAdder = deployDummyAdder();
|
||||
final Web3j web3j = minerNode.nodeRequests().eth();
|
||||
final String contractAddress = dummyAdder.getContractAddress();
|
||||
final Credentials credentials = Credentials.create(Accounts.GENESIS_ACCOUNT_ONE_PRIVATE_KEY);
|
||||
final String txData = dummyAdder.add(BigInteger.valueOf(100)).encodeFunctionCall();
|
||||
|
||||
final ArrayList<String> hashes = new ArrayList<>(5);
|
||||
for (int i = 0; i < 5; i++) {
|
||||
final RawTransaction transaction =
|
||||
RawTransaction.createTransaction(
|
||||
CHAIN_ID,
|
||||
BigInteger.valueOf(i + 1),
|
||||
GAS_LIMIT,
|
||||
contractAddress,
|
||||
VALUE,
|
||||
txData,
|
||||
GAS_PRICE,
|
||||
GAS_PRICE.multiply(BigInteger.TEN));
|
||||
final byte[] signedTransaction = TransactionEncoder.signMessage(transaction, credentials);
|
||||
final EthSendTransaction response =
|
||||
web3j.ethSendRawTransaction(Numeric.toHexString(signedTransaction)).send();
|
||||
hashes.add(response.getTransactionHash());
|
||||
}
|
||||
|
||||
// make sure that there are no more than one transaction per block, because the limit for the
|
||||
// add module only allows for one of these transactions.
|
||||
assertTransactionsMinedInSeparateBlocks(web3j, hashes);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,167 @@
|
||||
/*
|
||||
* Copyright Consensys Software Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
|
||||
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations under the License.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
package linea.plugin.acc.test.extradata;
|
||||
|
||||
import static java.util.Map.entry;
|
||||
import static net.consensys.linea.metrics.LineaMetricCategory.PRICING_CONF;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.math.BigInteger;
|
||||
import java.util.List;
|
||||
|
||||
import linea.plugin.acc.test.LineaPluginTestBase;
|
||||
import linea.plugin.acc.test.TestCommandLineOptionsBuilder;
|
||||
import org.apache.tuweni.bytes.Bytes32;
|
||||
import org.hyperledger.besu.datatypes.Wei;
|
||||
import org.hyperledger.besu.tests.acceptance.dsl.account.Account;
|
||||
import org.hyperledger.besu.tests.acceptance.dsl.account.Accounts;
|
||||
import org.hyperledger.besu.tests.acceptance.dsl.transaction.NodeRequests;
|
||||
import org.hyperledger.besu.tests.acceptance.dsl.transaction.Transaction;
|
||||
import org.hyperledger.besu.tests.acceptance.dsl.transaction.account.TransferTransaction;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.web3j.crypto.Credentials;
|
||||
import org.web3j.crypto.RawTransaction;
|
||||
import org.web3j.crypto.TransactionEncoder;
|
||||
import org.web3j.protocol.Web3j;
|
||||
import org.web3j.protocol.core.Request;
|
||||
import org.web3j.protocol.core.methods.response.EthSendTransaction;
|
||||
import org.web3j.utils.Numeric;
|
||||
|
||||
public class ExtraDataPricingTest extends LineaPluginTestBase {
|
||||
protected static final Wei MIN_GAS_PRICE = Wei.of(1_000_000_000);
|
||||
protected static final int WEI_IN_KWEI = 1000;
|
||||
|
||||
@Override
|
||||
public List<String> getTestCliOptions() {
|
||||
return getTestCommandLineOptionsBuilder().build();
|
||||
}
|
||||
|
||||
protected TestCommandLineOptionsBuilder getTestCommandLineOptionsBuilder() {
|
||||
return new TestCommandLineOptionsBuilder()
|
||||
.set("--plugin-linea-extra-data-pricing-enabled=", Boolean.TRUE.toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void updateMinGasPriceViaExtraData() {
|
||||
minerNode.getMiningParameters().setMinTransactionGasPrice(MIN_GAS_PRICE);
|
||||
final var doubleMinGasPrice = MIN_GAS_PRICE.multiply(2);
|
||||
|
||||
final var extraData =
|
||||
createExtraDataPricingField(
|
||||
0, MIN_GAS_PRICE.toLong() / WEI_IN_KWEI, doubleMinGasPrice.toLong() / WEI_IN_KWEI);
|
||||
final var reqSetExtraData = new MinerSetExtraDataRequest(extraData);
|
||||
final var respSetExtraData = reqSetExtraData.execute(minerNode.nodeRequests());
|
||||
|
||||
assertThat(respSetExtraData).isTrue();
|
||||
|
||||
final Account sender = accounts.getSecondaryBenefactor();
|
||||
final Account recipient = accounts.createAccount("recipient");
|
||||
|
||||
final TransferTransaction transferTx = accountTransactions.createTransfer(sender, recipient, 1);
|
||||
final var txHash = minerNode.execute(transferTx);
|
||||
|
||||
minerNode.verify(eth.expectSuccessfulTransactionReceipt(txHash.toHexString()));
|
||||
|
||||
assertThat(minerNode.getMiningParameters().getMinTransactionGasPrice())
|
||||
.isEqualTo(doubleMinGasPrice);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void updateProfitabilityParamsViaExtraData() throws IOException, InterruptedException {
|
||||
final Web3j web3j = minerNode.nodeRequests().eth();
|
||||
final Account sender = accounts.getSecondaryBenefactor();
|
||||
final Account recipient = accounts.createAccount("recipient");
|
||||
minerNode.getMiningParameters().setMinTransactionGasPrice(MIN_GAS_PRICE);
|
||||
|
||||
final var extraData =
|
||||
createExtraDataPricingField(
|
||||
MIN_GAS_PRICE.multiply(2).toLong() / WEI_IN_KWEI,
|
||||
MIN_GAS_PRICE.toLong() / WEI_IN_KWEI,
|
||||
MIN_GAS_PRICE.toLong() / WEI_IN_KWEI);
|
||||
final var reqSetExtraData = new ExtraDataPricingTest.MinerSetExtraDataRequest(extraData);
|
||||
final var respSetExtraData = reqSetExtraData.execute(minerNode.nodeRequests());
|
||||
|
||||
assertThat(respSetExtraData).isTrue();
|
||||
|
||||
// when this first tx is mined the above extra data pricing will have effect on following txs
|
||||
final TransferTransaction profitableTx =
|
||||
accountTransactions.createTransfer(sender, recipient, 1);
|
||||
final var profitableTxHash = minerNode.execute(profitableTx);
|
||||
|
||||
minerNode.verify(eth.expectSuccessfulTransactionReceipt(profitableTxHash.toHexString()));
|
||||
|
||||
// this tx will be evaluated with the previously set extra data pricing to be unprofitable
|
||||
final RawTransaction unprofitableTx =
|
||||
RawTransaction.createTransaction(
|
||||
BigInteger.ZERO,
|
||||
MIN_GAS_PRICE.getAsBigInteger(),
|
||||
BigInteger.valueOf(21000),
|
||||
recipient.getAddress(),
|
||||
"");
|
||||
|
||||
final byte[] signedUnprofitableTx =
|
||||
TransactionEncoder.signMessage(
|
||||
unprofitableTx, Credentials.create(Accounts.GENESIS_ACCOUNT_ONE_PRIVATE_KEY));
|
||||
|
||||
final EthSendTransaction signedUnprofitableTxResp =
|
||||
web3j.ethSendRawTransaction(Numeric.toHexString(signedUnprofitableTx)).send();
|
||||
|
||||
assertThat(signedUnprofitableTxResp.hasError()).isTrue();
|
||||
assertThat(signedUnprofitableTxResp.getError().getMessage()).isEqualTo("Gas price too low");
|
||||
|
||||
assertThat(getTxPoolContent()).isEmpty();
|
||||
|
||||
final var fixedCostMetric =
|
||||
getMetricValue(PRICING_CONF, "values", List.of(entry("field", "fixed_cost_wei")));
|
||||
|
||||
assertThat(fixedCostMetric).isEqualTo(MIN_GAS_PRICE.multiply(2).getValue().doubleValue());
|
||||
|
||||
final var variableCostMetric =
|
||||
getMetricValue(PRICING_CONF, "values", List.of(entry("field", "variable_cost_wei")));
|
||||
|
||||
assertThat(variableCostMetric).isEqualTo(MIN_GAS_PRICE.getValue().doubleValue());
|
||||
|
||||
final var ethGasPriceMetric =
|
||||
getMetricValue(PRICING_CONF, "values", List.of(entry("field", "eth_gas_price_wei")));
|
||||
|
||||
assertThat(ethGasPriceMetric).isEqualTo(MIN_GAS_PRICE.getValue().doubleValue());
|
||||
}
|
||||
|
||||
static class MinerSetExtraDataRequest implements Transaction<Boolean> {
|
||||
private final Bytes32 extraData;
|
||||
|
||||
public MinerSetExtraDataRequest(final Bytes32 extraData) {
|
||||
this.extraData = extraData;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean execute(final NodeRequests nodeRequests) {
|
||||
try {
|
||||
return new Request<>(
|
||||
"miner_setExtraData",
|
||||
List.of(extraData.toHexString()),
|
||||
nodeRequests.getWeb3jService(),
|
||||
MinerSetExtraDataResponse.class)
|
||||
.send()
|
||||
.getResult();
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
static class MinerSetExtraDataResponse extends org.web3j.protocol.core.Response<Boolean> {}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
/*
|
||||
* Copyright Consensys Software Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
|
||||
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations under the License.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
package linea.plugin.acc.test.extradata;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import linea.plugin.acc.test.TestCommandLineOptionsBuilder;
|
||||
import org.hyperledger.besu.tests.acceptance.dsl.account.Account;
|
||||
import org.hyperledger.besu.tests.acceptance.dsl.transaction.account.TransferTransaction;
|
||||
import org.junit.jupiter.api.Disabled;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
public class ExtraDataPricingTestWithoutSetMinGasPrice extends ExtraDataPricingTest {
|
||||
|
||||
@Override
|
||||
public List<String> getTestCliOptions() {
|
||||
return getTestCommandLineOptionsBuilder().build();
|
||||
}
|
||||
|
||||
protected TestCommandLineOptionsBuilder getTestCommandLineOptionsBuilder() {
|
||||
return new TestCommandLineOptionsBuilder()
|
||||
.set("--plugin-linea-extra-data-pricing-enabled=", Boolean.TRUE.toString())
|
||||
.set("--plugin-linea-extra-data-set-min-gas-price-enabled=", Boolean.FALSE.toString());
|
||||
}
|
||||
|
||||
@Disabled("disable since minGasPrice is not updated with this test")
|
||||
@Test
|
||||
public void updateMinGasPriceViaExtraData() {}
|
||||
|
||||
@Test
|
||||
public void minGasPriceNotUpdatedViaExtraData() {
|
||||
minerNode.getMiningParameters().setMinTransactionGasPrice(MIN_GAS_PRICE);
|
||||
final var doubleMinGasPrice = MIN_GAS_PRICE.multiply(2);
|
||||
|
||||
final var extraData =
|
||||
createExtraDataPricingField(
|
||||
0, MIN_GAS_PRICE.toLong() / WEI_IN_KWEI, doubleMinGasPrice.toLong() / WEI_IN_KWEI);
|
||||
final var reqSetExtraData = new MinerSetExtraDataRequest(extraData);
|
||||
final var respSetExtraData = reqSetExtraData.execute(minerNode.nodeRequests());
|
||||
|
||||
assertThat(respSetExtraData).isTrue();
|
||||
|
||||
final Account sender = accounts.getSecondaryBenefactor();
|
||||
final Account recipient = accounts.createAccount("recipient");
|
||||
|
||||
final TransferTransaction transferTx = accountTransactions.createTransfer(sender, recipient, 1);
|
||||
final var txHash = minerNode.execute(transferTx);
|
||||
|
||||
minerNode.verify(eth.expectSuccessfulTransactionReceipt(txHash.toHexString()));
|
||||
|
||||
assertThat(minerNode.getMiningParameters().getMinTransactionGasPrice())
|
||||
.isEqualTo(MIN_GAS_PRICE);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
/*
|
||||
* Copyright Consensys Software Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
|
||||
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations under the License.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
package linea.plugin.acc.test.extradata;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
import linea.plugin.acc.test.LineaPluginTestBase;
|
||||
import linea.plugin.acc.test.TestCommandLineOptionsBuilder;
|
||||
import org.apache.tuweni.bytes.Bytes32;
|
||||
import org.hyperledger.besu.datatypes.Wei;
|
||||
import org.hyperledger.besu.tests.acceptance.dsl.account.Account;
|
||||
import org.hyperledger.besu.tests.acceptance.dsl.transaction.account.TransferTransaction;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
public class StartupExtraDataPricingTest extends LineaPluginTestBase {
|
||||
private static final Wei VARIABLE_GAS_COST = Wei.of(1_200_300_000);
|
||||
private static final Wei MIN_GAS_PRICE = VARIABLE_GAS_COST.divide(2);
|
||||
private static final int WEI_IN_KWEI = 1000;
|
||||
|
||||
@Override
|
||||
public List<String> getTestCliOptions() {
|
||||
return getTestCommandLineOptionsBuilder().build();
|
||||
}
|
||||
|
||||
protected TestCommandLineOptionsBuilder getTestCommandLineOptionsBuilder() {
|
||||
return new TestCommandLineOptionsBuilder()
|
||||
.set("--plugin-linea-extra-data-pricing-enabled=", Boolean.TRUE.toString());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Optional<Bytes32> maybeCustomGenesisExtraData() {
|
||||
final var genesisExtraData =
|
||||
createExtraDataPricingField(
|
||||
0, VARIABLE_GAS_COST.toLong() / WEI_IN_KWEI, MIN_GAS_PRICE.toLong() / WEI_IN_KWEI);
|
||||
|
||||
return Optional.of(genesisExtraData);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void minGasPriceSetFromChainHeadExtraDataAtStartup() {
|
||||
// at startup the min gas price should be set from the current chain head block extra data
|
||||
assertThat(minerNode.getMiningParameters().getMinTransactionGasPrice())
|
||||
.isEqualTo(MIN_GAS_PRICE);
|
||||
|
||||
final Account sender = accounts.getSecondaryBenefactor();
|
||||
final Account recipient = accounts.createAccount("recipient");
|
||||
|
||||
final TransferTransaction transferTx = accountTransactions.createTransfer(sender, recipient, 1);
|
||||
final var txHash = minerNode.execute(transferTx);
|
||||
|
||||
minerNode.verify(eth.expectSuccessfulTransactionReceipt(txHash.toHexString()));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,122 @@
|
||||
/*
|
||||
* Copyright Consensys Software Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
|
||||
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations under the License.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
package linea.plugin.acc.test.rpc.linea;
|
||||
|
||||
import static org.web3j.crypto.Hash.sha3;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.math.BigInteger;
|
||||
import java.util.Arrays;
|
||||
|
||||
import linea.plugin.acc.test.LineaPluginTestBase;
|
||||
import linea.plugin.acc.test.tests.web3j.generated.AcceptanceTestToken;
|
||||
import linea.plugin.acc.test.tests.web3j.generated.MulmodExecutor;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.hyperledger.besu.tests.acceptance.dsl.account.Account;
|
||||
import org.hyperledger.besu.tests.acceptance.dsl.transaction.NodeRequests;
|
||||
import org.hyperledger.besu.tests.acceptance.dsl.transaction.Transaction;
|
||||
import org.web3j.crypto.RawTransaction;
|
||||
import org.web3j.crypto.TransactionEncoder;
|
||||
import org.web3j.protocol.core.Request;
|
||||
import org.web3j.utils.Numeric;
|
||||
|
||||
public class AbstractSendBundleTest extends LineaPluginTestBase {
|
||||
protected static final BigInteger TRANSFER_GAS_LIMIT = BigInteger.valueOf(100_000L);
|
||||
protected static final BigInteger MULMOD_GAS_LIMIT = BigInteger.valueOf(10_000_000L);
|
||||
protected static final BigInteger GAS_PRICE = BigInteger.TEN.pow(9);
|
||||
|
||||
protected TokenTransfer transferTokens(
|
||||
final AcceptanceTestToken token,
|
||||
final Account sender,
|
||||
final int nonce,
|
||||
final Account recipient,
|
||||
final int amount) {
|
||||
final var transferCalldata =
|
||||
token.transfer(recipient.getAddress(), BigInteger.valueOf(amount)).encodeFunctionCall();
|
||||
|
||||
final var transferTx =
|
||||
RawTransaction.createTransaction(
|
||||
CHAIN_ID,
|
||||
BigInteger.valueOf(nonce),
|
||||
TRANSFER_GAS_LIMIT,
|
||||
token.getContractAddress(),
|
||||
BigInteger.ZERO,
|
||||
transferCalldata,
|
||||
GAS_PRICE,
|
||||
GAS_PRICE.multiply(BigInteger.TEN).add(BigInteger.ONE));
|
||||
|
||||
final String signedTransferTx =
|
||||
Numeric.toHexString(
|
||||
TransactionEncoder.signMessage(transferTx, sender.web3jCredentialsOrThrow()));
|
||||
|
||||
final String hashTx = sha3(signedTransferTx);
|
||||
|
||||
return new TokenTransfer(recipient, hashTx, signedTransferTx);
|
||||
}
|
||||
|
||||
protected MulmodCall mulmodOperation(
|
||||
final MulmodExecutor executor, final Account sender, final int nonce, final int iterations) {
|
||||
final var operationCalldata =
|
||||
executor.executeMulmod(BigInteger.valueOf(iterations)).encodeFunctionCall();
|
||||
|
||||
final var operationTx =
|
||||
RawTransaction.createTransaction(
|
||||
CHAIN_ID,
|
||||
BigInteger.valueOf(nonce),
|
||||
MULMOD_GAS_LIMIT,
|
||||
executor.getContractAddress(),
|
||||
BigInteger.ZERO,
|
||||
operationCalldata,
|
||||
GAS_PRICE,
|
||||
GAS_PRICE.multiply(BigInteger.TEN).add(BigInteger.ONE));
|
||||
|
||||
final String signedTransferTx =
|
||||
Numeric.toHexString(
|
||||
TransactionEncoder.signMessage(operationTx, sender.web3jCredentialsOrThrow()));
|
||||
|
||||
final String hashTx = sha3(signedTransferTx);
|
||||
|
||||
return new MulmodCall(hashTx, signedTransferTx);
|
||||
}
|
||||
|
||||
@RequiredArgsConstructor
|
||||
static class SendBundleRequest implements Transaction<SendBundleRequest.SendBundleResponse> {
|
||||
private final BundleParams bundleParams;
|
||||
|
||||
@Override
|
||||
public SendBundleResponse execute(final NodeRequests nodeRequests) {
|
||||
try {
|
||||
return new Request<>(
|
||||
"linea_sendBundle",
|
||||
Arrays.asList(bundleParams),
|
||||
nodeRequests.getWeb3jService(),
|
||||
SendBundleResponse.class)
|
||||
.send();
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
static class SendBundleResponse extends org.web3j.protocol.core.Response<Response> {}
|
||||
|
||||
record Response(String bundleHash) {}
|
||||
}
|
||||
|
||||
record BundleParams(String[] txs, String blockNumber) {}
|
||||
|
||||
record TokenTransfer(Account recipient, String txHash, String rawTx) {}
|
||||
|
||||
record MulmodCall(String txHash, String rawTx) {}
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
/*
|
||||
* Copyright Consensys Software Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
|
||||
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations under the License.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
package linea.plugin.acc.test.rpc.linea;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.math.RoundingMode;
|
||||
import java.util.List;
|
||||
|
||||
import org.hyperledger.besu.datatypes.Wei;
|
||||
import org.hyperledger.besu.ethereum.core.Transaction;
|
||||
import org.hyperledger.besu.tests.acceptance.dsl.account.Account;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
public class EstimateGasCompatibilityModeTest extends EstimateGasTest {
|
||||
private static final BigDecimal PRICE_MULTIPLIER = BigDecimal.valueOf(1.2);
|
||||
|
||||
@Override
|
||||
public List<String> getTestCliOptions() {
|
||||
return getTestCommandLineOptionsBuilder()
|
||||
.set("--plugin-linea-estimate-gas-compatibility-mode-enabled=", "true")
|
||||
.set(
|
||||
"--plugin-linea-estimate-gas-compatibility-mode-multiplier=",
|
||||
PRICE_MULTIPLIER.toPlainString())
|
||||
.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void assertIsProfitable(
|
||||
final Transaction tx,
|
||||
final Wei baseFee,
|
||||
final Wei estimatedMaxGasPrice,
|
||||
final long estimatedGasLimit) {
|
||||
final var minGasPrice = minerNode.getMiningParameters().getMinTransactionGasPrice();
|
||||
final var minPriorityFee = minGasPrice.subtract(baseFee);
|
||||
final var compatibilityMinPriorityFee =
|
||||
Wei.of(
|
||||
PRICE_MULTIPLIER
|
||||
.multiply(new BigDecimal(minPriorityFee.getAsBigInteger()))
|
||||
.setScale(0, RoundingMode.CEILING)
|
||||
.toBigInteger());
|
||||
|
||||
// since we are in compatibility mode, we want to check that returned profitable priority fee is
|
||||
// the min priority fee per gas * multiplier + base fee
|
||||
final var expectedMaxGasPrice = baseFee.add(compatibilityMinPriorityFee);
|
||||
assertThat(estimatedMaxGasPrice).isEqualTo(expectedMaxGasPrice);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void assertMinGasPriceLowerBound(final Wei baseFee, final Wei estimatedMaxGasPrice) {
|
||||
// since we are in compatibility mode, we want to check that returned profitable priority fee is
|
||||
// the min priority fee per gas * multiplier + base fee
|
||||
assertIsProfitable(null, baseFee, estimatedMaxGasPrice, 0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void lineaEstimateGasPriorityFeeMinGasPriceLowerBound() {
|
||||
final Account sender = accounts.getSecondaryBenefactor();
|
||||
|
||||
final CallParams callParams =
|
||||
new CallParams(null, sender.getAddress(), null, null, "", "", "0", null, null, null);
|
||||
|
||||
final var reqLinea = new LineaEstimateGasRequest(callParams);
|
||||
final var respLinea = reqLinea.execute(minerNode.nodeRequests()).getResult();
|
||||
|
||||
final var baseFee = Wei.fromHexString(respLinea.baseFeePerGas());
|
||||
final var estimatedPriorityFee = Wei.fromHexString(respLinea.priorityFeePerGas());
|
||||
final var estimatedMaxGasPrice = baseFee.add(estimatedPriorityFee);
|
||||
|
||||
assertMinGasPriceLowerBound(baseFee, estimatedMaxGasPrice);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
/*
|
||||
* Copyright Consensys Software Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
|
||||
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations under the License.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
package linea.plugin.acc.test.rpc.linea;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.util.List;
|
||||
|
||||
import linea.plugin.acc.test.LineaPluginTestBase;
|
||||
import linea.plugin.acc.test.TestCommandLineOptionsBuilder;
|
||||
import linea.plugin.acc.test.tests.web3j.generated.DummyAdder;
|
||||
import org.hyperledger.besu.tests.acceptance.dsl.account.Account;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.web3j.tx.gas.DefaultGasProvider;
|
||||
|
||||
public class EstimateGasModuleLimitOverflowTest extends LineaPluginTestBase {
|
||||
@Override
|
||||
public List<String> getTestCliOptions() {
|
||||
return new TestCommandLineOptionsBuilder()
|
||||
.set(
|
||||
"--plugin-linea-module-limit-file-path=",
|
||||
getResourcePath("/txOverflowModuleLimits.toml"))
|
||||
.build();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void estimateGasFailsForExceedingModuleLineCountTest() throws Exception {
|
||||
|
||||
final Account sender = accounts.getPrimaryBenefactor();
|
||||
|
||||
final DummyAdder dummyAdder = deployDummyAdder();
|
||||
final String txData = dummyAdder.add(BigInteger.valueOf(1)).encodeFunctionCall();
|
||||
|
||||
final EstimateGasTest.CallParams callParams =
|
||||
new EstimateGasTest.CallParams(
|
||||
null,
|
||||
sender.getAddress(),
|
||||
null,
|
||||
dummyAdder.getContractAddress(),
|
||||
null,
|
||||
txData,
|
||||
"0",
|
||||
DefaultGasProvider.GAS_PRICE.toString(),
|
||||
null,
|
||||
null);
|
||||
|
||||
final var reqLinea = new EstimateGasTest.BadLineaEstimateGasRequest(callParams);
|
||||
final var respLinea = reqLinea.execute(minerNode.nodeRequests());
|
||||
assertThat(respLinea.getCode()).isEqualTo(-32000);
|
||||
assertThat(respLinea.getMessage())
|
||||
.isEqualTo("Transaction line count for module WCP=349 is above the limit 306");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,560 @@
|
||||
/*
|
||||
* Copyright Consensys Software Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
|
||||
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations under the License.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
package linea.plugin.acc.test.rpc.linea;
|
||||
|
||||
import static com.fasterxml.jackson.annotation.JsonInclude.Include.NON_NULL;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.math.BigInteger;
|
||||
import java.net.URI;
|
||||
import java.net.http.HttpClient;
|
||||
import java.net.http.HttpRequest;
|
||||
import java.net.http.HttpResponse;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||
import linea.plugin.acc.test.LineaPluginTestBase;
|
||||
import linea.plugin.acc.test.TestCommandLineOptionsBuilder;
|
||||
import linea.plugin.acc.test.tests.web3j.generated.SimpleStorage;
|
||||
import net.consensys.linea.bl.TransactionProfitabilityCalculator;
|
||||
import net.consensys.linea.config.LineaProfitabilityCliOptions;
|
||||
import net.consensys.linea.config.LineaProfitabilityConfiguration;
|
||||
import net.consensys.linea.rpc.methods.LineaEstimateGas;
|
||||
import org.apache.tuweni.bytes.Bytes;
|
||||
import org.apache.tuweni.units.bigints.UInt64;
|
||||
import org.bouncycastle.crypto.digests.KeccakDigest;
|
||||
import org.hyperledger.besu.datatypes.Address;
|
||||
import org.hyperledger.besu.datatypes.Wei;
|
||||
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.RpcErrorType;
|
||||
import org.hyperledger.besu.tests.acceptance.dsl.account.Account;
|
||||
import org.hyperledger.besu.tests.acceptance.dsl.account.Accounts;
|
||||
import org.hyperledger.besu.tests.acceptance.dsl.transaction.NodeRequests;
|
||||
import org.hyperledger.besu.tests.acceptance.dsl.transaction.Transaction;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.web3j.protocol.core.Request;
|
||||
import org.web3j.protocol.http.HttpService;
|
||||
|
||||
public class EstimateGasTest extends LineaPluginTestBase {
|
||||
protected static final int FIXED_GAS_COST_WEI = 0;
|
||||
protected static final int VARIABLE_GAS_COST_WEI = 1_000_000_000;
|
||||
protected static final double MIN_MARGIN = 1.0;
|
||||
protected static final double ESTIMATE_GAS_MIN_MARGIN = 1.1;
|
||||
protected static final Wei MIN_GAS_PRICE = Wei.of(1_000_000_000);
|
||||
protected static final int MAX_TRANSACTION_GAS_LIMIT = 30_000_000;
|
||||
protected LineaProfitabilityConfiguration profitabilityConf;
|
||||
|
||||
@Override
|
||||
public List<String> getTestCliOptions() {
|
||||
return getTestCommandLineOptionsBuilder().build();
|
||||
}
|
||||
|
||||
protected TestCommandLineOptionsBuilder getTestCommandLineOptionsBuilder() {
|
||||
return new TestCommandLineOptionsBuilder()
|
||||
.set("--plugin-linea-fixed-gas-cost-wei=", String.valueOf(FIXED_GAS_COST_WEI))
|
||||
.set("--plugin-linea-variable-gas-cost-wei=", String.valueOf(VARIABLE_GAS_COST_WEI))
|
||||
.set("--plugin-linea-min-margin=", String.valueOf(MIN_MARGIN))
|
||||
.set("--plugin-linea-estimate-gas-min-margin=", String.valueOf(ESTIMATE_GAS_MIN_MARGIN))
|
||||
.set("--plugin-linea-max-tx-gas-limit=", String.valueOf(MAX_TRANSACTION_GAS_LIMIT));
|
||||
}
|
||||
|
||||
@BeforeEach
|
||||
public void setMinGasPrice() {
|
||||
minerNode.getMiningParameters().setMinTransactionGasPrice(MIN_GAS_PRICE);
|
||||
}
|
||||
|
||||
@BeforeEach
|
||||
public void createDefaultConfigurations() {
|
||||
profitabilityConf =
|
||||
LineaProfitabilityCliOptions.create().toDomainObject().toBuilder()
|
||||
.fixedCostWei(FIXED_GAS_COST_WEI)
|
||||
.variableCostWei(VARIABLE_GAS_COST_WEI)
|
||||
.minMargin(MIN_MARGIN)
|
||||
.estimateGasMinMargin(ESTIMATE_GAS_MIN_MARGIN)
|
||||
.build();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void lineaEstimateGasMatchesEthEstimateGas() {
|
||||
|
||||
final Account sender = accounts.getSecondaryBenefactor();
|
||||
|
||||
final CallParams callParams =
|
||||
new CallParams(
|
||||
null,
|
||||
sender.getAddress(),
|
||||
null,
|
||||
sender.getAddress(),
|
||||
null,
|
||||
Bytes.EMPTY.toHexString(),
|
||||
"0",
|
||||
null,
|
||||
null,
|
||||
null);
|
||||
|
||||
final var reqEth = new RawEstimateGasRequest(callParams);
|
||||
final var reqLinea = new LineaEstimateGasRequest(callParams);
|
||||
final var respEth = reqEth.execute(minerNode.nodeRequests());
|
||||
final var respLinea = reqLinea.execute(minerNode.nodeRequests());
|
||||
assertThat(respEth).isEqualTo(respLinea.getResult().gasLimit());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void passingGasPriceFieldWorks() {
|
||||
|
||||
final Account sender = accounts.getSecondaryBenefactor();
|
||||
|
||||
final CallParams callParams =
|
||||
new CallParams(
|
||||
null,
|
||||
sender.getAddress(),
|
||||
null,
|
||||
sender.getAddress(),
|
||||
null,
|
||||
Bytes.EMPTY.toHexString(),
|
||||
"0",
|
||||
"0x1234",
|
||||
null,
|
||||
null);
|
||||
|
||||
final var reqLinea = new LineaEstimateGasRequest(callParams);
|
||||
final var respLinea = reqLinea.execute(minerNode.nodeRequests());
|
||||
assertThat(respLinea.hasError()).isFalse();
|
||||
assertThat(respLinea.getResult()).isNotNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void passingChainIdFieldWorks() {
|
||||
|
||||
final Account sender = accounts.getSecondaryBenefactor();
|
||||
|
||||
final CallParams callParams =
|
||||
new CallParams(
|
||||
"0x539",
|
||||
sender.getAddress(),
|
||||
null,
|
||||
sender.getAddress(),
|
||||
null,
|
||||
Bytes.EMPTY.toHexString(),
|
||||
"0",
|
||||
"0x1234",
|
||||
null,
|
||||
null);
|
||||
|
||||
final var reqLinea = new LineaEstimateGasRequest(callParams);
|
||||
final var respLinea = reqLinea.execute(minerNode.nodeRequests());
|
||||
assertThat(respLinea.hasError()).isFalse();
|
||||
assertThat(respLinea.getResult()).isNotNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void passingEIP1559FieldsWorks() {
|
||||
|
||||
final Account sender = accounts.getSecondaryBenefactor();
|
||||
|
||||
final CallParams callParams =
|
||||
new CallParams(
|
||||
null,
|
||||
sender.getAddress(),
|
||||
null,
|
||||
sender.getAddress(),
|
||||
null,
|
||||
Bytes.EMPTY.toHexString(),
|
||||
"0",
|
||||
null,
|
||||
"0x1234",
|
||||
"0x1");
|
||||
|
||||
final var reqLinea = new LineaEstimateGasRequest(callParams);
|
||||
final var respLinea = reqLinea.execute(minerNode.nodeRequests());
|
||||
assertThat(respLinea.hasError()).isFalse();
|
||||
assertThat(respLinea.getResult()).isNotNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void passingChainIdAndEIP1559FieldsWorks() {
|
||||
|
||||
final Account sender = accounts.getSecondaryBenefactor();
|
||||
|
||||
final CallParams callParams =
|
||||
new CallParams(
|
||||
"0x539",
|
||||
sender.getAddress(),
|
||||
null,
|
||||
sender.getAddress(),
|
||||
null,
|
||||
Bytes.EMPTY.toHexString(),
|
||||
"0",
|
||||
null,
|
||||
"0x1234",
|
||||
null);
|
||||
|
||||
final var reqLinea = new LineaEstimateGasRequest(callParams);
|
||||
final var respLinea = reqLinea.execute(minerNode.nodeRequests());
|
||||
assertThat(respLinea.hasError()).isFalse();
|
||||
assertThat(respLinea.getResult()).isNotNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void passingStateOverridesWorks() {
|
||||
|
||||
final Account sender = accounts.getSecondaryBenefactor();
|
||||
|
||||
final var actualBalance = minerNode.execute(ethTransactions.getBalance(sender));
|
||||
|
||||
assertThat(actualBalance).isGreaterThan(BigInteger.ONE);
|
||||
|
||||
final CallParams callParams =
|
||||
new CallParams(
|
||||
"0x539",
|
||||
sender.getAddress(),
|
||||
null,
|
||||
sender.getAddress(),
|
||||
"1",
|
||||
Bytes.EMPTY.toHexString(),
|
||||
"0",
|
||||
"0x1234",
|
||||
null,
|
||||
null);
|
||||
|
||||
final var zeroBalance = Map.of("balance", Wei.ZERO.toHexString());
|
||||
|
||||
final var stateOverrides = Map.of(accounts.getSecondaryBenefactor().getAddress(), zeroBalance);
|
||||
|
||||
final var reqLinea = new LineaEstimateGasRequest(callParams, stateOverrides);
|
||||
final var respLinea = reqLinea.execute(minerNode.nodeRequests());
|
||||
assertThat(respLinea.hasError()).isTrue();
|
||||
assertThat(respLinea.getError().getCode()).isEqualTo(-32004);
|
||||
assertThat(respLinea.getError().getMessage())
|
||||
.isEqualTo(
|
||||
"Upfront cost exceeds account balance (transaction up-front cost 0x208cbab601 exceeds transaction sender account balance 0x0)");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void passingNonceWorks() {
|
||||
|
||||
final Account sender = accounts.getSecondaryBenefactor();
|
||||
|
||||
final CallParams callParams =
|
||||
new CallParams(
|
||||
null,
|
||||
sender.getAddress(),
|
||||
"0",
|
||||
sender.getAddress(),
|
||||
null,
|
||||
Bytes.EMPTY.toHexString(),
|
||||
"0",
|
||||
null,
|
||||
"0x1234",
|
||||
null);
|
||||
|
||||
final var reqLinea = new LineaEstimateGasRequest(callParams);
|
||||
final var respLinea = reqLinea.execute(minerNode.nodeRequests());
|
||||
assertThat(respLinea.hasError()).isFalse();
|
||||
assertThat(respLinea.getResult()).isNotNull();
|
||||
|
||||
// try with a future nonce
|
||||
final CallParams callParamsFuture =
|
||||
new CallParams(
|
||||
null,
|
||||
sender.getAddress(),
|
||||
"10",
|
||||
sender.getAddress(),
|
||||
null,
|
||||
Bytes.EMPTY.toHexString(),
|
||||
"0",
|
||||
null,
|
||||
"0x1234",
|
||||
null);
|
||||
|
||||
final var reqLineaFuture = new LineaEstimateGasRequest(callParamsFuture);
|
||||
final var respLineaFuture = reqLineaFuture.execute(minerNode.nodeRequests());
|
||||
assertThat(respLineaFuture.hasError()).isFalse();
|
||||
assertThat(respLineaFuture.getResult()).isNotNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void lineaEstimateGasIsProfitable() {
|
||||
|
||||
final Account sender = accounts.getSecondaryBenefactor();
|
||||
|
||||
final KeccakDigest keccakDigest = new KeccakDigest(256);
|
||||
final StringBuilder txData = new StringBuilder();
|
||||
txData.append("0x");
|
||||
for (int i = 0; i < 5; i++) {
|
||||
keccakDigest.update(new byte[] {(byte) i}, 0, 1);
|
||||
final byte[] out = new byte[32];
|
||||
keccakDigest.doFinal(out, 0);
|
||||
txData.append(new BigInteger(out).abs());
|
||||
}
|
||||
final var payload = Bytes.wrap(txData.toString().getBytes(StandardCharsets.UTF_8));
|
||||
|
||||
final CallParams callParams =
|
||||
new CallParams(
|
||||
null,
|
||||
sender.getAddress(),
|
||||
null,
|
||||
sender.getAddress(),
|
||||
null,
|
||||
payload.toHexString(),
|
||||
"0",
|
||||
null,
|
||||
null,
|
||||
null);
|
||||
|
||||
final var reqLinea = new LineaEstimateGasRequest(callParams);
|
||||
final var respLinea = reqLinea.execute(minerNode.nodeRequests()).getResult();
|
||||
|
||||
final var estimatedGasLimit = UInt64.fromHexString(respLinea.gasLimit()).toLong();
|
||||
final var baseFee = Wei.fromHexString(respLinea.baseFeePerGas());
|
||||
final var estimatedPriorityFee = Wei.fromHexString(respLinea.priorityFeePerGas());
|
||||
final var estimatedMaxGasPrice = baseFee.add(estimatedPriorityFee);
|
||||
|
||||
final var tx =
|
||||
org.hyperledger.besu.ethereum.core.Transaction.builder()
|
||||
.sender(Address.fromHexString(sender.getAddress()))
|
||||
.to(Address.fromHexString(sender.getAddress()))
|
||||
.gasLimit(estimatedGasLimit)
|
||||
.gasPrice(estimatedMaxGasPrice)
|
||||
.chainId(BigInteger.valueOf(CHAIN_ID))
|
||||
.value(Wei.ZERO)
|
||||
.payload(payload)
|
||||
.signature(LineaEstimateGas.FAKE_SIGNATURE_FOR_SIZE_CALCULATION)
|
||||
.build();
|
||||
|
||||
assertIsProfitable(tx, baseFee, estimatedMaxGasPrice, estimatedGasLimit);
|
||||
}
|
||||
|
||||
protected void assertIsProfitable(
|
||||
final org.hyperledger.besu.ethereum.core.Transaction tx,
|
||||
final Wei baseFee,
|
||||
final Wei estimatedMaxGasPrice,
|
||||
final long estimatedGasLimit) {
|
||||
|
||||
final var minGasPrice = minerNode.getMiningParameters().getMinTransactionGasPrice();
|
||||
|
||||
final var profitabilityCalculator = new TransactionProfitabilityCalculator(profitabilityConf);
|
||||
|
||||
assertThat(
|
||||
profitabilityCalculator.isProfitable(
|
||||
"Test",
|
||||
tx,
|
||||
profitabilityConf.minMargin(),
|
||||
baseFee,
|
||||
estimatedMaxGasPrice,
|
||||
estimatedGasLimit,
|
||||
minGasPrice))
|
||||
.isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void invalidParametersLineaEstimateGasRequestReturnErrorResponse() {
|
||||
final Account sender = accounts.getSecondaryBenefactor();
|
||||
final CallParams callParams =
|
||||
new CallParams(
|
||||
null,
|
||||
sender.getAddress(),
|
||||
null,
|
||||
null,
|
||||
"",
|
||||
"",
|
||||
String.valueOf(Integer.MAX_VALUE),
|
||||
null,
|
||||
null,
|
||||
null);
|
||||
final var reqLinea = new BadLineaEstimateGasRequest(callParams);
|
||||
final var respLinea = reqLinea.execute(minerNode.nodeRequests());
|
||||
assertThat(respLinea.getCode()).isEqualTo(RpcErrorType.INVALID_PARAMS.getCode());
|
||||
assertThat(respLinea.getMessage()).isEqualTo(RpcErrorType.INVALID_PARAMS.getMessage());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void revertedTransactionReturnErrorResponse() throws Exception {
|
||||
final SimpleStorage simpleStorage = deploySimpleStorage();
|
||||
final Account sender = accounts.getSecondaryBenefactor();
|
||||
final var reqLinea =
|
||||
new BadLineaEstimateGasRequest(
|
||||
new CallParams(
|
||||
null,
|
||||
sender.getAddress(),
|
||||
null,
|
||||
simpleStorage.getContractAddress(),
|
||||
"",
|
||||
"",
|
||||
"0",
|
||||
null,
|
||||
null,
|
||||
null));
|
||||
final var respLinea = reqLinea.execute(minerNode.nodeRequests());
|
||||
assertThat(respLinea.getCode()).isEqualTo(-32000);
|
||||
assertThat(respLinea.getMessage()).isEqualTo("Execution reverted");
|
||||
assertThat(respLinea.getData()).isEqualTo("\"0x\"");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void failedTransactionReturnErrorResponse() {
|
||||
final Account sender = accounts.getSecondaryBenefactor();
|
||||
final var reqLinea =
|
||||
new BadLineaEstimateGasRequest(
|
||||
new CallParams(
|
||||
null,
|
||||
sender.getAddress(),
|
||||
null,
|
||||
null,
|
||||
"",
|
||||
Accounts.GENESIS_ACCOUNT_TWO_PRIVATE_KEY,
|
||||
"0",
|
||||
null,
|
||||
null,
|
||||
null));
|
||||
final var respLinea = reqLinea.execute(minerNode.nodeRequests());
|
||||
assertThat(respLinea.getCode()).isEqualTo(-32000);
|
||||
assertThat(respLinea.getMessage())
|
||||
.isEqualTo(
|
||||
"Transaction processing could not be completed due to an exception (Invalid opcode: 0xc8)");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void parseErrorLineaEstimateGasRequestReturnErrorResponse()
|
||||
throws IOException, InterruptedException {
|
||||
final var httpService = (HttpService) minerNode.nodeRequests().getWeb3jService();
|
||||
final var httpClient = HttpClient.newHttpClient();
|
||||
final var badJsonRequest =
|
||||
HttpRequest.newBuilder(URI.create(httpService.getUrl()))
|
||||
.headers("Content-Type", "application/json")
|
||||
.POST(
|
||||
HttpRequest.BodyPublishers.ofString(
|
||||
"""
|
||||
{"jsonrpc":"2.0","method":"linea_estimateGas","params":[malformed json],"id":53}
|
||||
"""))
|
||||
.build();
|
||||
final var errorResponse = httpClient.send(badJsonRequest, HttpResponse.BodyHandlers.ofString());
|
||||
assertThat(errorResponse.body())
|
||||
.isEqualTo(
|
||||
"""
|
||||
{"jsonrpc":"2.0","id":null,"error":{"code":-32700,"message":"Parse error"}}""");
|
||||
}
|
||||
|
||||
protected void assertMinGasPriceLowerBound(final Wei baseFee, final Wei estimatedMaxGasPrice) {
|
||||
final var minGasPrice = minerNode.getMiningParameters().getMinTransactionGasPrice();
|
||||
assertThat(estimatedMaxGasPrice).isEqualTo(minGasPrice);
|
||||
}
|
||||
|
||||
static class LineaEstimateGasRequest
|
||||
implements Transaction<LineaEstimateGasRequest.LineaEstimateGasResponse> {
|
||||
private final CallParams callParams;
|
||||
private final Map<String, Map<String, String>> stateOverrides;
|
||||
|
||||
public LineaEstimateGasRequest(final CallParams callParams) {
|
||||
this(callParams, null);
|
||||
}
|
||||
|
||||
public LineaEstimateGasRequest(
|
||||
final CallParams callParams, final Map<String, Map<String, String>> stateOverrides) {
|
||||
this.callParams = callParams;
|
||||
this.stateOverrides = stateOverrides;
|
||||
}
|
||||
|
||||
@Override
|
||||
public LineaEstimateGasResponse execute(final NodeRequests nodeRequests) {
|
||||
try {
|
||||
return new Request<>(
|
||||
"linea_estimateGas",
|
||||
Arrays.asList(callParams, stateOverrides),
|
||||
nodeRequests.getWeb3jService(),
|
||||
LineaEstimateGasResponse.class)
|
||||
.send();
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
static class LineaEstimateGasResponse extends org.web3j.protocol.core.Response<Response> {}
|
||||
|
||||
record Response(String gasLimit, String baseFeePerGas, String priorityFeePerGas) {}
|
||||
}
|
||||
|
||||
static class BadLineaEstimateGasRequest
|
||||
implements Transaction<org.web3j.protocol.core.Response.Error> {
|
||||
private final CallParams badCallParams;
|
||||
|
||||
public BadLineaEstimateGasRequest(final CallParams badCallParams) {
|
||||
this.badCallParams = badCallParams;
|
||||
}
|
||||
|
||||
@Override
|
||||
public org.web3j.protocol.core.Response.Error execute(final NodeRequests nodeRequests) {
|
||||
try {
|
||||
return new Request<>(
|
||||
"linea_estimateGas",
|
||||
List.of(badCallParams),
|
||||
nodeRequests.getWeb3jService(),
|
||||
BadLineaEstimateGasResponse.class)
|
||||
.send()
|
||||
.getError();
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
static class BadLineaEstimateGasResponse extends org.web3j.protocol.core.Response<Response> {}
|
||||
|
||||
record Response(String gasLimit, String baseFeePerGas, String priorityFeePerGas) {}
|
||||
}
|
||||
|
||||
static class RawEstimateGasRequest implements Transaction<String> {
|
||||
private final CallParams callParams;
|
||||
|
||||
public RawEstimateGasRequest(final CallParams callParams) {
|
||||
this.callParams = callParams;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String execute(final NodeRequests nodeRequests) {
|
||||
try {
|
||||
return new Request<>(
|
||||
"eth_estimateGas",
|
||||
List.of(callParams),
|
||||
nodeRequests.getWeb3jService(),
|
||||
RawEstimateGasResponse.class)
|
||||
.send()
|
||||
.getResult();
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
static class RawEstimateGasResponse extends org.web3j.protocol.core.Response<String> {}
|
||||
}
|
||||
|
||||
@JsonInclude(NON_NULL)
|
||||
record CallParams(
|
||||
String chainId,
|
||||
String from,
|
||||
String nonce,
|
||||
String to,
|
||||
String value,
|
||||
String data,
|
||||
String gas,
|
||||
String gasPrice,
|
||||
String maxFeePerGas,
|
||||
String maxPriorityFeePerGas) {}
|
||||
|
||||
record StateOverride(String account, String balance) {}
|
||||
}
|
||||
@@ -0,0 +1,234 @@
|
||||
/*
|
||||
* Copyright Consensys Software Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
|
||||
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations under the License.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
package linea.plugin.acc.test.rpc.linea;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.math.BigInteger;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.stream.IntStream;
|
||||
|
||||
import linea.plugin.acc.test.LineaPluginTestBase;
|
||||
import linea.plugin.acc.test.TestCommandLineOptionsBuilder;
|
||||
import linea.plugin.acc.test.tests.web3j.generated.ExcludedPrecompiles;
|
||||
import linea.plugin.acc.test.tests.web3j.generated.MulmodExecutor;
|
||||
import linea.plugin.acc.test.tests.web3j.generated.RevertExample;
|
||||
import org.apache.tuweni.bytes.Bytes32;
|
||||
import org.hyperledger.besu.datatypes.Hash;
|
||||
import org.hyperledger.besu.tests.acceptance.dsl.account.Account;
|
||||
import org.hyperledger.besu.tests.acceptance.dsl.account.Accounts;
|
||||
import org.hyperledger.besu.tests.acceptance.dsl.transaction.account.TransferTransactionSet;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.web3j.abi.datatypes.generated.Bytes8;
|
||||
import org.web3j.crypto.Credentials;
|
||||
import org.web3j.crypto.RawTransaction;
|
||||
import org.web3j.crypto.TransactionEncoder;
|
||||
import org.web3j.protocol.Web3j;
|
||||
import org.web3j.protocol.core.methods.response.EthSendTransaction;
|
||||
import org.web3j.tx.gas.DefaultGasProvider;
|
||||
import org.web3j.utils.Numeric;
|
||||
|
||||
public class EthSendRawTransactionSimulationCheckTest extends LineaPluginTestBase {
|
||||
private static final BigInteger GAS_LIMIT = DefaultGasProvider.GAS_LIMIT;
|
||||
private static final BigInteger VALUE = BigInteger.ZERO;
|
||||
private static final BigInteger GAS_PRICE = BigInteger.TEN.pow(9);
|
||||
|
||||
@Override
|
||||
public List<String> getTestCliOptions() {
|
||||
return new TestCommandLineOptionsBuilder()
|
||||
.set(
|
||||
"--plugin-linea-module-limit-file-path=",
|
||||
getResourcePath("/moduleLimits_sendRawTx.toml"))
|
||||
.set("--plugin-linea-tx-pool-simulation-check-api-enabled=", "true")
|
||||
.build();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void transactionOverModuleLineCountNotAccepted() throws Exception {
|
||||
final var mulmodExecutor = deployMulmodExecutor();
|
||||
|
||||
final var mulmodOverflow =
|
||||
encodedCallMulmodOperation(mulmodExecutor, accounts.getPrimaryBenefactor(), 1, 5_000);
|
||||
|
||||
final Web3j web3j = minerNode.nodeRequests().eth();
|
||||
final var resp = web3j.ethSendRawTransaction(Numeric.toHexString(mulmodOverflow)).send();
|
||||
assertThat(resp.hasError()).isTrue();
|
||||
assertThat(resp.getError().getMessage())
|
||||
.isEqualTo(
|
||||
"Transaction 0x6928439fd82ddf40709238e2df0f54ab2e51b252404fbf0efeebb515e6a405e0 line count for module EXT=33939 is above the limit 20");
|
||||
|
||||
assertThat(getTxPoolContent()).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void validTransactionsAreAccepted() {
|
||||
// these are under the line count limit and should be accepted and selected
|
||||
final Account recipient = accounts.createAccount("recipient");
|
||||
final List<Hash> expectedConfirmedTxs = new ArrayList<>(4);
|
||||
|
||||
final var transfers =
|
||||
IntStream.range(0, 4)
|
||||
.mapToObj(
|
||||
i ->
|
||||
accountTransactions.createTransfer(
|
||||
accounts.getSecondaryBenefactor(), recipient, i + 1, BigInteger.valueOf(i)))
|
||||
.toList()
|
||||
.reversed();
|
||||
// reversed, so we are sure no tx is selected before all are sent due to the nonce gap,
|
||||
// otherwise a block can be built with some txs before we can check the txpool content
|
||||
|
||||
expectedConfirmedTxs.addAll(minerNode.execute(new TransferTransactionSet(transfers)));
|
||||
|
||||
final var txPoolContentByHash = getTxPoolContent().stream().map(e -> e.get("hash")).toList();
|
||||
assertThat(txPoolContentByHash)
|
||||
.containsExactlyInAnyOrderElementsOf(
|
||||
expectedConfirmedTxs.stream().map(Hash::toHexString).toList());
|
||||
|
||||
expectedConfirmedTxs.stream()
|
||||
.map(Hash::toHexString)
|
||||
.forEach(hash -> minerNode.verify(eth.expectSuccessfulTransactionReceipt(hash)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void transactionsThatRevertAreAccepted() throws Exception {
|
||||
final RevertExample revertExample = deployRevertExample();
|
||||
final Web3j web3j = minerNode.nodeRequests().eth();
|
||||
final String contractAddress = revertExample.getContractAddress();
|
||||
final String txData = revertExample.setValue(BigInteger.ZERO).encodeFunctionCall();
|
||||
|
||||
// this tx reverts but nevertheless it is accepted in the pool
|
||||
final RawTransaction txThatReverts =
|
||||
RawTransaction.createTransaction(
|
||||
CHAIN_ID,
|
||||
BigInteger.ZERO,
|
||||
GAS_LIMIT.divide(BigInteger.TEN),
|
||||
contractAddress,
|
||||
VALUE,
|
||||
txData,
|
||||
GAS_PRICE,
|
||||
GAS_PRICE.multiply(BigInteger.TEN).add(BigInteger.ONE));
|
||||
final byte[] signedTxContractInteraction =
|
||||
TransactionEncoder.signMessage(
|
||||
txThatReverts, Credentials.create(Accounts.GENESIS_ACCOUNT_TWO_PRIVATE_KEY));
|
||||
|
||||
final EthSendTransaction signedTxContractInteractionResp =
|
||||
web3j.ethSendRawTransaction(Numeric.toHexString(signedTxContractInteraction)).send();
|
||||
|
||||
assertThat(signedTxContractInteractionResp.hasError()).isFalse();
|
||||
|
||||
final var expectedConfirmedTxHash = signedTxContractInteractionResp.getTransactionHash();
|
||||
|
||||
minerNode.verify(eth.expectSuccessfulTransactionReceipt(expectedConfirmedTxHash));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void transactionsWithExcludedPrecompilesAreNotAccepted() throws Exception {
|
||||
final ExcludedPrecompiles excludedPrecompiles = deployExcludedPrecompiles();
|
||||
final Web3j web3j = minerNode.nodeRequests().eth();
|
||||
final String contractAddress = excludedPrecompiles.getContractAddress();
|
||||
|
||||
record InvalidCall(String encodedContractCall, String expectedErrorMessage) {}
|
||||
|
||||
final InvalidCall[] invalidCalls = {
|
||||
new InvalidCall(
|
||||
excludedPrecompiles
|
||||
.callRIPEMD160("I am not allowed here".getBytes(StandardCharsets.UTF_8))
|
||||
.encodeFunctionCall(),
|
||||
"Transaction 0x35451c83b480b45df19105a30f22704df8750b7e328e1ebc646e6442f2f426f9 line count for module PRECOMPILE_RIPEMD_BLOCKS=1 is above the limit 0"),
|
||||
new InvalidCall(
|
||||
encodedCallBlake2F(excludedPrecompiles),
|
||||
"Transaction 0xfd447b2b688f7448c875f68d9c85ffcb976e1cc722b70dae53e4f2e30d871be8 line count for module PRECOMPILE_BLAKE_ROUNDS=12 is above the limit 0")
|
||||
};
|
||||
|
||||
Arrays.stream(invalidCalls)
|
||||
.forEach(
|
||||
invalidCall -> {
|
||||
// this tx must not be accepted
|
||||
final RawTransaction txInvalid =
|
||||
RawTransaction.createTransaction(
|
||||
CHAIN_ID,
|
||||
BigInteger.ZERO,
|
||||
GAS_LIMIT.divide(BigInteger.TEN),
|
||||
contractAddress,
|
||||
VALUE,
|
||||
invalidCall.encodedContractCall,
|
||||
GAS_PRICE,
|
||||
GAS_PRICE.multiply(BigInteger.TEN).add(BigInteger.ONE));
|
||||
|
||||
final byte[] signedTxInvalid =
|
||||
TransactionEncoder.signMessage(
|
||||
txInvalid, Credentials.create(Accounts.GENESIS_ACCOUNT_TWO_PRIVATE_KEY));
|
||||
|
||||
final EthSendTransaction signedTxContractInteractionResp;
|
||||
try {
|
||||
signedTxContractInteractionResp =
|
||||
web3j.ethSendRawTransaction(Numeric.toHexString(signedTxInvalid)).send();
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
||||
assertThat(signedTxContractInteractionResp.hasError()).isTrue();
|
||||
assertThat(signedTxContractInteractionResp.getError().getMessage())
|
||||
.isEqualTo(invalidCall.expectedErrorMessage);
|
||||
});
|
||||
assertThat(getTxPoolContent()).isEmpty();
|
||||
}
|
||||
|
||||
protected byte[] encodedCallMulmodOperation(
|
||||
final MulmodExecutor executor, final Account sender, final int nonce, final int iterations) {
|
||||
final var operationCalldata =
|
||||
executor.executeMulmod(BigInteger.valueOf(iterations)).encodeFunctionCall();
|
||||
|
||||
final var operationTx =
|
||||
RawTransaction.createTransaction(
|
||||
CHAIN_ID,
|
||||
BigInteger.valueOf(nonce),
|
||||
GAS_LIMIT,
|
||||
executor.getContractAddress(),
|
||||
BigInteger.ZERO,
|
||||
operationCalldata,
|
||||
GAS_PRICE,
|
||||
GAS_PRICE.multiply(BigInteger.TEN).add(BigInteger.ONE));
|
||||
|
||||
return TransactionEncoder.signMessage(operationTx, sender.web3jCredentialsOrThrow());
|
||||
}
|
||||
|
||||
private String encodedCallBlake2F(final ExcludedPrecompiles excludedPrecompiles) {
|
||||
return excludedPrecompiles
|
||||
.callBlake2f(
|
||||
BigInteger.valueOf(12),
|
||||
List.of(
|
||||
Bytes32.fromHexString(
|
||||
"0x48c9bdf267e6096a3ba7ca8485ae67bb2bf894fe72f36e3cf1361d5f3af54fa5")
|
||||
.toArrayUnsafe(),
|
||||
Bytes32.fromHexString(
|
||||
"0xd182e6ad7f520e511f6c3e2b8c68059b6bbd41fbabd9831f79217e1319cde05b")
|
||||
.toArrayUnsafe()),
|
||||
List.of(
|
||||
Bytes32.fromHexString(
|
||||
"0x6162630000000000000000000000000000000000000000000000000000000000")
|
||||
.toArrayUnsafe(),
|
||||
Bytes32.ZERO.toArrayUnsafe(),
|
||||
Bytes32.ZERO.toArrayUnsafe(),
|
||||
Bytes32.ZERO.toArrayUnsafe()),
|
||||
List.of(Bytes8.DEFAULT.getValue(), Bytes8.DEFAULT.getValue()),
|
||||
true)
|
||||
.encodeFunctionCall();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
/*
|
||||
* Copyright Consensys Software Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
|
||||
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations under the License.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
package linea.plugin.acc.test.rpc.linea;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.web3j.crypto.Hash.sha3;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import linea.plugin.acc.test.LineaPluginTestBase;
|
||||
import linea.plugin.acc.test.TestCommandLineOptionsBuilder;
|
||||
import org.apache.tuweni.bytes.Bytes;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.web3j.protocol.Web3j;
|
||||
import org.web3j.protocol.core.methods.response.EthSendTransaction;
|
||||
import org.web3j.utils.Numeric;
|
||||
|
||||
public class EthSendRawTransactionSimulationModExpTest extends LineaPluginTestBase {
|
||||
|
||||
@Override
|
||||
public List<String> getTestCliOptions() {
|
||||
return new TestCommandLineOptionsBuilder()
|
||||
.set("--plugin-linea-module-limit-file-path=", getResourcePath("/noModuleLimits.toml"))
|
||||
.set("--plugin-linea-tx-pool-simulation-check-api-enabled=", "true")
|
||||
.build();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void validModExpCallsAreAccepted() throws Exception {
|
||||
final var modExp = deployModExp();
|
||||
|
||||
final Bytes[] validInputs = {
|
||||
Bytes.EMPTY,
|
||||
Bytes.fromHexString("0000000000000000000000000000000000000000000000000000000000000000"),
|
||||
Bytes.fromHexString("000000000000000000000000000000000000000000000000000000000000013f"),
|
||||
Bytes.fromHexString("0000000000000000000000000000000000000000000000000000000000000200"),
|
||||
Bytes.fromHexString("00000000000000000000000000000000000000000000000000000000000002")
|
||||
};
|
||||
|
||||
for (int i = 0; i < validInputs.length; i++) {
|
||||
|
||||
final var mulmodOverflow =
|
||||
encodedCallModExp(modExp, accounts.getSecondaryBenefactor(), i, validInputs[i]);
|
||||
|
||||
final Web3j web3j = minerNode.nodeRequests().eth();
|
||||
final EthSendTransaction resp =
|
||||
web3j.ethSendRawTransaction(Numeric.toHexString(mulmodOverflow)).send();
|
||||
assertThat(resp.hasError()).isFalse();
|
||||
|
||||
minerNode.verify(eth.expectSuccessfulTransactionReceipt(resp.getTransactionHash()));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void invalidModExpCallsAreRejected() throws Exception {
|
||||
final var modExp = deployModExp();
|
||||
|
||||
final Bytes[] invalidInputs = {
|
||||
Bytes.fromHexString("0000000000000000000000000000000000000000000000000000000000000201"),
|
||||
Bytes.fromHexString("00000000000000000000000000000000000000000000000000000000000003"),
|
||||
Bytes.fromHexString("ff"),
|
||||
Bytes.fromHexString("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff")
|
||||
};
|
||||
|
||||
for (int i = 0; i < invalidInputs.length; i++) {
|
||||
|
||||
final var mulmodOverflow =
|
||||
encodedCallModExp(modExp, accounts.getSecondaryBenefactor(), i, invalidInputs[i]);
|
||||
|
||||
final Web3j web3j = minerNode.nodeRequests().eth();
|
||||
final EthSendTransaction resp =
|
||||
web3j.ethSendRawTransaction(Numeric.toHexString(mulmodOverflow)).send();
|
||||
|
||||
assertThat(resp.hasError()).isTrue();
|
||||
assertThat(resp.getError().getMessage())
|
||||
.isEqualTo(
|
||||
"Transaction "
|
||||
+ Numeric.toHexString(sha3(mulmodOverflow))
|
||||
+ " line count for module PRECOMPILE_MODEXP_EFFECTIVE_CALLS=2147483647 is above the limit 10000");
|
||||
|
||||
assertThat(getTxPoolContent()).isEmpty();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,238 @@
|
||||
/*
|
||||
* Copyright Consensys Software Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
|
||||
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations under the License.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
package linea.plugin.acc.test.rpc.linea;
|
||||
|
||||
import static com.github.tomakehurst.wiremock.client.WireMock.aResponse;
|
||||
import static com.github.tomakehurst.wiremock.client.WireMock.equalTo;
|
||||
import static com.github.tomakehurst.wiremock.client.WireMock.exactly;
|
||||
import static com.github.tomakehurst.wiremock.client.WireMock.getAllServeEvents;
|
||||
import static com.github.tomakehurst.wiremock.client.WireMock.matchingJsonPath;
|
||||
import static com.github.tomakehurst.wiremock.client.WireMock.post;
|
||||
import static com.github.tomakehurst.wiremock.client.WireMock.postRequestedFor;
|
||||
import static com.github.tomakehurst.wiremock.client.WireMock.request;
|
||||
import static com.github.tomakehurst.wiremock.client.WireMock.stubFor;
|
||||
import static com.github.tomakehurst.wiremock.client.WireMock.urlEqualTo;
|
||||
import static com.github.tomakehurst.wiremock.client.WireMock.verify;
|
||||
import static java.util.concurrent.TimeUnit.SECONDS;
|
||||
import static net.consensys.linea.bundles.BundleForwarder.RETRY_COUNT_HEADER;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.awaitility.Awaitility.await;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.github.tomakehurst.wiremock.http.Fault;
|
||||
import com.github.tomakehurst.wiremock.http.LoggedResponse;
|
||||
import com.github.tomakehurst.wiremock.http.Request;
|
||||
import com.github.tomakehurst.wiremock.junit5.WireMockRuntimeInfo;
|
||||
import com.github.tomakehurst.wiremock.junit5.WireMockTest;
|
||||
import com.github.tomakehurst.wiremock.matching.MatchResult;
|
||||
import com.github.tomakehurst.wiremock.matching.StringValuePattern;
|
||||
import com.github.tomakehurst.wiremock.stubbing.ServeEvent;
|
||||
import linea.plugin.acc.test.TestCommandLineOptionsBuilder;
|
||||
import org.hyperledger.besu.tests.acceptance.dsl.account.Account;
|
||||
import org.hyperledger.besu.tests.acceptance.dsl.transaction.account.TransferTransaction;
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
@WireMockTest
|
||||
public class ForwardBundleTest extends AbstractSendBundleTest {
|
||||
protected static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();
|
||||
private static WireMockRuntimeInfo wireMockRuntimeInfo;
|
||||
|
||||
@BeforeAll
|
||||
public static void beforeAll(final WireMockRuntimeInfo wireMockRuntimeInfo) {
|
||||
ForwardBundleTest.wireMockRuntimeInfo = wireMockRuntimeInfo;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getTestCliOptions() {
|
||||
return new TestCommandLineOptionsBuilder()
|
||||
.set("--plugin-linea-bundles-forward-urls=", wireMockRuntimeInfo.getHttpBaseUrl())
|
||||
.set(
|
||||
"--plugin-linea-bundles-forward-timeout=",
|
||||
String.valueOf(Duration.ofSeconds(1).toMillis()))
|
||||
.build();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void bundleIsForwarded() {
|
||||
final var bundleParams = sendBundle(1);
|
||||
stubSuccessResponseFor(bundleParams, 0);
|
||||
verifyRequestForwarded(bundleParams);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void forwardIsRetriedAfterTimeout() {
|
||||
final var bundleParams = sendBundle(2);
|
||||
stubSuccessResponseFor(bundleParams, 0, Duration.ofSeconds(2));
|
||||
|
||||
verifyResponseSent(bundleParams);
|
||||
|
||||
verifyRequestForwarded(bundleParams, 1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void forwardIsRetriedAfterNetworkFailure() {
|
||||
final var bundleParams = sendBundle(3);
|
||||
stubFailureFor(bundleParams);
|
||||
stubSuccessResponseFor(bundleParams, 1);
|
||||
|
||||
verifyRequestForwarded(bundleParams);
|
||||
verifyRequestForwarded(bundleParams, 1);
|
||||
}
|
||||
|
||||
private void stubSuccessResponseFor(final BundleParams bundleParams, final int retryCount) {
|
||||
stubSuccessResponseFor(bundleParams, retryCount, Duration.ZERO);
|
||||
}
|
||||
|
||||
private void stubSuccessResponseFor(
|
||||
final BundleParams bundleParams, final int retryCount, final Duration delay) {
|
||||
final var requestMatcher = post(urlEqualTo("/"));
|
||||
|
||||
if (retryCount > 0) {
|
||||
requestMatcher.withHeader(RETRY_COUNT_HEADER, equalTo(String.valueOf(retryCount)));
|
||||
} else {
|
||||
requestMatcher.andMatching(this::noRetryCountHeader);
|
||||
}
|
||||
|
||||
stubFor(
|
||||
requestMatcher
|
||||
.withHeader("Content-Type", equalTo("application/json; charset=UTF-8"))
|
||||
.withRequestBody(matchingBlockNumber(bundleParams.blockNumber()))
|
||||
.willReturn(
|
||||
aResponse()
|
||||
.withFixedDelay((int) delay.toMillis())
|
||||
.withTransformers("response-template")
|
||||
.withStatus(200)
|
||||
.withHeader("Content-Type", "application/json")
|
||||
.withBody(
|
||||
"""
|
||||
{
|
||||
"jsonrpc": "2.0",
|
||||
"result": {
|
||||
"bundleHash": "<bundleHash>"
|
||||
},
|
||||
"id": {{jsonPath request.body '$.id'}}
|
||||
}"""
|
||||
.replace("<bundleHash>", "0xb" + bundleParams.blockNumber()))));
|
||||
}
|
||||
|
||||
private void stubFailureFor(final BundleParams bundleParams) {
|
||||
stubFor(
|
||||
post(urlEqualTo("/"))
|
||||
.withHeader("Content-Type", equalTo("application/json; charset=UTF-8"))
|
||||
.withRequestBody(matchingBlockNumber(bundleParams.blockNumber()))
|
||||
.andMatching(this::noRetryCountHeader)
|
||||
.willReturn(aResponse().withFault(Fault.CONNECTION_RESET_BY_PEER)));
|
||||
}
|
||||
|
||||
private MatchResult noRetryCountHeader(final Request request) {
|
||||
return new MatchResult() {
|
||||
@Override
|
||||
public boolean isExactMatch() {
|
||||
return !request.getAllHeaderKeys().contains(RETRY_COUNT_HEADER);
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getDistance() {
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private static StringValuePattern matchingBlockNumber(final String blockNumber) {
|
||||
return matchingJsonPath("$.params[?(@.blockNumber == %s)]".formatted(blockNumber));
|
||||
}
|
||||
|
||||
private static void verifyRequestForwarded(final BundleParams bundleParams) {
|
||||
verifyRequestForwarded(bundleParams, 0);
|
||||
}
|
||||
|
||||
private static void verifyRequestForwarded(
|
||||
final BundleParams bundleParams, final int retryCount) {
|
||||
|
||||
final var patternBuilder =
|
||||
postRequestedFor(urlEqualTo("/"))
|
||||
.withHeader("Content-Type", equalTo("application/json; charset=UTF-8"))
|
||||
.withRequestBody(matchingBundleParams(bundleParams));
|
||||
|
||||
if (retryCount > 0) {
|
||||
patternBuilder.withHeader(RETRY_COUNT_HEADER, equalTo(String.valueOf(retryCount)));
|
||||
} else {
|
||||
patternBuilder.withoutHeader(RETRY_COUNT_HEADER);
|
||||
}
|
||||
|
||||
await().atMost(2, SECONDS).untilAsserted(() -> verify(exactly(1), patternBuilder));
|
||||
}
|
||||
|
||||
private static void verifyResponseSent(final BundleParams bundleParams) {
|
||||
await()
|
||||
.atMost(2, SECONDS)
|
||||
.until(
|
||||
() ->
|
||||
getAllServeEvents().stream()
|
||||
.map(ServeEvent::getResponse)
|
||||
.map(LoggedResponse::getBodyAsString)
|
||||
.map(
|
||||
body -> {
|
||||
try {
|
||||
return OBJECT_MAPPER.readTree(body);
|
||||
} catch (JsonProcessingException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
})
|
||||
.anyMatch(
|
||||
jsonNode ->
|
||||
jsonNode
|
||||
.findPath("bundleHash")
|
||||
.textValue()
|
||||
.equals("0xb" + bundleParams.blockNumber())));
|
||||
}
|
||||
|
||||
private static StringValuePattern matchingBundleParams(final BundleParams bundleParams) {
|
||||
return matchingJsonPath(
|
||||
"$.params[?(@.blockNumber == %s)]".formatted(bundleParams.blockNumber()))
|
||||
.and(
|
||||
matchingJsonPath(
|
||||
"$.params[?(@.txs == [%s])]"
|
||||
.formatted(
|
||||
Arrays.stream(bundleParams.txs()).collect(Collectors.joining(",")))));
|
||||
}
|
||||
|
||||
private BundleParams sendBundle(final int blockNumber) {
|
||||
final Account sender = accounts.getSecondaryBenefactor();
|
||||
final Account recipient = accounts.getPrimaryBenefactor();
|
||||
|
||||
final TransferTransaction tx = accountTransactions.createTransfer(sender, recipient, 1);
|
||||
|
||||
final String bundleRawTx = tx.signedTransactionData();
|
||||
|
||||
final var bundleParams =
|
||||
new BundleParams(new String[] {bundleRawTx}, Integer.toHexString(blockNumber));
|
||||
|
||||
final var sendBundleRequest = new SendBundleRequest(bundleParams);
|
||||
final var sendBundleResponse = sendBundleRequest.execute(minerNode.nodeRequests());
|
||||
|
||||
assertThat(sendBundleResponse.hasError()).isFalse();
|
||||
assertThat(sendBundleResponse.getResult().bundleHash()).isNotBlank();
|
||||
|
||||
return bundleParams;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,137 @@
|
||||
/*
|
||||
* Copyright Consensys Software Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
|
||||
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations under the License.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
package linea.plugin.acc.test.rpc.linea;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import linea.plugin.acc.test.LineaPluginTestBase;
|
||||
import linea.plugin.acc.test.TestCommandLineOptionsBuilder;
|
||||
import linea.plugin.acc.test.tests.web3j.generated.ModExp;
|
||||
import net.consensys.linea.config.LineaTracerConfiguration;
|
||||
import net.consensys.linea.sequencer.modulelimit.ModuleLineCountValidator;
|
||||
import org.apache.tuweni.bytes.Bytes;
|
||||
import org.hyperledger.besu.datatypes.Hash;
|
||||
import org.hyperledger.besu.tests.acceptance.dsl.account.Account;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.web3j.protocol.Web3j;
|
||||
import org.web3j.protocol.core.methods.response.EthSendTransaction;
|
||||
import org.web3j.utils.Numeric;
|
||||
|
||||
public class ModExpLimitsTest extends LineaPluginTestBase {
|
||||
|
||||
@Override
|
||||
public List<String> getTestCliOptions() {
|
||||
return new TestCommandLineOptionsBuilder()
|
||||
// disable line count validation to accept excluded precompile txs in the txpool
|
||||
.set("--plugin-linea-tx-pool-simulation-check-api-enabled=", "false")
|
||||
// set the module limits file
|
||||
.set("--plugin-linea-module-limit-file-path=", getResourcePath("/moduleLimits.toml"))
|
||||
.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the ModExp PRECOMPILE_MODEXP_EFFECTIVE_CALLS limit, that is the number of times the
|
||||
* corresponding circuit may be invoked in a single block.
|
||||
*/
|
||||
@Test
|
||||
public void modExpLimitTest() throws Exception {
|
||||
Map<String, Integer> moduleLimits =
|
||||
ModuleLineCountValidator.createLimitModules(
|
||||
new LineaTracerConfiguration(getResourcePath("/moduleLimits.toml")));
|
||||
final int PRECOMPILE_MODEXP_EFFECTIVE_CALLS =
|
||||
moduleLimits.get("PRECOMPILE_MODEXP_EFFECTIVE_CALLS");
|
||||
|
||||
/*
|
||||
* nTransactions: the number of transactions to try to include in the same block. The last
|
||||
* one is not supposed to fit as it exceeds the limit, thus it is included in the next block
|
||||
* input: input data for each transaction
|
||||
* target: the expected string to be found in the blocks log
|
||||
*/
|
||||
final int nTransactions = PRECOMPILE_MODEXP_EFFECTIVE_CALLS + 1;
|
||||
final String input =
|
||||
"000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000001aabbcc";
|
||||
final String target =
|
||||
"Cumulated line count for module PRECOMPILE_MODEXP_EFFECTIVE_CALLS="
|
||||
+ (PRECOMPILE_MODEXP_EFFECTIVE_CALLS + 1)
|
||||
+ " is above the limit "
|
||||
+ PRECOMPILE_MODEXP_EFFECTIVE_CALLS
|
||||
+ ", stopping selection";
|
||||
|
||||
// Deploy the ModExp contract
|
||||
final ModExp modExp = deployModExp();
|
||||
|
||||
// Create an account to send the transactions
|
||||
Account modExpSender = accounts.createAccount("modExpSender");
|
||||
|
||||
// Fund the account using secondary benefactor
|
||||
String fundTxHash =
|
||||
accountTransactions
|
||||
.createTransfer(accounts.getSecondaryBenefactor(), modExpSender, 1, BigInteger.ZERO)
|
||||
.execute(minerNode.nodeRequests())
|
||||
.toHexString();
|
||||
// Verify that the transaction for transferring funds was successful
|
||||
minerNode.verify(eth.expectSuccessfulTransactionReceipt(fundTxHash));
|
||||
|
||||
String[] txHashes = new String[nTransactions];
|
||||
for (int i = 0; i < nTransactions; i++) {
|
||||
// With decreasing nonce we force the transactions to be included in the same block
|
||||
// i = 0 , 1 , ..., nTransactions - 1
|
||||
// nonce = nTransactions - 1, nTransactions - 2, ..., 0
|
||||
int nonce = nTransactions - 1 - i;
|
||||
|
||||
// Craft the transaction data
|
||||
final byte[] encodedCallEcRecover =
|
||||
encodedCallModExp(modExp, modExpSender, nonce, Bytes.fromHexString(input));
|
||||
|
||||
// Send the transaction
|
||||
final Web3j web3j = minerNode.nodeRequests().eth();
|
||||
final EthSendTransaction resp =
|
||||
web3j.ethSendRawTransaction(Numeric.toHexString(encodedCallEcRecover)).send();
|
||||
|
||||
// Store the transaction hash
|
||||
txHashes[nonce] = resp.getTransactionHash();
|
||||
}
|
||||
|
||||
// Transfer used as sentry to ensure a new block is mined
|
||||
final Hash transferTxHash =
|
||||
accountTransactions
|
||||
.createTransfer(
|
||||
accounts.getPrimaryBenefactor(),
|
||||
accounts.getSecondaryBenefactor(),
|
||||
1,
|
||||
BigInteger.ONE) // nonce is 1 as primary benefactor also deploys the contract
|
||||
.execute(minerNode.nodeRequests());
|
||||
// Wait for the sentry to be mined
|
||||
minerNode.verify(eth.expectSuccessfulTransactionReceipt(transferTxHash.toHexString()));
|
||||
|
||||
// Assert that all the transactions involving the EcPairing precompile, but the last one, were
|
||||
// included in the same block
|
||||
assertTransactionsMinedInSameBlock(
|
||||
minerNode.nodeRequests().eth(), Arrays.asList(txHashes).subList(0, nTransactions - 1));
|
||||
|
||||
// Assert that the last transaction was included in another block
|
||||
assertTransactionsMinedInSeparateBlocks(
|
||||
minerNode.nodeRequests().eth(), List.of(txHashes[0], txHashes[nTransactions - 1]));
|
||||
|
||||
// Assert that the target string is contained in the blocks log
|
||||
final String blockLog = getAndResetLog();
|
||||
assertThat(blockLog).contains(target);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
/*
|
||||
* Copyright Consensys Software Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
|
||||
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations under the License.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
package linea.plugin.acc.test.rpc.linea;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import linea.plugin.acc.test.TestCommandLineOptionsBuilder;
|
||||
import linea.plugin.acc.test.tests.web3j.generated.AcceptanceTestToken;
|
||||
import org.hyperledger.besu.tests.acceptance.dsl.transaction.account.TransferTransaction;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
public class SendBundleMaxBlockGasTest extends AbstractSendBundleTest {
|
||||
private static final BigInteger BUNDLE_BLOCK_GAS_LIMIT = BigInteger.valueOf(100_000L);
|
||||
|
||||
@Override
|
||||
public List<String> getTestCliOptions() {
|
||||
return new TestCommandLineOptionsBuilder()
|
||||
.set("--plugin-linea-max-bundle-block-gas=", BUNDLE_BLOCK_GAS_LIMIT.toString())
|
||||
.build();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void maxBlockGasForBundlesIsRespected() throws Exception {
|
||||
final AcceptanceTestToken token = deployAcceptanceTestToken();
|
||||
|
||||
final int numOfTransfers = 2;
|
||||
|
||||
// each token transfer has a gas limit of 100k so the bundle does not fit in the max block gas
|
||||
// reserved for bundles
|
||||
final TokenTransfer[] tokenTransfers = new TokenTransfer[numOfTransfers];
|
||||
for (int i = 0; i < numOfTransfers; i++) {
|
||||
tokenTransfers[i] =
|
||||
transferTokens(
|
||||
token,
|
||||
accounts.getPrimaryBenefactor(),
|
||||
i + 1,
|
||||
accounts.createAccount("recipient " + i),
|
||||
1);
|
||||
}
|
||||
|
||||
final var tokenTransferBundleRawTxs =
|
||||
Arrays.stream(tokenTransfers).map(TokenTransfer::rawTx).toArray(String[]::new);
|
||||
|
||||
final var tokenTransferSendBundleRequest =
|
||||
new SendBundleRequest(new BundleParams(tokenTransferBundleRawTxs, Integer.toHexString(2)));
|
||||
final var tokenTransferSendBundleResponse =
|
||||
tokenTransferSendBundleRequest.execute(minerNode.nodeRequests());
|
||||
|
||||
assertThat(tokenTransferSendBundleResponse.hasError()).isFalse();
|
||||
assertThat(tokenTransferSendBundleResponse.getResult().bundleHash()).isNotBlank();
|
||||
|
||||
// while 2 simple transfers each with a gas limit of 21k fit
|
||||
final TransferTransaction tx1 =
|
||||
accountTransactions.createTransfer(
|
||||
accounts.getSecondaryBenefactor(), accounts.getPrimaryBenefactor(), 1);
|
||||
final TransferTransaction tx2 =
|
||||
accountTransactions.createTransfer(
|
||||
accounts.getSecondaryBenefactor(), accounts.getPrimaryBenefactor(), 1);
|
||||
|
||||
final String[] bundleRawTxs =
|
||||
new String[] {tx1.signedTransactionData(), tx2.signedTransactionData()};
|
||||
|
||||
final var sendBundleRequest =
|
||||
new SendBundleRequest(new BundleParams(bundleRawTxs, Integer.toHexString(2)));
|
||||
final var sendBundleResponse = sendBundleRequest.execute(minerNode.nodeRequests());
|
||||
|
||||
assertThat(sendBundleResponse.hasError()).isFalse();
|
||||
assertThat(sendBundleResponse.getResult().bundleHash()).isNotBlank();
|
||||
|
||||
// verify simple transfers are mined
|
||||
minerNode.verify(eth.expectSuccessfulTransactionReceipt(tx1.transactionHash()));
|
||||
minerNode.verify(eth.expectSuccessfulTransactionReceipt(tx2.transactionHash()));
|
||||
|
||||
// but token transfers are not
|
||||
Arrays.stream(tokenTransfers)
|
||||
.forEach(
|
||||
tokenTransfer -> {
|
||||
minerNode.verify(eth.expectNoTransactionReceipt(tokenTransfer.txHash()));
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,379 @@
|
||||
/*
|
||||
* Copyright Consensys Software Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
|
||||
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations under the License.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
package linea.plugin.acc.test.rpc.linea;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.web3j.crypto.Hash.sha3;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.util.Arrays;
|
||||
|
||||
import linea.plugin.acc.test.tests.web3j.generated.AcceptanceTestToken;
|
||||
import linea.plugin.acc.test.tests.web3j.generated.RevertExample;
|
||||
import org.hyperledger.besu.datatypes.Wei;
|
||||
import org.hyperledger.besu.tests.acceptance.dsl.account.Account;
|
||||
import org.hyperledger.besu.tests.acceptance.dsl.account.Accounts;
|
||||
import org.hyperledger.besu.tests.acceptance.dsl.blockchain.Amount;
|
||||
import org.hyperledger.besu.tests.acceptance.dsl.transaction.account.TransferTransaction;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.web3j.crypto.Credentials;
|
||||
import org.web3j.crypto.RawTransaction;
|
||||
import org.web3j.crypto.TransactionEncoder;
|
||||
import org.web3j.utils.Numeric;
|
||||
|
||||
public class SendBundleTest extends AbstractSendBundleTest {
|
||||
|
||||
@Test
|
||||
public void singleTxBundleIsAcceptedAndMined() {
|
||||
final Account sender = accounts.getSecondaryBenefactor();
|
||||
final Account recipient = accounts.getPrimaryBenefactor();
|
||||
|
||||
final TransferTransaction tx = accountTransactions.createTransfer(sender, recipient, 1);
|
||||
|
||||
final String bundleRawTx = tx.signedTransactionData();
|
||||
|
||||
final var sendBundleRequest =
|
||||
new SendBundleRequest(new BundleParams(new String[] {bundleRawTx}, Integer.toHexString(1)));
|
||||
final var sendBundleResponse = sendBundleRequest.execute(minerNode.nodeRequests());
|
||||
|
||||
assertThat(sendBundleResponse.hasError()).isFalse();
|
||||
assertThat(sendBundleResponse.getResult().bundleHash()).isNotBlank();
|
||||
|
||||
minerNode.verify(eth.expectSuccessfulTransactionReceipt(tx.transactionHash()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void bundleIsAcceptedAndMined() {
|
||||
final Account sender = accounts.getSecondaryBenefactor();
|
||||
final Account recipient = accounts.getPrimaryBenefactor();
|
||||
|
||||
final TransferTransaction tx1 = accountTransactions.createTransfer(sender, recipient, 1);
|
||||
final TransferTransaction tx2 = accountTransactions.createTransfer(recipient, sender, 1);
|
||||
|
||||
final String[] bundleRawTxs =
|
||||
new String[] {tx1.signedTransactionData(), tx2.signedTransactionData()};
|
||||
|
||||
final var sendBundleRequest =
|
||||
new SendBundleRequest(new BundleParams(bundleRawTxs, Integer.toHexString(1)));
|
||||
final var sendBundleResponse = sendBundleRequest.execute(minerNode.nodeRequests());
|
||||
|
||||
assertThat(sendBundleResponse.hasError()).isFalse();
|
||||
assertThat(sendBundleResponse.getResult().bundleHash()).isNotBlank();
|
||||
|
||||
minerNode.verify(eth.expectSuccessfulTransactionReceipt(tx1.transactionHash()));
|
||||
minerNode.verify(eth.expectSuccessfulTransactionReceipt(tx2.transactionHash()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void distributeTokensInBundle() throws Exception {
|
||||
final AcceptanceTestToken token = deployAcceptanceTestToken();
|
||||
|
||||
final int numOfTransfers = 10;
|
||||
|
||||
final TokenTransfer[] tokenTransfers = new TokenTransfer[numOfTransfers];
|
||||
for (int i = 0; i < numOfTransfers; i++) {
|
||||
tokenTransfers[i] =
|
||||
transferTokens(
|
||||
token,
|
||||
accounts.getPrimaryBenefactor(),
|
||||
i + 1,
|
||||
accounts.createAccount("recipient " + i),
|
||||
1);
|
||||
}
|
||||
|
||||
final var bundleRawTxs =
|
||||
Arrays.stream(tokenTransfers).map(TokenTransfer::rawTx).toArray(String[]::new);
|
||||
|
||||
final var sendBundleRequest =
|
||||
new SendBundleRequest(new BundleParams(bundleRawTxs, Integer.toHexString(2)));
|
||||
final var sendBundleResponse = sendBundleRequest.execute(minerNode.nodeRequests());
|
||||
|
||||
assertThat(sendBundleResponse.hasError()).isFalse();
|
||||
assertThat(sendBundleResponse.getResult().bundleHash()).isNotBlank();
|
||||
|
||||
Arrays.stream(tokenTransfers)
|
||||
.forEach(
|
||||
tokenTransfer -> {
|
||||
minerNode.verify(eth.expectSuccessfulTransactionReceipt(tokenTransfer.txHash()));
|
||||
try {
|
||||
assertThat(token.balanceOf(tokenTransfer.recipient().getAddress()).send())
|
||||
.isEqualTo(1);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void payGasWithTokensInBundle() throws Exception {
|
||||
final AcceptanceTestToken token = deployAcceptanceTestToken();
|
||||
|
||||
final var recipient = accounts.createAccount("recipient");
|
||||
final var transferReceipt = token.transfer(recipient.getAddress(), BigInteger.TEN).send();
|
||||
assertThat(transferReceipt.isStatusOK()).isTrue();
|
||||
assertThat(token.balanceOf(recipient.getAddress()).send()).isEqualTo(10);
|
||||
|
||||
final var transferGasTx =
|
||||
accountTransactions.createTransfer(accounts.getSecondaryBenefactor(), recipient, 1);
|
||||
final var payGasWithTokenRawTx =
|
||||
transferTokens(token, recipient, 0, accounts.getSecondaryBenefactor(), 1);
|
||||
|
||||
final var bundleRawTxs =
|
||||
new String[] {transferGasTx.signedTransactionData(), payGasWithTokenRawTx.rawTx()};
|
||||
|
||||
final var sendBundleRequest =
|
||||
new SendBundleRequest(new BundleParams(bundleRawTxs, Integer.toHexString(3)));
|
||||
final var sendBundleResponse = sendBundleRequest.execute(minerNode.nodeRequests());
|
||||
|
||||
assertThat(sendBundleResponse.hasError()).isFalse();
|
||||
assertThat(sendBundleResponse.getResult().bundleHash()).isNotBlank();
|
||||
|
||||
minerNode.verify(eth.expectSuccessfulTransactionReceipt(transferGasTx.transactionHash()));
|
||||
minerNode.verify(eth.expectSuccessfulTransactionReceipt(payGasWithTokenRawTx.txHash()));
|
||||
|
||||
final var payGasWithTokenReceipt =
|
||||
ethTransactions
|
||||
.getTransactionReceipt(payGasWithTokenRawTx.txHash())
|
||||
.execute(minerNode.nodeRequests())
|
||||
.orElseThrow();
|
||||
final var gasPrice =
|
||||
Wei.fromHexString(payGasWithTokenReceipt.getEffectiveGasPrice()).toBigInteger();
|
||||
|
||||
final var expectedBalance =
|
||||
Amount.ether(1)
|
||||
.subtract(Amount.wei(gasPrice.multiply(payGasWithTokenReceipt.getGasUsed())));
|
||||
|
||||
minerNode.verify(recipient.balanceEquals(expectedBalance));
|
||||
|
||||
assertThat(token.balanceOf(recipient.getAddress()).send()).isEqualTo(9);
|
||||
assertThat(token.balanceOf(accounts.getSecondaryBenefactor().getAddress()).send()).isEqualTo(1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void singleNotSelectedTxBundleIsNotMined() throws Exception {
|
||||
final var mulmodExecutor = deployMulmodExecutor();
|
||||
|
||||
final var mulmodOverflow =
|
||||
mulmodOperation(mulmodExecutor, accounts.getPrimaryBenefactor(), 1, 5_000);
|
||||
|
||||
final var sendBundleRequest =
|
||||
new SendBundleRequest(
|
||||
new BundleParams(new String[] {mulmodOverflow.rawTx()}, Integer.toHexString(2)));
|
||||
final var sendBundleResponse = sendBundleRequest.execute(minerNode.nodeRequests());
|
||||
|
||||
assertThat(sendBundleResponse.hasError()).isFalse();
|
||||
assertThat(sendBundleResponse.getResult().bundleHash()).isNotBlank();
|
||||
|
||||
// transfer used as sentry to ensure a new block is mined without the bundles
|
||||
final var transferTxHash =
|
||||
accountTransactions
|
||||
.createTransfer(accounts.getSecondaryBenefactor(), accounts.getPrimaryBenefactor(), 1)
|
||||
.execute(minerNode.nodeRequests());
|
||||
|
||||
minerNode.verify(eth.expectSuccessfulTransactionReceipt(transferTxHash.toHexString()));
|
||||
minerNode.verify(eth.expectNoTransactionReceipt(mulmodOverflow.txHash()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void bundleWithNotSelectedTxIsNotMined() throws Exception {
|
||||
final var mulmodExecutor = deployMulmodExecutor();
|
||||
final var recipient = accounts.createAccount("recipient");
|
||||
|
||||
final var mulmodOverflow =
|
||||
mulmodOperation(mulmodExecutor, accounts.getPrimaryBenefactor(), 1, 5_000);
|
||||
final var inBundleTransferTx =
|
||||
accountTransactions.createTransfer(recipient, accounts.getPrimaryBenefactor(), 1);
|
||||
|
||||
// first is not selected because exceeds line count limit
|
||||
final var bundleRawTxs =
|
||||
new String[] {mulmodOverflow.rawTx(), inBundleTransferTx.signedTransactionData()};
|
||||
|
||||
final var sendBundleRequest =
|
||||
new SendBundleRequest(new BundleParams(bundleRawTxs, Integer.toHexString(2)));
|
||||
final var sendBundleResponse = sendBundleRequest.execute(minerNode.nodeRequests());
|
||||
|
||||
assertThat(sendBundleResponse.hasError()).isFalse();
|
||||
assertThat(sendBundleResponse.getResult().bundleHash()).isNotBlank();
|
||||
|
||||
// transfer used as sentry to ensure a new block is mined without the bundles
|
||||
final var transferTxHash1 =
|
||||
accountTransactions
|
||||
.createTransfer(accounts.getSecondaryBenefactor(), recipient, 10)
|
||||
.execute(minerNode.nodeRequests());
|
||||
|
||||
// first sentry is mined and no tx of the bundle is mined
|
||||
minerNode.verify(eth.expectSuccessfulTransactionReceipt(transferTxHash1.toHexString()));
|
||||
minerNode.verify(eth.expectNoTransactionReceipt(mulmodOverflow.txHash()));
|
||||
minerNode.verify(eth.expectNoTransactionReceipt(inBundleTransferTx.transactionHash()));
|
||||
|
||||
// try with a bundle where first is selected but second no
|
||||
final var reverseBundleRawTxs =
|
||||
new String[] {inBundleTransferTx.signedTransactionData(), mulmodOverflow.rawTx()};
|
||||
final var sendReverseBundleRequest =
|
||||
new SendBundleRequest(new BundleParams(reverseBundleRawTxs, Integer.toHexString(3)));
|
||||
final var sendReverseBundleResponse =
|
||||
sendReverseBundleRequest.execute(minerNode.nodeRequests());
|
||||
|
||||
assertThat(sendReverseBundleResponse.hasError()).isFalse();
|
||||
assertThat(sendReverseBundleResponse.getResult().bundleHash()).isNotBlank();
|
||||
|
||||
// transfer used as sentry to ensure a new block is mined without the bundles
|
||||
final var transferTxHash2 =
|
||||
accountTransactions
|
||||
.createTransfer(
|
||||
accounts.getSecondaryBenefactor(),
|
||||
accounts.getPrimaryBenefactor(),
|
||||
1,
|
||||
BigInteger.valueOf(1))
|
||||
.execute(minerNode.nodeRequests());
|
||||
|
||||
// second sentry is mined and no tx of the bundle is mined
|
||||
minerNode.verify(eth.expectSuccessfulTransactionReceipt(transferTxHash2.toHexString()));
|
||||
minerNode.verify(eth.expectNoTransactionReceipt(mulmodOverflow.txHash()));
|
||||
minerNode.verify(eth.expectNoTransactionReceipt(inBundleTransferTx.transactionHash()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void mixOfSelectedNotSelectedBundles() throws Exception {
|
||||
final var mulmodExecutor = deployMulmodExecutor();
|
||||
|
||||
final var mulmodOverflow =
|
||||
mulmodOperation(mulmodExecutor, accounts.getPrimaryBenefactor(), 1, 5_000);
|
||||
final var inBundleTransferTx1 =
|
||||
accountTransactions.createTransfer(
|
||||
accounts.getSecondaryBenefactor(), accounts.getPrimaryBenefactor(), 1, BigInteger.ZERO);
|
||||
|
||||
// first is not selected because exceeds line count limit
|
||||
final var notSelectedBundleRawTxs =
|
||||
new String[] {mulmodOverflow.rawTx(), inBundleTransferTx1.signedTransactionData()};
|
||||
|
||||
final var sendNotSelectedBundleRequest =
|
||||
new SendBundleRequest(new BundleParams(notSelectedBundleRawTxs, Integer.toHexString(2)));
|
||||
final var sendNotSelectedBundleResponse =
|
||||
sendNotSelectedBundleRequest.execute(minerNode.nodeRequests());
|
||||
|
||||
assertThat(sendNotSelectedBundleResponse.hasError()).isFalse();
|
||||
assertThat(sendNotSelectedBundleResponse.getResult().bundleHash()).isNotBlank();
|
||||
|
||||
final var mulmodOk = mulmodOperation(mulmodExecutor, accounts.getPrimaryBenefactor(), 1, 1_000);
|
||||
final var inBundleTransferTx2 =
|
||||
accountTransactions.createTransfer(
|
||||
accounts.getSecondaryBenefactor(), accounts.getPrimaryBenefactor(), 2, BigInteger.ZERO);
|
||||
|
||||
// both txs are valid
|
||||
final var selectedBundleRawTxs =
|
||||
new String[] {mulmodOk.rawTx(), inBundleTransferTx2.signedTransactionData()};
|
||||
|
||||
final var sendSelectedBundleRequest =
|
||||
new SendBundleRequest(new BundleParams(selectedBundleRawTxs, Integer.toHexString(2)));
|
||||
final var sendSelectedBundleResponse =
|
||||
sendSelectedBundleRequest.execute(minerNode.nodeRequests());
|
||||
|
||||
assertThat(sendSelectedBundleResponse.hasError()).isFalse();
|
||||
assertThat(sendSelectedBundleResponse.getResult().bundleHash()).isNotBlank();
|
||||
|
||||
// assert second bundle is mined
|
||||
minerNode.verify(eth.expectSuccessfulTransactionReceipt(mulmodOk.txHash()));
|
||||
minerNode.verify(eth.expectSuccessfulTransactionReceipt(inBundleTransferTx2.transactionHash()));
|
||||
|
||||
// while first bundle is not selected
|
||||
minerNode.verify(eth.expectNoTransactionReceipt(mulmodOverflow.txHash()));
|
||||
minerNode.verify(eth.expectNoTransactionReceipt(inBundleTransferTx1.transactionHash()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void bundleWithRevertedTxIsNotMined() throws Exception {
|
||||
final RevertExample revertExample = deployRevertExample();
|
||||
|
||||
// fund a new account
|
||||
final var recipient = accounts.createAccount("recipient");
|
||||
final var txHashFundRecipient =
|
||||
accountTransactions
|
||||
.createTransfer(accounts.getPrimaryBenefactor(), recipient, 10, BigInteger.valueOf(1))
|
||||
.execute(minerNode.nodeRequests());
|
||||
minerNode.verify(eth.expectSuccessfulTransactionReceipt(txHashFundRecipient.toHexString()));
|
||||
|
||||
// create a tx that reverts
|
||||
final String contractAddress = revertExample.getContractAddress();
|
||||
final String txData = revertExample.setValue(BigInteger.ZERO).encodeFunctionCall();
|
||||
|
||||
final RawTransaction txThatReverts =
|
||||
RawTransaction.createTransaction(
|
||||
CHAIN_ID,
|
||||
BigInteger.ZERO,
|
||||
TRANSFER_GAS_LIMIT,
|
||||
contractAddress,
|
||||
BigInteger.ZERO,
|
||||
txData,
|
||||
GAS_PRICE,
|
||||
GAS_PRICE.multiply(BigInteger.TEN).add(BigInteger.ONE));
|
||||
final var signedTxThatReverts =
|
||||
Numeric.toHexString(
|
||||
TransactionEncoder.signMessage(
|
||||
txThatReverts, Credentials.create(Accounts.GENESIS_ACCOUNT_TWO_PRIVATE_KEY)));
|
||||
final var txThatRevertsHash = sha3(signedTxThatReverts);
|
||||
|
||||
final var inBundleTransferTx =
|
||||
accountTransactions.createTransfer(recipient, accounts.getSecondaryBenefactor(), 1);
|
||||
|
||||
// first tx reverts and bundle is not selected
|
||||
final var bundleRawTxs =
|
||||
new String[] {signedTxThatReverts, inBundleTransferTx.signedTransactionData()};
|
||||
|
||||
final var sendBundleRequest =
|
||||
new SendBundleRequest(new BundleParams(bundleRawTxs, Integer.toHexString(3)));
|
||||
final var sendBundleResponse = sendBundleRequest.execute(minerNode.nodeRequests());
|
||||
|
||||
assertThat(sendBundleResponse.hasError()).isFalse();
|
||||
assertThat(sendBundleResponse.getResult().bundleHash()).isNotBlank();
|
||||
|
||||
// transfer used as sentry to ensure a new block is mined without the bundles
|
||||
final var transferTxHash1 =
|
||||
accountTransactions
|
||||
.createTransfer(
|
||||
accounts.getPrimaryBenefactor(),
|
||||
accounts.getSecondaryBenefactor(),
|
||||
1,
|
||||
BigInteger.valueOf(2))
|
||||
.execute(minerNode.nodeRequests());
|
||||
|
||||
// first sentry is mined and no tx of the bundle is mined
|
||||
minerNode.verify(eth.expectSuccessfulTransactionReceipt(transferTxHash1.toHexString()));
|
||||
minerNode.verify(eth.expectNoTransactionReceipt(txThatRevertsHash));
|
||||
minerNode.verify(eth.expectNoTransactionReceipt(inBundleTransferTx.transactionHash()));
|
||||
|
||||
// try with a bundle where first is selected but second reverts
|
||||
final var reverseBundleRawTxs =
|
||||
new String[] {inBundleTransferTx.signedTransactionData(), signedTxThatReverts};
|
||||
final var sendReverseBundleRequest =
|
||||
new SendBundleRequest(new BundleParams(reverseBundleRawTxs, Integer.toHexString(4)));
|
||||
final var sendReverseBundleResponse =
|
||||
sendReverseBundleRequest.execute(minerNode.nodeRequests());
|
||||
|
||||
assertThat(sendReverseBundleResponse.hasError()).isFalse();
|
||||
assertThat(sendReverseBundleResponse.getResult().bundleHash()).isNotBlank();
|
||||
|
||||
// transfer used as sentry to ensure a new block is mined without the bundles
|
||||
final var transferTxHash2 =
|
||||
accountTransactions
|
||||
.createTransfer(accounts.getPrimaryBenefactor(), recipient, 1, BigInteger.valueOf(3))
|
||||
.execute(minerNode.nodeRequests());
|
||||
|
||||
// second sentry is mined and no tx of the bundle is mined
|
||||
minerNode.verify(eth.expectSuccessfulTransactionReceipt(transferTxHash2.toHexString()));
|
||||
minerNode.verify(eth.expectNoTransactionReceipt(txThatRevertsHash));
|
||||
minerNode.verify(eth.expectNoTransactionReceipt(inBundleTransferTx.transactionHash()));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,264 @@
|
||||
/*
|
||||
* Copyright Consensys Software Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
|
||||
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations under the License.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
package linea.plugin.acc.test.rpc.linea;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.math.BigInteger;
|
||||
import java.net.URI;
|
||||
import java.net.http.HttpClient;
|
||||
import java.net.http.HttpRequest;
|
||||
import java.net.http.HttpResponse;
|
||||
import java.util.List;
|
||||
|
||||
import com.google.common.base.Strings;
|
||||
import linea.plugin.acc.test.LineaPluginTestBase;
|
||||
import linea.plugin.acc.test.TestCommandLineOptionsBuilder;
|
||||
import net.consensys.linea.config.LineaProfitabilityCliOptions;
|
||||
import net.consensys.linea.config.LineaProfitabilityConfiguration;
|
||||
import org.apache.tuweni.bytes.Bytes;
|
||||
import org.apache.tuweni.bytes.Bytes32;
|
||||
import org.bouncycastle.crypto.digests.KeccakDigest;
|
||||
import org.hyperledger.besu.datatypes.Wei;
|
||||
import org.hyperledger.besu.tests.acceptance.dsl.account.Account;
|
||||
import org.hyperledger.besu.tests.acceptance.dsl.account.Accounts;
|
||||
import org.hyperledger.besu.tests.acceptance.dsl.transaction.NodeRequests;
|
||||
import org.hyperledger.besu.tests.acceptance.dsl.transaction.Transaction;
|
||||
import org.hyperledger.besu.tests.acceptance.dsl.transaction.account.TransferTransaction;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.web3j.crypto.Credentials;
|
||||
import org.web3j.protocol.Web3j;
|
||||
import org.web3j.protocol.core.Request;
|
||||
import org.web3j.protocol.core.Response;
|
||||
import org.web3j.protocol.http.HttpService;
|
||||
import org.web3j.tx.RawTransactionManager;
|
||||
import org.web3j.tx.TransactionManager;
|
||||
|
||||
public class SetExtraDataTest extends LineaPluginTestBase {
|
||||
protected static final int FIXED_GAS_COST_WEI = 0;
|
||||
protected static final int VARIABLE_GAS_COST_WEI = 1_000_000_000;
|
||||
protected static final double MIN_MARGIN = 1.5;
|
||||
protected static final Wei MIN_GAS_PRICE = Wei.of(1_000_000);
|
||||
protected static final int MAX_TRANSACTION_GAS_LIMIT = 30_000_000;
|
||||
protected LineaProfitabilityConfiguration profitabilityConf;
|
||||
|
||||
@Override
|
||||
public List<String> getTestCliOptions() {
|
||||
return getTestCommandLineOptionsBuilder().build();
|
||||
}
|
||||
|
||||
protected TestCommandLineOptionsBuilder getTestCommandLineOptionsBuilder() {
|
||||
return new TestCommandLineOptionsBuilder()
|
||||
.set("--plugin-linea-fixed-gas-cost-wei=", String.valueOf(FIXED_GAS_COST_WEI))
|
||||
.set("--plugin-linea-variable-gas-cost-wei=", String.valueOf(VARIABLE_GAS_COST_WEI))
|
||||
.set("--plugin-linea-min-margin=", String.valueOf(MIN_MARGIN))
|
||||
.set("--plugin-linea-max-tx-gas-limit=", String.valueOf(MAX_TRANSACTION_GAS_LIMIT))
|
||||
.set("--plugin-linea-extra-data-pricing-enabled=", "true");
|
||||
}
|
||||
|
||||
@BeforeEach
|
||||
public void setMinGasPrice() {
|
||||
minerNode.getMiningParameters().setMinTransactionGasPrice(MIN_GAS_PRICE);
|
||||
}
|
||||
|
||||
@BeforeEach
|
||||
public void createDefaultConfigurations() {
|
||||
profitabilityConf =
|
||||
LineaProfitabilityCliOptions.create().toDomainObject().toBuilder()
|
||||
.fixedCostWei(FIXED_GAS_COST_WEI)
|
||||
.variableCostWei(VARIABLE_GAS_COST_WEI)
|
||||
.minMargin(MIN_MARGIN)
|
||||
.build();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void setUnsupportedExtraDataReturnsError() {
|
||||
final var unsupportedExtraData = Bytes32.ZERO;
|
||||
|
||||
final var reqLinea = new FailingLineaSetExtraDataRequest(unsupportedExtraData);
|
||||
final var respLinea = reqLinea.execute(minerNode.nodeRequests());
|
||||
assertThat(respLinea.getMessage())
|
||||
.isEqualTo(
|
||||
"Unsupported extra data field 0x0000000000000000000000000000000000000000000000000000000000000000");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void setTooLongExtraDataReturnsError() {
|
||||
final var tooLongExtraData = Bytes.concatenate(Bytes.of(1), Bytes32.ZERO);
|
||||
|
||||
final var reqLinea = new FailingLineaSetExtraDataRequest(tooLongExtraData);
|
||||
final var respLinea = reqLinea.execute(minerNode.nodeRequests());
|
||||
assertThat(respLinea.getMessage()).isEqualTo("Expected 32 bytes but got 33");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void setTooShortExtraDataReturnsError() {
|
||||
final var tooShortExtraData = Bytes32.ZERO.slice(1);
|
||||
|
||||
final var reqLinea = new FailingLineaSetExtraDataRequest(tooShortExtraData);
|
||||
final var respLinea = reqLinea.execute(minerNode.nodeRequests());
|
||||
assertThat(respLinea.getMessage()).isEqualTo("Expected 32 bytes but got 31");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void successfulSetExtraData() {
|
||||
final var extraData =
|
||||
Bytes32.fromHexString("0x0100000000000000000000000000000000000000000000000000000000000000");
|
||||
|
||||
final var reqLinea = new LineaSetExtraDataRequest(extraData);
|
||||
final var respLinea = reqLinea.execute(minerNode.nodeRequests());
|
||||
assertThat(respLinea).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void successfulUpdateMinGasPrice() {
|
||||
final var doubledMinGasPriceKWei = MIN_GAS_PRICE.multiply(2).divide(1000);
|
||||
final var hexMinGasPrice =
|
||||
Strings.padStart(doubledMinGasPriceKWei.toShortHexString().substring(2), 8, '0');
|
||||
final var extraData =
|
||||
Bytes32.fromHexString(
|
||||
"0x010000000000000000" + hexMinGasPrice + "00000000000000000000000000000000000000");
|
||||
|
||||
final var reqLinea = new LineaSetExtraDataRequest(extraData);
|
||||
final var respLinea = reqLinea.execute(minerNode.nodeRequests());
|
||||
assertThat(respLinea).isTrue();
|
||||
assertThat(minerNode.getMiningParameters().getMinTransactionGasPrice())
|
||||
.isEqualTo(MIN_GAS_PRICE.multiply(2));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void successfulUpdatePricingParameters() throws IOException {
|
||||
final Web3j web3j = minerNode.nodeRequests().eth();
|
||||
final Credentials credentials = Credentials.create(Accounts.GENESIS_ACCOUNT_ONE_PRIVATE_KEY);
|
||||
final TransactionManager txManager = new RawTransactionManager(web3j, credentials, CHAIN_ID);
|
||||
|
||||
final KeccakDigest keccakDigest = new KeccakDigest(256);
|
||||
final StringBuilder txData = new StringBuilder();
|
||||
txData.append("0x");
|
||||
for (int i = 0; i < 10; i++) {
|
||||
keccakDigest.update(new byte[] {(byte) i}, 0, 1);
|
||||
final byte[] out = new byte[32];
|
||||
keccakDigest.doFinal(out, 0);
|
||||
txData.append(new BigInteger(out));
|
||||
}
|
||||
|
||||
final var txUnprofitable =
|
||||
txManager.sendTransaction(
|
||||
MIN_GAS_PRICE.getAsBigInteger(),
|
||||
BigInteger.valueOf(MAX_TX_GAS_LIMIT / 2),
|
||||
credentials.getAddress(),
|
||||
txData.toString(),
|
||||
BigInteger.ZERO);
|
||||
|
||||
final Account sender = accounts.getSecondaryBenefactor();
|
||||
final Account recipient = accounts.createAccount("recipient");
|
||||
final TransferTransaction transferTx = accountTransactions.createTransfer(sender, recipient, 1);
|
||||
final var txHash = minerNode.execute(transferTx);
|
||||
|
||||
minerNode.verify(eth.expectSuccessfulTransactionReceipt(txHash.toHexString()));
|
||||
|
||||
// assert that tx below margin is not confirmed
|
||||
minerNode.verify(eth.expectNoTransactionReceipt(txUnprofitable.getTransactionHash()));
|
||||
|
||||
final var zeroFixedCostKWei = "00000000";
|
||||
final var minimalVariableCostKWei = "00000001";
|
||||
final var minimalMinGasPriceKWei = "00000002";
|
||||
final var extraData =
|
||||
Bytes32.fromHexString(
|
||||
"0x01"
|
||||
+ zeroFixedCostKWei
|
||||
+ minimalVariableCostKWei
|
||||
+ minimalMinGasPriceKWei
|
||||
+ "00000000000000000000000000000000000000");
|
||||
|
||||
final var reqLinea = new LineaSetExtraDataRequest(extraData);
|
||||
final var respLinea = reqLinea.execute(minerNode.nodeRequests());
|
||||
assertThat(respLinea).isTrue();
|
||||
assertThat(minerNode.getMiningParameters().getMinTransactionGasPrice()).isEqualTo(Wei.of(2000));
|
||||
// assert that tx is confirmed now
|
||||
minerNode.verify(eth.expectSuccessfulTransactionReceipt(txUnprofitable.getTransactionHash()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void parseErrorLineaEstimateGasRequestReturnErrorResponse()
|
||||
throws IOException, InterruptedException {
|
||||
final var httpService = (HttpService) minerNode.nodeRequests().getWeb3jService();
|
||||
final var httpClient = HttpClient.newHttpClient();
|
||||
final var badJsonRequest =
|
||||
HttpRequest.newBuilder(URI.create(httpService.getUrl()))
|
||||
.headers("Content-Type", "application/json")
|
||||
.POST(
|
||||
HttpRequest.BodyPublishers.ofString(
|
||||
"""
|
||||
{"jsonrpc":"2.0","method":"linea_setExtraData","params":[malformed json],"id":53}
|
||||
"""))
|
||||
.build();
|
||||
final var errorResponse = httpClient.send(badJsonRequest, HttpResponse.BodyHandlers.ofString());
|
||||
assertThat(errorResponse.body())
|
||||
.isEqualTo(
|
||||
"""
|
||||
{"jsonrpc":"2.0","id":null,"error":{"code":-32700,"message":"Parse error"}}""");
|
||||
}
|
||||
|
||||
static class LineaSetExtraDataRequest implements Transaction<Boolean> {
|
||||
private final Bytes32 extraData;
|
||||
|
||||
public LineaSetExtraDataRequest(final Bytes32 extraData) {
|
||||
this.extraData = extraData;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean execute(final NodeRequests nodeRequests) {
|
||||
try {
|
||||
return new Request<>(
|
||||
"linea_setExtraData",
|
||||
List.of(extraData.toHexString()),
|
||||
nodeRequests.getWeb3jService(),
|
||||
LineaSetExtraDataResponse.class)
|
||||
.send()
|
||||
.getResult();
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static class FailingLineaSetExtraDataRequest implements Transaction<Response.Error> {
|
||||
private final Bytes extraData;
|
||||
|
||||
public FailingLineaSetExtraDataRequest(final Bytes extraData) {
|
||||
this.extraData = extraData;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Response.Error execute(final NodeRequests nodeRequests) {
|
||||
try {
|
||||
return new Request<>(
|
||||
"linea_setExtraData",
|
||||
List.of(extraData.toHexString()),
|
||||
nodeRequests.getWeb3jService(),
|
||||
LineaSetExtraDataResponse.class)
|
||||
.send()
|
||||
.getError();
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static class LineaSetExtraDataResponse extends org.web3j.protocol.core.Response<Boolean> {}
|
||||
}
|
||||
@@ -0,0 +1,177 @@
|
||||
/*
|
||||
* Copyright Consensys Software Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
|
||||
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations under the License.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package org.hyperledger.besu.tests.acceptance.dsl;
|
||||
|
||||
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.lang.ProcessBuilder.Redirect;
|
||||
import java.math.BigInteger;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.hyperledger.besu.tests.acceptance.dsl.account.Accounts;
|
||||
import org.hyperledger.besu.tests.acceptance.dsl.blockchain.Blockchain;
|
||||
import org.hyperledger.besu.tests.acceptance.dsl.condition.admin.AdminConditions;
|
||||
import org.hyperledger.besu.tests.acceptance.dsl.condition.bft.BftConditions;
|
||||
import org.hyperledger.besu.tests.acceptance.dsl.condition.clique.CliqueConditions;
|
||||
import org.hyperledger.besu.tests.acceptance.dsl.condition.eth.EthConditions;
|
||||
import org.hyperledger.besu.tests.acceptance.dsl.condition.login.LoginConditions;
|
||||
import org.hyperledger.besu.tests.acceptance.dsl.condition.net.NetConditions;
|
||||
import org.hyperledger.besu.tests.acceptance.dsl.condition.perm.PermissioningConditions;
|
||||
import org.hyperledger.besu.tests.acceptance.dsl.condition.process.ExitedWithCode;
|
||||
import org.hyperledger.besu.tests.acceptance.dsl.condition.txpool.TxPoolConditions;
|
||||
import org.hyperledger.besu.tests.acceptance.dsl.condition.web3.Web3Conditions;
|
||||
import org.hyperledger.besu.tests.acceptance.dsl.contract.ContractVerifier;
|
||||
import org.hyperledger.besu.tests.acceptance.dsl.node.Node;
|
||||
import org.hyperledger.besu.tests.acceptance.dsl.node.cluster.Cluster;
|
||||
import org.hyperledger.besu.tests.acceptance.dsl.node.configuration.BesuNodeFactory;
|
||||
import org.hyperledger.besu.tests.acceptance.dsl.node.configuration.permissioning.PermissionedNodeBuilder;
|
||||
import org.hyperledger.besu.tests.acceptance.dsl.transaction.account.AccountTransactions;
|
||||
import org.hyperledger.besu.tests.acceptance.dsl.transaction.admin.AdminTransactions;
|
||||
import org.hyperledger.besu.tests.acceptance.dsl.transaction.bft.BftTransactions;
|
||||
import org.hyperledger.besu.tests.acceptance.dsl.transaction.clique.CliqueTransactions;
|
||||
import org.hyperledger.besu.tests.acceptance.dsl.transaction.contract.ContractTransactions;
|
||||
import org.hyperledger.besu.tests.acceptance.dsl.transaction.eth.EthTransactions;
|
||||
import org.hyperledger.besu.tests.acceptance.dsl.transaction.miner.MinerTransactions;
|
||||
import org.hyperledger.besu.tests.acceptance.dsl.transaction.net.NetTransactions;
|
||||
import org.hyperledger.besu.tests.acceptance.dsl.transaction.perm.PermissioningTransactions;
|
||||
import org.hyperledger.besu.tests.acceptance.dsl.transaction.txpool.TxPoolTransactions;
|
||||
import org.hyperledger.besu.tests.acceptance.dsl.transaction.web3.Web3Transactions;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.Tag;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
|
||||
/** Base class for acceptance tests. */
|
||||
@ExtendWith(AcceptanceTestBaseTestWatcher.class)
|
||||
@Tag("AcceptanceTest")
|
||||
@Slf4j
|
||||
public abstract class AcceptanceTestBase {
|
||||
protected final Accounts accounts;
|
||||
protected final AccountTransactions accountTransactions;
|
||||
protected final AdminConditions admin;
|
||||
protected final AdminTransactions adminTransactions;
|
||||
protected final Blockchain blockchain;
|
||||
protected final CliqueConditions clique;
|
||||
protected final CliqueTransactions cliqueTransactions;
|
||||
protected final Cluster cluster;
|
||||
protected final ContractVerifier contractVerifier;
|
||||
protected final ContractTransactions contractTransactions;
|
||||
protected final EthConditions eth;
|
||||
protected final EthTransactions ethTransactions;
|
||||
protected final BftTransactions bftTransactions;
|
||||
protected final BftConditions bft;
|
||||
protected final LoginConditions login;
|
||||
protected final NetConditions net;
|
||||
protected final BesuNodeFactory besu;
|
||||
protected final PermissioningConditions perm;
|
||||
protected final PermissionedNodeBuilder permissionedNodeBuilder;
|
||||
protected final PermissioningTransactions permissioningTransactions;
|
||||
protected final MinerTransactions minerTransactions;
|
||||
protected final Web3Conditions web3;
|
||||
protected final TxPoolConditions txPoolConditions;
|
||||
protected final TxPoolTransactions txPoolTransactions;
|
||||
protected final ExitedWithCode exitedSuccessfully;
|
||||
|
||||
private final ExecutorService outputProcessorExecutor = Executors.newCachedThreadPool();
|
||||
|
||||
protected AcceptanceTestBase() {
|
||||
ethTransactions = new EthTransactions();
|
||||
accounts = new Accounts(ethTransactions);
|
||||
adminTransactions = new AdminTransactions();
|
||||
cliqueTransactions = new CliqueTransactions();
|
||||
bftTransactions = new BftTransactions();
|
||||
accountTransactions = new AccountTransactions(accounts);
|
||||
permissioningTransactions = new PermissioningTransactions();
|
||||
contractTransactions = new ContractTransactions();
|
||||
minerTransactions = new MinerTransactions();
|
||||
|
||||
blockchain = new Blockchain(ethTransactions);
|
||||
clique = new CliqueConditions(ethTransactions, cliqueTransactions);
|
||||
eth = new EthConditions(ethTransactions);
|
||||
bft = new BftConditions(bftTransactions);
|
||||
login = new LoginConditions();
|
||||
net = new NetConditions(new NetTransactions());
|
||||
cluster = new Cluster(net);
|
||||
perm = new PermissioningConditions(permissioningTransactions);
|
||||
admin = new AdminConditions(adminTransactions);
|
||||
web3 = new Web3Conditions(new Web3Transactions());
|
||||
besu = new BesuNodeFactory();
|
||||
txPoolTransactions = new TxPoolTransactions();
|
||||
txPoolConditions = new TxPoolConditions(txPoolTransactions);
|
||||
contractVerifier = new ContractVerifier(accounts.getPrimaryBenefactor());
|
||||
permissionedNodeBuilder = new PermissionedNodeBuilder();
|
||||
exitedSuccessfully = new ExitedWithCode(0);
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
public void tearDownAcceptanceTestBase() {
|
||||
reportMemory();
|
||||
cluster.close();
|
||||
}
|
||||
|
||||
/** Report memory usage after test execution. */
|
||||
public void reportMemory() {
|
||||
String os = System.getProperty("os.name");
|
||||
String[] command = null;
|
||||
if (os.contains("Linux")) {
|
||||
command = new String[] {"/usr/bin/top", "-n", "1", "-o", "%MEM", "-b", "-c", "-w", "180"};
|
||||
}
|
||||
if (os.contains("Mac")) {
|
||||
command = new String[] {"/usr/bin/top", "-l", "1", "-o", "mem", "-n", "20"};
|
||||
}
|
||||
if (command != null) {
|
||||
log.info("Memory usage at end of test:");
|
||||
final ProcessBuilder processBuilder =
|
||||
new ProcessBuilder(command).redirectErrorStream(true).redirectInput(Redirect.INHERIT);
|
||||
try {
|
||||
final Process memInfoProcess = processBuilder.start();
|
||||
outputProcessorExecutor.execute(() -> printOutput(memInfoProcess));
|
||||
memInfoProcess.waitFor();
|
||||
log.debug("Memory info process exited with code {}", memInfoProcess.exitValue());
|
||||
} catch (final Exception e) {
|
||||
log.warn("Error running memory information process", e);
|
||||
}
|
||||
} else {
|
||||
log.info("Don't know how to report memory for OS {}", os);
|
||||
}
|
||||
}
|
||||
|
||||
private void printOutput(final Process process) {
|
||||
try (final BufferedReader in =
|
||||
new BufferedReader(new InputStreamReader(process.getInputStream(), UTF_8))) {
|
||||
String line = in.readLine();
|
||||
while (line != null) {
|
||||
log.info(line);
|
||||
line = in.readLine();
|
||||
}
|
||||
} catch (final IOException e) {
|
||||
log.warn("Failed to read output from memory information process: ", e);
|
||||
}
|
||||
}
|
||||
|
||||
protected void waitForBlockHeight(final Node node, final long blockchainHeight) {
|
||||
WaitUtils.waitFor(
|
||||
120,
|
||||
() ->
|
||||
assertThat(node.execute(ethTransactions.blockNumber()))
|
||||
.isGreaterThanOrEqualTo(BigInteger.valueOf(blockchainHeight)));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,218 @@
|
||||
/*
|
||||
* Copyright Consensys Software Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
|
||||
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations under the License.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package org.hyperledger.besu.tests.acceptance.dsl;
|
||||
|
||||
import static org.assertj.core.api.Assertions.*;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Optional;
|
||||
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.fasterxml.jackson.databind.node.ArrayNode;
|
||||
import com.fasterxml.jackson.databind.node.ObjectNode;
|
||||
import okhttp3.Call;
|
||||
import okhttp3.MediaType;
|
||||
import okhttp3.OkHttpClient;
|
||||
import okhttp3.Request;
|
||||
import okhttp3.RequestBody;
|
||||
import okhttp3.Response;
|
||||
import org.hyperledger.besu.datatypes.Hash;
|
||||
import org.hyperledger.besu.tests.acceptance.dsl.node.BesuNode;
|
||||
import org.hyperledger.besu.tests.acceptance.dsl.transaction.eth.EthTransactions;
|
||||
import org.web3j.protocol.core.methods.response.EthBlock;
|
||||
|
||||
/*
|
||||
* Inspired by PragueAcceptanceTestHelper class in Besu codebase. We use this class to
|
||||
* emulate Engine API calls to the Besu Node, so that we can run tests for post-merge EVM forks.
|
||||
*/
|
||||
public class EngineAPIService {
|
||||
private final OkHttpClient httpClient;
|
||||
private final ObjectMapper mapper;
|
||||
private final BesuNode node;
|
||||
private final EthTransactions ethTransactions;
|
||||
|
||||
private static final String JSONRPC_VERSION = "2.0";
|
||||
private static final long JSONRPC_REQUEST_ID = 67;
|
||||
private static final String SUGGESTED_BLOCK_FEE_RECIPIENT =
|
||||
"0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b";
|
||||
|
||||
public EngineAPIService(BesuNode node, EthTransactions ethTransactions, ObjectMapper mapper) {
|
||||
httpClient = new OkHttpClient();
|
||||
this.mapper = mapper;
|
||||
this.node = node;
|
||||
this.ethTransactions = ethTransactions;
|
||||
}
|
||||
|
||||
/*
|
||||
* See https://hackmd.io/@danielrachi/engine_api
|
||||
*
|
||||
* The flow to build a block with the Engine API is as follows:
|
||||
* 1. Send engine_forkchoiceUpdated(EngineForkchoiceUpdatedParameter, EnginePayloadAttributesParameter) request to Besu node
|
||||
* 2. Besu node responds with payloadId
|
||||
* The Besu Node will start building a proposed block
|
||||
*
|
||||
* 3. Send engine_getPayload(payloadId) request to Besu node
|
||||
* 4. Besu node responds with executionPayload
|
||||
* Get the proposed block from the Besu node
|
||||
*
|
||||
* 5. Send engine_newPayload request to Besu node
|
||||
* Validate the proposed block. Then store the validated block for future reference.
|
||||
* Unsure why the proposed block is not stored in the previous steps where it was built.
|
||||
*
|
||||
* 6. Send engine_forkchoiceUpdated(EngineForkchoiceUpdatedParameter) request to Besu node
|
||||
* Add validated block to blockchain head.
|
||||
*
|
||||
* @param blockTimestampSeconds The Unix timestamp (in seconds) to assign to the new block.
|
||||
* @param blockBuildingTimeMs The duration (in milliseconds) allocated for the Besu node to build the block.
|
||||
*/
|
||||
public void buildNewBlock(long blockTimestampSeconds, long blockBuildingTimeMs)
|
||||
throws IOException, InterruptedException {
|
||||
final EthBlock.Block latestBlock = node.execute(ethTransactions.block());
|
||||
|
||||
final Call buildBlockRequest =
|
||||
createForkChoiceRequest(latestBlock.getHash(), blockTimestampSeconds);
|
||||
|
||||
final String payloadId;
|
||||
try (final Response buildBlockResponse = buildBlockRequest.execute()) {
|
||||
// Ideally, we would deserialize directly into Besu native types such as
|
||||
// EngineUpdateForkchoiceResult and JsonRpcSuccessResponse. However, neither class
|
||||
// provides a default constructor or a constructor annotated with @JsonCreator.
|
||||
// As a result, deserializing them would require hefty boilerplate code (custom
|
||||
// deserializers and DTOs). To keep things simple and lightweight, we instead
|
||||
// parse the relevant fields manually from the expected JSON structure.
|
||||
payloadId =
|
||||
mapper
|
||||
.readTree(buildBlockResponse.body().string())
|
||||
.get("result")
|
||||
.get("payloadId")
|
||||
.asText();
|
||||
assertThat(payloadId).isNotEmpty();
|
||||
}
|
||||
|
||||
// This is required to give the Besu node time to build the block. As per the Engine API spec,
|
||||
// engine_forkChoice will begin the payload build process and engine_getPayload may stop the
|
||||
// payload build process. Besu node behaviour is to stop the payload build process on
|
||||
// engine_getPayload. So unfortunately we lack a means to inspect a payload in-building without
|
||||
// interrupting it. Hence we must be conservative and wait for the 'SECONDS_PER_SLOT' time,
|
||||
// especially for slower machines running the tests.
|
||||
// See - https://github.com/ethereum/execution-apis/blob/main/src/engine/paris.md
|
||||
Thread.sleep(blockBuildingTimeMs);
|
||||
|
||||
final Call getPayloadRequest = createGetPayloadRequest(payloadId);
|
||||
|
||||
final ObjectNode executionPayload;
|
||||
final ArrayNode executionRequests;
|
||||
final String newBlockHash;
|
||||
final String parentBeaconBlockRoot;
|
||||
try (final Response getPayloadResponse = getPayloadRequest.execute()) {
|
||||
assertThat(getPayloadResponse.code()).isEqualTo(200);
|
||||
JsonNode result = mapper.readTree(getPayloadResponse.body().string()).get("result");
|
||||
executionPayload = (ObjectNode) result.get("executionPayload");
|
||||
executionRequests = (ArrayNode) result.get("executionRequests");
|
||||
newBlockHash = executionPayload.get("blockHash").asText();
|
||||
parentBeaconBlockRoot = executionPayload.remove("parentBeaconBlockRoot").asText();
|
||||
assertThat(newBlockHash).isNotEmpty();
|
||||
}
|
||||
|
||||
final Call newPayloadRequest =
|
||||
createNewPayloadRequest(executionPayload, parentBeaconBlockRoot, executionRequests);
|
||||
|
||||
try (final Response newPayloadResponse = newPayloadRequest.execute()) {
|
||||
assertThat(newPayloadResponse.code()).isEqualTo(200);
|
||||
final String responseStatus =
|
||||
mapper.readTree(newPayloadResponse.body().string()).get("result").get("status").asText();
|
||||
assertThat(responseStatus).isEqualTo("VALID");
|
||||
}
|
||||
|
||||
final Call moveChainAheadRequest = createForkChoiceRequest(newBlockHash);
|
||||
|
||||
try (final Response moveChainAheadResponse = moveChainAheadRequest.execute()) {
|
||||
assertThat(moveChainAheadResponse.code()).isEqualTo(200);
|
||||
}
|
||||
}
|
||||
|
||||
private Call createForkChoiceRequest(final String blockHash) {
|
||||
return createForkChoiceRequest(blockHash, null);
|
||||
}
|
||||
|
||||
private Call createForkChoiceRequest(final String parentBlockHash, final Long blockTimestamp) {
|
||||
final Optional<Long> maybeTimeStamp = Optional.ofNullable(blockTimestamp);
|
||||
|
||||
// Construct the first param - EngineForkchoiceUpdatedParameter
|
||||
ArrayNode params = mapper.createArrayNode();
|
||||
ObjectNode forkchoiceState = mapper.createObjectNode();
|
||||
forkchoiceState.put("headBlockHash", parentBlockHash);
|
||||
forkchoiceState.put("safeBlockHash", parentBlockHash);
|
||||
forkchoiceState.put("finalizedBlockHash", parentBlockHash);
|
||||
params.add(forkchoiceState);
|
||||
|
||||
// Optionally construct the second param - EnginePayloadAttributesParameter
|
||||
if (maybeTimeStamp.isPresent()) {
|
||||
ObjectNode payloadAttributes = mapper.createObjectNode();
|
||||
payloadAttributes.put("timestamp", blockTimestamp);
|
||||
payloadAttributes.put("prevRandao", Hash.ZERO.toString());
|
||||
payloadAttributes.put("suggestedFeeRecipient", SUGGESTED_BLOCK_FEE_RECIPIENT);
|
||||
payloadAttributes.set("withdrawals", mapper.createArrayNode());
|
||||
payloadAttributes.put("parentBeaconBlockRoot", Hash.ZERO.toString());
|
||||
params.add(payloadAttributes);
|
||||
}
|
||||
return createEngineCall("engine_forkchoiceUpdatedV3", params);
|
||||
}
|
||||
|
||||
private Call createGetPayloadRequest(final String payloadId) {
|
||||
ArrayNode params = mapper.createArrayNode();
|
||||
params.add(payloadId);
|
||||
return createEngineCall("engine_getPayloadV4", params);
|
||||
}
|
||||
|
||||
private Call createNewPayloadRequest(
|
||||
final ObjectNode executionPayload,
|
||||
final String parentBeaconBlockRoot,
|
||||
final ArrayNode executionRequests) {
|
||||
ArrayNode params = mapper.createArrayNode();
|
||||
params.add(executionPayload);
|
||||
params.add(mapper.createArrayNode()); // empty withdrawals
|
||||
params.add(parentBeaconBlockRoot);
|
||||
params.add(executionRequests);
|
||||
|
||||
return createEngineCall("engine_newPayloadV4", params);
|
||||
}
|
||||
|
||||
private Call createEngineCall(final String rpcMethod, ArrayNode params) {
|
||||
ObjectNode request = mapper.createObjectNode();
|
||||
request.put("jsonrpc", JSONRPC_VERSION);
|
||||
request.put("method", rpcMethod);
|
||||
request.set("params", params);
|
||||
request.put("id", JSONRPC_REQUEST_ID);
|
||||
|
||||
String requestString;
|
||||
try {
|
||||
requestString = mapper.writeValueAsString(request);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(
|
||||
"Failed to serialize JSON-RPC request for method " + rpcMethod + ":", e);
|
||||
}
|
||||
|
||||
return httpClient.newCall(
|
||||
new Request.Builder()
|
||||
.url(node.engineRpcUrl().get())
|
||||
.post(
|
||||
RequestBody.create(
|
||||
requestString, MediaType.parse("application/json; charset=utf-8")))
|
||||
.build());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* Copyright Consensys Software Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
|
||||
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations under the License.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package org.hyperledger.besu.tests.acceptance.dsl;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class StaticNodesUtils {
|
||||
|
||||
public static Path createStaticNodesFile(final Path directory, final List<String> staticNodes) {
|
||||
try {
|
||||
final Path tempFile = Files.createTempFile(directory, "", "");
|
||||
tempFile.toFile().deleteOnExit();
|
||||
|
||||
final Path staticNodesFile = tempFile.getParent().resolve("static-nodes.json");
|
||||
Files.move(tempFile, staticNodesFile);
|
||||
staticNodesFile.toFile().deleteOnExit();
|
||||
|
||||
final String json =
|
||||
staticNodes.stream()
|
||||
.map(s -> String.format("\"%s\"", s))
|
||||
.collect(Collectors.joining(",", "[", "]"));
|
||||
|
||||
Files.writeString(staticNodesFile, json);
|
||||
|
||||
return staticNodesFile;
|
||||
} catch (final IOException e) {
|
||||
throw new IllegalStateException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* Copyright Consensys Software Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
|
||||
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations under the License.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package org.hyperledger.besu.tests.acceptance.dsl;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.awaitility.Awaitility;
|
||||
import org.awaitility.core.ThrowingRunnable;
|
||||
|
||||
/** Contains functionality for timeouts. */
|
||||
public class WaitUtils {
|
||||
public static void waitFor(final ThrowingRunnable condition) {
|
||||
waitFor(30, condition);
|
||||
}
|
||||
|
||||
public static void waitFor(final int timeout, final ThrowingRunnable condition) {
|
||||
Awaitility.await()
|
||||
.pollInterval(5, TimeUnit.SECONDS)
|
||||
.ignoreExceptions()
|
||||
.atMost(timeout, TimeUnit.SECONDS)
|
||||
.untilAsserted(condition);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,117 @@
|
||||
{
|
||||
"config": {
|
||||
"chainId": 1337,
|
||||
"petersburgBlock": 0,
|
||||
"istanbulBlock": 0,
|
||||
"berlinBlock": 0,
|
||||
"londonBlock": 0,
|
||||
"terminalTotalDifficulty":0,
|
||||
"cancunTime":0,
|
||||
"pragueTime":0,
|
||||
"blobSchedule": {
|
||||
"cancun": {
|
||||
"target": 0,
|
||||
"max": 0,
|
||||
"baseFeeUpdateFraction": 3338477
|
||||
},
|
||||
"prague": {
|
||||
"target": 0,
|
||||
"max": 0,
|
||||
"baseFeeUpdateFraction": 5007716
|
||||
},
|
||||
"osaka": {
|
||||
"target": 0,
|
||||
"max": 0,
|
||||
"baseFeeUpdateFraction": 5007716
|
||||
}
|
||||
},
|
||||
"clique": {
|
||||
"blockperiodseconds": %blockperiodseconds%,
|
||||
"epochlength": %epochlength%,
|
||||
"createemptyblocks": %createemptyblocks%
|
||||
},
|
||||
"depositContractAddress": "0x4242424242424242424242424242424242424242",
|
||||
"withdrawalRequestContractAddress": "0x00A3ca265EBcb825B45F985A16CEFB49958cE017",
|
||||
"consolidationRequestContractAddress": "0x00b42dbF2194e931E80326D950320f7d9Dbeac02"
|
||||
},
|
||||
"zeroBaseFee": false,
|
||||
"baseFeePerGas": "7",
|
||||
"nonce": "0x0",
|
||||
"timestamp": "0x0",
|
||||
"extraData": "%extraData%",
|
||||
"gasLimit": "0x1C9C380",
|
||||
"difficulty": "0x1",
|
||||
"mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
|
||||
"coinbase": "0x0000000000000000000000000000000000000000",
|
||||
"alloc": {
|
||||
"fe3b557e8fb62b89f4916b721be55ceb828dbd73": {
|
||||
"privateKey": "8f2a55949038a9610f50fb23b5883af3b4ecb3c3bb792cbcefbd1542c692be63",
|
||||
"comment": "private key and this comment are ignored. In a real chain, the private key should NOT be stored",
|
||||
"balance": "0xad78ebc5ac6200000"
|
||||
},
|
||||
"627306090abaB3A6e1400e9345bC60c78a8BEf57": {
|
||||
"privateKey": "c87509a1c067bbde78beb793e6fa76530b6382a4c0241e5e4a9ec0a0f44dc0d3",
|
||||
"comment": "private key and this comment are ignored. In a real chain, the private key should NOT be stored",
|
||||
"balance": "90000000000000000000000"
|
||||
},
|
||||
"f17f52151EbEF6C7334FAD080c5704D77216b732": {
|
||||
"privateKey": "ae6ae8e5ccbfb04590405997ee2d52d2b330726137b875053c36d94e974d162f",
|
||||
"comment": "private key and this comment are ignored. In a real chain, the private key should NOT be stored",
|
||||
"balance": "90000000000000000000000"
|
||||
},
|
||||
"a05b21E5186Ce93d2a226722b85D6e550Ac7D6E3": {
|
||||
"privateKey": "3a4ff6d22d7502ef2452368165422861c01a0f72f851793b372b87888dc3c453",
|
||||
"balance": "90000000000000000000000"
|
||||
},
|
||||
"8da48afC965480220a3dB9244771bd3afcB5d895": {
|
||||
"comment": "This account has signed a authorization for contract 0x0000000000000000000000000000000000009999 to send a 7702 transaction",
|
||||
"privateKey": "11f2e7b6a734ab03fa682450e0d4681d18a944f8b83c99bf7b9b4de6c0f35ea1",
|
||||
"balance": "90000000000000000000000"
|
||||
},
|
||||
"0x0000000000000000000000000000000000000666": {
|
||||
"comment": "Contract reverts immediately when called",
|
||||
"balance": "0",
|
||||
"code": "5F5FFD",
|
||||
"codeDecompiled": "PUSH0 PUSH0 REVERT",
|
||||
"storage": {}
|
||||
},
|
||||
"0x0000000000000000000000000000000000009999": {
|
||||
"comment": "Contract sends all its Ether to the address provided as a call data.",
|
||||
"balance": "0",
|
||||
"code": "5F5F5F5F475F355AF100",
|
||||
"codeDecompiled": "PUSH0 PUSH0 PUSH0 PUSH0 SELFBALANCE PUSH0 CALLDATALOAD GAS CALL STOP",
|
||||
"storage": {}
|
||||
},
|
||||
"0xa4664C40AACeBD82A2Db79f0ea36C06Bc6A19Adb": {
|
||||
"balance": "1000000000000000000000000000"
|
||||
},
|
||||
"0x23618e81E3f5cdF7f54C3d65f7FBc0aBf5B21E8f": {
|
||||
"comment": "This is the account used to sign the transaction that creates a validator exit",
|
||||
"balance": "1000000000000000000000000000"
|
||||
},
|
||||
"0x00A3ca265EBcb825B45F985A16CEFB49958cE017": {
|
||||
"comment": "This is the runtime bytecode for the Withdrawal Request Smart Contract. It was created from the generated alloc section of fork_Prague_blockchain_test_engine_single_block_single_withdrawal_request_from_contract spec test",
|
||||
"balance": "0",
|
||||
"code": "0x3373fffffffffffffffffffffffffffffffffffffffe1460c7573615156028575f545f5260205ff35b36603814156101f05760115f54807fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff146101f057600182026001905f5b5f821115608057810190830284830290049160010191906065565b9093900434106101f057600154600101600155600354806003026004013381556001015f35815560010160203590553360601b5f5260385f601437604c5fa0600101600355005b6003546002548082038060101160db575060105b5f5b81811461017f5780604c02838201600302600401805490600101805490600101549160601b83528260140152807fffffffffffffffffffffffffffffffff0000000000000000000000000000000016826034015260401c906044018160381c81600701538160301c81600601538160281c81600501538160201c81600401538160181c81600301538160101c81600201538160081c81600101535360010160dd565b9101809214610191579060025561019c565b90505f6002555f6003555b5f54807fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff14156101c957505f5b6001546002828201116101de5750505f6101e4565b01600290035b5f555f600155604c025ff35b5f5ffd",
|
||||
"storage": {
|
||||
"0x0000000000000000000000000000000000000000000000000000000000000000": "0000000000000000000000000000000000000000000000000000000000000000",
|
||||
"0x0000000000000000000000000000000000000000000000000000000000000001": "0000000000000000000000000000000000000000000000000000000000000001",
|
||||
"0x0000000000000000000000000000000000000000000000000000000000000002": "0000000000000000000000000000000000000000000000000000000000000000",
|
||||
"0x0000000000000000000000000000000000000000000000000000000000000003": "0000000000000000000000000000000000000000000000000000000000000001",
|
||||
"0x0000000000000000000000000000000000000000000000000000000000000004": "000000000000000000000000a4664C40AACeBD82A2Db79f0ea36C06Bc6A19Adb",
|
||||
"0x0000000000000000000000000000000000000000000000000000000000000005": "b10a4a15bf67b328c9b101d09e5c6ee6672978fdad9ef0d9e2ceffaee9922355",
|
||||
"0x0000000000000000000000000000000000000000000000000000000000000006": "5d8601f0cb3bcc4ce1af9864779a416e00000000000000000000000000000000"
|
||||
}
|
||||
},
|
||||
"0x00b42dbF2194e931E80326D950320f7d9Dbeac02": {
|
||||
"comment": "This is the runtime bytecode for the Consolidation Request Smart Contract",
|
||||
"nonce": "0x01",
|
||||
"balance": "0x00",
|
||||
"code": "0x3373fffffffffffffffffffffffffffffffffffffffe14604d57602036146024575f5ffd5b5f35801560495762001fff810690815414603c575f5ffd5b62001fff01545f5260205ff35b5f5ffd5b62001fff42064281555f359062001fff015500",
|
||||
"storage": {}
|
||||
}
|
||||
},
|
||||
"number": "0x0",
|
||||
"gasUsed": "0x0",
|
||||
"parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000"
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
{
|
||||
"config": {
|
||||
"chainId": 1337,
|
||||
"petersburgBlock": 0,
|
||||
"istanbulBlock": 0,
|
||||
"berlinBlock": 0,
|
||||
"londonBlock": 0,
|
||||
"clique": {
|
||||
"blockperiodseconds": %blockperiodseconds%,
|
||||
"epochlength": %epochlength%,
|
||||
"createemptyblocks": %createemptyblocks%
|
||||
}
|
||||
},
|
||||
"zeroBaseFee": false,
|
||||
"baseFeePerGas": "7",
|
||||
"nonce": "0x0",
|
||||
"timestamp": "0x6391BFF3",
|
||||
"extraData": "%extraData%",
|
||||
"gasLimit": "0x1C9C380",
|
||||
"difficulty": "0x1",
|
||||
"mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
|
||||
"coinbase": "0x0000000000000000000000000000000000000000",
|
||||
"alloc": {
|
||||
"fe3b557e8fb62b89f4916b721be55ceb828dbd73": {
|
||||
"privateKey": "8f2a55949038a9610f50fb23b5883af3b4ecb3c3bb792cbcefbd1542c692be63",
|
||||
"comment": "private key and this comment are ignored. In a real chain, the private key should NOT be stored",
|
||||
"balance": "0xad78ebc5ac6200000"
|
||||
},
|
||||
"627306090abaB3A6e1400e9345bC60c78a8BEf57": {
|
||||
"privateKey": "c87509a1c067bbde78beb793e6fa76530b6382a4c0241e5e4a9ec0a0f44dc0d3",
|
||||
"comment": "private key and this comment are ignored. In a real chain, the private key should NOT be stored",
|
||||
"balance": "90000000000000000000000"
|
||||
},
|
||||
"f17f52151EbEF6C7334FAD080c5704D77216b732": {
|
||||
"privateKey": "ae6ae8e5ccbfb04590405997ee2d52d2b330726137b875053c36d94e974d162f",
|
||||
"comment": "private key and this comment are ignored. In a real chain, the private key should NOT be stored",
|
||||
"balance": "90000000000000000000000"
|
||||
}
|
||||
},
|
||||
"number": "0x0",
|
||||
"gasUsed": "0x0",
|
||||
"parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000"
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
0x627306090abab3a6e1400e9345bc60c78a8bef57
|
||||
@@ -0,0 +1,25 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Configuration level="INFO">
|
||||
<Properties>
|
||||
<Property name="root.log.level">TRACE</Property>
|
||||
</Properties>
|
||||
<Appenders>
|
||||
<Console name="Console">
|
||||
<PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss.SSSZZZ} | %t | %-5level | %c{1} | %msg%n" />
|
||||
</Console>
|
||||
<Memory name="Memory">
|
||||
<PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss.SSSZZZ} | %t | %-5level | %c{1} | %msg%n" />
|
||||
</Memory>
|
||||
</Appenders>
|
||||
<Loggers>
|
||||
<Logger level="INFO" name="io.vertx" />
|
||||
<Logger level="INFO" name="io.netty" />
|
||||
<Logger level="INFO" name="org.web3j.protocol.http.HttpService" />
|
||||
<Logger level="INFO" name="org.hyperledger.besu.ethereum.p2p.network.DefaultP2PNetwork" />
|
||||
<Logger level="INFO" name="org.hyperledger.besu.ethereum.eth.manager.EthPeers" />
|
||||
<Root level="${sys:root.log.level}">
|
||||
<AppenderRef ref="Console" />
|
||||
<AppenderRef ref="Memory" />
|
||||
</Root>
|
||||
</Loggers>
|
||||
</Configuration>
|
||||
@@ -0,0 +1,82 @@
|
||||
##
|
||||
# This file specifies prover limit by each EVM module
|
||||
# WARN: The prover/arithmetization team has the owneship of this.
|
||||
# Changing this values may compromise the system.
|
||||
# issue: https://github.com/Consensys/zkevm-monorepo/issues/525
|
||||
##
|
||||
|
||||
[traces-limits]
|
||||
#
|
||||
# Arithmetization module limits
|
||||
#
|
||||
ADD = 524288
|
||||
BIN = 262144
|
||||
BLAKE_MODEXP_DATA = 16384
|
||||
BLOCK_DATA = 4096
|
||||
BLOCK_HASH = 2048
|
||||
EC_DATA = 262144
|
||||
EUC = 65536
|
||||
EXP = 8192
|
||||
EXT = 65536
|
||||
GAS = 65536
|
||||
HUB = 2097152
|
||||
LOG_DATA = 65536
|
||||
LOG_INFO = 4096
|
||||
MMIO = 4194304
|
||||
MMU = 4194304
|
||||
MOD = 131072
|
||||
MUL = 65536
|
||||
MXP = 524288
|
||||
OOB = 262144
|
||||
RLP_ADDR = 4096
|
||||
RLP_TXN = 131072
|
||||
RLP_TXN_RCPT = 65536
|
||||
ROM = 4194304
|
||||
ROM_LEX = 1024
|
||||
SHAKIRA_DATA = 32768
|
||||
SHF = 65536
|
||||
STP = 16384
|
||||
TRM = 32768
|
||||
TXN_DATA = 8192
|
||||
WCP = 262144
|
||||
# NOTE: in the original file the limits were just shy of powers of 2, e.g. ADD = 524286
|
||||
# Question: this seemed deliberate; it could be related to spillings; if so we may want
|
||||
# to reduce all limits above by the corresponding spillings value (or 16 for simplicity)
|
||||
|
||||
#
|
||||
# Precompiles limits
|
||||
# compare with https://github.com/Consensys/linea-arithmetization/issues/257
|
||||
#
|
||||
PRECOMPILE_ECRECOVER_EFFECTIVE_CALLS = 128
|
||||
PRECOMPILE_SHA2_BLOCKS = 671
|
||||
PRECOMPILE_RIPEMD_BLOCKS = 671
|
||||
PRECOMPILE_MODEXP_EFFECTIVE_CALLS = 4
|
||||
PRECOMPILE_ECADD_EFFECTIVE_CALLS = 512
|
||||
PRECOMPILE_ECMUL_EFFECTIVE_CALLS = 32
|
||||
PRECOMPILE_ECPAIRING_FINAL_EXPONENTIATIONS = 16
|
||||
PRECOMPILE_ECPAIRING_G2_MEMBERSHIP_CALLS = 64
|
||||
PRECOMPILE_ECPAIRING_MILLER_LOOPS = 64
|
||||
PRECOMPILE_BLAKE_EFFECTIVE_CALLS = 0 # there are no gnarks circuit atm
|
||||
PRECOMPILE_BLAKE_ROUNDS = 0 # it is possible to call BLAKE2f with r = 0; this is a nontrivial operation ...
|
||||
# # Notes:
|
||||
# - there are no IDENTITY related limits
|
||||
# - we used to have the following limits
|
||||
# * PRECOMPILE_SHA2_EFFECTIVE_CALLS = 1000000
|
||||
# * PRECOMPILE_RIPEMD_EFFECTIVE_CALLS = 1000000
|
||||
# (the values are nonsensical);
|
||||
# as per Alex they are not required by the prover;
|
||||
|
||||
#
|
||||
# Block-specific limits
|
||||
#
|
||||
BLOCK_KECCAK = 8192
|
||||
BLOCK_L1_SIZE = 1000000
|
||||
BLOCK_L2_L1_LOGS = 16
|
||||
BLOCK_TRANSACTIONS = 200 # max number of tx in an L2 block
|
||||
|
||||
#
|
||||
# Fixed size, static tables
|
||||
#
|
||||
BIN_REFERENCE_TABLE = 262144 # contains 3 * 256^2 + 256 data rows + 1 padding row
|
||||
SHF_REFERENCE_TABLE = 4096 # contains 9 * 256 data rows + 1 padding row
|
||||
INSTRUCTION_DECODER = 512 # contains 256 data rows + 1 padding row
|
||||
@@ -0,0 +1,75 @@
|
||||
|
||||
##
|
||||
# This file specifies prover limit by each EVM module
|
||||
# WARN: The prover/arithmetization team has the owneship of this.
|
||||
# Changing this values may compromise the system.
|
||||
# issue: https://github.com/Consensys/zkevm-monorepo/issues/525
|
||||
##
|
||||
|
||||
[traces-limits]
|
||||
#
|
||||
# Arithmetization module limits
|
||||
#
|
||||
ADD = 247
|
||||
BIN = 262144
|
||||
BLAKE_MODEXP_DATA = 262144
|
||||
BLOCK_DATA = 26
|
||||
BLOCK_HASH = 6
|
||||
EC_DATA = 4096
|
||||
EUC = 16384 # can probably be lower
|
||||
EXP = 32760
|
||||
EXT = 20
|
||||
GAS = 262144
|
||||
HUB = 321
|
||||
MMIO = 1048576
|
||||
MMU = 524288
|
||||
MOD = 23
|
||||
MUL = 20
|
||||
MXP = 311
|
||||
PHONEY_RLP = 65536 # can probably get lower
|
||||
ROM = 3009
|
||||
ROM_LEX = 20
|
||||
SHF = 63
|
||||
TX_RLP = 131072
|
||||
TRM = 120
|
||||
WCP = 700
|
||||
LOG_DATA = 20
|
||||
LOG_INFO = 20
|
||||
RLP_ADDR = 20
|
||||
RLP_TXN = 1590
|
||||
RLP_TXN_RCPT = 100
|
||||
TXN_DATA = 30
|
||||
SHAKIRA_DATA = 262144
|
||||
STP = 20
|
||||
OOB = 262144
|
||||
|
||||
#
|
||||
# Block-specific limits
|
||||
#
|
||||
BLOCK_KECCAK = 8192
|
||||
BLOCK_L1_SIZE = 1000000
|
||||
BLOCK_L2_L1_LOGS = 16
|
||||
BLOCK_TRANSACTIONS = 200 # max number of tx in an L2 block
|
||||
|
||||
#
|
||||
# Fixed size, static tables
|
||||
#
|
||||
BIN_REFERENCE_TABLE = 262144 # contains 3 * 256^2 + 256 data rows + 1 padding row
|
||||
SHF_REFERENCE_TABLE = 4096 # contains 9 * 256 data rows + 1 padding row
|
||||
INSTRUCTION_DECODER = 512 # contains 256 data rows + 1 padding row
|
||||
|
||||
#
|
||||
# Precompiles limits
|
||||
#
|
||||
PRECOMPILE_ECRECOVER_EFFECTIVE_CALLS = 10000
|
||||
PRECOMPILE_SHA2_BLOCKS = 10000
|
||||
PRECOMPILE_RIPEMD_BLOCKS = 0
|
||||
PRECOMPILE_ECPAIRING_MILLER_LOOPS = 10000
|
||||
PRECOMPILE_MODEXP_EFFECTIVE_CALLS = 10000
|
||||
PRECOMPILE_ECADD_EFFECTIVE_CALLS = 10000
|
||||
PRECOMPILE_ECMUL_EFFECTIVE_CALLS = 10000
|
||||
PRECOMPILE_ECPAIRING_FINAL_EXPONENTIATIONS = 10000
|
||||
PRECOMPILE_ECPAIRING_G2_MEMBERSHIP_CALLS = 10000
|
||||
PRECOMPILE_ECPAIRING_MILLER_LOOPS = 10000
|
||||
PRECOMPILE_BLAKE_EFFECTIVE_CALLS = 0 # there are no gnarks circuit atm
|
||||
PRECOMPILE_BLAKE_ROUNDS = 0
|
||||
@@ -0,0 +1,74 @@
|
||||
##
|
||||
# This file specifies prover limit by each EVM module
|
||||
# WARN: The prover/arithmetization team has the owneship of this.
|
||||
# Changing this values may compromise the system.
|
||||
# issue: https://github.com/Consensys/zkevm-monorepo/issues/525
|
||||
##
|
||||
|
||||
[traces-limits]
|
||||
#
|
||||
# Arithmetization module limits
|
||||
#
|
||||
ADD = 262144
|
||||
BIN = 262144
|
||||
BIN_RT = 262144
|
||||
BLAKE_MODEXP_DATA = 262144
|
||||
BLOCK_DATA = 26
|
||||
BLOCK_HASH = 6
|
||||
EC_DATA = 4096
|
||||
EUC = 16384 # can probably be lower
|
||||
EXP = 32760
|
||||
EXT = 32768
|
||||
GAS = 262144
|
||||
HUB = 2097152
|
||||
LOG_DATA = 262144
|
||||
LOG_INFO = 262144
|
||||
MMIO = 1048576
|
||||
MMU = 524288
|
||||
MOD = 131072
|
||||
MUL = 65536
|
||||
MXP = 524288
|
||||
OOB = 262144
|
||||
PHONEY_RLP = 65536 # can probably get lower
|
||||
RLP_ADDR = 262144
|
||||
RLP_TXN = 262144
|
||||
RLP_TXN_RCPT = 262144
|
||||
ROM = 1048576
|
||||
ROM_LEX = 1048576
|
||||
SHAKIRA_DATA = 262144
|
||||
SHF = 65536
|
||||
STP = 262144
|
||||
TRM = 131072
|
||||
TXN_DATA = 262144
|
||||
TX_RLP = 131072
|
||||
WCP = 262144
|
||||
|
||||
#
|
||||
# Fixed size, static tables
|
||||
#
|
||||
BIN_REFERENCE_TABLE = 262144 # contains 3 * 256^2 + 256 data rows + 1 padding row
|
||||
SHF_REFERENCE_TABLE = 4096 # contains 9 * 256 data rows + 1 padding row
|
||||
INSTRUCTION_DECODER = 512 # contains 256 data rows + 1 padding row
|
||||
|
||||
#
|
||||
# Block-specific limits
|
||||
#
|
||||
BLOCK_KECCAK = 8192
|
||||
BLOCK_L1_SIZE = 1000000
|
||||
BLOCK_L2_L1_LOGS = 16
|
||||
BLOCK_TRANSACTIONS = 200 # max number of tx in an L2 block
|
||||
|
||||
#
|
||||
# Precompiles limits
|
||||
#
|
||||
PRECOMPILE_ECRECOVER_EFFECTIVE_CALLS = 10000
|
||||
PRECOMPILE_SHA2_BLOCKS = 10000
|
||||
PRECOMPILE_RIPEMD_BLOCKS = 0
|
||||
PRECOMPILE_MODEXP_EFFECTIVE_CALLS = 10000
|
||||
PRECOMPILE_ECADD_EFFECTIVE_CALLS = 10000
|
||||
PRECOMPILE_ECMUL_EFFECTIVE_CALLS = 10000
|
||||
PRECOMPILE_ECPAIRING_FINAL_EXPONENTIATIONS = 10000
|
||||
PRECOMPILE_ECPAIRING_G2_MEMBERSHIP_CALLS = 10000
|
||||
PRECOMPILE_ECPAIRING_MILLER_LOOPS = 10000
|
||||
PRECOMPILE_BLAKE_EFFECTIVE_CALLS = 0
|
||||
PRECOMPILE_BLAKE_ROUNDS = 0
|
||||
@@ -0,0 +1,76 @@
|
||||
##
|
||||
# This file specifies prover limit by each EVM module
|
||||
# WARN: The prover/arithmetization team has the owneship of this.
|
||||
# Changing this values may compromise the system.
|
||||
# issue: https://github.com/Consensys/zkevm-monorepo/issues/525
|
||||
##
|
||||
|
||||
[traces-limits]
|
||||
#
|
||||
# Arithmetization module limits
|
||||
#
|
||||
ADD = 7000
|
||||
BIN = 262144
|
||||
BLAKE_MODEXP_DATA = 262144
|
||||
EC_DATA = 4096
|
||||
EUC = 16384 # can probably be lower
|
||||
EXP = 32760
|
||||
EXT = 32768
|
||||
GAS = 262144
|
||||
HUB = 10000
|
||||
MMIO = 1048576
|
||||
MMU = 524288
|
||||
MOD = 131072
|
||||
MUL = 65536
|
||||
MXP = 524288
|
||||
PHONEY_RLP = 65536 # can probably get lower
|
||||
PUB_LOG = 16384
|
||||
PUB_LOG_INFO = 16384
|
||||
ROM = 1048576
|
||||
ROM_LEX = 1048576
|
||||
SHF = 65536
|
||||
TX_RLP = 131072
|
||||
TRM = 131072
|
||||
WCP = 262144
|
||||
LOG_DATA = 262144
|
||||
LOG_INFO = 262144
|
||||
RLP_ADDR = 262144
|
||||
RLP_TXN = 262144
|
||||
RLP_TXN_RCPT = 262144
|
||||
TXN_DATA = 262144
|
||||
SHAKIRA_DATA = 262144
|
||||
STP = 262144
|
||||
OOB = 262144
|
||||
BLOCK_DATA = 26
|
||||
BLOCK_HASH = 6
|
||||
|
||||
#
|
||||
# Block-specific limits
|
||||
#
|
||||
BLOCK_KECCAK = 8192
|
||||
BLOCK_L1_SIZE = 1000000
|
||||
BLOCK_L2_L1_LOGS = 16
|
||||
BLOCK_TRANSACTIONS = 200 # max number of tx in an L2 block
|
||||
|
||||
#
|
||||
# Fixed size, static tables
|
||||
#
|
||||
BIN_REFERENCE_TABLE = 262144 # contains 3 * 256^2 + 256 data rows + 1 padding row
|
||||
SHF_REFERENCE_TABLE = 4096 # contains 9 * 256 data rows + 1 padding row
|
||||
INSTRUCTION_DECODER = 512 # contains 256 data rows + 1 padding row
|
||||
|
||||
#
|
||||
# Precompiles limits
|
||||
#
|
||||
PRECOMPILE_ECRECOVER_EFFECTIVE_CALLS = 10000
|
||||
PRECOMPILE_SHA2_BLOCKS = 10000
|
||||
PRECOMPILE_RIPEMD_BLOCKS = 10000
|
||||
PRECOMPILE_ECPAIRING_MILLER_LOOPS = 64
|
||||
PRECOMPILE_MODEXP_EFFECTIVE_CALLS = 10000
|
||||
PRECOMPILE_ECADD_EFFECTIVE_CALLS = 10000
|
||||
PRECOMPILE_ECMUL_EFFECTIVE_CALLS = 10000
|
||||
PRECOMPILE_ECPAIRING_FINAL_EXPONENTIATIONS = 10000
|
||||
PRECOMPILE_ECPAIRING_G2_MEMBERSHIP_CALLS = 10000
|
||||
PRECOMPILE_ECPAIRING_MILLER_LOOPS = 10000
|
||||
PRECOMPILE_BLAKE_EFFECTIVE_CALLS = 0
|
||||
PRECOMPILE_BLAKE_ROUNDS = 0
|
||||
@@ -0,0 +1,75 @@
|
||||
|
||||
##
|
||||
# This file specifies prover limit by each EVM module
|
||||
# WARN: The prover/arithmetization team has the owneship of this.
|
||||
# Changing this values may compromise the system.
|
||||
# issue: https://github.com/Consensys/zkevm-monorepo/issues/525
|
||||
##
|
||||
|
||||
[traces-limits]
|
||||
#
|
||||
# Arithmetization module limits
|
||||
#
|
||||
ADD = 70
|
||||
BIN = 262144
|
||||
BLAKE_MODEXP_DATA = 262144
|
||||
BLOCK_DATA = 26
|
||||
BLOCK_HASH = 6
|
||||
EC_DATA = 4096
|
||||
EUC = 16384 # can probably be lower
|
||||
EXP = 32760
|
||||
EXT = 20
|
||||
GAS = 262144
|
||||
HUB = 52
|
||||
MMIO = 1048576
|
||||
MMU = 524288
|
||||
MOD = 20
|
||||
MUL = 20
|
||||
MXP = 20
|
||||
PHONEY_RLP = 65536 # can probably get lower
|
||||
ROM = 2402
|
||||
ROM_LEX = 20
|
||||
SHF = 20
|
||||
TX_RLP = 131072
|
||||
TRM = 120
|
||||
WCP = 306
|
||||
LOG_DATA = 20
|
||||
LOG_INFO = 20
|
||||
RLP_ADDR = 20
|
||||
RLP_TXN = 1300
|
||||
RLP_TXN_RCPT = 100
|
||||
TXN_DATA = 30
|
||||
SHAKIRA_DATA = 262144
|
||||
STP = 20
|
||||
OOB = 262144
|
||||
|
||||
#
|
||||
# Block-specific limits
|
||||
#
|
||||
BLOCK_KECCAK = 8192
|
||||
BLOCK_L1_SIZE = 1000000
|
||||
BLOCK_L2_L1_LOGS = 16
|
||||
BLOCK_TRANSACTIONS = 200 # max number of tx in an L2 block
|
||||
|
||||
#
|
||||
# Fixed size, static tables
|
||||
#
|
||||
BIN_REFERENCE_TABLE = 262144 # contains 3 * 256^2 + 256 data rows + 1 padding row
|
||||
SHF_REFERENCE_TABLE = 4096 # contains 9 * 256 data rows + 1 padding row
|
||||
INSTRUCTION_DECODER = 512 # contains 256 data rows + 1 padding row
|
||||
|
||||
#
|
||||
# Precompiles limits
|
||||
#
|
||||
PRECOMPILE_ECRECOVER_EFFECTIVE_CALLS = 10000
|
||||
PRECOMPILE_SHA2_BLOCKS = 10000
|
||||
PRECOMPILE_RIPEMD_BLOCKS = 10000
|
||||
PRECOMPILE_ECPAIRING_MILLER_LOOPS = 10000
|
||||
PRECOMPILE_MODEXP_EFFECTIVE_CALLS = 10000
|
||||
PRECOMPILE_ECADD_EFFECTIVE_CALLS = 10000
|
||||
PRECOMPILE_ECMUL_EFFECTIVE_CALLS = 10000
|
||||
PRECOMPILE_ECPAIRING_FINAL_EXPONENTIATIONS = 10000
|
||||
PRECOMPILE_ECPAIRING_G2_MEMBERSHIP_CALLS = 10000
|
||||
PRECOMPILE_ECPAIRING_MILLER_LOOPS = 10000
|
||||
PRECOMPILE_BLAKE_EFFECTIVE_CALLS = 0
|
||||
PRECOMPILE_BLAKE_ROUNDS = 0
|
||||
@@ -0,0 +1,23 @@
|
||||
/*
|
||||
* Copyright Consensys Software Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
|
||||
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations under the License.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
pragma solidity >=0.7.0 <0.9.0;
|
||||
|
||||
import "./libs/ERC20/ERC20.sol";
|
||||
|
||||
contract AcceptanceTestToken is ERC20 {
|
||||
constructor() public ERC20("Acceptance Test", "AT") {
|
||||
_mint(msg.sender, 1000);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* Copyright Consensys Software Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
|
||||
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations under the License.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
pragma solidity >=0.7.0 <0.9.0;
|
||||
|
||||
contract DummyAdder {
|
||||
uint sum;
|
||||
|
||||
function add(int count) public {
|
||||
for (int i = 0; i < count; i++) {
|
||||
sum++;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
pragma solidity ^0.8.0;
|
||||
|
||||
contract EcAdd {
|
||||
function callEcAdd(bytes memory input)
|
||||
public
|
||||
view
|
||||
returns (bytes memory)
|
||||
{
|
||||
uint256 callDataSize = input.length;
|
||||
bytes memory output = new bytes(32); // Allocate memory for the output
|
||||
|
||||
// Several calls to ECADD per transaction
|
||||
uint callsPerTransaction = 32;
|
||||
for (uint i = 0; i < callsPerTransaction; i++) {
|
||||
assembly {
|
||||
let callDataOffset := add(input, 0x20) // Move pointer past length prefix to actual input
|
||||
let returnAtOffset := add(output, 0x20) // Move pointer past length prefix to store output
|
||||
|
||||
let success := staticcall(
|
||||
gas(),
|
||||
0x06, // ECADD address
|
||||
callDataOffset,
|
||||
callDataSize,
|
||||
returnAtOffset,
|
||||
0 // returnAtCapacity
|
||||
)
|
||||
}
|
||||
}
|
||||
return output;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
pragma solidity ^0.8.0;
|
||||
|
||||
contract EcMul {
|
||||
function callEcMul(bytes memory input)
|
||||
public
|
||||
view
|
||||
returns (bytes memory)
|
||||
{
|
||||
uint256 callDataSize = input.length;
|
||||
bytes memory output = new bytes(32); // Allocate memory for the output
|
||||
|
||||
assembly {
|
||||
let callDataOffset := add(input, 0x20) // Move pointer past length prefix to actual input
|
||||
let returnAtOffset := add(output, 0x20) // Move pointer past length prefix to store output
|
||||
|
||||
let success := staticcall(
|
||||
gas(),
|
||||
0x07, // ECMUL address
|
||||
callDataOffset,
|
||||
callDataSize,
|
||||
returnAtOffset,
|
||||
0 // returnAtCapacity
|
||||
)
|
||||
}
|
||||
return output;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
pragma solidity ^0.8.0;
|
||||
|
||||
contract EcPairing {
|
||||
function callEcPairing(bytes memory input)
|
||||
public
|
||||
view
|
||||
returns (bytes memory)
|
||||
{
|
||||
uint256 callDataSize = input.length;
|
||||
bytes memory output = new bytes(32); // Allocate memory for the output
|
||||
|
||||
assembly {
|
||||
let callDataOffset := add(input, 0x20) // Move pointer past length prefix to actual input
|
||||
let returnAtOffset := add(output, 0x20) // Move pointer past length prefix to store output
|
||||
|
||||
let success := staticcall(
|
||||
gas(),
|
||||
0x08, // ECPAIRING address
|
||||
callDataOffset,
|
||||
callDataSize,
|
||||
returnAtOffset,
|
||||
0 // returnAtCapacity
|
||||
)
|
||||
}
|
||||
return output;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
pragma solidity ^0.8.0;
|
||||
|
||||
contract EcRecover {
|
||||
function callEcRecover(bytes memory input)
|
||||
public
|
||||
view
|
||||
returns (bytes memory)
|
||||
{
|
||||
uint256 callDataSize = input.length;
|
||||
bytes memory output = new bytes(32); // Allocate memory for the output
|
||||
|
||||
assembly {
|
||||
let callDataOffset := add(input, 0x20) // Move pointer past length prefix to actual input
|
||||
let returnAtOffset := add(output, 0x20) // Move pointer past length prefix to store output
|
||||
|
||||
let success := staticcall(
|
||||
gas(),
|
||||
0x01, // ECRECOVER address
|
||||
callDataOffset,
|
||||
callDataSize,
|
||||
returnAtOffset,
|
||||
0 // returnAtCapacity
|
||||
)
|
||||
}
|
||||
return output;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
pragma solidity ^0.8.0;
|
||||
|
||||
contract ExcludedPrecompiles {
|
||||
function callRIPEMD160(bytes memory data) public view returns (bytes20 result) {
|
||||
// The RIPEMD-160 precompile is located at address 0x3
|
||||
address ripemdPrecompile = address(0x3);
|
||||
|
||||
// Prepare the input data
|
||||
bytes memory input = data;
|
||||
|
||||
// Prepare variables for the assembly call
|
||||
bool success;
|
||||
|
||||
// Use inline assembly to call the precompile
|
||||
assembly {
|
||||
// Call the precompile
|
||||
// Arguments: gas, address, input offset, input size, output offset, output size
|
||||
success := staticcall(gas(), ripemdPrecompile, add(input, 32), mload(input), result, 20)
|
||||
}
|
||||
|
||||
// Check if the call was successful
|
||||
require(success, "RIPEMD-160 call failed");
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
function callBlake2f(
|
||||
uint32 rounds,
|
||||
bytes32[2] memory h,
|
||||
bytes32[4] memory m,
|
||||
bytes8[2] memory t,
|
||||
bool f
|
||||
) public view returns (bytes32[2] memory) {
|
||||
// Blake2f precompile address
|
||||
address BLAKE2F_PRECOMPILE = address(0x09);
|
||||
|
||||
bytes memory input = abi.encodePacked(
|
||||
rounds,
|
||||
h[0], h[1],
|
||||
m[0], m[1], m[2], m[3],
|
||||
t[0], t[1],
|
||||
f ? bytes1(0x01) : bytes1(0x00)
|
||||
);
|
||||
|
||||
(bool success, bytes memory result) = BLAKE2F_PRECOMPILE.staticcall(input);
|
||||
require(success, "Blake2f precompile call failed");
|
||||
|
||||
bytes32[2] memory output;
|
||||
assembly {
|
||||
mstore(output, mload(add(result, 32)))
|
||||
mstore(add(output, 32), mload(add(result, 64)))
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
pragma solidity ^0.8.0;
|
||||
|
||||
// example of input:
|
||||
// 0x000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000001aabbcc
|
||||
contract ModExp {
|
||||
function callModExp(bytes memory input)
|
||||
public
|
||||
view
|
||||
returns (bytes memory)
|
||||
{
|
||||
uint256 callDataSize = input.length;
|
||||
bytes memory output = new bytes(32); // Allocate memory for the output
|
||||
|
||||
assembly {
|
||||
let callDataOffset := add(input, 0x20) // Move pointer past length prefix to actual input
|
||||
let returnAtOffset := add(output, 0x20) // Move pointer past length prefix to store output
|
||||
|
||||
let success := staticcall(
|
||||
gas(),
|
||||
0x05, // MODEXP address
|
||||
callDataOffset,
|
||||
callDataSize,
|
||||
returnAtOffset,
|
||||
0 // returnAtCapacity
|
||||
)
|
||||
}
|
||||
return output;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
pragma solidity ^0.8.0;
|
||||
|
||||
contract MulmodExecutor {
|
||||
|
||||
// This function executes a loop with a high number of mulmod operations.
|
||||
// The input parameter `iterations` controls how many times the loop runs.
|
||||
function executeMulmod(uint256 iterations) public pure returns (uint256) {
|
||||
uint256 result = 1; // Start with 1 to avoid multiplying by zero
|
||||
uint256 result2 = 1;
|
||||
uint256 result3 = 1;
|
||||
uint256 result4 = 1;
|
||||
uint256 result5 = 1;
|
||||
for (uint256 i = 1; i <= iterations; i++) {
|
||||
// Perform the mulmod operation
|
||||
result = mulmod(result, i, 2**255); // MULMOD opcode
|
||||
result2 = mulmod(result, i, 2**254);
|
||||
result3 = mulmod(result, i, 2**253);
|
||||
result4 = mulmod(result, i, 2**252);
|
||||
result5 = mulmod(result, i, 2**251);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* Copyright Consensys Software Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
|
||||
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations under the License.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
pragma solidity >=0.7.0 <0.9.0;
|
||||
|
||||
contract RevertExample {
|
||||
uint256 public value;
|
||||
|
||||
function setValue(uint256 _newValue) public {
|
||||
require(_newValue != 0, "Value cannot be zero");
|
||||
value = _newValue;
|
||||
}
|
||||
|
||||
function forceRevert() public pure {
|
||||
revert("This function always reverts");
|
||||
}
|
||||
|
||||
function conditionalRevert(uint256 _input) public pure returns (uint256) {
|
||||
if (_input < 10) {
|
||||
revert("Input must be 10 or greater");
|
||||
}
|
||||
return _input * 2;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
* Copyright Consensys Software Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
|
||||
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations under the License.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
pragma solidity >=0.7.0 <0.9.0;
|
||||
|
||||
contract SimpleStorage {
|
||||
string data;
|
||||
|
||||
function set(string memory value) public {
|
||||
require(bytes(value).length != 0);
|
||||
data = value;
|
||||
}
|
||||
|
||||
function get() public view returns (string memory) {
|
||||
return data;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
// OpenZeppelin Contracts (last updated v4.9.4) (utils/Context.sol)
|
||||
|
||||
pragma solidity ^0.8.0;
|
||||
|
||||
/**
|
||||
* @dev Provides information about the current execution context, including the
|
||||
* sender of the transaction and its data. While these are generally available
|
||||
* via msg.sender and msg.data, they should not be accessed in such a direct
|
||||
* manner, since when dealing with meta-transactions the account sending and
|
||||
* paying for execution may not be the actual sender (as far as an application
|
||||
* is concerned).
|
||||
*
|
||||
* This contract is only required for intermediate, library-like contracts.
|
||||
*/
|
||||
abstract contract Context {
|
||||
function _msgSender() internal view virtual returns (address) {
|
||||
return msg.sender;
|
||||
}
|
||||
|
||||
function _msgData() internal view virtual returns (bytes calldata) {
|
||||
return msg.data;
|
||||
}
|
||||
|
||||
function _contextSuffixLength() internal view virtual returns (uint256) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,365 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
// OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/ERC20.sol)
|
||||
|
||||
pragma solidity ^0.8.0;
|
||||
|
||||
import "./IERC20.sol";
|
||||
import "./IERC20Metadata.sol";
|
||||
import "./Context.sol";
|
||||
|
||||
/**
|
||||
* @dev Implementation of the {IERC20} interface.
|
||||
*
|
||||
* This implementation is agnostic to the way tokens are created. This means
|
||||
* that a supply mechanism has to be added in a derived contract using {_mint}.
|
||||
* For a generic mechanism see {ERC20PresetMinterPauser}.
|
||||
*
|
||||
* TIP: For a detailed writeup see our guide
|
||||
* https://forum.openzeppelin.com/t/how-to-implement-erc20-supply-mechanisms/226[How
|
||||
* to implement supply mechanisms].
|
||||
*
|
||||
* The default value of {decimals} is 18. To change this, you should override
|
||||
* this function so it returns a different value.
|
||||
*
|
||||
* We have followed general OpenZeppelin Contracts guidelines: functions revert
|
||||
* instead returning `false` on failure. This behavior is nonetheless
|
||||
* conventional and does not conflict with the expectations of ERC20
|
||||
* applications.
|
||||
*
|
||||
* Additionally, an {Approval} event is emitted on calls to {transferFrom}.
|
||||
* This allows applications to reconstruct the allowance for all accounts just
|
||||
* by listening to said events. Other implementations of the EIP may not emit
|
||||
* these events, as it isn't required by the specification.
|
||||
*
|
||||
* Finally, the non-standard {decreaseAllowance} and {increaseAllowance}
|
||||
* functions have been added to mitigate the well-known issues around setting
|
||||
* allowances. See {IERC20-approve}.
|
||||
*/
|
||||
contract ERC20 is Context, IERC20, IERC20Metadata {
|
||||
mapping(address => uint256) private _balances;
|
||||
|
||||
mapping(address => mapping(address => uint256)) private _allowances;
|
||||
|
||||
uint256 private _totalSupply;
|
||||
|
||||
string private _name;
|
||||
string private _symbol;
|
||||
|
||||
/**
|
||||
* @dev Sets the values for {name} and {symbol}.
|
||||
*
|
||||
* All two of these values are immutable: they can only be set once during
|
||||
* construction.
|
||||
*/
|
||||
constructor(string memory name_, string memory symbol_) {
|
||||
_name = name_;
|
||||
_symbol = symbol_;
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Returns the name of the token.
|
||||
*/
|
||||
function name() public view virtual override returns (string memory) {
|
||||
return _name;
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Returns the symbol of the token, usually a shorter version of the
|
||||
* name.
|
||||
*/
|
||||
function symbol() public view virtual override returns (string memory) {
|
||||
return _symbol;
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Returns the number of decimals used to get its user representation.
|
||||
* For example, if `decimals` equals `2`, a balance of `505` tokens should
|
||||
* be displayed to a user as `5.05` (`505 / 10 ** 2`).
|
||||
*
|
||||
* Tokens usually opt for a value of 18, imitating the relationship between
|
||||
* Ether and Wei. This is the default value returned by this function, unless
|
||||
* it's overridden.
|
||||
*
|
||||
* NOTE: This information is only used for _display_ purposes: it in
|
||||
* no way affects any of the arithmetic of the contract, including
|
||||
* {IERC20-balanceOf} and {IERC20-transfer}.
|
||||
*/
|
||||
function decimals() public view virtual override returns (uint8) {
|
||||
return 18;
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev See {IERC20-totalSupply}.
|
||||
*/
|
||||
function totalSupply() public view virtual override returns (uint256) {
|
||||
return _totalSupply;
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev See {IERC20-balanceOf}.
|
||||
*/
|
||||
function balanceOf(address account) public view virtual override returns (uint256) {
|
||||
return _balances[account];
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev See {IERC20-transfer}.
|
||||
*
|
||||
* Requirements:
|
||||
*
|
||||
* - `to` cannot be the zero address.
|
||||
* - the caller must have a balance of at least `amount`.
|
||||
*/
|
||||
function transfer(address to, uint256 amount) public virtual override returns (bool) {
|
||||
address owner = _msgSender();
|
||||
_transfer(owner, to, amount);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev See {IERC20-allowance}.
|
||||
*/
|
||||
function allowance(address owner, address spender) public view virtual override returns (uint256) {
|
||||
return _allowances[owner][spender];
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev See {IERC20-approve}.
|
||||
*
|
||||
* NOTE: If `amount` is the maximum `uint256`, the allowance is not updated on
|
||||
* `transferFrom`. This is semantically equivalent to an infinite approval.
|
||||
*
|
||||
* Requirements:
|
||||
*
|
||||
* - `spender` cannot be the zero address.
|
||||
*/
|
||||
function approve(address spender, uint256 amount) public virtual override returns (bool) {
|
||||
address owner = _msgSender();
|
||||
_approve(owner, spender, amount);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev See {IERC20-transferFrom}.
|
||||
*
|
||||
* Emits an {Approval} event indicating the updated allowance. This is not
|
||||
* required by the EIP. See the note at the beginning of {ERC20}.
|
||||
*
|
||||
* NOTE: Does not update the allowance if the current allowance
|
||||
* is the maximum `uint256`.
|
||||
*
|
||||
* Requirements:
|
||||
*
|
||||
* - `from` and `to` cannot be the zero address.
|
||||
* - `from` must have a balance of at least `amount`.
|
||||
* - the caller must have allowance for ``from``'s tokens of at least
|
||||
* `amount`.
|
||||
*/
|
||||
function transferFrom(address from, address to, uint256 amount) public virtual override returns (bool) {
|
||||
address spender = _msgSender();
|
||||
_spendAllowance(from, spender, amount);
|
||||
_transfer(from, to, amount);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Atomically increases the allowance granted to `spender` by the caller.
|
||||
*
|
||||
* This is an alternative to {approve} that can be used as a mitigation for
|
||||
* problems described in {IERC20-approve}.
|
||||
*
|
||||
* Emits an {Approval} event indicating the updated allowance.
|
||||
*
|
||||
* Requirements:
|
||||
*
|
||||
* - `spender` cannot be the zero address.
|
||||
*/
|
||||
function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) {
|
||||
address owner = _msgSender();
|
||||
_approve(owner, spender, allowance(owner, spender) + addedValue);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Atomically decreases the allowance granted to `spender` by the caller.
|
||||
*
|
||||
* This is an alternative to {approve} that can be used as a mitigation for
|
||||
* problems described in {IERC20-approve}.
|
||||
*
|
||||
* Emits an {Approval} event indicating the updated allowance.
|
||||
*
|
||||
* Requirements:
|
||||
*
|
||||
* - `spender` cannot be the zero address.
|
||||
* - `spender` must have allowance for the caller of at least
|
||||
* `subtractedValue`.
|
||||
*/
|
||||
function decreaseAllowance(address spender, uint256 subtractedValue) public virtual returns (bool) {
|
||||
address owner = _msgSender();
|
||||
uint256 currentAllowance = allowance(owner, spender);
|
||||
require(currentAllowance >= subtractedValue, "ERC20: decreased allowance below zero");
|
||||
unchecked {
|
||||
_approve(owner, spender, currentAllowance - subtractedValue);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Moves `amount` of tokens from `from` to `to`.
|
||||
*
|
||||
* This internal function is equivalent to {transfer}, and can be used to
|
||||
* e.g. implement automatic token fees, slashing mechanisms, etc.
|
||||
*
|
||||
* Emits a {Transfer} event.
|
||||
*
|
||||
* Requirements:
|
||||
*
|
||||
* - `from` cannot be the zero address.
|
||||
* - `to` cannot be the zero address.
|
||||
* - `from` must have a balance of at least `amount`.
|
||||
*/
|
||||
function _transfer(address from, address to, uint256 amount) internal virtual {
|
||||
require(from != address(0), "ERC20: transfer from the zero address");
|
||||
require(to != address(0), "ERC20: transfer to the zero address");
|
||||
|
||||
_beforeTokenTransfer(from, to, amount);
|
||||
|
||||
uint256 fromBalance = _balances[from];
|
||||
require(fromBalance >= amount, "ERC20: transfer amount exceeds balance");
|
||||
unchecked {
|
||||
_balances[from] = fromBalance - amount;
|
||||
// Overflow not possible: the sum of all balances is capped by totalSupply, and the sum is preserved by
|
||||
// decrementing then incrementing.
|
||||
_balances[to] += amount;
|
||||
}
|
||||
|
||||
emit Transfer(from, to, amount);
|
||||
|
||||
_afterTokenTransfer(from, to, amount);
|
||||
}
|
||||
|
||||
/** @dev Creates `amount` tokens and assigns them to `account`, increasing
|
||||
* the total supply.
|
||||
*
|
||||
* Emits a {Transfer} event with `from` set to the zero address.
|
||||
*
|
||||
* Requirements:
|
||||
*
|
||||
* - `account` cannot be the zero address.
|
||||
*/
|
||||
function _mint(address account, uint256 amount) internal virtual {
|
||||
require(account != address(0), "ERC20: mint to the zero address");
|
||||
|
||||
_beforeTokenTransfer(address(0), account, amount);
|
||||
|
||||
_totalSupply += amount;
|
||||
unchecked {
|
||||
// Overflow not possible: balance + amount is at most totalSupply + amount, which is checked above.
|
||||
_balances[account] += amount;
|
||||
}
|
||||
emit Transfer(address(0), account, amount);
|
||||
|
||||
_afterTokenTransfer(address(0), account, amount);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Destroys `amount` tokens from `account`, reducing the
|
||||
* total supply.
|
||||
*
|
||||
* Emits a {Transfer} event with `to` set to the zero address.
|
||||
*
|
||||
* Requirements:
|
||||
*
|
||||
* - `account` cannot be the zero address.
|
||||
* - `account` must have at least `amount` tokens.
|
||||
*/
|
||||
function _burn(address account, uint256 amount) internal virtual {
|
||||
require(account != address(0), "ERC20: burn from the zero address");
|
||||
|
||||
_beforeTokenTransfer(account, address(0), amount);
|
||||
|
||||
uint256 accountBalance = _balances[account];
|
||||
require(accountBalance >= amount, "ERC20: burn amount exceeds balance");
|
||||
unchecked {
|
||||
_balances[account] = accountBalance - amount;
|
||||
// Overflow not possible: amount <= accountBalance <= totalSupply.
|
||||
_totalSupply -= amount;
|
||||
}
|
||||
|
||||
emit Transfer(account, address(0), amount);
|
||||
|
||||
_afterTokenTransfer(account, address(0), amount);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Sets `amount` as the allowance of `spender` over the `owner` s tokens.
|
||||
*
|
||||
* This internal function is equivalent to `approve`, and can be used to
|
||||
* e.g. set automatic allowances for certain subsystems, etc.
|
||||
*
|
||||
* Emits an {Approval} event.
|
||||
*
|
||||
* Requirements:
|
||||
*
|
||||
* - `owner` cannot be the zero address.
|
||||
* - `spender` cannot be the zero address.
|
||||
*/
|
||||
function _approve(address owner, address spender, uint256 amount) internal virtual {
|
||||
require(owner != address(0), "ERC20: approve from the zero address");
|
||||
require(spender != address(0), "ERC20: approve to the zero address");
|
||||
|
||||
_allowances[owner][spender] = amount;
|
||||
emit Approval(owner, spender, amount);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Updates `owner` s allowance for `spender` based on spent `amount`.
|
||||
*
|
||||
* Does not update the allowance amount in case of infinite allowance.
|
||||
* Revert if not enough allowance is available.
|
||||
*
|
||||
* Might emit an {Approval} event.
|
||||
*/
|
||||
function _spendAllowance(address owner, address spender, uint256 amount) internal virtual {
|
||||
uint256 currentAllowance = allowance(owner, spender);
|
||||
if (currentAllowance != type(uint256).max) {
|
||||
require(currentAllowance >= amount, "ERC20: insufficient allowance");
|
||||
unchecked {
|
||||
_approve(owner, spender, currentAllowance - amount);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Hook that is called before any transfer of tokens. This includes
|
||||
* minting and burning.
|
||||
*
|
||||
* Calling conditions:
|
||||
*
|
||||
* - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens
|
||||
* will be transferred to `to`.
|
||||
* - when `from` is zero, `amount` tokens will be minted for `to`.
|
||||
* - when `to` is zero, `amount` of ``from``'s tokens will be burned.
|
||||
* - `from` and `to` are never both zero.
|
||||
*
|
||||
* To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
|
||||
*/
|
||||
function _beforeTokenTransfer(address from, address to, uint256 amount) internal virtual {}
|
||||
|
||||
/**
|
||||
* @dev Hook that is called after any transfer of tokens. This includes
|
||||
* minting and burning.
|
||||
*
|
||||
* Calling conditions:
|
||||
*
|
||||
* - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens
|
||||
* has been transferred to `to`.
|
||||
* - when `from` is zero, `amount` tokens have been minted for `to`.
|
||||
* - when `to` is zero, `amount` of ``from``'s tokens have been burned.
|
||||
* - `from` and `to` are never both zero.
|
||||
*
|
||||
* To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
|
||||
*/
|
||||
function _afterTokenTransfer(address from, address to, uint256 amount) internal virtual {}
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
// OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/IERC20.sol)
|
||||
|
||||
pragma solidity ^0.8.0;
|
||||
|
||||
/**
|
||||
* @dev Interface of the ERC20 standard as defined in the EIP.
|
||||
*/
|
||||
interface IERC20 {
|
||||
/**
|
||||
* @dev Emitted when `value` tokens are moved from one account (`from`) to
|
||||
* another (`to`).
|
||||
*
|
||||
* Note that `value` may be zero.
|
||||
*/
|
||||
event Transfer(address indexed from, address indexed to, uint256 value);
|
||||
|
||||
/**
|
||||
* @dev Emitted when the allowance of a `spender` for an `owner` is set by
|
||||
* a call to {approve}. `value` is the new allowance.
|
||||
*/
|
||||
event Approval(address indexed owner, address indexed spender, uint256 value);
|
||||
|
||||
/**
|
||||
* @dev Returns the amount of tokens in existence.
|
||||
*/
|
||||
function totalSupply() external view returns (uint256);
|
||||
|
||||
/**
|
||||
* @dev Returns the amount of tokens owned by `account`.
|
||||
*/
|
||||
function balanceOf(address account) external view returns (uint256);
|
||||
|
||||
/**
|
||||
* @dev Moves `amount` tokens from the caller's account to `to`.
|
||||
*
|
||||
* Returns a boolean value indicating whether the operation succeeded.
|
||||
*
|
||||
* Emits a {Transfer} event.
|
||||
*/
|
||||
function transfer(address to, uint256 amount) external returns (bool);
|
||||
|
||||
/**
|
||||
* @dev Returns the remaining number of tokens that `spender` will be
|
||||
* allowed to spend on behalf of `owner` through {transferFrom}. This is
|
||||
* zero by default.
|
||||
*
|
||||
* This value changes when {approve} or {transferFrom} are called.
|
||||
*/
|
||||
function allowance(address owner, address spender) external view returns (uint256);
|
||||
|
||||
/**
|
||||
* @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
|
||||
*
|
||||
* Returns a boolean value indicating whether the operation succeeded.
|
||||
*
|
||||
* IMPORTANT: Beware that changing an allowance with this method brings the risk
|
||||
* that someone may use both the old and the new allowance by unfortunate
|
||||
* transaction ordering. One possible solution to mitigate this race
|
||||
* condition is to first reduce the spender's allowance to 0 and set the
|
||||
* desired value afterwards:
|
||||
* https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
|
||||
*
|
||||
* Emits an {Approval} event.
|
||||
*/
|
||||
function approve(address spender, uint256 amount) external returns (bool);
|
||||
|
||||
/**
|
||||
* @dev Moves `amount` tokens from `from` to `to` using the
|
||||
* allowance mechanism. `amount` is then deducted from the caller's
|
||||
* allowance.
|
||||
*
|
||||
* Returns a boolean value indicating whether the operation succeeded.
|
||||
*
|
||||
* Emits a {Transfer} event.
|
||||
*/
|
||||
function transferFrom(address from, address to, uint256 amount) external returns (bool);
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
// OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/IERC20Metadata.sol)
|
||||
|
||||
pragma solidity ^0.8.0;
|
||||
|
||||
import "./IERC20.sol";
|
||||
|
||||
/**
|
||||
* @dev Interface for the optional metadata functions from the ERC20 standard.
|
||||
*
|
||||
* _Available since v4.1._
|
||||
*/
|
||||
interface IERC20Metadata is IERC20 {
|
||||
/**
|
||||
* @dev Returns the name of the token.
|
||||
*/
|
||||
function name() external view returns (string memory);
|
||||
|
||||
/**
|
||||
* @dev Returns the symbol of the token.
|
||||
*/
|
||||
function symbol() external view returns (string memory);
|
||||
|
||||
/**
|
||||
* @dev Returns the decimals places of the token.
|
||||
*/
|
||||
function decimals() external view returns (uint8);
|
||||
}
|
||||
42
besu-plugins/linea-sequencer/build.gradle
Normal file
42
besu-plugins/linea-sequencer/build.gradle
Normal file
@@ -0,0 +1,42 @@
|
||||
import com.github.jk1.license.filter.LicenseBundleNormalizer
|
||||
|
||||
buildscript {
|
||||
ext {
|
||||
distributionIdentifier = "linea-sequencer"
|
||||
releaseVersion = "v2.1-rc16.2.4"
|
||||
distributionBaseUrl = "https://github.com/Consensys/linea-besu-upstream/releases/download/"
|
||||
besuIdentifier = "besu-${libs.versions.besu.get()}"
|
||||
besuFilename = "${besuIdentifier}.tar.gz"
|
||||
besuUrl = "${distributionBaseUrl}${libs.versions.besu.get()}/${besuFilename}"
|
||||
besuPluginsIdentifier = "${distributionIdentifier}-${version}"
|
||||
besuPluginDir = File.createTempDir("plugins")
|
||||
}
|
||||
}
|
||||
|
||||
plugins {
|
||||
id 'java-library'
|
||||
alias(libs.plugins.dependencyLicenseReport)
|
||||
}
|
||||
|
||||
licenseReport {
|
||||
// This is for the allowed-licenses-file in checkLicense Task
|
||||
// Accepts File, URL or String path to local or remote file
|
||||
allowedLicensesFile = project.file("gradle/allowed-licenses.json")
|
||||
|
||||
excludes = []
|
||||
|
||||
// If set to true, then all boms will be excluded from the report
|
||||
excludeBoms = true
|
||||
|
||||
filters = [
|
||||
new LicenseBundleNormalizer(bundlePath: project.file("gradle/license-normalizer-bundle.json"))
|
||||
]
|
||||
}
|
||||
|
||||
build {
|
||||
dependsOn checkLicense
|
||||
}
|
||||
|
||||
jar {
|
||||
enabled = false
|
||||
}
|
||||
9
besu-plugins/linea-sequencer/docs/plugin-release.md
Normal file
9
besu-plugins/linea-sequencer/docs/plugin-release.md
Normal file
@@ -0,0 +1,9 @@
|
||||
## Plugin release process
|
||||
|
||||
Here are the steps for releasing a new version of the plugins:
|
||||
1. Create a tag with the release version number in the format vX.Y.Z (e.g., v0.2.0 creates a release version 0.2.0).
|
||||
2. Push the tag to the repository.
|
||||
3. GitHub Actions will automatically create a draft release for the release tag.
|
||||
4. Once the release workflow completes, update the release notes, uncheck "Draft", and publish the release.
|
||||
|
||||
Note: Release tags (of the form v*) are protected and can only be pushed by organization and/or repository owners.
|
||||
145
besu-plugins/linea-sequencer/docs/plugins.md
Normal file
145
besu-plugins/linea-sequencer/docs/plugins.md
Normal file
@@ -0,0 +1,145 @@
|
||||
# Linea plugins
|
||||
|
||||
## Shared components
|
||||
|
||||
### Profitability calculator
|
||||
The profitability calculator is a shared component, that is used to check if a tx is profitable.
|
||||
It's applied, with different configuration to:
|
||||
1. `linea_estimateGas` endpoint
|
||||
2. Tx validation for the txpool (if tx profitability check is enabled)
|
||||
3. Tx selection during block creation
|
||||
|
||||
#### CLI options
|
||||
|
||||
| Command Line Argument | Default Value |
|
||||
|-------------------------------------------------------|---------------|
|
||||
| `--plugin-linea-fixed-gas-cost-wei` | 0 |
|
||||
| `--plugin-linea-variable-gas-cost-wei` | 1_000_000_000 |
|
||||
| `--plugin-linea-extra-data-pricing-enabled` | false |
|
||||
| `--plugin-linea-min-margin` | 1.0 |
|
||||
| `--plugin-linea-estimate-gas-min-margin` | 1.0 |
|
||||
| `--plugin-linea-tx-pool-min-margin` | 0.5 |
|
||||
| `--plugin-linea-extra-data-set-min-gas-price-enabled` | true |
|
||||
|
||||
|
||||
### Module line count validator
|
||||
The Module line count validator is a shared component, that is used to check if a tx exceeds any of the configured line count limits.
|
||||
It is used in:
|
||||
1. `linea_estimateGas` endpoint
|
||||
2. Tx validation for the txpool (if tx simulation is enabled)
|
||||
3. Tx selection during block creation
|
||||
|
||||
#### CLI options
|
||||
|
||||
| Command Line Argument | Default Value |
|
||||
|-------------------------------------------------------|----------------------|
|
||||
| `--plugin-linea-module-limit-file-path` | moduleLimitFile.toml |
|
||||
| `--plugin-linea-over-line-count-limit-cache-size` | 10_000 |
|
||||
|
||||
|
||||
### L1<>L2 bridge
|
||||
|
||||
These values are just passed to the ZkTracer
|
||||
|
||||
#### CLI Options
|
||||
|
||||
| Command Line Argument | Default Value |
|
||||
|----------------------------------------|---------------|
|
||||
| `--plugin-linea-l1l2-bridge-contract` | |
|
||||
| `--plugin-linea-l1l2-bridge-topic` | |
|
||||
|
||||
|
||||
## Sequencer
|
||||
|
||||
### Transaction selection - LineaTransactionSelectorPlugin
|
||||
This plugin extends the standard transaction selection protocols employed by Besu for block creation.
|
||||
It leverages the `TransactionSelectionService` to manage and customize the process of transaction selection.
|
||||
This includes setting limits such as `TraceLineLimit`, `maxBlockGas`, and `maxCallData`, and check the profitability
|
||||
of a transaction.
|
||||
The selectors are in the package `net.consensys.linea.sequencer.txselection.selectors`.
|
||||
|
||||
#### CLI options
|
||||
|
||||
| Command Line Argument | Default Value |
|
||||
|--------------------------------------------------------|----------------------|
|
||||
| `--plugin-linea-max-block-calldata-size` | 70000 |
|
||||
| `--plugin-linea-module-limit-file-path` | moduleLimitFile.toml |
|
||||
| `--plugin-linea-over-line-count-limit-cache-size` | 10_000 |
|
||||
| `--plugin-linea-max-block-gas` | 30_000_000L |
|
||||
| `--plugin-linea-unprofitable-cache-size` | 100_000 |
|
||||
| `--plugin-linea-unprofitable-retry-limit` | 10 |
|
||||
|
||||
|
||||
### Transaction validation - LineaTransactionPoolValidatorPlugin
|
||||
|
||||
This plugin extends the default transaction validation rules for adding transactions to the
|
||||
transaction pool. It leverages the `PluginTransactionValidatorService` to manage and customize the
|
||||
process of transaction validation.
|
||||
This includes setting limits such as `TraceLineLimit`, `maxTxGasLimit`, and `maxTxCallData`, and checking the profitability
|
||||
of a transaction.
|
||||
The validators are in the package `net.consensys.linea.sequencer.txpoolvalidation.validators`.
|
||||
|
||||
#### CLI options
|
||||
|
||||
| Command Line Argument | Default Value |
|
||||
|----------------------------------------------------------|-------------------|
|
||||
| `--plugin-linea-deny-list-path` | lineaDenyList.txt |
|
||||
| `--plugin-linea-max-tx-gas-limit` | 30_000_000 |
|
||||
| `--plugin-linea-max-tx-calldata-size` | 60_000 |
|
||||
| `--plugin-linea-tx-pool-simulation-check-api-enabled` | false |
|
||||
| `--plugin-linea-tx-pool-simulation-check-p2p-enabled` | false |
|
||||
| `--plugin-linea-tx-pool-profitability-check-api-enabled` | true |
|
||||
| `--plugin-linea-tx-pool-profitability-check-p2p-enabled` | false |
|
||||
|
||||
### Reporting rejected transactions
|
||||
The transaction selection and validation plugins can report rejected transactions as JSON-RPC calls to an external
|
||||
service. This feature can be enabled by setting the following CLI options:
|
||||
|
||||
| Command Line Argument | Default Value | Expected Values |
|
||||
|---------------------------------------|---------------|--------------------------------------------------------------|
|
||||
| `--plugin-linea-rejected-tx-endpoint` | `null` | A valid URL e.g. `http://localhost:9363` to enable reporting |
|
||||
| `--plugin-linea-node-type` | `null` | One of `SEQUENCER`, `RPC`, `P2P` |
|
||||
|
||||
## RPC methods
|
||||
|
||||
### Linea Estimate Gas
|
||||
#### `linea_estimateGas`
|
||||
|
||||
This endpoint simulates a transaction, including line count limit validation, and returns the estimated gas used
|
||||
(as the standard `eth_estimateGas` with `strict=true`) plus the estimated gas price to be used when submitting the tx.
|
||||
|
||||
#### Parameters
|
||||
same as `eth_estimateGas`
|
||||
|
||||
#### Result
|
||||
```json
|
||||
{
|
||||
"jsonrpc": "2.0",
|
||||
"id": 53,
|
||||
"result": {
|
||||
"gasLimit": "0x5208",
|
||||
"baseFeePerGas": "0x7",
|
||||
"priorityFeePerGas": "0x123456"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Linea Set Extra Data
|
||||
#### `linea_setExtraData`
|
||||
|
||||
This endpoint is used to configure the extra data based pricing, and it only makes sense to call it on the sequencer.
|
||||
Internally it sets runtime pricing configuration and then calls, via the in-process RPC service, `miner_setExtraData`
|
||||
and `miner_setMinGasPrice` to update internal Besu configuration, and add the extra data pricing to the future built blocks.
|
||||
|
||||
#### Parameters
|
||||
same as `miner_setExtraData` with the added constraint that the number of bytes must be 32
|
||||
|
||||
#### Result
|
||||
```json
|
||||
{
|
||||
"jsonrpc": "2.0",
|
||||
"id": 53,
|
||||
"result": "true"
|
||||
}
|
||||
```
|
||||
|
||||
83
besu-plugins/linea-sequencer/docs/quickstart.md
Normal file
83
besu-plugins/linea-sequencer/docs/quickstart.md
Normal file
@@ -0,0 +1,83 @@
|
||||
## Quickstart: running [Linea Besu](https://github.com/Consensys/linea-besu) with plugins
|
||||
|
||||
- compile linea-plugins `gradlew installDist`
|
||||
- copy jar file to besu runtime plugins/ directory (where you will run Besu from, not where you're building Besu)
|
||||
- add `LINEA` to besu config to enable the plugin RPC methods
|
||||
- rpc-http-api=\["ADMIN","ETH","NET","WEB3","LINEA"\]
|
||||
- start besu (command line or from IDE) and you should see plugins registered at startup
|
||||
- call the RPC endpoint eg:
|
||||
|
||||
```shell
|
||||
curl --location --request POST 'http://localhost:8545' --data-raw '{
|
||||
"jsonrpc": "2.0",
|
||||
"method": "linea_estimateGas",
|
||||
"params": [
|
||||
"from": "0x73b2e0E54510239E22cC936F0b4a6dE1acf0AbdE",
|
||||
"to": "0xBb977B2EE8a111D788B3477D242078d0B837E72b",
|
||||
"value": "0x123"
|
||||
],
|
||||
"id": 1
|
||||
}'
|
||||
```
|
||||
|
||||
## Development Setup
|
||||
|
||||
### Install Java 21
|
||||
|
||||
### Native Lib Prerequisites
|
||||
|
||||
Linux/MacOs
|
||||
* Install the relevant CGo compiler for your platform
|
||||
* Install the Go toolchain
|
||||
|
||||
Windows
|
||||
* Requirement [Docker Desktop WSL 2 backend on Windows](https://docs.docker.com/desktop/wsl/)
|
||||
|
||||
On release native libs are built for all the supported platforms,
|
||||
if you want to test this process locally run `./gradlew -PreleaseNativeLibs jar`,
|
||||
jar is generated in `sequencer/build/libs`.
|
||||
|
||||
### Run tests
|
||||
|
||||
```shell
|
||||
# Run all tests
|
||||
./gradlew clean test
|
||||
|
||||
# Run only acceptance tests
|
||||
./gradlew clean acceptanceTests
|
||||
```
|
||||
|
||||
## IntelliJ IDEA Setup
|
||||
|
||||
### Enable Annotation Processing
|
||||
|
||||
- Go to `Settings | Build, Execution, Deployment | Compiler | Annotation Processors` and tick the following
|
||||
checkbox:
|
||||
|
||||

|
||||
|
||||
______________________________________________________________________
|
||||
|
||||
NOTE
|
||||
|
||||
> This setting is required to avoid IDE compilation errors because of the [Lombok](https://projectlombok.org/features/)
|
||||
> library used for code generation of boilerplate Java code such as:
|
||||
>
|
||||
> - Getters/Setters (via [`@Getter/@Setter`](https://projectlombok.org/features/GetterSetter))
|
||||
> - Class log instances (via [`@Slf4j`](https://projectlombok.org/features/log))
|
||||
> - Builder classes (via [`@Builder`](https://projectlombok.org/features/Builder))
|
||||
> - Constructors (
|
||||
> via [`@NoArgsConstructor/@RequiredArgsConstructor/@AllArgsConstructor`](https://projectlombok.org/features/constructor))
|
||||
> - etc.
|
||||
>
|
||||
> Learn more about how Java annotation processing
|
||||
> works [here](https://www.baeldung.com/java-annotation-processing-builder).
|
||||
|
||||
______________________________________________________________________
|
||||
|
||||
### Install Optional Plugins
|
||||
|
||||
- Install [Spotless Gradle](https://plugins.jetbrains.com/plugin/18321-spotless-gradle) plugin to re-format through
|
||||
the IDE according to spotless configuration.
|
||||
|
||||
Learn more [about available plugins](plugins.md).
|
||||
83
besu-plugins/linea-sequencer/gradle/allowed-licenses.json
Normal file
83
besu-plugins/linea-sequencer/gradle/allowed-licenses.json
Normal file
@@ -0,0 +1,83 @@
|
||||
{
|
||||
"allowedLicenses": [
|
||||
{
|
||||
"moduleLicense": "Apache License, Version 2.0",
|
||||
"moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0"
|
||||
},
|
||||
{
|
||||
"moduleLicense": "BSD Zero Clause License",
|
||||
"moduleLicenseUrl": "https://opensource.org/licenses/0BSD"
|
||||
},
|
||||
{
|
||||
"moduleLicense" : "The 2-Clause BSD License",
|
||||
"moduleLicenseUrl" : "https://opensource.org/licenses/BSD-2-Clause"
|
||||
},
|
||||
{
|
||||
"moduleLicense": "The 3-Clause BSD License",
|
||||
"moduleLicenseUrl": "https://opensource.org/licenses/BSD-3-Clause"
|
||||
},
|
||||
{
|
||||
"moduleLicense": "Bouncy Castle Licence",
|
||||
"moduleLicenseUrl": "https://www.bouncycastle.org/licence.html"
|
||||
},
|
||||
{
|
||||
"moduleLicense": "COMMON DEVELOPMENT AND DISTRIBUTION LICENSE (CDDL) Version 1.0",
|
||||
"moduleLicenseUrl": "https://oss.oracle.com/licenses/CDDL"
|
||||
},
|
||||
{
|
||||
"moduleLicense" : "COMMON DEVELOPMENT AND DISTRIBUTION LICENSE (CDDL) Version 1.1",
|
||||
"moduleLicenseUrl" : "https://oss.oracle.com/licenses/CDDL-1.1"
|
||||
},
|
||||
{
|
||||
"moduleLicense": "Eclipse Distribution License - v 1.0",
|
||||
"moduleLicenseUrl": "http://www.eclipse.org/legal/epl-v10.html"
|
||||
},
|
||||
{
|
||||
"moduleLicense": "Eclipse Public License - v 1.0",
|
||||
"moduleLicenseUrl": "http://www.eclipse.org/legal/epl-v10.html"
|
||||
},
|
||||
{
|
||||
"moduleLicense": "Eclipse Public License - v 2.0",
|
||||
"moduleLicenseUrl": "https://www.eclipse.org/org/documents/epl-2.0/EPL-2.0.txt"
|
||||
},
|
||||
{
|
||||
"moduleLicense": "MIT License",
|
||||
"moduleLicenseUrl": "https://opensource.org/licenses/MIT"
|
||||
},
|
||||
{
|
||||
"moduleLicense": "CC0-1.0",
|
||||
"moduleLicenseUrl": "https://creativecommons.org/publicdomain/zero/1.0/legalcode"
|
||||
},
|
||||
{
|
||||
"moduleLicense": "Public-Domain"
|
||||
},
|
||||
{
|
||||
"moduleLicense": "Unicode/ICU License",
|
||||
"moduleLicenseUrl": "https://raw.githubusercontent.com/unicode-org/icu/main/icu4c/LICENSE"
|
||||
},
|
||||
{
|
||||
"moduleLicense": "Creative Commons Legal Code",
|
||||
"moduleVersion": "1.0.3",
|
||||
"moduleName": "org.reactivestreams:reactive-streams"
|
||||
},
|
||||
{
|
||||
"moduleLicense": "MIT-0",
|
||||
"moduleVersion": "1.0.4",
|
||||
"moduleName": "org.reactivestreams:reactive-streams"
|
||||
},
|
||||
{
|
||||
"moduleName": "org.jetbrains.kotlin:kotlin-stdlib-common",
|
||||
"moduleVersion": "1.9.22"
|
||||
},
|
||||
{
|
||||
"moduleLicense": "CDDL-1.0",
|
||||
"moduleVersion": "3.0.3",
|
||||
"moduleName": "org.jupnp:org.jupnp"
|
||||
},
|
||||
{
|
||||
"moduleLicense": "CDDL-1.0",
|
||||
"moduleVersion": "3.0.3",
|
||||
"moduleName": "org.jupnp:org.jupnp.support"
|
||||
}
|
||||
]
|
||||
}
|
||||
35
besu-plugins/linea-sequencer/gradle/build-aliases.gradle
Normal file
35
besu-plugins/linea-sequencer/gradle/build-aliases.gradle
Normal file
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* Copyright Consensys Software Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
|
||||
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations under the License.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
// Default tasks and build aliases
|
||||
defaultTasks 'build', 'checkLicense', 'javadoc', 'artifacts'
|
||||
|
||||
def buildAliases = [
|
||||
'dev': [
|
||||
'spotlessApply',
|
||||
'build',
|
||||
'checkLicenses'
|
||||
],
|
||||
'artifacts' : [
|
||||
'jar',
|
||||
'distPlugin'
|
||||
]
|
||||
]
|
||||
|
||||
def expandedTaskList = []
|
||||
gradle.startParameter.taskNames.each {
|
||||
expandedTaskList << (buildAliases[it] ? buildAliases[it] : it)
|
||||
}
|
||||
gradle.startParameter.taskNames = expandedTaskList.flatten()
|
||||
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* Copyright Consensys Software Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
|
||||
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations under the License.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
dependencies {
|
||||
implementation 'org.slf4j:slf4j-api'
|
||||
|
||||
testImplementation 'org.apache.commons:commons-lang3'
|
||||
testImplementation 'com.google.guava:guava'
|
||||
testImplementation 'org.assertj:assertj-core'
|
||||
|
||||
testImplementation 'org.junit.jupiter:junit-jupiter-api'
|
||||
testImplementation 'org.junit.jupiter:junit-jupiter-params'
|
||||
|
||||
testImplementation 'org.mockito:mockito-core'
|
||||
testImplementation 'org.mockito:mockito-junit-jupiter'
|
||||
|
||||
testImplementation 'org.wiremock:wiremock'
|
||||
|
||||
testRuntimeOnly 'org.apache.logging.log4j:log4j-core'
|
||||
testRuntimeOnly 'org.apache.logging.log4j:log4j-slf4j2-impl'
|
||||
|
||||
testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine'
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
/*
|
||||
* Copyright Consensys Software Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
|
||||
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations under the License.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
repositories {
|
||||
maven {
|
||||
url "https://artifacts.consensys.net/public/linea-besu/maven/"
|
||||
content {
|
||||
includeGroupByRegex('io\\.consensys\\..*')
|
||||
includeGroupByRegex('org\\.hyperledger\\..*')
|
||||
}
|
||||
}
|
||||
maven {
|
||||
url 'https://hyperledger.jfrog.io/hyperledger/besu-maven'
|
||||
content { includeGroupByRegex('org\\.hyperledger\\..*') }
|
||||
}
|
||||
maven {
|
||||
url 'https://artifacts.consensys.net/public/maven/maven/'
|
||||
content {
|
||||
includeGroupByRegex('tech\\.pegasys(\\..*)?')
|
||||
}
|
||||
}
|
||||
maven {
|
||||
url "https://artifacts.consensys.net/public/linea-arithmetization/maven/"
|
||||
content {
|
||||
includeGroupByRegex('net\\.consensys\\.linea\\..*?')
|
||||
}
|
||||
}
|
||||
maven {
|
||||
url 'https://splunk.jfrog.io/splunk/ext-releases-local'
|
||||
content { includeGroupByRegex('com\\.splunk\\..*') }
|
||||
}
|
||||
mavenCentral()
|
||||
mavenLocal()
|
||||
}
|
||||
|
||||
apply plugin: 'io.spring.dependency-management'
|
||||
|
||||
dependencyManagement {
|
||||
applyMavenExclusions = false
|
||||
generatedPomCustomization {
|
||||
enabled = false
|
||||
}
|
||||
imports {
|
||||
mavenBom "${besuArtifactGroup}:bom:${libs.versions.besu.get()}"
|
||||
}
|
||||
|
||||
dependencies {
|
||||
dependencySet(group: "net.consensys.linea.zktracer", version: "${libs.versions.arithmetization.get()}") {
|
||||
entry "arithmetization"
|
||||
}
|
||||
|
||||
dependency "com.google.code.gson:gson:${libs.versions.gson.get()}"
|
||||
|
||||
dependency "io.tmio:tuweni-bytes:${libs.versions.tuweni.get()}"
|
||||
dependency "io.tmio:tuweni-units:${libs.versions.tuweni.get()}"
|
||||
dependency "io.tmio:tuweni-toml:${libs.versions.tuweni.get()}"
|
||||
|
||||
// ToDo: remove when fixed in Besu, force version to avoid conflict with previous version
|
||||
dependency "org.apache.logging.log4j:log4j-api:${libs.versions.log4j.get()}"
|
||||
dependency "org.apache.logging.log4j:log4j-core:${libs.versions.log4j.get()}"
|
||||
|
||||
dependency "org.wiremock:wiremock:${libs.versions.wiremock.get()}"
|
||||
}
|
||||
}
|
||||
139
besu-plugins/linea-sequencer/gradle/dist.gradle
Normal file
139
besu-plugins/linea-sequencer/gradle/dist.gradle
Normal file
@@ -0,0 +1,139 @@
|
||||
/*
|
||||
* Copyright Consensys Software Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
|
||||
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations under the License.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
def lineaSequencerProject = project(lineaSequencerProjectPath)
|
||||
|
||||
tasks.register('sourcesJar', Jar) {
|
||||
dependsOn classes
|
||||
archiveClassifier = 'sources'
|
||||
from sourceSets.main.allSource
|
||||
}
|
||||
|
||||
tasks.register('javadocJar', Jar) {
|
||||
dependsOn javadoc
|
||||
archiveClassifier = 'javadoc'
|
||||
from javadoc.destinationDir
|
||||
}
|
||||
|
||||
def besuDistTar = new File(new File(buildDir, "downloads"), lineaSequencerProject.besuFilename)
|
||||
task downloadBesu {
|
||||
outputs.file(besuDistTar)
|
||||
doLast {
|
||||
try {
|
||||
download.run {
|
||||
src lineaSequencerProject.besuUrl
|
||||
dest besuDistTar
|
||||
onlyIfModified true
|
||||
}
|
||||
} catch (Exception e) {
|
||||
def localBesuDir =
|
||||
project.hasProperty('useLocalBesuDir')
|
||||
? file("${findProperty('useLocalBesuDir')}".replaceFirst('^~', System.getProperty('user.home')))
|
||||
: new File(projectDir, "../../besu")
|
||||
|
||||
def localBesuFile = new File("${localBesuDir.canonicalPath}/build/distributions/${lineaSequencerProject.besuFilename}")
|
||||
|
||||
logger.warn("Could not download " + lineaSequencerProject.besuUrl + " trying local copy from " + localBesuFile + " as fallback")
|
||||
if (!file(localBesuFile).exists()) {
|
||||
throw new GradleException("Could not download Besu distribution from: " + lineaSequencerProject.besuUrl +
|
||||
", and could not find it locally at ${localBesuFile} either")
|
||||
}
|
||||
|
||||
copy {
|
||||
from localBesuFile
|
||||
into besuDistTar.parentFile
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
task unTarBesu(type: Copy) {
|
||||
dependsOn downloadBesu
|
||||
|
||||
from tarTree(besuDistTar)
|
||||
into besuDistTar.parentFile
|
||||
}
|
||||
|
||||
def besuLibDir = new File(besuDistTar.parentFile, lineaSequencerProject.besuIdentifier + '/lib')
|
||||
def besuLibs = []
|
||||
|
||||
def excludeBesuProvidedDeps = {
|
||||
if(besuLibs.isEmpty()) {
|
||||
// Get all the dependencies that are provided by Besu
|
||||
fileTree(dir: besuLibDir, include: '*.jar').visit {
|
||||
FileVisitDetails details ->
|
||||
besuLibs << details.file.name
|
||||
}
|
||||
}
|
||||
// include the dependency in the jar only if it is not already provided by Besu
|
||||
!besuLibs.any { artifactName ->
|
||||
if(artifactName == it.name) {
|
||||
return true
|
||||
}
|
||||
// exclude Besu group
|
||||
if(it.toString().contains(besuArtifactGroup)) {
|
||||
return true
|
||||
}
|
||||
// try ignoring the version
|
||||
def libName = it.name =~ dependencyNamePattern()
|
||||
def artName = artifactName =~ dependencyNamePattern()
|
||||
libName[0][1] == artName[0][1]
|
||||
}
|
||||
}
|
||||
|
||||
jar {
|
||||
dependsOn unTarBesu
|
||||
archiveBaseName = lineaSequencerProject.distributionIdentifier
|
||||
version = calculateVersion()
|
||||
|
||||
manifest {
|
||||
attributes(
|
||||
'Specification-Title': 'arithmetization',
|
||||
'Specification-Version': "${libs.versions.arithmetization.get()}",
|
||||
'Implementation-Title': 'arithmetization',
|
||||
'Implementation-Version': "${libs.versions.arithmetization.get()}"
|
||||
)
|
||||
}
|
||||
|
||||
from {
|
||||
configurations.runtimeClasspath.filter(excludeBesuProvidedDeps).collect {
|
||||
it.isDirectory() ? it : zipTree(it)
|
||||
}
|
||||
}
|
||||
|
||||
duplicatesStrategy('exclude')
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a distribution of the plugin, that only contains the plugin jar and the
|
||||
* dependencies that are not provided by Besu itself, so that is can be simply
|
||||
* extracted in the Besu plugins dir.
|
||||
*/
|
||||
tasks.register('distPlugin', Zip) {
|
||||
dependsOn installDist
|
||||
|
||||
archiveBaseName = lineaSequencerProject.distributionIdentifier
|
||||
|
||||
from("${buildDir}/libs/${lineaSequencerProject.distributionIdentifier}-${calculateVersion()}.jar")
|
||||
from {
|
||||
configurations.runtimeClasspath.filter(
|
||||
excludeBesuProvidedDeps)
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
static def dependencyNamePattern() {
|
||||
/(.*)(\-.*?)\.jar/
|
||||
}
|
||||
57
besu-plugins/linea-sequencer/gradle/java.gradle
Normal file
57
besu-plugins/linea-sequencer/gradle/java.gradle
Normal file
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
* Copyright Consensys Software Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
|
||||
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations under the License.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
apply plugin: 'java-library'
|
||||
|
||||
if (!JavaVersion.current().isCompatibleWith(JavaVersion.VERSION_21)) {
|
||||
throw new GradleException("Java 21 or later is required to build Besu.\n" +
|
||||
" Detected version ${JavaVersion.current()}")
|
||||
}
|
||||
|
||||
sourceCompatibility = 21
|
||||
targetCompatibility = 21
|
||||
|
||||
tasks.withType(JavaCompile) {
|
||||
options.compilerArgs += [
|
||||
'-Xlint:unchecked',
|
||||
'-Xlint:cast',
|
||||
'-Xlint:rawtypes',
|
||||
'-Xlint:overloads',
|
||||
'-Xlint:divzero',
|
||||
'-Xlint:finally',
|
||||
'-Xlint:static',
|
||||
'-Werror',
|
||||
]
|
||||
|
||||
options.encoding = 'UTF-8'
|
||||
}
|
||||
|
||||
def lineaSequencerProject = project(lineaSequencerProjectPath)
|
||||
|
||||
// Takes the version, and if -SNAPSHOT is part of it replaces SNAPSHOT
|
||||
// with the git commit version.
|
||||
ext.calculateVersion = { ->
|
||||
String version = lineaSequencerProject.releaseVersion
|
||||
if (version.endsWith("-SNAPSHOT")) {
|
||||
version = version.replace("-SNAPSHOT", "-dev-${getCheckedOutGitCommitHash()}")
|
||||
}
|
||||
|
||||
return version
|
||||
}
|
||||
|
||||
static def getCheckedOutGitCommitHash() {
|
||||
def hashLength = 8
|
||||
"git rev-parse HEAD".execute().text.take(hashLength)
|
||||
}
|
||||
@@ -0,0 +1,106 @@
|
||||
/**
|
||||
* Copyright 2018 Evgeny Naumenko <jk.vc@mail.ru>
|
||||
* Copyright Hyperledger Besu contributors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
{
|
||||
"bundles" : [
|
||||
{ "bundleName" : "Apache-1.1", "licenseName" : "Apache Software License, Version 1.1", "licenseUrl" : "https://www.apache.org/licenses/LICENSE-1.1" },
|
||||
{ "bundleName" : "Apache-2.0", "licenseName" : "Apache License, Version 2.0", "licenseUrl" : "https://www.apache.org/licenses/LICENSE-2.0" },
|
||||
{ "bundleName" : "0BSD", "licenseName" : "BSD Zero Clause License", "licenseUrl" : "https://opensource.org/licenses/0BSD" },
|
||||
{ "bundleName" : "BSD-2-Clause", "licenseName" : "The 2-Clause BSD License", "licenseUrl" : "https://opensource.org/licenses/BSD-2-Clause" },
|
||||
{ "bundleName" : "BSD-3-Clause", "licenseName" : "The 3-Clause BSD License", "licenseUrl" : "https://opensource.org/licenses/BSD-3-Clause" },
|
||||
{ "bundleName" : "CC0-1.0", "licenseName" : "Creative Commons Legal Code", "licenseUrl" : "https://creativecommons.org/publicdomain/zero/1.0/legalcode" },
|
||||
{ "bundleName" : "CDDL-1.0", "licenseName" : "COMMON DEVELOPMENT AND DISTRIBUTION LICENSE (CDDL) Version 1.0", "licenseUrl" : "https://oss.oracle.com/licenses/CDDL" },
|
||||
{ "bundleName" : "CDDL-1.1", "licenseName" : "COMMON DEVELOPMENT AND DISTRIBUTION LICENSE (CDDL) Version 1.1", "licenseUrl" : "https://oss.oracle.com/licenses/CDDL-1.1" },
|
||||
{ "bundleName" : "CPL-1.0", "licenseName" : "Common Public License - v 1.0", "licenseUrl" : "https://www.eclipse.org/legal/cpl-v10.html" },
|
||||
{ "bundleName" : "EPL-1.0", "licenseName" : "Eclipse Public License - v 1.0", "licenseUrl" : "http://www.eclipse.org/legal/epl-v10.html" },
|
||||
{ "bundleName" : "EPL-2.0", "licenseName" : "Eclipse Public License - v 2.0", "licenseUrl" : "https://www.eclipse.org/org/documents/epl-2.0/EPL-2.0.txt" },
|
||||
{ "bundleName" : "EDL-1.0", "licenseName" : "Eclipse Distribution License - v 1.0", "licenseUrl" : "https://www.eclipse.org/org/documents/edl-v10.html" },
|
||||
{ "bundleName" : "GPL-1.0", "licenseName" : "GNU GENERAL PUBLIC LICENSE, Version 1", "licenseUrl" : "https://www.gnu.org/licenses/gpl-1.0" },
|
||||
{ "bundleName" : "GPL-2.0-only", "licenseName" : "GNU GENERAL PUBLIC LICENSE, Version 2", "licenseUrl" : "https://www.gnu.org/licenses/gpl-2.0" },
|
||||
{ "bundleName" : "GPL-3.0-only", "licenseName" : "GNU GENERAL PUBLIC LICENSE, Version 3", "licenseUrl" : "https://www.gnu.org/licenses/gpl-3.0" },
|
||||
{ "bundleName" : "GPL-2.0 WITH Classpath-exception-2.0", "licenseName" : "GNU GENERAL PUBLIC LICENSE, Version 2 + Classpath Exception", "licenseUrl" : "https://openjdk.java.net/legal/gplv2+ce.html" },
|
||||
{ "bundleName" : "LGPL-2.1-only", "licenseName" : "GNU LESSER GENERAL PUBLIC LICENSE, Version 2.1", "licenseUrl" : "https://www.gnu.org/licenses/lgpl-2.1" },
|
||||
{ "bundleName" : "LGPL-3.0-only", "licenseName" : "GNU LESSER GENERAL PUBLIC LICENSE, Version 3", "licenseUrl" : "https://www.gnu.org/licenses/lgpl-3.0" },
|
||||
{ "bundleName" : "MIT", "licenseName" : "MIT License", "licenseUrl" : "https://opensource.org/licenses/MIT" },
|
||||
{ "bundleName" : "MPL-1.1", "licenseName" : "Mozilla Public License Version 1.1", "licenseUrl" : "https://www.mozilla.org/en-US/MPL/1.1" },
|
||||
{ "bundleName" : "MPL-2.0", "licenseName" : "Mozilla Public License, Version 2.0", "licenseUrl" : "https://www.mozilla.org/en-US/MPL/2.0" },
|
||||
{ "bundleName" : "Public-Domain", "licenseName" : "PUBLIC DOMAIN", "licenseUrl" : "" }
|
||||
],
|
||||
"transformationRules" : [
|
||||
{ "bundleName" : "0BSD", "licenseNamePattern" : "BSD Zero Clause License" },
|
||||
{ "bundleName" : "0BSD", "licenseNamePattern" : "BSD$" },
|
||||
{ "bundleName" : "0BSD", "licenseNamePattern" : "BSD( |-)clause.*" },
|
||||
{ "bundleName" : "0BSD", "licenseNamePattern" : "(The )?BSD( |-)(l|L)icen(s|c)e.*" },
|
||||
{ "bundleName" : "Apache-2.0", "licenseNamePattern" : ".*The Apache Software License, Version 2\\.0.*" },
|
||||
{ "bundleName" : "Apache-2.0", "licenseNamePattern" : ".*?Apache( |-|_)2.*" },
|
||||
{ "bundleName" : "Apache-2.0", "licenseNamePattern" : "ASL 2\\.0" },
|
||||
{ "bundleName" : "Apache-2.0", "licenseNamePattern" : ".*Apache License,?( Version)? 2.*" },
|
||||
{ "bundleName" : "Apache-2.0", "licenseUrlPattern" : ".*(www\\.)?opensource\\.org/licenses/Apache-2\\.0.*" },
|
||||
{ "bundleName" : "Apache-2.0", "licenseUrlPattern" : ".*www\\.apache\\.org/licenses/LICENSE-2\\.0.*" },
|
||||
{ "bundleName" : "Apache-2.0", "licenseFileContentPattern" : ".*Apache License,?( Version)? 2.*" },
|
||||
{ "bundleName" : "Apache-1.1", "licenseFileContentPattern" : ".*Apache Software License, Version 1\\.1.*" },
|
||||
{ "bundleName" : "LGPL-2.1-only", "licenseUrlPattern" : ".*www\\.gnu\\.org/licenses/old-licenses/lgpl-2\\.1\\.html" },
|
||||
{ "bundleName" : "CC0-1.0", "licenseNamePattern" : "CC0(( |-)1(\\.0)?)?" },
|
||||
{ "bundleName" : "CC0-1.0", "licenseUrlPattern" : ".*(www\\.)?creativecommons\\.org/publicdomain/zero/1\\.0/" },
|
||||
{ "bundleName" : "CDDL-1.0", "licenseFileContentPattern" : ".*CDDL.*1\\.0" },
|
||||
{ "bundleName" : "CDDL-1.0", "licenseUrlPattern" : ".*CDDL.*.?1\\.0" },
|
||||
{ "bundleName" : "CDDL-1.1", "licenseUrlPattern" : ".*CDDL.*.?1\\.1" },
|
||||
{ "bundleName" : "CDDL-1.0", "licenseNamePattern" : "Common Development and Distribution License( \\(CDDL\\),?)? (version )?(.?\\s?)?1\\.0" },
|
||||
{ "bundleName" : "CDDL-1.1", "licenseNamePattern" : "Common Development and Distribution License( \\(CDDL\\),?)? (version )?(.?\\s?)?1\\.1" },
|
||||
{ "bundleName" : "BSD-3-Clause", "licenseNamePattern" : ".*BSD( |-)3-clause.*" },
|
||||
{ "bundleName" : "BSD-3-Clause", "licenseUrlPattern" : ".*(www\\.)?opensource\\.org/licenses/BSD-3-Clause" },
|
||||
{ "bundleName" : "BSD-3-Clause", "licenseNamePattern" : ".*?(The )New BSD License.*" },
|
||||
{ "bundleName" : "BSD-3-Clause", "licenseNamePattern" : ".*?Modified BSD License.*" },
|
||||
{ "bundleName" : "BSD-2-Clause", "licenseNamePattern" : "BSD( |-)2-clause.*" },
|
||||
{ "bundleName" : "BSD-2-Clause", "licenseUrlPattern" : ".*(www\\.)?opensource\\.org/licenses/BSD-2-Clause" },
|
||||
{ "bundleName" : "BSD-2-Clause", "licenseUrlPattern" : ".*(www\\.)?opensource\\.org/licenses/bsd-license(\\.php)?" },
|
||||
{ "bundleName" : "CDDL-1.0", "licenseNamePattern" : "Common Development and Distribution( License)?" },
|
||||
{ "bundleName" : "CDDL-1.0", "licenseNamePattern" : "CDDL 1(\\.0)" },
|
||||
{ "bundleName" : "CDDL-1.1", "licenseNamePattern" : "CDDL 1\\.1" },
|
||||
{ "bundleName" : "CDDL-1.1", "licenseUrlPattern" : ".*(www\\.).opensource\\.org/licenses/CDDL-1\\.0" },
|
||||
{ "bundleName" : "EPL-1.0", "licenseNamePattern" : "Eclipse Publish License.*(v|version)\\.?\\s?1(\\.?0)?" },
|
||||
{ "bundleName" : "EPL-1.0", "licenseNamePattern" : "Eclipse Public License.*(v|version)\\.?\\s?1(\\.?0)?" },
|
||||
{ "bundleName" : "EPL-2.0", "licenseNamePattern" : "Eclipse Public License.*(v|version)\\.?\\s?2(\\.?0)?" },
|
||||
{ "bundleName" : "EPL-2.0", "licenseUrlPattern" : ".*(www\\.).opensource\\.org/licenses/EPL-2\\.0" },
|
||||
{ "bundleName" : "EPL-2.0", "licenseUrlPattern" : ".*http.?://www\\.eclipse\\.org/legal/epl-.?2\\.?0.*" },
|
||||
{ "bundleName" : "EPL-2.0", "licenseUrlPattern" : ".*http.?://www\\.eclipse\\.org/org.*/epl-.?2\\.?0.*" },
|
||||
{ "bundleName" : "EPL-2.0", "licenseUrlPattern" : ".*http.?://projects\\.eclipse\\.org/.*/epl-.?2\\.?0.*" },
|
||||
{ "bundleName" : "EDL-1.0", "licenseNamePattern" : "Eclipse Distribution License.*(v|version)\\.?\\s?1(\\.0)?" },
|
||||
{ "bundleName" : "EDL-1.0", "licenseNamePattern" : "Eclipse Distribution License \\(New BSD License\\)" },
|
||||
{ "bundleName" : "EDL-1.0", "licenseUrlPattern" : ".*http.?://(www\\.)?eclipse\\.org/org.*/edl-.?1\\.?0.*" },
|
||||
{ "bundleName" : "GPL-2.0-only", "licenseUrlPattern" : ".*(www\\.)?opensource\\.org/licenses/GPL-2\\.0" },
|
||||
{ "bundleName" : "GPL-2.0 WITH Classpath-exception-2.0", "licenseNamePattern" : "GNU General Public License, version 2.*classpath exception" },
|
||||
{ "bundleName" : "GPL-2.0 WITH Classpath-exception-2.0", "licenseNamePattern" : "GNU General Public License, version 2.*cp?e" },
|
||||
{ "bundleName" : "GPL-2.0 WITH Classpath-exception-2.0", "licenseNamePattern" : "GNU General Public License, version 2.*, with the classpath exception" },
|
||||
{ "bundleName" : "GPL-2.0 WITH Classpath-exception-2.0", "licenseNamePattern" : "GPL2 w/ CPE" },
|
||||
{ "bundleName" : "GPL-3.0-only", "licenseUrlPattern" : ".*(www\\.)?opensource\\.org/licenses/GPL-3\\.0" },
|
||||
{ "bundleName" : "LGPL-2.1-only", "licenseUrlPattern" : ".*(www\\.)?opensource\\.org/licenses/LGPL-2\\.1" },
|
||||
{ "bundleName" : "LGPL-2.1-only", "licenseUrlPattern" : ".*(www\\.)?gnu\\.org/licenses(/old-licenses)?/lgpl-2\\.1(\\.(html|txt))?" },
|
||||
{ "bundleName" : "LGPL-2.1-only", "licenseNamePattern" : "LGPL 2\\.1" },
|
||||
{ "bundleName" : "LGPL-2.1-only", "licenseNamePattern" : "LGPL.*(v|version)\\.?\\s?2\\.1" },
|
||||
{ "bundleName" : "LGPL-2.1-only", "licenseUrlPattern" : ".*(www\\.)?repository.jboss.org/licenses/lgpl-2.1\\.txt" },
|
||||
{ "bundleName" : "LGPL-3.0-only", "licenseUrlPattern" : ".*(www\\.).opensource\\.org/licenses/LGPL-3\\.0" },
|
||||
{ "bundleName" : "LGPL-3.0-only", "licenseNamePattern" : "lgplv?3" },
|
||||
{ "bundleName" : "LGPL-3.0-only", "licenseUrlPattern" : ".*(www\\.)?gnu\\.org/licenses(/old-licenses)?/lgpl(-3)?(\\.(html|txt))?" },
|
||||
{ "bundleName" : "MIT", "licenseNamePattern" : "(The\\s)?MIT(\\slicen(c|s)e)?(\\s\\(MIT\\))?" },
|
||||
{ "bundleName" : "MIT", "licenseUrlPattern" : ".*(www\\.)?opensource\\.org/licenses/MIT(\\.php)?" },
|
||||
{ "bundleName" : "MPL-1.1", "licenseNamePattern" : "MPL 1\\.1" },
|
||||
{ "bundleName" : "MPL-2.0", "licenseUrlPattern" : ".*(www\\.).opensource\\.org/licenses/MPL-2\\.0" },
|
||||
{ "bundleName" : "Public-Domain", "licenseNamePattern" : "((public)\\s(domain)).*", "transformUrl" : false },
|
||||
{ "bundleName" : "Public-Domain", "licenseFileContentPattern" : ".*(Creative)\\s(Commons).*", "transformUrl" : false },
|
||||
{ "bundleName" : "Public-Domain", "licenseFileContentPattern" : ".*((Public)\\s(Domain)).*", "transformUrl" : false }
|
||||
]
|
||||
}
|
||||
104
besu-plugins/linea-sequencer/gradle/lint.gradle
Normal file
104
besu-plugins/linea-sequencer/gradle/lint.gradle
Normal file
@@ -0,0 +1,104 @@
|
||||
/*
|
||||
* Copyright Consensys Software Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
|
||||
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations under the License.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
import groovy.io.FileType
|
||||
import org.gradle.api.DefaultTask
|
||||
import org.gradle.api.tasks.Input
|
||||
import org.gradle.api.tasks.TaskAction
|
||||
import org.gradle.tooling.BuildException
|
||||
|
||||
class CheckSpdxHeader extends DefaultTask {
|
||||
private String rootPath
|
||||
private String spdxHeader
|
||||
private String filesRegex
|
||||
private String excludeRegex
|
||||
|
||||
@Input
|
||||
String getRootPath() {
|
||||
return rootPath
|
||||
}
|
||||
|
||||
void setRootPath(final String rootPath) {
|
||||
this.rootPath = rootPath
|
||||
}
|
||||
|
||||
@Input
|
||||
String getSpdxHeader() {
|
||||
return spdxHeader
|
||||
}
|
||||
|
||||
void setSpdxHeader(final String spdxHeader) {
|
||||
this.spdxHeader = spdxHeader
|
||||
}
|
||||
|
||||
@Input
|
||||
String getFilesRegex() {
|
||||
return filesRegex
|
||||
}
|
||||
|
||||
void setFilesRegex(final String filesRegex) {
|
||||
this.filesRegex = filesRegex
|
||||
}
|
||||
|
||||
@Input
|
||||
String getExcludeRegex() {
|
||||
return excludeRegex
|
||||
}
|
||||
|
||||
void setExcludeRegex(final String excludeRegex) {
|
||||
this.excludeRegex = excludeRegex
|
||||
}
|
||||
|
||||
@TaskAction
|
||||
void checkHeaders() {
|
||||
def filesWithoutHeader = []
|
||||
|
||||
new File(rootPath).traverse(
|
||||
type: FileType.FILES,
|
||||
nameFilter: ~/${filesRegex}/,
|
||||
excludeFilter: ~/${excludeRegex}/
|
||||
) {
|
||||
f ->
|
||||
if (!f.getText().contains(spdxHeader)) {
|
||||
filesWithoutHeader.add(f)
|
||||
}
|
||||
}
|
||||
|
||||
if (!filesWithoutHeader.isEmpty()) {
|
||||
throw new BuildException("Files without headers: " + filesWithoutHeader.join('\n'), null)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
javadoc {
|
||||
options.addStringOption('Xdoclint:all', '-quiet')
|
||||
options.addStringOption('Xwerror', '-html5')
|
||||
options.encoding = 'UTF-8'
|
||||
}
|
||||
|
||||
task checkSpdxHeader(type: CheckSpdxHeader) {
|
||||
rootPath = "${projectDir}"
|
||||
spdxHeader = "* SPDX-License-Identifier: Apache-2.0"
|
||||
filesRegex = "(.*.java)|(.*.groovy)"
|
||||
excludeRegex = [
|
||||
"(.*/.gradle/.*)",
|
||||
"(.*/.idea/.*)",
|
||||
"(.*/out/.*)",
|
||||
"(.*/build/.*)",
|
||||
"(.*/src/[^/]+/generated/.*)",
|
||||
].join("|")
|
||||
}
|
||||
|
||||
tasks.check.dependsOn(checkSpdxHeader)
|
||||
14
besu-plugins/linea-sequencer/gradle/spotless.java.license
Normal file
14
besu-plugins/linea-sequencer/gradle/spotless.java.license
Normal file
@@ -0,0 +1,14 @@
|
||||
/*
|
||||
* Copyright Consensys Software Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
|
||||
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations under the License.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 12 KiB |
73
besu-plugins/linea-sequencer/sequencer/build.gradle
Normal file
73
besu-plugins/linea-sequencer/sequencer/build.gradle
Normal file
@@ -0,0 +1,73 @@
|
||||
/*
|
||||
* Copyright Consensys Software Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
|
||||
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations under the License.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
plugins {
|
||||
id 'java'
|
||||
id 'java-library-distribution'
|
||||
alias(libs.plugins.lombok)
|
||||
alias(libs.plugins.gradleVersions)
|
||||
alias(libs.plugins.dependencyManagement)
|
||||
alias(libs.plugins.download)
|
||||
}
|
||||
|
||||
group = 'net.consensys.linea.besu.plugin'
|
||||
|
||||
def lineaSequencerProject = project(lineaSequencerProjectPath)
|
||||
apply from: lineaSequencerProject.file("gradle/java.gradle")
|
||||
apply from: lineaSequencerProject.file("gradle/dependency-management.gradle")
|
||||
apply from: lineaSequencerProject.file('gradle/common-dependencies.gradle')
|
||||
apply from: lineaSequencerProject.file("gradle/build-aliases.gradle")
|
||||
apply from: lineaSequencerProject.file("gradle/lint.gradle")
|
||||
|
||||
dependencies {
|
||||
// annotationProcessor generates the file META-INF/services/org.hyperledger.besu.plugin.BesuPlugin
|
||||
annotationProcessor 'com.google.auto.service:auto-service'
|
||||
compileOnly 'com.google.auto.service:auto-service'
|
||||
|
||||
api "build.linea:blob-compressor:${libs.versions.blobCompressor.get()}"
|
||||
api "build.linea.internal:kotlin:${libs.versions.lineaKotlin.get()}"
|
||||
|
||||
implementation "${besuArtifactGroup}:besu-datatypes"
|
||||
implementation "${besuArtifactGroup}:evm"
|
||||
implementation "${besuArtifactGroup}:plugin-api"
|
||||
implementation "${besuArtifactGroup}.internal:algorithms"
|
||||
implementation "${besuArtifactGroup}.internal:api"
|
||||
implementation "${besuArtifactGroup}.internal:core"
|
||||
implementation "${besuArtifactGroup}.internal:eth"
|
||||
implementation "${besuArtifactGroup}.internal:rlp"
|
||||
|
||||
implementation 'com.fasterxml.jackson.datatype:jackson-datatype-jdk8'
|
||||
|
||||
implementation 'com.github.ben-manes.caffeine:caffeine'
|
||||
|
||||
implementation 'com.google.code.gson:gson'
|
||||
|
||||
implementation "io.tmio:tuweni-bytes"
|
||||
implementation "io.tmio:tuweni-units"
|
||||
implementation "io.tmio:tuweni-toml"
|
||||
|
||||
implementation 'info.picocli:picocli'
|
||||
|
||||
implementation 'net.consensys.linea.zktracer:arithmetization'
|
||||
|
||||
implementation 'org.hibernate.validator:hibernate-validator'
|
||||
|
||||
testImplementation "${besuArtifactGroup}.internal:besu"
|
||||
testImplementation group: "${besuArtifactGroup}.internal", name: "core", classifier: "test-support"
|
||||
|
||||
testImplementation 'org.awaitility:awaitility'
|
||||
}
|
||||
|
||||
apply from: lineaSequencerProject.file("gradle/dist.gradle")
|
||||
@@ -0,0 +1,72 @@
|
||||
/*
|
||||
* Copyright Consensys Software Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
|
||||
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations under the License.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package net.consensys.linea;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.hyperledger.besu.plugin.BesuPlugin;
|
||||
import org.hyperledger.besu.plugin.ServiceManager;
|
||||
|
||||
/**
|
||||
* Linea plugins extending this class will halt startup of Besu in case of exception during
|
||||
* registration.
|
||||
*
|
||||
* <p>If that's NOT desired, the plugin should implement {@link BesuPlugin} directly.
|
||||
*/
|
||||
@Slf4j
|
||||
public abstract class AbstractLineaRequiredPlugin extends AbstractLineaSharedPrivateOptionsPlugin {
|
||||
|
||||
@Override
|
||||
public void register(final ServiceManager serviceManager) {
|
||||
super.register(serviceManager);
|
||||
try {
|
||||
log.info("Registering Linea plugin {}", this.getClass().getName());
|
||||
|
||||
doRegister(serviceManager);
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("Halting Besu startup: exception in plugin registration: ", e);
|
||||
e.printStackTrace();
|
||||
// System.exit will cause besu to exit
|
||||
System.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Linea plugins need to implement this method. Called by {@link BesuPlugin} register method
|
||||
*
|
||||
* @param serviceManager the ServiceManager to be used.
|
||||
*/
|
||||
public abstract void doRegister(final ServiceManager serviceManager);
|
||||
|
||||
@Override
|
||||
public void start() {
|
||||
super.start();
|
||||
try {
|
||||
log.info("Starting Linea plugin {}", this.getClass().getName());
|
||||
|
||||
doStart();
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("Halting Besu startup: exception in plugin startup: ", e);
|
||||
e.printStackTrace();
|
||||
// System.exit will cause besu to exit
|
||||
System.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
/** Linea plugins can implement this method. Called by {@link BesuPlugin} start method */
|
||||
public abstract void doStart();
|
||||
}
|
||||
@@ -0,0 +1,240 @@
|
||||
/*
|
||||
* Copyright Consensys Software Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
|
||||
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations under the License.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package net.consensys.linea;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import net.consensys.linea.bundles.BundlePoolService;
|
||||
import net.consensys.linea.bundles.LineaLimitedBundlePool;
|
||||
import net.consensys.linea.config.LineaBundleCliOptions;
|
||||
import net.consensys.linea.config.LineaBundleConfiguration;
|
||||
import net.consensys.linea.config.LineaProfitabilityCliOptions;
|
||||
import net.consensys.linea.config.LineaProfitabilityConfiguration;
|
||||
import net.consensys.linea.config.LineaRejectedTxReportingCliOptions;
|
||||
import net.consensys.linea.config.LineaRejectedTxReportingConfiguration;
|
||||
import net.consensys.linea.config.LineaRpcCliOptions;
|
||||
import net.consensys.linea.config.LineaRpcConfiguration;
|
||||
import net.consensys.linea.config.LineaTracerCliOptions;
|
||||
import net.consensys.linea.config.LineaTracerConfiguration;
|
||||
import net.consensys.linea.config.LineaTransactionPoolValidatorCliOptions;
|
||||
import net.consensys.linea.config.LineaTransactionPoolValidatorConfiguration;
|
||||
import net.consensys.linea.config.LineaTransactionSelectorCliOptions;
|
||||
import net.consensys.linea.config.LineaTransactionSelectorConfiguration;
|
||||
import net.consensys.linea.plugins.AbstractLineaSharedOptionsPlugin;
|
||||
import net.consensys.linea.plugins.LineaOptionsPluginConfiguration;
|
||||
import net.consensys.linea.utils.Compressor;
|
||||
import org.hyperledger.besu.plugin.ServiceManager;
|
||||
import org.hyperledger.besu.plugin.services.BesuConfiguration;
|
||||
import org.hyperledger.besu.plugin.services.BesuEvents;
|
||||
import org.hyperledger.besu.plugin.services.BlockchainService;
|
||||
import org.hyperledger.besu.plugin.services.MetricsSystem;
|
||||
import org.hyperledger.besu.plugin.services.RpcEndpointService;
|
||||
import org.hyperledger.besu.plugin.services.metrics.MetricCategoryRegistry;
|
||||
|
||||
/**
|
||||
* This abstract class is used as superclass for all the plugins that share one or more
|
||||
* configuration options, services and common initializations.
|
||||
*
|
||||
* <p>Configuration options that are exclusive of a single plugin, are not required to be added
|
||||
* here, but they could stay in the class that implement a plugin, but in case that configuration
|
||||
* becomes to be used by multiple plugins, then to avoid code duplications and possible different
|
||||
* management of the options, it is better to move the configuration here so all plugins will
|
||||
* automatically see it.
|
||||
*
|
||||
* <p>Same for services and other initialization tasks, that are shared by more than one plugin,
|
||||
* like registration of metrics categories or check to perform once at startup
|
||||
*/
|
||||
@Slf4j
|
||||
public abstract class AbstractLineaSharedPrivateOptionsPlugin
|
||||
extends AbstractLineaSharedOptionsPlugin {
|
||||
protected static BesuConfiguration besuConfiguration;
|
||||
protected static BlockchainService blockchainService;
|
||||
protected static MetricsSystem metricsSystem;
|
||||
protected static BesuEvents besuEvents;
|
||||
protected static BundlePoolService bundlePoolService;
|
||||
protected static MetricCategoryRegistry metricCategoryRegistry;
|
||||
protected static RpcEndpointService rpcEndpointService;
|
||||
|
||||
private static final AtomicBoolean sharedRegisterTasksDone = new AtomicBoolean(false);
|
||||
private static final AtomicBoolean sharedStartTasksDone = new AtomicBoolean(false);
|
||||
|
||||
static {
|
||||
// force the initialization of the gnark compress native library to fail fast in case of issues
|
||||
Compressor.instance.compressedSize(new byte[1024]);
|
||||
}
|
||||
|
||||
private ServiceManager serviceManager;
|
||||
|
||||
@Override
|
||||
public Map<String, LineaOptionsPluginConfiguration> getLineaPluginConfigMap() {
|
||||
final var configMap = new HashMap<>(super.getLineaPluginConfigMap());
|
||||
|
||||
configMap.put(
|
||||
LineaTransactionSelectorCliOptions.CONFIG_KEY,
|
||||
LineaTransactionSelectorCliOptions.create().asPluginConfig());
|
||||
configMap.put(
|
||||
LineaTransactionPoolValidatorCliOptions.CONFIG_KEY,
|
||||
LineaTransactionPoolValidatorCliOptions.create().asPluginConfig());
|
||||
configMap.put(LineaRpcCliOptions.CONFIG_KEY, LineaRpcCliOptions.create().asPluginConfig());
|
||||
configMap.put(
|
||||
LineaProfitabilityCliOptions.CONFIG_KEY,
|
||||
LineaProfitabilityCliOptions.create().asPluginConfig());
|
||||
configMap.put(
|
||||
LineaTracerCliOptions.CONFIG_KEY, LineaTracerCliOptions.create().asPluginConfig());
|
||||
configMap.put(
|
||||
LineaRejectedTxReportingCliOptions.CONFIG_KEY,
|
||||
LineaRejectedTxReportingCliOptions.create().asPluginConfig());
|
||||
configMap.put(
|
||||
LineaBundleCliOptions.CONFIG_KEY, LineaBundleCliOptions.create().asPluginConfig());
|
||||
return configMap;
|
||||
}
|
||||
|
||||
public LineaTransactionSelectorConfiguration transactionSelectorConfiguration() {
|
||||
return (LineaTransactionSelectorConfiguration)
|
||||
getConfigurationByKey(LineaTransactionSelectorCliOptions.CONFIG_KEY).optionsConfig();
|
||||
}
|
||||
|
||||
public LineaTransactionPoolValidatorConfiguration transactionPoolValidatorConfiguration() {
|
||||
return (LineaTransactionPoolValidatorConfiguration)
|
||||
getConfigurationByKey(LineaTransactionPoolValidatorCliOptions.CONFIG_KEY).optionsConfig();
|
||||
}
|
||||
|
||||
public LineaRpcConfiguration lineaRpcConfiguration() {
|
||||
return (LineaRpcConfiguration)
|
||||
getConfigurationByKey(LineaRpcCliOptions.CONFIG_KEY).optionsConfig();
|
||||
}
|
||||
|
||||
public LineaProfitabilityConfiguration profitabilityConfiguration() {
|
||||
return (LineaProfitabilityConfiguration)
|
||||
getConfigurationByKey(LineaProfitabilityCliOptions.CONFIG_KEY).optionsConfig();
|
||||
}
|
||||
|
||||
public LineaTracerConfiguration tracerConfiguration() {
|
||||
return (LineaTracerConfiguration)
|
||||
getConfigurationByKey(LineaTracerCliOptions.CONFIG_KEY).optionsConfig();
|
||||
}
|
||||
|
||||
public LineaRejectedTxReportingConfiguration rejectedTxReportingConfiguration() {
|
||||
return (LineaRejectedTxReportingConfiguration)
|
||||
getConfigurationByKey(LineaRejectedTxReportingCliOptions.CONFIG_KEY).optionsConfig();
|
||||
}
|
||||
|
||||
public LineaBundleConfiguration bundleConfiguration() {
|
||||
return (LineaBundleConfiguration)
|
||||
getConfigurationByKey(LineaBundleCliOptions.CONFIG_KEY).optionsConfig();
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void register(final ServiceManager serviceManager) {
|
||||
super.register(serviceManager);
|
||||
|
||||
this.serviceManager = serviceManager;
|
||||
|
||||
if (sharedRegisterTasksDone.compareAndSet(false, true)) {
|
||||
performSharedRegisterTasksOnce(serviceManager);
|
||||
}
|
||||
}
|
||||
|
||||
protected static void performSharedRegisterTasksOnce(final ServiceManager serviceManager) {
|
||||
besuConfiguration =
|
||||
serviceManager
|
||||
.getService(BesuConfiguration.class)
|
||||
.orElseThrow(
|
||||
() ->
|
||||
new RuntimeException(
|
||||
"Failed to obtain BesuConfiguration from the ServiceManager."));
|
||||
blockchainService =
|
||||
serviceManager
|
||||
.getService(BlockchainService.class)
|
||||
.orElseThrow(
|
||||
() ->
|
||||
new RuntimeException(
|
||||
"Failed to obtain BlockchainService from the ServiceManager."));
|
||||
|
||||
metricCategoryRegistry =
|
||||
serviceManager
|
||||
.getService(MetricCategoryRegistry.class)
|
||||
.orElseThrow(
|
||||
() ->
|
||||
new RuntimeException(
|
||||
"Failed to obtain MetricCategoryRegistry from the ServiceManager."));
|
||||
|
||||
rpcEndpointService =
|
||||
serviceManager
|
||||
.getService(RpcEndpointService.class)
|
||||
.orElseThrow(
|
||||
() ->
|
||||
new RuntimeException(
|
||||
"Failed to obtain RpcEndpointService from the ServiceManager."));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void start() {
|
||||
super.start();
|
||||
|
||||
if (sharedStartTasksDone.compareAndSet(false, true)) {
|
||||
performSharedStartTasksOnce(serviceManager);
|
||||
}
|
||||
}
|
||||
|
||||
private void performSharedStartTasksOnce(final ServiceManager serviceManager) {
|
||||
|
||||
blockchainService
|
||||
.getChainId()
|
||||
.ifPresentOrElse(
|
||||
chainId -> {
|
||||
if (chainId.signum() <= 0) {
|
||||
throw new IllegalArgumentException("Chain id must be greater than zero.");
|
||||
}
|
||||
},
|
||||
() -> {
|
||||
throw new IllegalArgumentException("Chain id required");
|
||||
});
|
||||
|
||||
metricsSystem =
|
||||
serviceManager
|
||||
.getService(MetricsSystem.class)
|
||||
.orElseThrow(
|
||||
() ->
|
||||
new RuntimeException("Failed to obtain MetricSystem from the ServiceManager."));
|
||||
|
||||
besuEvents =
|
||||
serviceManager
|
||||
.getService(BesuEvents.class)
|
||||
.orElseThrow(
|
||||
() -> new RuntimeException("Failed to obtain BesuEvents from the ServiceManager."));
|
||||
|
||||
bundlePoolService =
|
||||
new LineaLimitedBundlePool(
|
||||
besuConfiguration.getDataPath(),
|
||||
transactionSelectorConfiguration().maxBundlePoolSizeBytes(),
|
||||
besuEvents,
|
||||
blockchainService);
|
||||
bundlePoolService.loadFromDisk();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stop() {
|
||||
super.stop();
|
||||
sharedRegisterTasksDone.set(false);
|
||||
sharedStartTasksDone.set(false);
|
||||
blockchainService = null;
|
||||
metricsSystem = null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,216 @@
|
||||
/*
|
||||
* Copyright Consensys Software Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
|
||||
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations under the License.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
package net.consensys.linea.bl;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import net.consensys.linea.config.LineaProfitabilityConfiguration;
|
||||
import net.consensys.linea.utils.Compressor;
|
||||
import org.hyperledger.besu.datatypes.Transaction;
|
||||
import org.hyperledger.besu.datatypes.Wei;
|
||||
import org.slf4j.spi.LoggingEventBuilder;
|
||||
|
||||
/**
|
||||
* This class implements the profitability formula, and it is used both to check if a tx is
|
||||
* profitable and to give an estimation of the profitable priorityFeePerGas for a given tx. The
|
||||
* profitability depends on the context, so it could mean that it is priced enough to have a chance:
|
||||
* to be accepted in the txpool and to be a candidate for new block creation, it is also used to
|
||||
* give an estimated priorityFeePerGas in response to a linea_estimateGas call. Each context has it
|
||||
* own minMargin configuration, so that is possible to accept in the txpool txs, that are not yet
|
||||
* profitable for block inclusion, but could be in future if the gas price decrease and likewise, it
|
||||
* is possible to return an estimated priorityFeePerGas that has a profitability buffer to address
|
||||
* small fluctuations in the gas market.
|
||||
*/
|
||||
@Slf4j
|
||||
public class TransactionProfitabilityCalculator {
|
||||
private final LineaProfitabilityConfiguration profitabilityConf;
|
||||
|
||||
public TransactionProfitabilityCalculator(
|
||||
final LineaProfitabilityConfiguration profitabilityConf) {
|
||||
this.profitabilityConf = profitabilityConf;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate the estimation of priorityFeePerGas that is considered profitable for the given tx,
|
||||
* according to the current pricing config and the minMargin.
|
||||
*
|
||||
* @param transaction the tx we want to get the estimated priorityFeePerGas for
|
||||
* @param minMargin the min margin to use for this calculation
|
||||
* @param gas the gas to use for this calculation, could be the gasUsed of the tx, if it has been
|
||||
* processed/simulated, otherwise the gasLimit of the tx
|
||||
* @param minGasPriceWei the current minGasPrice, only used in place of the variable cost from the
|
||||
* config, in case the extra data pricing is disabled
|
||||
* @return the estimation of priorityFeePerGas that is considered profitable for the given tx
|
||||
*/
|
||||
public Wei profitablePriorityFeePerGas(
|
||||
final Transaction transaction,
|
||||
final double minMargin,
|
||||
final long gas,
|
||||
final Wei minGasPriceWei) {
|
||||
final int compressedTxSize = getCompressedTxSize(transaction);
|
||||
|
||||
final long variableCostWei =
|
||||
profitabilityConf.extraDataPricingEnabled()
|
||||
? profitabilityConf.variableCostWei()
|
||||
: minGasPriceWei.toLong();
|
||||
|
||||
final var profitAt =
|
||||
minMargin * (variableCostWei * compressedTxSize / gas + profitabilityConf.fixedCostWei());
|
||||
|
||||
final var profitAtWei = Wei.ofNumber(BigDecimal.valueOf(profitAt).toBigInteger());
|
||||
|
||||
log.atDebug()
|
||||
.setMessage(
|
||||
"Estimated profitable priorityFeePerGas: {}; minMargin={}, fixedCostWei={}, "
|
||||
+ "variableCostWei={}, gas={}, txSize={}, compressedTxSize={}")
|
||||
.addArgument(profitAtWei::toHumanReadableString)
|
||||
.addArgument(minMargin)
|
||||
.addArgument(profitabilityConf.fixedCostWei())
|
||||
.addArgument(variableCostWei)
|
||||
.addArgument(gas)
|
||||
.addArgument(transaction::getSize)
|
||||
.addArgument(compressedTxSize)
|
||||
.log();
|
||||
|
||||
return profitAtWei;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if then given gas price is considered profitable for the given tx, according to the
|
||||
* current pricing config, the minMargin and gas used, or gasLimit of the tx.
|
||||
*
|
||||
* @param context a string to name the context in which it is called, used for logs
|
||||
* @param transaction the tx we want to check if profitable
|
||||
* @param minMargin the min margin to use for this check
|
||||
* @param payingGasPrice the gas price the tx is willing to pay
|
||||
* @param gas the gas to use for this check, could be the gasUsed of the tx, if it has been
|
||||
* processed/simulated, otherwise the gasLimit of the tx
|
||||
* @param minGasPriceWei the current minGasPrice, only used in place of the variable cost from the
|
||||
* config, in case the extra data pricing is disabled
|
||||
* @return true if the tx is priced enough to be profitable, false otherwise
|
||||
*/
|
||||
public boolean isProfitable(
|
||||
final String context,
|
||||
final Transaction transaction,
|
||||
final double minMargin,
|
||||
final Wei baseFee,
|
||||
final Wei payingGasPrice,
|
||||
final long gas,
|
||||
final Wei minGasPriceWei) {
|
||||
|
||||
final Wei profitablePriorityFee =
|
||||
profitablePriorityFeePerGas(transaction, minMargin, gas, minGasPriceWei);
|
||||
|
||||
return isProfitable(
|
||||
context,
|
||||
profitablePriorityFee,
|
||||
transaction,
|
||||
minMargin,
|
||||
baseFee,
|
||||
payingGasPrice,
|
||||
gas,
|
||||
minGasPriceWei);
|
||||
}
|
||||
|
||||
public boolean isProfitable(
|
||||
final String context,
|
||||
final Wei profitablePriorityFee,
|
||||
final Transaction transaction,
|
||||
final double minMargin,
|
||||
final Wei baseFee,
|
||||
final Wei payingGasPrice,
|
||||
final long gas,
|
||||
final Wei minGasPriceWei) {
|
||||
|
||||
final Wei profitableGasPrice = baseFee.add(profitablePriorityFee);
|
||||
|
||||
if (payingGasPrice.lessThan(profitableGasPrice)) {
|
||||
log(
|
||||
log.atDebug(),
|
||||
context,
|
||||
transaction,
|
||||
minMargin,
|
||||
payingGasPrice,
|
||||
baseFee,
|
||||
profitablePriorityFee,
|
||||
profitableGasPrice,
|
||||
gas,
|
||||
minGasPriceWei);
|
||||
return false;
|
||||
}
|
||||
|
||||
log(
|
||||
log.atTrace(),
|
||||
context,
|
||||
transaction,
|
||||
minMargin,
|
||||
payingGasPrice,
|
||||
baseFee,
|
||||
profitablePriorityFee,
|
||||
profitableGasPrice,
|
||||
gas,
|
||||
minGasPriceWei);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method calculates the compressed size of a tx using the native lib
|
||||
*
|
||||
* @param transaction the tx
|
||||
* @return the compressed size
|
||||
*/
|
||||
private int getCompressedTxSize(final Transaction transaction) {
|
||||
final byte[] bytes = transaction.encoded().toArrayUnsafe();
|
||||
return Compressor.instance.compressedSize(bytes);
|
||||
}
|
||||
|
||||
private void log(
|
||||
final LoggingEventBuilder leb,
|
||||
final String context,
|
||||
final Transaction transaction,
|
||||
final double minMargin,
|
||||
final Wei payingGasPrice,
|
||||
final Wei baseFee,
|
||||
final Wei profitablePriorityFee,
|
||||
final Wei profitableGasPrice,
|
||||
final long gasUsed,
|
||||
final Wei minGasPriceWei) {
|
||||
|
||||
leb.setMessage(
|
||||
"Context {}. Transaction {} has a margin of {}, minMargin={}, payingGasPrice={},"
|
||||
+ " profitableGasPrice={}, baseFee={}, profitablePriorityFee={}, fixedCostWei={}, variableCostWei={}, "
|
||||
+ " gasUsed={}")
|
||||
.addArgument(context)
|
||||
.addArgument(transaction::getHash)
|
||||
.addArgument(
|
||||
() ->
|
||||
payingGasPrice.toBigInteger().doubleValue()
|
||||
/ profitablePriorityFee.toBigInteger().doubleValue())
|
||||
.addArgument(minMargin)
|
||||
.addArgument(payingGasPrice::toHumanReadableString)
|
||||
.addArgument(profitableGasPrice::toHumanReadableString)
|
||||
.addArgument(baseFee::toHumanReadableString)
|
||||
.addArgument(profitablePriorityFee::toHumanReadableString)
|
||||
.addArgument(profitabilityConf::fixedCostWei)
|
||||
.addArgument(
|
||||
() ->
|
||||
profitabilityConf.extraDataPricingEnabled()
|
||||
? profitabilityConf.variableCostWei()
|
||||
: minGasPriceWei.toLong())
|
||||
.addArgument(gasUsed)
|
||||
.log();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,209 @@
|
||||
/*
|
||||
* Copyright Consensys Software Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
|
||||
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations under the License.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
package net.consensys.linea.bundles;
|
||||
|
||||
import static com.fasterxml.jackson.annotation.JsonAutoDetect.Visibility.ANY;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URL;
|
||||
import java.util.OptionalLong;
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonAutoDetect;
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.fasterxml.jackson.datatype.jdk8.Jdk8Module;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.Getter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.ToString;
|
||||
import lombok.experimental.Accessors;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import net.consensys.linea.bundles.BundlePoolService.TransactionBundleAddedListener;
|
||||
import net.consensys.linea.bundles.BundlePoolService.TransactionBundleRemovedListener;
|
||||
import net.consensys.linea.config.LineaBundleConfiguration;
|
||||
import net.consensys.linea.utils.PriorityThreadPoolExecutor;
|
||||
import okhttp3.MediaType;
|
||||
import okhttp3.OkHttpClient;
|
||||
import okhttp3.Request;
|
||||
import okhttp3.RequestBody;
|
||||
import okhttp3.Response;
|
||||
import org.hyperledger.besu.plugin.services.BlockchainService;
|
||||
|
||||
@Slf4j
|
||||
@RequiredArgsConstructor
|
||||
public class BundleForwarder
|
||||
implements TransactionBundleAddedListener, TransactionBundleRemovedListener {
|
||||
public static final String RETRY_COUNT_HEADER = "X-Retry-Count";
|
||||
private final AtomicLong reqIdProvider = new AtomicLong(0L);
|
||||
private final LineaBundleConfiguration config;
|
||||
private final PriorityThreadPoolExecutor executor;
|
||||
private final ScheduledExecutorService retryScheduler;
|
||||
private final BlockchainService blockchainService;
|
||||
private final OkHttpClient rpcClient;
|
||||
private final URL recipientUrl;
|
||||
|
||||
@Override
|
||||
public void onTransactionBundleAdded(final TransactionBundle bundle) {
|
||||
executor.submit(new SendBundleTask(bundle, 0));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTransactionBundleRemoved(final TransactionBundle transactionBundle) {
|
||||
executor.remove(new SendBundleTask(transactionBundle, 0));
|
||||
}
|
||||
|
||||
void retry(final TransactionBundle bundle, final int retry) {
|
||||
retryScheduler.schedule(
|
||||
() -> executor.submit(new SendBundleTask(bundle, retry)),
|
||||
config.retryDelayMillis(),
|
||||
TimeUnit.MILLISECONDS);
|
||||
}
|
||||
|
||||
@RequiredArgsConstructor
|
||||
@EqualsAndHashCode(onlyExplicitlyIncluded = true)
|
||||
class SendBundleTask implements Callable<SendBundleResponse>, Comparable<SendBundleTask> {
|
||||
private static final ObjectMapper OBJECT_MAPPER =
|
||||
new ObjectMapper().registerModule(new Jdk8Module());
|
||||
private static final MediaType JSON = MediaType.get("application/json; charset=utf-8");
|
||||
@Getter @EqualsAndHashCode.Include private final TransactionBundle bundle;
|
||||
private final int retryCount;
|
||||
|
||||
@Override
|
||||
public SendBundleResponse call() throws BundleForwarderException {
|
||||
final var chainHeadBlockNumber = blockchainService.getChainHeadHeader().getNumber();
|
||||
if (bundle.blockNumber() <= chainHeadBlockNumber) {
|
||||
throw new BundleForwarderException(
|
||||
"Skip forwarding bundle for past block number "
|
||||
+ bundle.blockNumber()
|
||||
+ " since chain head block number is "
|
||||
+ chainHeadBlockNumber,
|
||||
bundle);
|
||||
}
|
||||
|
||||
final long reqId = reqIdProvider.getAndIncrement();
|
||||
final var jsonRpcRequest = new JsonRpcEnvelope(reqId, bundle.toBundleParameter(false));
|
||||
|
||||
log.trace("Forwarding request {}, retry count {}", jsonRpcRequest, retryCount);
|
||||
|
||||
final RequestBody body;
|
||||
try {
|
||||
body = RequestBody.create(OBJECT_MAPPER.writeValueAsString(jsonRpcRequest), JSON);
|
||||
} catch (JsonProcessingException e) {
|
||||
log.error("Error creating send bundle request body", e);
|
||||
throw new BundleForwarderException(
|
||||
"Error creating send bundle request body", e, bundle, reqId);
|
||||
}
|
||||
|
||||
final var requestBuilder = new Request.Builder().url(recipientUrl).post(body);
|
||||
|
||||
if (retryCount > 0) {
|
||||
requestBuilder.addHeader(RETRY_COUNT_HEADER, String.valueOf(retryCount));
|
||||
}
|
||||
|
||||
try (final Response response = rpcClient.newCall(requestBuilder.build()).execute()) {
|
||||
final var result =
|
||||
new SendBundleResponse(reqId, bundle, response, response.body().string());
|
||||
if (response.isSuccessful()) {
|
||||
log.trace(
|
||||
"Bundle {} forwarded successfully at retry count {}", jsonRpcRequest, retryCount);
|
||||
} else {
|
||||
log.error(
|
||||
"Bundle {} forward failed with status {} at retry count {}",
|
||||
jsonRpcRequest,
|
||||
response.code(),
|
||||
retryCount);
|
||||
}
|
||||
return result;
|
||||
} catch (IOException e) {
|
||||
log.warn(
|
||||
"Error forwarding bundle request {}, at retry count {}, retrying later",
|
||||
jsonRpcRequest,
|
||||
retryCount,
|
||||
e);
|
||||
retry(bundle, retryCount + 1);
|
||||
throw new BundleForwarderException(
|
||||
"Error send bundle request, retrying later", e, bundle, reqId);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(final SendBundleTask o) {
|
||||
final int blockNumberPlusRetriesComp =
|
||||
Long.compare(this.blockNumberPlusRetries(), o.blockNumberPlusRetries());
|
||||
if (blockNumberPlusRetriesComp == 0) {
|
||||
// put retries at the end
|
||||
final int retryCountComp = Integer.compare(this.retryCount, o.retryCount);
|
||||
if (retryCountComp == 0) {
|
||||
// at last disambiguate by sequence
|
||||
return Long.compare(this.bundle.sequence(), o.bundle.sequence());
|
||||
}
|
||||
return retryCountComp;
|
||||
}
|
||||
return blockNumberPlusRetriesComp;
|
||||
}
|
||||
|
||||
private long blockNumberPlusRetries() {
|
||||
return this.bundle.blockNumber() + retryCount;
|
||||
}
|
||||
}
|
||||
|
||||
record SendBundleResponse(long reqId, TransactionBundle bundle, Response response, String body) {}
|
||||
|
||||
@JsonAutoDetect(fieldVisibility = ANY)
|
||||
@ToString(onlyExplicitlyIncluded = true)
|
||||
private static class JsonRpcEnvelope {
|
||||
private final String jsonrpc = "2.0";
|
||||
private final String method = "linea_sendBundle";
|
||||
@ToString.Include private final long id;
|
||||
@ToString.Include private final BundleParameter[] params;
|
||||
|
||||
public JsonRpcEnvelope(final long id, final BundleParameter params) {
|
||||
this.id = id;
|
||||
this.params = new BundleParameter[] {params};
|
||||
}
|
||||
}
|
||||
|
||||
@Accessors(fluent = true)
|
||||
@Getter
|
||||
public class BundleForwarderException extends RuntimeException {
|
||||
private final OptionalLong reqId;
|
||||
private final TransactionBundle bundle;
|
||||
|
||||
public BundleForwarderException(final String message, final TransactionBundle bundle) {
|
||||
super(message);
|
||||
this.reqId = OptionalLong.empty();
|
||||
this.bundle = bundle;
|
||||
}
|
||||
|
||||
public BundleForwarderException(
|
||||
final String message,
|
||||
final Throwable cause,
|
||||
final TransactionBundle bundle,
|
||||
final long reqId) {
|
||||
super(message, cause);
|
||||
this.reqId = OptionalLong.of(reqId);
|
||||
this.bundle = bundle;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getMessage() {
|
||||
return "[ReqId:" + reqId + "] " + super.getMessage();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
/*
|
||||
* Copyright Consensys Software Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
|
||||
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations under the License.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
package net.consensys.linea.bundles;
|
||||
|
||||
import static com.fasterxml.jackson.annotation.JsonInclude.Include.NON_ABSENT;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonCreator;
|
||||
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import com.fasterxml.jackson.annotation.JsonPropertyOrder;
|
||||
import org.hyperledger.besu.datatypes.Hash;
|
||||
import org.hyperledger.besu.datatypes.parameters.UnsignedLongParameter;
|
||||
|
||||
@JsonInclude(NON_ABSENT)
|
||||
@JsonPropertyOrder({"blockNumber", "minTimestamp", "maxTimestamp"})
|
||||
public record BundleParameter(
|
||||
/* array of signed transactions to execute in a bundle */
|
||||
List<String> txs,
|
||||
/* block number for which this bundle is valid */
|
||||
Long blockNumber,
|
||||
/* Optional minimum timestamp from which this bundle is valid */
|
||||
Optional<Long> minTimestamp,
|
||||
/* Optional max timestamp for which this bundle is valid */
|
||||
Optional<Long> maxTimestamp,
|
||||
/* Optional list of transaction hashes which are allowed to revert */
|
||||
Optional<List<Hash>> revertingTxHashes,
|
||||
/* Optional UUID which can be used to replace or cancel this bundle */
|
||||
Optional<String> replacementUUID,
|
||||
/* Optional list of builders to share this bundle with */
|
||||
Optional<List<String>> builders) {
|
||||
@JsonCreator
|
||||
public BundleParameter(
|
||||
@JsonProperty("txs") final List<String> txs,
|
||||
@JsonProperty("blockNumber") final UnsignedLongParameter blockNumber,
|
||||
@JsonProperty("minTimestamp") final Optional<Long> minTimestamp,
|
||||
@JsonProperty("maxTimestamp") final Optional<Long> maxTimestamp,
|
||||
@JsonProperty("revertingTxHashes") final Optional<List<Hash>> revertingTxHashes,
|
||||
@JsonProperty("replacementUUID") final Optional<String> replacementUUID,
|
||||
@JsonProperty("builders") final Optional<List<String>> builders) {
|
||||
this(
|
||||
txs,
|
||||
blockNumber.getValue(),
|
||||
minTimestamp,
|
||||
maxTimestamp,
|
||||
revertingTxHashes,
|
||||
replacementUUID,
|
||||
builders);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,129 @@
|
||||
/*
|
||||
* Copyright Consensys Software Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
|
||||
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations under the License.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
package net.consensys.linea.bundles;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
import org.hyperledger.besu.datatypes.Hash;
|
||||
import org.hyperledger.besu.plugin.services.BesuService;
|
||||
|
||||
public interface BundlePoolService extends BesuService {
|
||||
@FunctionalInterface
|
||||
interface TransactionBundleAddedListener {
|
||||
void onTransactionBundleAdded(TransactionBundle transactionBundle);
|
||||
}
|
||||
|
||||
@FunctionalInterface
|
||||
interface TransactionBundleRemovedListener {
|
||||
void onTransactionBundleRemoved(TransactionBundle transactionBundle);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves a list of TransactionBundles associated with a block number.
|
||||
*
|
||||
* @param blockNumber The block number to look up.
|
||||
* @return A list of TransactionBundles for the given block number, or an empty list if none are
|
||||
* found.
|
||||
*/
|
||||
List<TransactionBundle> getBundlesByBlockNumber(long blockNumber);
|
||||
|
||||
/**
|
||||
* Retrieves a TransactionBundle by its unique hash identifier.
|
||||
*
|
||||
* @param hash The hash identifier of the TransactionBundle.
|
||||
* @return The TransactionBundle associated with the hash, or null if not found.
|
||||
*/
|
||||
TransactionBundle get(Hash hash);
|
||||
|
||||
/**
|
||||
* Retrieves a TransactionBundle by its replacement UUID
|
||||
*
|
||||
* @param replacementUUID identifier of the TransactionBundle.
|
||||
* @return The TransactionBundle associated with the uuid, or null if not found.
|
||||
*/
|
||||
TransactionBundle get(UUID replacementUUID);
|
||||
|
||||
/**
|
||||
* Puts or replaces an existing TransactionBundle in the cache and updates the block index.
|
||||
*
|
||||
* @param hash The hash identifier of the TransactionBundle.
|
||||
* @param bundle The new TransactionBundle to replace the existing one.
|
||||
* @throws IllegalStateException if the pool is frozen
|
||||
*/
|
||||
void putOrReplace(Hash hash, TransactionBundle bundle);
|
||||
|
||||
/**
|
||||
* Puts or replaces an existing TransactionBundle by UUIDin the cache and updates the block index.
|
||||
*
|
||||
* @param replacementUUID identifier of the TransactionBundle.
|
||||
* @param bundle The new TransactionBundle to replace the existing one.
|
||||
* @throws IllegalStateException if the pool is frozen
|
||||
*/
|
||||
void putOrReplace(UUID replacementUUID, TransactionBundle bundle);
|
||||
|
||||
/**
|
||||
* Removes an existing TransactionBundle in the cache and updates the block index.
|
||||
*
|
||||
* @param replacementUUID identifier of the TransactionBundle.
|
||||
* @return boolean indicating if bundle was found and removed
|
||||
* @throws IllegalStateException if the pool is frozen
|
||||
*/
|
||||
boolean remove(UUID replacementUUID);
|
||||
|
||||
/**
|
||||
* Removes an existing TransactionBundle in the cache and updates the block index.
|
||||
*
|
||||
* @param hash The hash identifier of the TransactionBundle.
|
||||
* @return boolean indicating if bundle was found and removed
|
||||
* @throws IllegalStateException if the pool is frozen
|
||||
*/
|
||||
boolean remove(Hash hash);
|
||||
|
||||
/**
|
||||
* Get the number of bundles in the pool
|
||||
*
|
||||
* @return the number of bundles in the pool
|
||||
*/
|
||||
long size();
|
||||
|
||||
/**
|
||||
* Return true if the pool does not accept modifications anymore
|
||||
*
|
||||
* @return true if the pool does not accept modifications anymore
|
||||
*/
|
||||
boolean isFrozen();
|
||||
|
||||
/**
|
||||
* Save the content of the pool to disk. Note that once this operation starts, the pool will be
|
||||
* frozen and will not be possible to modify it anymore.
|
||||
*/
|
||||
void saveToDisk();
|
||||
|
||||
/**
|
||||
* Load the content of the pool from disk.
|
||||
*
|
||||
* @throws IllegalStateException if the pool is frozen
|
||||
*/
|
||||
void loadFromDisk();
|
||||
|
||||
long subscribeTransactionBundleAdded(TransactionBundleAddedListener listener);
|
||||
|
||||
long subscribeTransactionBundleRemoved(TransactionBundleRemovedListener listener);
|
||||
|
||||
void unsubscribeTransactionBundleAdded(long listenerId);
|
||||
|
||||
void unsubscribeTransactionBundleRemoved(long listenerId);
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
/*
|
||||
* Copyright Consensys Software Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
|
||||
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations under the License.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package net.consensys.linea.bundles;
|
||||
|
||||
import java.net.URL;
|
||||
import java.time.Duration;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import com.google.auto.service.AutoService;
|
||||
import net.consensys.linea.AbstractLineaRequiredPlugin;
|
||||
import net.consensys.linea.utils.PriorityThreadPoolExecutor;
|
||||
import okhttp3.OkHttpClient;
|
||||
import org.hyperledger.besu.plugin.BesuPlugin;
|
||||
import org.hyperledger.besu.plugin.ServiceManager;
|
||||
|
||||
@AutoService(BesuPlugin.class)
|
||||
public class ForwardBundlesPlugin extends AbstractLineaRequiredPlugin {
|
||||
|
||||
@Override
|
||||
public void doRegister(final ServiceManager serviceManager) {}
|
||||
|
||||
@Override
|
||||
public void doStart() {
|
||||
final var config = bundleConfiguration();
|
||||
final var forwardUrls = config.forwardUrls();
|
||||
if (!forwardUrls.isEmpty()) {
|
||||
final var rpcClient = createRpcClient(config.timeoutMillis());
|
||||
final var retryScheduler = createRetryScheduler();
|
||||
forwardUrls.stream()
|
||||
.map(
|
||||
url ->
|
||||
new BundleForwarder(
|
||||
config,
|
||||
createExecutor(url),
|
||||
retryScheduler,
|
||||
blockchainService,
|
||||
rpcClient,
|
||||
url))
|
||||
.peek(bundlePoolService::subscribeTransactionBundleAdded)
|
||||
.toList();
|
||||
}
|
||||
}
|
||||
|
||||
private OkHttpClient createRpcClient(final int timeoutMillis) {
|
||||
return new OkHttpClient.Builder()
|
||||
.retryOnConnectionFailure(false)
|
||||
.callTimeout(Duration.ofMillis(timeoutMillis))
|
||||
.build();
|
||||
}
|
||||
|
||||
private PriorityThreadPoolExecutor createExecutor(final URL recipientUrl) {
|
||||
return new PriorityThreadPoolExecutor(
|
||||
0,
|
||||
1,
|
||||
10,
|
||||
TimeUnit.MINUTES,
|
||||
Thread.ofVirtual().name("BundleForwarder[" + recipientUrl.toString() + "]", 0L).factory());
|
||||
}
|
||||
|
||||
private ScheduledExecutorService createRetryScheduler() {
|
||||
return Executors.newSingleThreadScheduledExecutor(
|
||||
Thread.ofPlatform().name("BundleForwarderRetry", 0L).factory());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stop() {
|
||||
// stop forwarders?
|
||||
super.stop();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,416 @@
|
||||
/*
|
||||
* Copyright Consensys Software Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
|
||||
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations under the License.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package net.consensys.linea.bundles;
|
||||
|
||||
import static java.util.Collections.emptyList;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.BufferedWriter;
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import com.fasterxml.jackson.databind.MappingIterator;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.fasterxml.jackson.databind.SequenceWriter;
|
||||
import com.fasterxml.jackson.datatype.jdk8.Jdk8Module;
|
||||
import com.github.benmanes.caffeine.cache.Cache;
|
||||
import com.github.benmanes.caffeine.cache.Caffeine;
|
||||
import com.github.benmanes.caffeine.cache.RemovalCause;
|
||||
import com.github.benmanes.caffeine.cache.Scheduler;
|
||||
import com.google.auto.service.AutoService;
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.tuweni.bytes.Bytes;
|
||||
import org.hyperledger.besu.datatypes.Hash;
|
||||
import org.hyperledger.besu.datatypes.PendingTransaction;
|
||||
import org.hyperledger.besu.plugin.data.AddedBlockContext;
|
||||
import org.hyperledger.besu.plugin.services.BesuEvents;
|
||||
import org.hyperledger.besu.plugin.services.BesuService;
|
||||
import org.hyperledger.besu.plugin.services.BlockchainService;
|
||||
import org.hyperledger.besu.util.Subscribers;
|
||||
|
||||
/**
|
||||
* A pool for managing TransactionBundles with limited size and FIFO eviction. Provides access via
|
||||
* hash identifiers or block numbers.
|
||||
*/
|
||||
@AutoService(BesuService.class)
|
||||
@Slf4j
|
||||
public class LineaLimitedBundlePool implements BundlePoolService, BesuEvents.BlockAddedListener {
|
||||
public static final String BUNDLE_SAVE_FILENAME = "bundles.ndjson";
|
||||
private final ObjectMapper objectMapper = new ObjectMapper().registerModule(new Jdk8Module());
|
||||
private final BlockchainService blockchainService;
|
||||
private final Cache<Hash, TransactionBundle> cache;
|
||||
private final Map<Long, List<TransactionBundle>> blockIndex;
|
||||
private final Path saveFilePath;
|
||||
private final AtomicBoolean isFrozen = new AtomicBoolean(false);
|
||||
private final Subscribers<TransactionBundleAddedListener> transactionBundleAddedListeners =
|
||||
Subscribers.create();
|
||||
private final Subscribers<TransactionBundleRemovedListener> transactionBundleRemovedListeners =
|
||||
Subscribers.create();
|
||||
|
||||
/**
|
||||
* Initializes the LineaLimitedBundlePool with a maximum size and expiration time, and registers
|
||||
* as a blockAddedEvent listener.
|
||||
*
|
||||
* @param maxSizeInBytes The maximum size in bytes of the pool objects.
|
||||
*/
|
||||
@VisibleForTesting
|
||||
public LineaLimitedBundlePool(
|
||||
final Path dataDir,
|
||||
final long maxSizeInBytes,
|
||||
final BesuEvents eventService,
|
||||
final BlockchainService blockchainService) {
|
||||
this.saveFilePath = dataDir.resolve(BUNDLE_SAVE_FILENAME);
|
||||
this.blockchainService = blockchainService;
|
||||
this.cache =
|
||||
Caffeine.newBuilder()
|
||||
.maximumWeight(maxSizeInBytes) // Maximum size in bytes
|
||||
.scheduler(
|
||||
Scheduler
|
||||
.systemScheduler()) // To ensure maintenance operation are not delayed too much
|
||||
.weigher(
|
||||
(Hash key, TransactionBundle value) -> {
|
||||
// Calculate the size of a TransactionBundle in bytes
|
||||
return calculateWeight(value);
|
||||
})
|
||||
.removalListener(
|
||||
(Hash key, TransactionBundle bundle, RemovalCause cause) -> {
|
||||
if (bundle != null) {
|
||||
if (cause.wasEvicted()) {
|
||||
log.atTrace()
|
||||
.setMessage("Dropping transaction bundle {}:{} due to {}")
|
||||
.addArgument(bundle::blockNumber)
|
||||
.addArgument(() -> bundle.bundleIdentifier().toHexString())
|
||||
.addArgument(cause::name)
|
||||
.log();
|
||||
removeFromBlockIndex(bundle);
|
||||
}
|
||||
|
||||
transactionBundleRemovedListeners.forEach(
|
||||
listener -> listener.onTransactionBundleRemoved(bundle));
|
||||
}
|
||||
})
|
||||
.build();
|
||||
this.blockIndex = new ConcurrentHashMap<>();
|
||||
|
||||
// register ourselves as a block added listener:
|
||||
eventService.addBlockAddedListener(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves a list of TransactionBundles associated with a block number.
|
||||
*
|
||||
* @param blockNumber The block number to look up.
|
||||
* @return A list of TransactionBundles for the given block number, or an empty list if none are
|
||||
* found. The returned list if safe for modification since it is not backed by the original
|
||||
* one
|
||||
*/
|
||||
public List<TransactionBundle> getBundlesByBlockNumber(long blockNumber) {
|
||||
return List.copyOf(blockIndex.getOrDefault(blockNumber, emptyList()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves a TransactionBundle by its unique hash identifier.
|
||||
*
|
||||
* @param hash The hash identifier of the TransactionBundle.
|
||||
* @return The TransactionBundle associated with the hash, or null if not found.
|
||||
*/
|
||||
public TransactionBundle get(Hash hash) {
|
||||
return cache.getIfPresent(hash);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves a TransactionBundle by its replacement UUID
|
||||
*
|
||||
* @param replacementUUID identifier of the TransactionBundle.
|
||||
* @return The TransactionBundle associated with the uuid, or null if not found.
|
||||
*/
|
||||
public TransactionBundle get(UUID replacementUUID) {
|
||||
return cache.getIfPresent(UUIDToHash(replacementUUID));
|
||||
}
|
||||
|
||||
/**
|
||||
* Puts or replaces an existing TransactionBundle in the cache and updates the block index.
|
||||
*
|
||||
* @param hash The hash identifier of the TransactionBundle.
|
||||
* @param bundle The new TransactionBundle to replace the existing one.
|
||||
*/
|
||||
public void putOrReplace(Hash hash, TransactionBundle bundle) {
|
||||
failIfFrozen(
|
||||
() -> {
|
||||
TransactionBundle existing = cache.getIfPresent(hash);
|
||||
if (existing != null) {
|
||||
removeFromBlockIndex(existing);
|
||||
}
|
||||
cache.put(hash, bundle);
|
||||
addToBlockIndex(bundle);
|
||||
return null;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Puts or replaces an existing TransactionBundle by UUIDin the cache and updates the block index.
|
||||
*
|
||||
* @param replacementUUID identifier of the TransactionBundle.
|
||||
* @param bundle The new TransactionBundle to replace the existing one.
|
||||
*/
|
||||
public void putOrReplace(UUID replacementUUID, TransactionBundle bundle) {
|
||||
putOrReplace(UUIDToHash(replacementUUID), bundle);
|
||||
}
|
||||
|
||||
/**
|
||||
* removes an existing TransactionBundle in the cache and updates the block index.
|
||||
*
|
||||
* @param replacementUUID identifier of the TransactionBundle.
|
||||
* @return boolean indicating if bundle was found and removed
|
||||
*/
|
||||
public boolean remove(UUID replacementUUID) {
|
||||
return remove(UUIDToHash(replacementUUID));
|
||||
}
|
||||
|
||||
/**
|
||||
* removes an existing TransactionBundle in the cache and updates the block index.
|
||||
*
|
||||
* @param hash The hash identifier of the TransactionBundle.
|
||||
* @return boolean indicating if bundle was found and removed
|
||||
*/
|
||||
public boolean remove(Hash hash) {
|
||||
return failIfFrozen(
|
||||
() -> {
|
||||
var existingBundle = cache.getIfPresent(hash);
|
||||
if (existingBundle != null) {
|
||||
cache.invalidate(hash);
|
||||
removeFromBlockIndex(existingBundle);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public long size() {
|
||||
return cache.estimatedSize();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isFrozen() {
|
||||
return isFrozen.get();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void saveToDisk() {
|
||||
synchronized (isFrozen) {
|
||||
isFrozen.set(true);
|
||||
log.info("Saving bundles to {}", saveFilePath);
|
||||
|
||||
try (final BufferedWriter bw = Files.newBufferedWriter(saveFilePath, StandardCharsets.UTF_8);
|
||||
final SequenceWriter sequenceWriter =
|
||||
objectMapper
|
||||
.writerFor(TransactionBundle.class)
|
||||
.withRootValueSeparator(System.lineSeparator())
|
||||
.writeValues(bw)) {
|
||||
|
||||
// write the header
|
||||
bw.write(objectMapper.writeValueAsString(Map.of("version", 1)));
|
||||
bw.newLine();
|
||||
|
||||
// write the bundles sorted by block number
|
||||
final var savedCount =
|
||||
blockIndex.values().stream()
|
||||
.flatMap(List::stream)
|
||||
.sorted(Comparator.comparing(TransactionBundle::blockNumber))
|
||||
.peek(
|
||||
bundle -> {
|
||||
try {
|
||||
sequenceWriter.write(bundle);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
})
|
||||
.count();
|
||||
log.info("Saved {} bundles to {}", savedCount, saveFilePath);
|
||||
} catch (final Throwable ioe) {
|
||||
log.error("Error while saving bundles to {}", saveFilePath, ioe);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void loadFromDisk() {
|
||||
failIfFrozen(
|
||||
() -> {
|
||||
if (saveFilePath.toFile().exists()) {
|
||||
log.info("Loading bundles from {}", saveFilePath);
|
||||
final var chainHeadBlockNumber = blockchainService.getChainHeadHeader().getNumber();
|
||||
final var loadedCount = new AtomicLong(0L);
|
||||
final var skippedCount = new AtomicLong(0L);
|
||||
|
||||
try (final BufferedReader br =
|
||||
Files.newBufferedReader(saveFilePath, StandardCharsets.UTF_8)) {
|
||||
|
||||
// read header and check version
|
||||
final var headerNode = objectMapper.readTree(br.readLine());
|
||||
if (!headerNode.has("version") || headerNode.get("version").asInt() != 1) {
|
||||
throw new IllegalArgumentException(
|
||||
"Unsupported bundle serialization header " + headerNode);
|
||||
}
|
||||
log.info("Loading bundles from {}, header {}", saveFilePath, headerNode);
|
||||
|
||||
try (final MappingIterator<TransactionBundle> iterator =
|
||||
objectMapper.readerFor(TransactionBundle.class).readValues(br)) {
|
||||
iterator.forEachRemaining(
|
||||
bundle -> {
|
||||
if (bundle.blockNumber() > chainHeadBlockNumber) {
|
||||
this.putOrReplace(bundle.bundleIdentifier(), bundle);
|
||||
loadedCount.incrementAndGet();
|
||||
} else {
|
||||
log.debug(
|
||||
"Skipping bundle {} at location {}, since its block number {} is not greater than chain head block number {}",
|
||||
bundle.bundleIdentifier(),
|
||||
iterator.getCurrentLocation(),
|
||||
bundle.blockNumber(),
|
||||
chainHeadBlockNumber);
|
||||
skippedCount.incrementAndGet();
|
||||
}
|
||||
});
|
||||
log.info("Loaded {} bundles from {}", loadedCount.get(), saveFilePath);
|
||||
}
|
||||
} catch (final Throwable t) {
|
||||
log.error(
|
||||
"Error while reading bundles from {}, partially loaded {} bundles",
|
||||
saveFilePath,
|
||||
loadedCount.get(),
|
||||
t);
|
||||
}
|
||||
saveFilePath.toFile().delete();
|
||||
}
|
||||
return null;
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public long subscribeTransactionBundleAdded(final TransactionBundleAddedListener listener) {
|
||||
return transactionBundleAddedListeners.subscribe(listener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long subscribeTransactionBundleRemoved(final TransactionBundleRemovedListener listener) {
|
||||
return transactionBundleRemovedListeners.subscribe(listener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unsubscribeTransactionBundleAdded(final long listenerId) {
|
||||
transactionBundleAddedListeners.unsubscribe(listenerId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unsubscribeTransactionBundleRemoved(final long listenerId) {
|
||||
transactionBundleRemovedListeners.unsubscribe(listenerId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a TransactionBundle to the block index.
|
||||
*
|
||||
* @param bundle The TransactionBundle to add.
|
||||
*/
|
||||
private void addToBlockIndex(TransactionBundle bundle) {
|
||||
long blockNumber = bundle.blockNumber();
|
||||
blockIndex.computeIfAbsent(blockNumber, k -> new ArrayList<>()).add(bundle);
|
||||
transactionBundleAddedListeners.forEach(listener -> listener.onTransactionBundleAdded(bundle));
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a TransactionBundle from the block index.
|
||||
*
|
||||
* @param bundle The TransactionBundle to remove.
|
||||
*/
|
||||
private void removeFromBlockIndex(TransactionBundle bundle) {
|
||||
long blockNumber = bundle.blockNumber();
|
||||
List<TransactionBundle> bundles = blockIndex.get(blockNumber);
|
||||
if (bundles != null) {
|
||||
bundles.remove(bundle);
|
||||
if (bundles.isEmpty()) {
|
||||
blockIndex.remove(blockNumber);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private int calculateWeight(TransactionBundle bundle) {
|
||||
return bundle.pendingTransactions().stream().mapToInt(PendingTransaction::memorySize).sum();
|
||||
}
|
||||
|
||||
/**
|
||||
* convert a UUID into a hash used by the bundle pool.
|
||||
*
|
||||
* @param uuid the uuid to hash
|
||||
* @return Hash identifier for the uuid
|
||||
*/
|
||||
public static Hash UUIDToHash(UUID uuid) {
|
||||
return Hash.hash(
|
||||
Bytes.concatenate(
|
||||
Bytes.ofUnsignedLong(uuid.getMostSignificantBits()),
|
||||
Bytes.ofUnsignedLong(uuid.getLeastSignificantBits())));
|
||||
}
|
||||
|
||||
/**
|
||||
* Cull the bundle pool on the basis of blocks added.
|
||||
*
|
||||
* @param addedBlockContext
|
||||
*/
|
||||
@Override
|
||||
public void onBlockAdded(final AddedBlockContext addedBlockContext) {
|
||||
synchronized (isFrozen) {
|
||||
if (!isFrozen.get()) { // do nothing if frozen
|
||||
final var lastSeen = addedBlockContext.getBlockHeader().getNumber();
|
||||
final var latest = Math.max(lastSeen, blockchainService.getChainHeadHeader().getNumber());
|
||||
// keep it simple regarding reorgs and, cull the pool for any block numbers lower than
|
||||
// latest
|
||||
blockIndex.keySet().stream()
|
||||
.filter(k -> k < latest)
|
||||
// collecting to a set in order to not mutate the collection we are streaming:
|
||||
.collect(Collectors.toSet())
|
||||
.forEach(
|
||||
k -> {
|
||||
blockIndex.get(k).forEach(bundle -> cache.invalidate(bundle.bundleIdentifier()));
|
||||
// dropping from the cache should inherently remove from blockIndex, but this
|
||||
// is cheap insurance against blockIndex map leaking due to cache evictions
|
||||
blockIndex.remove(k);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private <R> R failIfFrozen(Supplier<R> modificationAction) {
|
||||
synchronized (isFrozen) {
|
||||
if (isFrozen.get()) {
|
||||
throw new IllegalStateException("Bundle pool is not accepting modifications");
|
||||
}
|
||||
return modificationAction.get();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,134 @@
|
||||
/*
|
||||
* Copyright Consensys Software Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
|
||||
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations under the License.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package net.consensys.linea.bundles;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.SequencedMap;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonCreator;
|
||||
import com.fasterxml.jackson.annotation.JsonValue;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.Getter;
|
||||
import lombok.ToString;
|
||||
import lombok.experimental.Accessors;
|
||||
import org.apache.tuweni.bytes.Bytes;
|
||||
import org.hyperledger.besu.datatypes.Hash;
|
||||
import org.hyperledger.besu.datatypes.parameters.UnsignedLongParameter;
|
||||
import org.hyperledger.besu.ethereum.core.Transaction;
|
||||
import org.hyperledger.besu.ethereum.rlp.BytesValueRLPOutput;
|
||||
|
||||
/** TransactionBundle class representing a collection of pending transactions with metadata. */
|
||||
@Accessors(fluent = true)
|
||||
@Getter
|
||||
@EqualsAndHashCode
|
||||
@ToString
|
||||
public class TransactionBundle {
|
||||
private static final AtomicLong BUNDLE_COUNT = new AtomicLong(0L);
|
||||
private final long sequence = BUNDLE_COUNT.incrementAndGet();
|
||||
private final Hash bundleIdentifier;
|
||||
private final List<PendingBundleTx> pendingTransactions;
|
||||
private final Long blockNumber;
|
||||
private final Optional<Long> minTimestamp;
|
||||
private final Optional<Long> maxTimestamp;
|
||||
private final Optional<List<Hash>> revertingTxHashes;
|
||||
private final Optional<UUID> replacementUUID;
|
||||
|
||||
public TransactionBundle(
|
||||
final Hash bundleIdentifier,
|
||||
final List<Transaction> transactions,
|
||||
final Long blockNumber,
|
||||
final Optional<Long> minTimestamp,
|
||||
final Optional<Long> maxTimestamp,
|
||||
final Optional<List<Hash>> revertingTxHashes,
|
||||
final Optional<UUID> replacementUUID) {
|
||||
this.bundleIdentifier = bundleIdentifier;
|
||||
this.pendingTransactions = transactions.stream().map(PendingBundleTx::new).toList();
|
||||
this.blockNumber = blockNumber;
|
||||
this.minTimestamp = minTimestamp;
|
||||
this.maxTimestamp = maxTimestamp;
|
||||
this.revertingTxHashes = revertingTxHashes;
|
||||
this.replacementUUID = replacementUUID;
|
||||
}
|
||||
|
||||
public BundleParameter toBundleParameter(final boolean compact) {
|
||||
return new BundleParameter(
|
||||
pendingTransactions.stream()
|
||||
.map(
|
||||
ptx ->
|
||||
compact ? ptx.toBase64String() : ptx.getTransaction().encoded().toHexString())
|
||||
.toList(),
|
||||
new UnsignedLongParameter(blockNumber),
|
||||
minTimestamp,
|
||||
maxTimestamp,
|
||||
revertingTxHashes,
|
||||
replacementUUID.map(UUID::toString),
|
||||
Optional.empty());
|
||||
}
|
||||
|
||||
@JsonValue
|
||||
public Map<Hash, BundleParameter> serialize() {
|
||||
return Map.of(bundleIdentifier, toBundleParameter(true));
|
||||
}
|
||||
|
||||
@JsonCreator
|
||||
public static TransactionBundle deserialize(
|
||||
final SequencedMap<Hash, BundleParameter> serialized) {
|
||||
final var entry = serialized.firstEntry();
|
||||
final var hash = entry.getKey();
|
||||
final var parameters = entry.getValue();
|
||||
|
||||
return new TransactionBundle(
|
||||
hash,
|
||||
parameters.txs().stream().map(Bytes::fromBase64String).map(Transaction::readFrom).toList(),
|
||||
parameters.blockNumber(),
|
||||
parameters.minTimestamp(),
|
||||
parameters.maxTimestamp(),
|
||||
parameters.revertingTxHashes(),
|
||||
parameters.replacementUUID().map(UUID::fromString));
|
||||
}
|
||||
|
||||
/** A pending transaction contained in a bundle. */
|
||||
public class PendingBundleTx
|
||||
extends org.hyperledger.besu.ethereum.eth.transactions.PendingTransaction.Local {
|
||||
|
||||
public PendingBundleTx(final Transaction transaction) {
|
||||
super(transaction);
|
||||
}
|
||||
|
||||
public TransactionBundle getBundle() {
|
||||
return TransactionBundle.this;
|
||||
}
|
||||
|
||||
public boolean isBundleStart() {
|
||||
return getBundle().pendingTransactions().getFirst().equals(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toTraceLog() {
|
||||
return "Bundle tx: " + super.toTraceLog();
|
||||
}
|
||||
|
||||
String toBase64String() {
|
||||
final var rlpOutput = new BytesValueRLPOutput();
|
||||
getTransaction().writeTo(rlpOutput);
|
||||
return rlpOutput.encoded().toBase64String();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,110 @@
|
||||
/*
|
||||
* Copyright Consensys Software Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
|
||||
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations under the License.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package net.consensys.linea.config;
|
||||
|
||||
import java.net.URL;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import com.google.common.base.MoreObjects;
|
||||
import net.consensys.linea.plugins.LineaCliOptions;
|
||||
import picocli.CommandLine;
|
||||
|
||||
/** The Linea Bundle CLI options. */
|
||||
public class LineaBundleCliOptions implements LineaCliOptions {
|
||||
public static final String CONFIG_KEY = "bundle-sequencer";
|
||||
|
||||
private static final String BUNDLES_FORWARD_URLS = "--plugin-linea-bundles-forward-urls";
|
||||
private static final Set<URL> DEFAULT_BUNDLES_FORWARD_URLS = Set.of();
|
||||
|
||||
private static final String BUNDLES_FORWARD_RETRY_DELAY =
|
||||
"--plugin-linea-bundles-forward-retry-delay";
|
||||
private static final int DEFAULT_BUNDLES_FORWARD_RETRY_DELAY_MILLIS = 1000;
|
||||
|
||||
private static final String BUNDLES_FORWARD_TIMEOUT = "--plugin-linea-bundles-forward-timeout";
|
||||
private static final int DEFAULT_BUNDLES_FORWARD_TIMEOUT_MILLIS = 5000;
|
||||
|
||||
@CommandLine.Option(
|
||||
names = {BUNDLES_FORWARD_URLS},
|
||||
paramLabel = "<SET<URL>>",
|
||||
description =
|
||||
"A comma separated list of endpoint to which incoming bundles will be forwarded (default: ${DEFAULT-VALUE})")
|
||||
private Set<URL> forwardUrls = DEFAULT_BUNDLES_FORWARD_URLS;
|
||||
|
||||
@CommandLine.Option(
|
||||
names = {BUNDLES_FORWARD_RETRY_DELAY},
|
||||
paramLabel = "<INTEGER>",
|
||||
description =
|
||||
"Number of milliseconds to wait before retrying a failed forward (default: ${DEFAULT-VALUE})")
|
||||
private int retryDelayMillis = DEFAULT_BUNDLES_FORWARD_RETRY_DELAY_MILLIS;
|
||||
|
||||
@CommandLine.Option(
|
||||
names = {BUNDLES_FORWARD_TIMEOUT},
|
||||
paramLabel = "<INTEGER>",
|
||||
description =
|
||||
"Number of milliseconds to wait before a forward times out (default: ${DEFAULT-VALUE})")
|
||||
private int timeoutMillis = DEFAULT_BUNDLES_FORWARD_TIMEOUT_MILLIS;
|
||||
|
||||
private LineaBundleCliOptions() {}
|
||||
|
||||
/**
|
||||
* Create Linea Bundle CLI options.
|
||||
*
|
||||
* @return the Linea RPC Bundle options
|
||||
*/
|
||||
public static LineaBundleCliOptions create() {
|
||||
return new LineaBundleCliOptions();
|
||||
}
|
||||
|
||||
/**
|
||||
* Linea Bundle CLI options from config.
|
||||
*
|
||||
* @param config the config
|
||||
* @return the Linea Bundle CLI options
|
||||
*/
|
||||
public static LineaBundleCliOptions fromConfig(final LineaBundleConfiguration config) {
|
||||
final LineaBundleCliOptions options = create();
|
||||
options.forwardUrls = config.forwardUrls();
|
||||
options.retryDelayMillis = config.retryDelayMillis();
|
||||
options.timeoutMillis = config.timeoutMillis();
|
||||
return options;
|
||||
}
|
||||
|
||||
/**
|
||||
* To domain object Linea factory configuration.
|
||||
*
|
||||
* @return the Linea factory configuration
|
||||
*/
|
||||
@Override
|
||||
public LineaBundleConfiguration toDomainObject() {
|
||||
return LineaBundleConfiguration.builder()
|
||||
.forwardUrls(forwardUrls)
|
||||
.retryDelayMillis(retryDelayMillis)
|
||||
.timeoutMillis(timeoutMillis)
|
||||
.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return MoreObjects.toStringHelper(this)
|
||||
.add(
|
||||
BUNDLES_FORWARD_URLS,
|
||||
forwardUrls.stream().map(URL::toString).collect(Collectors.joining(",")))
|
||||
.add(BUNDLES_FORWARD_RETRY_DELAY, retryDelayMillis)
|
||||
.add(BUNDLES_FORWARD_TIMEOUT, timeoutMillis)
|
||||
.toString();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* Copyright Consensys Software Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
|
||||
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations under the License.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package net.consensys.linea.config;
|
||||
|
||||
import java.net.URL;
|
||||
import java.util.Set;
|
||||
|
||||
import lombok.Builder;
|
||||
import lombok.Getter;
|
||||
import lombok.ToString;
|
||||
import lombok.experimental.Accessors;
|
||||
import net.consensys.linea.plugins.LineaOptionsConfiguration;
|
||||
|
||||
/** The Linea Bundle configuration. */
|
||||
@Builder(toBuilder = true)
|
||||
@Accessors(fluent = true)
|
||||
@Getter
|
||||
@ToString
|
||||
public class LineaBundleConfiguration implements LineaOptionsConfiguration {
|
||||
private Set<URL> forwardUrls;
|
||||
private int retryDelayMillis;
|
||||
private int timeoutMillis;
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
/*
|
||||
* Copyright Consensys Software Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
|
||||
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations under the License.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package net.consensys.linea.config;
|
||||
|
||||
/** Linea node type that is used when reporting rejected transactions. */
|
||||
public enum LineaNodeType {
|
||||
SEQUENCER,
|
||||
RPC,
|
||||
P2P
|
||||
}
|
||||
@@ -0,0 +1,224 @@
|
||||
/*
|
||||
* Copyright Consensys Software Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
|
||||
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations under the License.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package net.consensys.linea.config;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
import com.google.common.base.MoreObjects;
|
||||
import jakarta.validation.constraints.Positive;
|
||||
import net.consensys.linea.plugins.LineaCliOptions;
|
||||
import picocli.CommandLine;
|
||||
|
||||
/** The Linea profitability calculator CLI options. */
|
||||
public class LineaProfitabilityCliOptions implements LineaCliOptions {
|
||||
public static final String CONFIG_KEY = "profitability-config";
|
||||
|
||||
public static final String FIXED_GAS_COST_WEI = "--plugin-linea-fixed-gas-cost-wei";
|
||||
public static final long DEFAULT_FIXED_GAS_COST_WEI = 0;
|
||||
|
||||
public static final String VARIABLE_GAS_COST_WEI = "--plugin-linea-variable-gas-cost-wei";
|
||||
public static final long DEFAULT_VARIABLE_GAS_COST_WEI = 1_000_000_000;
|
||||
|
||||
public static final String MIN_MARGIN = "--plugin-linea-min-margin";
|
||||
public static final BigDecimal DEFAULT_MIN_MARGIN = BigDecimal.ONE;
|
||||
|
||||
public static final String ESTIMATE_GAS_MIN_MARGIN = "--plugin-linea-estimate-gas-min-margin";
|
||||
public static final BigDecimal DEFAULT_ESTIMATE_GAS_MIN_MARGIN = BigDecimal.ONE;
|
||||
|
||||
public static final String TX_POOL_MIN_MARGIN = "--plugin-linea-tx-pool-min-margin";
|
||||
public static final BigDecimal DEFAULT_TX_POOL_MIN_MARGIN = BigDecimal.valueOf(0.5);
|
||||
|
||||
public static final String TX_POOL_ENABLE_CHECK_API =
|
||||
"--plugin-linea-tx-pool-profitability-check-api-enabled";
|
||||
public static final boolean DEFAULT_TX_POOL_ENABLE_CHECK_API = true;
|
||||
|
||||
public static final String TX_POOL_ENABLE_CHECK_P2P =
|
||||
"--plugin-linea-tx-pool-profitability-check-p2p-enabled";
|
||||
public static final boolean DEFAULT_TX_POOL_ENABLE_CHECK_P2P = false;
|
||||
|
||||
public static final String EXTRA_DATA_PRICING_ENABLED =
|
||||
"--plugin-linea-extra-data-pricing-enabled";
|
||||
public static final boolean DEFAULT_EXTRA_DATA_PRICING_ENABLED = false;
|
||||
|
||||
public static final String EXTRA_DATA_SET_MIN_GAS_PRICE_ENABLED =
|
||||
"--plugin-linea-extra-data-set-min-gas-price-enabled";
|
||||
public static final boolean DEFAULT_EXTRA_DATA_SET_MIN_GAS_PRICE_ENABLED = true;
|
||||
|
||||
public static final String PROFITABILITY_METRICS_BUCKETS =
|
||||
"--plugin-linea-profitability-metrics-buckets";
|
||||
public static final double[] DEFAULT_PROFITABILITY_METRICS_BUCKETS = {
|
||||
0.1, 0.3, 0.5, 0.7, 0.9, 1.0, 1.1, 1.2, 1.5, 2.0, 5.0, 10.0
|
||||
};
|
||||
|
||||
@Positive
|
||||
@CommandLine.Option(
|
||||
names = {FIXED_GAS_COST_WEI},
|
||||
hidden = true,
|
||||
paramLabel = "<INTEGER>",
|
||||
description = "Fixed gas cost in Wei (default: ${DEFAULT-VALUE})")
|
||||
private long fixedGasCostWei = DEFAULT_FIXED_GAS_COST_WEI;
|
||||
|
||||
@Positive
|
||||
@CommandLine.Option(
|
||||
names = {VARIABLE_GAS_COST_WEI},
|
||||
hidden = true,
|
||||
paramLabel = "<INTEGER>",
|
||||
description = "Variable gas cost in Wei (default: ${DEFAULT-VALUE})")
|
||||
private long variableGasCostWei = DEFAULT_VARIABLE_GAS_COST_WEI;
|
||||
|
||||
@Positive
|
||||
@CommandLine.Option(
|
||||
names = {MIN_MARGIN},
|
||||
hidden = true,
|
||||
paramLabel = "<FLOAT>",
|
||||
description = "Minimum margin of a transaction to be selected (default: ${DEFAULT-VALUE})")
|
||||
private BigDecimal minMargin = DEFAULT_MIN_MARGIN;
|
||||
|
||||
@Positive
|
||||
@CommandLine.Option(
|
||||
names = {ESTIMATE_GAS_MIN_MARGIN},
|
||||
hidden = true,
|
||||
paramLabel = "<FLOAT>",
|
||||
description =
|
||||
"Recommend a specific gas price when using linea_estimateGas (default: ${DEFAULT-VALUE})")
|
||||
private BigDecimal estimateGasMinMargin = DEFAULT_ESTIMATE_GAS_MIN_MARGIN;
|
||||
|
||||
@Positive
|
||||
@CommandLine.Option(
|
||||
names = {TX_POOL_MIN_MARGIN},
|
||||
hidden = true,
|
||||
paramLabel = "<FLOAT>",
|
||||
description =
|
||||
"The min margin an incoming tx must have to be accepted in the txpool (default: ${DEFAULT-VALUE})")
|
||||
private BigDecimal txPoolMinMargin = DEFAULT_TX_POOL_MIN_MARGIN;
|
||||
|
||||
@CommandLine.Option(
|
||||
names = {TX_POOL_ENABLE_CHECK_API},
|
||||
arity = "0..1",
|
||||
hidden = true,
|
||||
paramLabel = "<BOOLEAN>",
|
||||
description =
|
||||
"Enable the profitability check for txs received via API? (default: ${DEFAULT-VALUE})")
|
||||
private boolean txPoolCheckApiEnabled = DEFAULT_TX_POOL_ENABLE_CHECK_API;
|
||||
|
||||
@CommandLine.Option(
|
||||
names = {TX_POOL_ENABLE_CHECK_P2P},
|
||||
arity = "0..1",
|
||||
hidden = true,
|
||||
paramLabel = "<BOOLEAN>",
|
||||
description =
|
||||
"Enable the profitability check for txs received via p2p? (default: ${DEFAULT-VALUE})")
|
||||
private boolean txPoolCheckP2pEnabled = DEFAULT_TX_POOL_ENABLE_CHECK_P2P;
|
||||
|
||||
@CommandLine.Option(
|
||||
names = {EXTRA_DATA_PRICING_ENABLED},
|
||||
arity = "0..1",
|
||||
hidden = true,
|
||||
paramLabel = "<BOOLEAN>",
|
||||
description =
|
||||
"Enable setting pricing parameters via extra data field (default: ${DEFAULT-VALUE})")
|
||||
private boolean extraDataPricingEnabled = DEFAULT_EXTRA_DATA_PRICING_ENABLED;
|
||||
|
||||
@CommandLine.Option(
|
||||
names = {EXTRA_DATA_SET_MIN_GAS_PRICE_ENABLED},
|
||||
arity = "0..1",
|
||||
hidden = true,
|
||||
paramLabel = "<BOOLEAN>",
|
||||
description =
|
||||
"Enable setting min gas price runtime value via extra data field (default: ${DEFAULT-VALUE})")
|
||||
private boolean extraDataSetMinGasPriceEnabled = DEFAULT_EXTRA_DATA_SET_MIN_GAS_PRICE_ENABLED;
|
||||
|
||||
@CommandLine.Option(
|
||||
names = {PROFITABILITY_METRICS_BUCKETS},
|
||||
arity = "1..*",
|
||||
split = ",",
|
||||
hidden = true,
|
||||
paramLabel = "<FLOAT[]>",
|
||||
description =
|
||||
"List of buckets to use to create the histogram for ratio between the effective priority fee "
|
||||
+ "and the calculate profitable priority of the tx (default: ${DEFAULT-VALUE})")
|
||||
private double[] profitabilityMetricsBuckets = DEFAULT_PROFITABILITY_METRICS_BUCKETS;
|
||||
|
||||
private LineaProfitabilityCliOptions() {}
|
||||
|
||||
/**
|
||||
* Create Linea cli options.
|
||||
*
|
||||
* @return the Linea cli options
|
||||
*/
|
||||
public static LineaProfitabilityCliOptions create() {
|
||||
return new LineaProfitabilityCliOptions();
|
||||
}
|
||||
|
||||
/**
|
||||
* Linea cli options from config.
|
||||
*
|
||||
* @param config the config
|
||||
* @return the Linea cli options
|
||||
*/
|
||||
public static LineaProfitabilityCliOptions fromConfig(
|
||||
final LineaProfitabilityConfiguration config) {
|
||||
final LineaProfitabilityCliOptions options = create();
|
||||
options.fixedGasCostWei = config.fixedCostWei();
|
||||
options.variableGasCostWei = config.variableCostWei();
|
||||
options.minMargin = BigDecimal.valueOf(config.minMargin());
|
||||
options.estimateGasMinMargin = BigDecimal.valueOf(config.estimateGasMinMargin());
|
||||
options.txPoolMinMargin = BigDecimal.valueOf(config.txPoolMinMargin());
|
||||
options.txPoolCheckApiEnabled = config.txPoolCheckApiEnabled();
|
||||
options.txPoolCheckP2pEnabled = config.txPoolCheckP2pEnabled();
|
||||
options.extraDataPricingEnabled = config.extraDataPricingEnabled();
|
||||
options.extraDataSetMinGasPriceEnabled = config.extraDataSetMinGasPriceEnabled();
|
||||
options.profitabilityMetricsBuckets = config.profitabilityMetricsBuckets();
|
||||
return options;
|
||||
}
|
||||
|
||||
/**
|
||||
* To domain object Linea factory configuration.
|
||||
*
|
||||
* @return the Linea factory configuration
|
||||
*/
|
||||
@Override
|
||||
public LineaProfitabilityConfiguration toDomainObject() {
|
||||
return LineaProfitabilityConfiguration.builder()
|
||||
.fixedCostWei(fixedGasCostWei)
|
||||
.variableCostWei(variableGasCostWei)
|
||||
.minMargin(minMargin.doubleValue())
|
||||
.estimateGasMinMargin(estimateGasMinMargin.doubleValue())
|
||||
.txPoolMinMargin(txPoolMinMargin.doubleValue())
|
||||
.txPoolCheckApiEnabled(txPoolCheckApiEnabled)
|
||||
.txPoolCheckP2pEnabled(txPoolCheckP2pEnabled)
|
||||
.extraDataPricingEnabled(extraDataPricingEnabled)
|
||||
.extraDataSetMinGasPriceEnabled(extraDataSetMinGasPriceEnabled)
|
||||
.profitabilityMetricsBuckets(profitabilityMetricsBuckets)
|
||||
.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return MoreObjects.toStringHelper(this)
|
||||
.add(FIXED_GAS_COST_WEI, fixedGasCostWei)
|
||||
.add(VARIABLE_GAS_COST_WEI, variableGasCostWei)
|
||||
.add(MIN_MARGIN, minMargin)
|
||||
.add(ESTIMATE_GAS_MIN_MARGIN, estimateGasMinMargin)
|
||||
.add(TX_POOL_MIN_MARGIN, txPoolMinMargin)
|
||||
.add(TX_POOL_ENABLE_CHECK_API, txPoolCheckApiEnabled)
|
||||
.add(TX_POOL_ENABLE_CHECK_P2P, txPoolCheckP2pEnabled)
|
||||
.add(EXTRA_DATA_PRICING_ENABLED, extraDataPricingEnabled)
|
||||
.add(EXTRA_DATA_SET_MIN_GAS_PRICE_ENABLED, extraDataSetMinGasPriceEnabled)
|
||||
.add(PROFITABILITY_METRICS_BUCKETS, profitabilityMetricsBuckets)
|
||||
.toString();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
/*
|
||||
* Copyright Consensys Software Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
|
||||
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations under the License.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package net.consensys.linea.config;
|
||||
|
||||
import lombok.Builder;
|
||||
import lombok.Getter;
|
||||
import lombok.ToString;
|
||||
import lombok.experimental.Accessors;
|
||||
import net.consensys.linea.plugins.LineaOptionsConfiguration;
|
||||
|
||||
/** The Linea profitability calculator configuration. */
|
||||
@Builder(toBuilder = true)
|
||||
@Accessors(fluent = true)
|
||||
@Getter
|
||||
@ToString
|
||||
public class LineaProfitabilityConfiguration implements LineaOptionsConfiguration {
|
||||
/** It is safe to keep this as long, since it will store value <= max_int * 1000 */
|
||||
private long fixedCostWei;
|
||||
|
||||
/** It is safe to keep this as long, since it will store value <= max_int * 1000 */
|
||||
private long variableCostWei;
|
||||
|
||||
/** It is safe to keep this as long, since it will store value <= max_int * 1000 */
|
||||
private long ethGasPriceWei;
|
||||
|
||||
private double minMargin;
|
||||
private double estimateGasMinMargin;
|
||||
private double txPoolMinMargin;
|
||||
private boolean txPoolCheckApiEnabled;
|
||||
private boolean txPoolCheckP2pEnabled;
|
||||
private boolean extraDataPricingEnabled;
|
||||
private boolean extraDataSetMinGasPriceEnabled;
|
||||
private double[] profitabilityMetricsBuckets;
|
||||
|
||||
/**
|
||||
* These 2 parameters must be atomically updated
|
||||
*
|
||||
* @param fixedCostWei fixed cost in Wei
|
||||
* @param variableCostWei variable cost in Wei
|
||||
* @param ethGasPriceWei gas price in Wei
|
||||
*/
|
||||
public synchronized void updateFixedVariableAndGasPrice(
|
||||
final long fixedCostWei, final long variableCostWei, final long ethGasPriceWei) {
|
||||
this.fixedCostWei = fixedCostWei;
|
||||
this.variableCostWei = variableCostWei;
|
||||
this.ethGasPriceWei = ethGasPriceWei;
|
||||
}
|
||||
|
||||
public synchronized long fixedCostWei() {
|
||||
return fixedCostWei;
|
||||
}
|
||||
|
||||
public synchronized long variableCostWei() {
|
||||
return variableCostWei;
|
||||
}
|
||||
|
||||
public synchronized long ethGasPriceWei() {
|
||||
return ethGasPriceWei;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,100 @@
|
||||
/*
|
||||
* Copyright Consensys Software Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
|
||||
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations under the License.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
package net.consensys.linea.config;
|
||||
|
||||
import java.net.URL;
|
||||
|
||||
import com.google.common.base.MoreObjects;
|
||||
import net.consensys.linea.plugins.LineaCliOptions;
|
||||
import picocli.CommandLine.Option;
|
||||
|
||||
/** The Linea Rejected Transaction Reporting CLI options. */
|
||||
public class LineaRejectedTxReportingCliOptions implements LineaCliOptions {
|
||||
/**
|
||||
* The configuration key used in AbstractLineaPrivateOptionsPlugin to identify the cli options.
|
||||
*/
|
||||
public static final String CONFIG_KEY = "rejected-tx-reporting-config";
|
||||
|
||||
/** The rejected transaction endpoint. */
|
||||
public static final String REJECTED_TX_ENDPOINT = "--plugin-linea-rejected-tx-endpoint";
|
||||
|
||||
/** The Linea node type. */
|
||||
public static final String LINEA_NODE_TYPE = "--plugin-linea-node-type";
|
||||
|
||||
@Option(
|
||||
names = {REJECTED_TX_ENDPOINT},
|
||||
hidden = true,
|
||||
paramLabel = "<URL>",
|
||||
description =
|
||||
"Endpoint URI for reporting rejected transactions. Specify a valid URI to enable reporting.")
|
||||
URL rejectedTxEndpoint = null;
|
||||
|
||||
@Option(
|
||||
names = {LINEA_NODE_TYPE},
|
||||
hidden = true,
|
||||
paramLabel = "<NODE_TYPE>",
|
||||
description =
|
||||
"Linea Node type to use when reporting rejected transactions. (Valid values: ${COMPLETION-CANDIDATES})")
|
||||
LineaNodeType lineaNodeType = null;
|
||||
|
||||
/** Default constructor. */
|
||||
private LineaRejectedTxReportingCliOptions() {}
|
||||
|
||||
/**
|
||||
* Create Linea Rejected Transaction Reporting CLI options.
|
||||
*
|
||||
* @return the Linea Rejected Transaction Reporting CLI options
|
||||
*/
|
||||
public static LineaRejectedTxReportingCliOptions create() {
|
||||
return new LineaRejectedTxReportingCliOptions();
|
||||
}
|
||||
|
||||
/**
|
||||
* Instantiates a new Linea rejected tx reporting cli options from Configuration object
|
||||
*
|
||||
* @param config An instance of LineaRejectedTxReportingConfiguration
|
||||
*/
|
||||
public static LineaRejectedTxReportingCliOptions fromConfig(
|
||||
final LineaRejectedTxReportingConfiguration config) {
|
||||
final LineaRejectedTxReportingCliOptions options = create();
|
||||
options.rejectedTxEndpoint = config.rejectedTxEndpoint();
|
||||
options.lineaNodeType = config.lineaNodeType();
|
||||
return options;
|
||||
}
|
||||
|
||||
@Override
|
||||
public LineaRejectedTxReportingConfiguration toDomainObject() {
|
||||
// perform validation here, if endpoint is specified then node type is required.
|
||||
// We can ignore node type if endpoint is not specified.
|
||||
if (rejectedTxEndpoint != null && lineaNodeType == null) {
|
||||
throw new IllegalArgumentException(
|
||||
"Error: Missing required argument(s): " + LINEA_NODE_TYPE + "=<NODE_TYPE>");
|
||||
}
|
||||
|
||||
return LineaRejectedTxReportingConfiguration.builder()
|
||||
.rejectedTxEndpoint(rejectedTxEndpoint)
|
||||
.lineaNodeType(lineaNodeType)
|
||||
.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
|
||||
return MoreObjects.toStringHelper(this)
|
||||
.add(REJECTED_TX_ENDPOINT, rejectedTxEndpoint)
|
||||
.add(LINEA_NODE_TYPE, lineaNodeType)
|
||||
.toString();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
/*
|
||||
* Copyright Consensys Software Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
|
||||
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations under the License.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package net.consensys.linea.config;
|
||||
|
||||
import java.net.URL;
|
||||
|
||||
import lombok.Builder;
|
||||
import net.consensys.linea.plugins.LineaOptionsConfiguration;
|
||||
|
||||
/** Linea Rejected Transactions Reporting Configuration */
|
||||
@Builder(toBuilder = true)
|
||||
public record LineaRejectedTxReportingConfiguration(
|
||||
URL rejectedTxEndpoint, LineaNodeType lineaNodeType) implements LineaOptionsConfiguration {}
|
||||
@@ -0,0 +1,96 @@
|
||||
/*
|
||||
* Copyright Consensys Software Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
|
||||
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations under the License.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package net.consensys.linea.config;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
import com.google.common.base.MoreObjects;
|
||||
import net.consensys.linea.plugins.LineaCliOptions;
|
||||
import picocli.CommandLine;
|
||||
|
||||
/** The Linea RPC CLI options. */
|
||||
public class LineaRpcCliOptions implements LineaCliOptions {
|
||||
public static final String CONFIG_KEY = "rpc-config-sequencer";
|
||||
|
||||
private static final String ESTIMATE_GAS_COMPATIBILITY_MODE_ENABLED =
|
||||
"--plugin-linea-estimate-gas-compatibility-mode-enabled";
|
||||
private static final boolean DEFAULT_ESTIMATE_GAS_COMPATIBILITY_MODE_ENABLED = false;
|
||||
private static final String ESTIMATE_GAS_COMPATIBILITY_MODE_MULTIPLIER =
|
||||
"--plugin-linea-estimate-gas-compatibility-mode-multiplier";
|
||||
private static final BigDecimal DEFAULT_ESTIMATE_GAS_COMPATIBILITY_MODE_MULTIPLIER =
|
||||
BigDecimal.valueOf(1.2);
|
||||
|
||||
@CommandLine.Option(
|
||||
names = {ESTIMATE_GAS_COMPATIBILITY_MODE_ENABLED},
|
||||
paramLabel = "<BOOLEAN>",
|
||||
description =
|
||||
"Set to true to return the min mineable gas price * multiplier, instead of the profitable price (default: ${DEFAULT-VALUE})")
|
||||
private boolean estimateGasCompatibilityModeEnabled =
|
||||
DEFAULT_ESTIMATE_GAS_COMPATIBILITY_MODE_ENABLED;
|
||||
|
||||
@CommandLine.Option(
|
||||
names = {ESTIMATE_GAS_COMPATIBILITY_MODE_MULTIPLIER},
|
||||
paramLabel = "<FLOAT>",
|
||||
description =
|
||||
"Set to multiplier to apply to the min priority fee per gas when the compatibility mode is enabled (default: ${DEFAULT-VALUE})")
|
||||
private BigDecimal estimateGasCompatibilityMultiplier =
|
||||
DEFAULT_ESTIMATE_GAS_COMPATIBILITY_MODE_MULTIPLIER;
|
||||
|
||||
private LineaRpcCliOptions() {}
|
||||
|
||||
/**
|
||||
* Create Linea RPC CLI options.
|
||||
*
|
||||
* @return the Linea RPC CLI options
|
||||
*/
|
||||
public static LineaRpcCliOptions create() {
|
||||
return new LineaRpcCliOptions();
|
||||
}
|
||||
|
||||
/**
|
||||
* Linea RPC CLI options from config.
|
||||
*
|
||||
* @param config the config
|
||||
* @return the Linea RPC CLI options
|
||||
*/
|
||||
public static LineaRpcCliOptions fromConfig(final LineaRpcConfiguration config) {
|
||||
final LineaRpcCliOptions options = create();
|
||||
options.estimateGasCompatibilityModeEnabled = config.estimateGasCompatibilityModeEnabled();
|
||||
options.estimateGasCompatibilityMultiplier = config.estimateGasCompatibilityMultiplier();
|
||||
return options;
|
||||
}
|
||||
|
||||
/**
|
||||
* To domain object Linea factory configuration.
|
||||
*
|
||||
* @return the Linea factory configuration
|
||||
*/
|
||||
@Override
|
||||
public LineaRpcConfiguration toDomainObject() {
|
||||
return LineaRpcConfiguration.builder()
|
||||
.estimateGasCompatibilityModeEnabled(estimateGasCompatibilityModeEnabled)
|
||||
.estimateGasCompatibilityMultiplier(estimateGasCompatibilityMultiplier)
|
||||
.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return MoreObjects.toStringHelper(this)
|
||||
.add(ESTIMATE_GAS_COMPATIBILITY_MODE_ENABLED, estimateGasCompatibilityModeEnabled)
|
||||
.add(ESTIMATE_GAS_COMPATIBILITY_MODE_MULTIPLIER, estimateGasCompatibilityMultiplier)
|
||||
.toString();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* Copyright Consensys Software Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
|
||||
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations under the License.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package net.consensys.linea.config;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
import lombok.Builder;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import lombok.ToString;
|
||||
import lombok.experimental.Accessors;
|
||||
import net.consensys.linea.plugins.LineaOptionsConfiguration;
|
||||
|
||||
/** The Linea RPC configuration. */
|
||||
@Builder(toBuilder = true)
|
||||
@Accessors(fluent = true)
|
||||
@Getter
|
||||
@ToString
|
||||
public class LineaRpcConfiguration implements LineaOptionsConfiguration {
|
||||
@Setter private volatile boolean estimateGasCompatibilityModeEnabled;
|
||||
private BigDecimal estimateGasCompatibilityMultiplier;
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
/*
|
||||
* Copyright Consensys Software Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
|
||||
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations under the License.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
package net.consensys.linea.config;
|
||||
|
||||
import com.google.common.base.MoreObjects;
|
||||
import net.consensys.linea.plugins.LineaCliOptions;
|
||||
import picocli.CommandLine;
|
||||
|
||||
public class LineaTracerCliOptions implements LineaCliOptions {
|
||||
public static final String CONFIG_KEY = "tracer-config";
|
||||
|
||||
public static final String MODULE_LIMIT_FILE_PATH = "--plugin-linea-module-limit-file-path";
|
||||
public static final String DEFAULT_MODULE_LIMIT_FILE_PATH = "moduleLimitFile.toml";
|
||||
|
||||
@CommandLine.Option(
|
||||
names = {MODULE_LIMIT_FILE_PATH},
|
||||
hidden = true,
|
||||
paramLabel = "<STRING>",
|
||||
description =
|
||||
"Path to the toml file containing the module limits (default: ${DEFAULT-VALUE})")
|
||||
private String moduleLimitFilePath = DEFAULT_MODULE_LIMIT_FILE_PATH;
|
||||
|
||||
private LineaTracerCliOptions() {}
|
||||
|
||||
/**
|
||||
* Create Linea cli options.
|
||||
*
|
||||
* @return the Linea cli options
|
||||
*/
|
||||
public static LineaTracerCliOptions create() {
|
||||
return new LineaTracerCliOptions();
|
||||
}
|
||||
|
||||
/**
|
||||
* Linea cli options from config.
|
||||
*
|
||||
* @param config the config
|
||||
* @return the Linea cli options
|
||||
*/
|
||||
public static LineaTracerCliOptions fromConfig(final LineaTracerConfiguration config) {
|
||||
final LineaTracerCliOptions options = create();
|
||||
options.moduleLimitFilePath = config.moduleLimitsFilePath();
|
||||
return options;
|
||||
}
|
||||
|
||||
/**
|
||||
* To domain object Linea factory configuration.
|
||||
*
|
||||
* @return the Linea factory configuration
|
||||
*/
|
||||
@Override
|
||||
public LineaTracerConfiguration toDomainObject() {
|
||||
return LineaTracerConfiguration.builder().moduleLimitsFilePath(moduleLimitFilePath).build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return MoreObjects.toStringHelper(this)
|
||||
.add(MODULE_LIMIT_FILE_PATH, moduleLimitFilePath)
|
||||
.toString();
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user