Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Monorepo: Lodestar-inspired Error Management PoC #1910

Open
wants to merge 44 commits into
base: develop-v6-old
Choose a base branch
from

Conversation

holgerd77
Copy link
Member

Continues/replaces #1469

This is a first PoC of a Lodestar-inspired monorepo error management based on the discussion here.

The basic Lodestar error class is defined here, I used the Eth1Error from here and applied e.g. here as some orientation on how things could be done.

I adopted and simplified various things and I am relatively pleased with the result since I have the impression that this should sufficiently satisfy our needs. 🙂

I adopted the following things:

  • code is not part of the - on the Lodestar side - called metadata any more but a separate constructor argument, this simplified some typings and the UI (from my PoV) gets a bit nicer
  • message is also a dedicated separate constructor argument being mandatory. I didn't want the code be used for the original error object message property but rather have this separated
  • I've renamed metadata to errorContext which I find a bit less "technocratic" 😋 and fitting

This is how an error is thrown:

const e = new HeaderValidationError(
  'transactionsTrie must be 32 bytes',
  HeaderValidationErrorCode.WRONG_TX_TRIE_LENGTH,
  {
    block: this.errorStr(),
    received: `${transactionsTrie.toString('hex')} (${transactionsTrie.length} bytes)`,
  }
)
throw e

(can of course also be thrown directly)

An error thrown with this now looks like this:

Uncaught Error: transactionsTrie must be 32 bytes
    at BlockHeader._validateHeaderFields (/ethereumjs-monorepo-develop/packages/block/src/header.ts:359:13)
    at new BlockHeader (/ethereumjs-monorepo-develop/packages/block/src/header.ts:303:10)
    at/ethereumjs-monorepo-develop/packages/block/<repl>.ts:2:9
    at Script.runInThisContext (node:vm:129:12)
    at runInContext (/node_modules/ts-node/src/repl.ts:603:19)
    at Object.execCommand (/node_modules/ts-node/src/repl.ts:569:28)
    at /node_modules/ts-node/src/repl.ts:591:47
    at Array.reduce (<anonymous>)
    at appendCompileAndEvalInput (/node_modules/ts-node/src/repl.ts:591:23)
    at evalCodeInternal (/node_modules/ts-node/src/repl.ts:212:12) {
  code: 'WRONG_TX_TRIE_LENGTH',
  errorContext: {
    block: 'block header number=0 hash=0x7939e4f199f7ec79ce8cff286a0b4b0af01bbd782ff0e10305915d0d2e099de0 hf=london baseFeePerGas=7',
    lengthReceived: 32
  }

All this leads to the following properties (which we didn't have before):

  • The more generic Error (here HeaderValidationError) can be checked with e instanceof HeaderValidationError (tested)
  • The specific error can be checked with e.code === HeaderValidationErrorCode.WRONG_TX_TRIE_LENGTH (tested) independently from an eventually changing error message
  • An arbitrary and typed error context can be provided via the errorContext property

At the same time I have the impression that this remains sufficiently simple.

Let me know what you think!

@gabrocheleau: it would be good if you can have some deepened look here if this respects and covers all eventual particularities which you might have already covered in some adopted form in #1469.

jochem-brouwer and others added 30 commits May 4, 2022 10:52
* vm: add codeHash db prefix to stateManager

* vm: tests: make tests use StateManager instead of trie at utilization
vm: tests: remove now passing test from skipTests

* vm: fix tests / fix runBlock/runBlockchain tests

* vm: tests: fix cleaning up untouched accounts in tests

* vm -> StateManager: added comment on CODEHASH_PREFIX

Co-authored-by: holgerd77 <[email protected]>
…orks to VM (#1649)

* common: remove onlySupported and onlyActive hf options

* vm: move supportedHardforks option from common

* common: remove supportedHardforks option

* common: update README for supportedHardforks removal

* vm: update README for supportedHardforks addition

* common: cleanup in src/index.ts

* vm: refactor supportedHardforks per PR feedback

* common: remove _chooseHardfork method per PR feedback

* vm: updated README per PR feedback

* vm: supportedHardforks local variable, switch to enum
* common: delete deprecated hardforkBlock method

* common: adapt tests to use hardforkBlockBN

* common: delete deprecated nextHardforkBlock method

* common/tests: rename nextHardforkBlock() calls
to nextHardforkBlockBN()

* common: adapt tests to use BN.eqn(number)

* common: delete deprecated networkId() method

* common: delete tests for networkId()

* common: delete deprecated chainId() method

* common: delete tests for .chainId()

* delete deprecated forCustomChain meethod

* common: delete forCustomChain() test

* blockchain: switch forCustomChain to Common.custom

* client: switch fromCustomChain to Common.custom

* tx: switch forCustomChain to Common.custom

* VM: switch forCustomChain to Common.custom

* common: internalize _getInitializedChains() method

* common: delete deprecaed chains/index.ts file

* client: switch to Common._getInitializedChains

* common: remove genesisStates/index.ts

* common: remove test for genesisStates
* common: rename nextHardforkBlockBN()
to nextHardforkBlock()

* common/tests: rename nextHardforkBlockBN()
to nextHardforkBlock()

* client: rename common.nextHardforkBlockBN()
to common.nextHardforkBlock()

* devp2p: rename common.nextHardforkBlockBN()
to common.nextHardforkBlock()

* common: rename hardforkBlockBN()
to hardforkBlock()

* common/tests: rename common.hardforkBlockBN()
to common.hardforkBlock()

* block: rename common.hardforkBlockBN()
to common.hardforkBlock()

* client: rename common.hardforkBlockBN()
to common.hardforkBlock()

* blockchain: rename common.hardforkBlockBN()
to common.hardforkBlock()

* devp2p: rename common.hardforkBlockBN()
to common.hardforkBlock()

* vm: rename common.hardforkBlockBN()
to common.hardforkBlock()

* common: rename chainIdBN()
to chainId()

* common/tests: rename common.chainIdBN()
to common.chainId()

* client: rename common.chainIdBN()
to common.chainId()

* blockchain: rename common.chainIdBN()
to common.chainId()

* devp2p: rename common.chainIdBN()
to common.chainId()

* tx: rename common.chainIdBN()
to common.chainId()

* vm: rename common.chainIdBN()
to common.chainId()

* common: rename networkIdBN() to networkId()

* common/test: rename common.networkIdBN()
to common.networkId()

* client: rename common.networkIdBN()
to common.networkId()

* block: rename common.networkIdBN()
to common.networkId()
* Replace BN.js with bigints in VM

* Fix byteLength computation in EXP handler

* Fix toTwos helper

* Compute TWO_POWE256 using math instead of hex

* Update packages/util/src/constants.ts

Co-authored-by: Ryan Ghods <[email protected]>

* Remove unused variable

* Fix exponent byte length calc

* Fix exp overflow

* Fix precompile bigint conversions

* Fix more bigint conversions

* Fix EXP opcode handler

* Fix logic bug in signextend

* vm/gas: fix EXTCODECOPY gas

* vm/gas: fix sha256 gas

* vm/gas: sha256 const -> let

* vm: lint

* vm/logic: fix sdiv/smod and fromTwos/toTwos

* vm/logic: fix SIGNEXTEND

* vm/logic: fix CALLDATALOAD padding

* vm/logic: remove weird comment

* Fix SAR opcode handler

* Use bufferToBigInt in Push opcode handler

* use bufferToBigInt everywhere

* Fix missing bufferToBigInt import

* Check for edge case in expmod

* Ignore prettier

* Update browser tsconfig to es2020 lib

* Remove dup ES2020 targets

* attempt to dedupe "target" and "lib" tsconfig values

* Update karma config to target es2020 in parser

* Various test fixes

* Lint and BN fixes

* Add bigint helpers to util

* lint fixes

* various bigint helper additions

* Lint fixes

* Fix bnToBigInt

* Lint/test fixes

* Switch Xn to BigInt(X)

* lint

* lint

* More Xn to BigInt(X) moves

Co-authored-by: Ryan Ghods <[email protected]>
Co-authored-by: Jochem Brouwer <[email protected]>
Co-authored-by: Jochem Brouwer <[email protected]>
* Tx: Remove baseTransaction.transactionType

* tx: remove get senderR()

* tx: remove test for senderR()

* tx: remove senderS()

* tx: remove test for senderS()

* tx: remove yParity / replace with v

* tx: remove test for yParity()

* Tx: remove fromRlpSerializedTx()

* Tx: remove test for fromRlpSerializedTx()

* Tx: remove _unsignedTxImplementsEIP155

* Tx: Remove _signedTxImplementsEIP155()

* Tx: Remove TransactionFactory.getTransactionClass

* Tx: Remove test for .getTransactionClass()
* common: set default hardfork to london

* vm: set default hardfork to london

* vm: update tests after default hardfork change

* vm: update default hardfork in tester config

* block: update default hardfork and fix tests

* blockchain: fix tests for london as default hardfork

* tx: set default hardfork to london

* block: fix for uncles at hardfork transition test

* blockchain: fix for hardfork in reorg test

* ethash: fix ethash tests for default hardfork change

* tx: remove README section about hardforks

* client: fix client tests for default hardfork change

* block: rename testdata files for pre-london

* blockchain: rename testdata files for pre-london

* vm: fix examples for London hardfork change

Co-authored-by: Emerson Macro <[email protected]>
* common: rename hardforkIsActiveOnChain to isIncludedHardfork

* common: remove activeHardfork and activeHardforks

* common: adapt _getHardfork, remove isIncludedHardfork

* common: type fixes in forkHash
* add modifyAccountFields method and tests
* use new method in eei
* update usage in various tests
…er (#1765)

* client fix parse: now with common default being london, explicitly setHardforkByBlockNumber(0) when creating genesis header
* vm: move new modifyAccountFields to baseStateManager
* fix lint
* util: use bigints. Expose new secp constants

* common: use bigints

* tx: use bigints

* lint: fix util, tx, common

* tx: fix isSigned

* update comment block style to be picked up by vscode

* remove doc typo (extra `)

* tx: add isSigned() tests to base.spec.ts to iterate across txTypes

* block: bn to bigint changes wip

block: finish header changes

block: test updates

block: test fixes

Partial difficulty fixes

* block: fix homestead difficulty

* block: fix >= Byzantium difficulty

* BigInt conversion fixes

* block: update st.ok to st.equals

* Update readme to use bigints

* ethash: bn to bigint

* blockchain: wip bn to bigint changes

* devp2p: bn to bigint

* vm: wip remaining bn -> bigint

* vm: more test fixes

* fix runTx negative value args test, ensure balance does not go negative when using skipBalance

* vm: lint:fix

* re-add newline

* Blockchain: small rebase fix

* vm: Fix API tests

* client: bn to bigint updates in source

* client: various fixes

* client: last fixes

* client: integration test fixes

* replace st.ok usage with st.equal

* normalize st.equals to st.equal

* update toType cases

* nits, lint

* fix vm benchmarks

* client: fix fullEthereumService tests

* touch ups, fix miner integration test

* use prefix increment

* client: fix full sync test

* test fixes

* Fix reorg logic bug

* bnTo... function renaming and cleanup

* Goodbye bn.js

* reverse changelog changes

* nits

* remove more BN, more nits

* turn off restrict-plus-operands and remove disable overrides (does not work properly)

* more nits, update package-lock

* fix build errors, re-add @types/bn.js for rlp v2 export to fix build (will be removed in subsequent PR for rlp)

* replace miller-rabin (bn.js) with bigint-crypto-utils isProbablyPrime

* more nits / fixes

* Fix misplaced paren

* Fix miner test

* more nits

* fix buffer to big int

* set expectedTests back to >= comparison

* last fixes

* Removed re-introduced Common methods

Co-authored-by: Paul Miller <[email protected]>
Co-authored-by: Ryan Ghods <[email protected]>
Co-authored-by: acolytec3 <[email protected]>
Co-authored-by: Jochem Brouwer <[email protected]>
* blockchain: remove storing unsettled promise

* vm: remove storing unsettled promises. make copy() async

* client: update to new async changes

* block: doc nit

* nits

* fix vm benchmarks, add _init to mockchain

* Make blockchain and vm constructors protected

* lint

* vm: update new tests to await

Co-authored-by: acolytec3 <[email protected]>
* vm: v6 breaking changes

* update benchmark util to use gasUsed as bigint

* VM: fixed gasUsed rebase related bug

Co-authored-by: Holger Drewes <[email protected]>
* add eip3074

* remove extra space

* use vm.create instead of new vm

Co-authored-by: Ryan Ghods <[email protected]>
* util: move misplaced functions

* add unpadBuffer back

* Fix tttttttttttttypo

* make unpadHexString return Hex String!
* blockchain/tests: remove tests for getHead()

* vm: change blockchain.getHead()
to blockchain.getIteratorHead()

* vm: remove tests for blockchain.getHead()

* client: change blockchain.getHead()
to blockchain.getIteratorHead()

* blockchain: remove setHead()
for setIteratorHead()

* blockchain: edit text of
setIteratorHead() test

* blockchain: rename getLatestHeader()
to getCanonicalHeadHeader()

* blockchain/tests: rename getLatestHeader()
to getCanonicalHeadHeader()

* client: rename blockchain.getLatestHeader()
to blockchain.getCanonicalHeadHeader()

* client/tests: rename blockchain.getLatestHeader()
to blockchain.getCanonicalHeadHeader()

* blockchain/tests: rename getLatestBlock()
to getCanonicalHeadBlock()

* client: rename blockchain.getLatestBlock
to blockchain.getCanonicalBlockHeader()

* client/tests: rename blockchain.getLatestBlock
to blockchain.getCanonicalBlockHeader()

* blockchain: remove get meta()

* VM: Remove blockchain.meta from tests

* Blockchain: Remove blockchain.meta from tests

* blockchain: fix linting errors

* BN to BigInt misses from rebase

* BN to BigInt fixes + lint

* Fix missing name replacement

* lint

* Add back blockchain testrunner last header check

* Add test for call with no gas

* Add back correct renamed functions

* lint fixes

Co-authored-by: ScottyPoi <[email protected]>
* move ethash depBrowserWorkaround fix for bigint-crypto-utils to blockchain karma.conf.js
  (karma has trouble with the default browser esm import without es6 transform, so it is easier to tell it to use umd)

* new DefaultStateManager will init a trie if not supplied, so we don't need to init one here

* remove backwards compatibility note (we will keep this property)

* trie: remove old backwards compat methods

* client: do undefined check first

* vm: undo doc change

* client: improve receipt size calculation

* vm: resolve more todos

* vm: more hardfork enum usage

* dedupe receipt rlp encoding

* lint

* runCall, runCode: add more param typedocs, simplify

* also add karma workaround to vm
acolytec3 and others added 5 commits May 4, 2022 11:01
* Common: Return BigInt for param() functionality
Fixes #1846

* Change param getters to throw on non-existent value

* Fixes

* switch return to bigint or undefined

* Switch param getters to return undefined and fix common tests

* tx: changes to accomodate param getters

* More changes

* More adjustments

* Fix NaN bug in opcode fee builder

* Hack to fix EIP-2537 edge case

* client: fix test syntax

* lint

* Return 0 when param doesn't exist

* Spelling, dedup param usage

* Update `paramByEip` to allow undefined

* Hardcode EIP-2537 gas discount array

* uncomment test
* block: update to rlp v3

* blockchain: update to rlp v3

* client: update to rlp v3

* devp2p: update to rlp v3

* ethash: fix test to pass buffer

* trie: update to rlp v3

* tx: update to rlp v3

* util: update to rlp v3

* vm: update to rlp v3

* lint, etc. fixes and update package-lock

* address review comments
* blockchain: remove 'void' from iterator() return

* vm: remove void from vm.runBlockchain() return types

* client: adapt VMExecution to stricter blockchain.iterator() call

* vm: reintroduce deleted tests for blockchain.getIteratorHead()

* vm: correct method name in test
* VM -> EVM/VM Refactor: new EVMOpts EVM options dict, move common and supported HFs to EVM

* VM -> EVM/VM Refactor: standalone DEBUG property in EVM and Interpreter

* VM -> EVM/VM Refactor: standalone EVM stateManager option and property

* VM -> EVM/VM Refactor: moved runCall()/runCode() to EVM, deleted dedicated files, top-level EVM instantiation in VM

* VM -> EVM/VM Refactor: made EVM AsyncEventEmitter, moved beforeMessage, afterMessage, newContract, step events to EVM

* VM -> EVM/VM Refactor: moved allowUnlimitedContractSize option to EVM

* VM -> EVM/VM Refactor: moved opcode and gas handler functionality and options to EVM, removed EVM/Precompiles VM dependency

* Client: fix cliqueActiveSigners method assignments

* VM: refactor TxContext to an interface

* VM: improve Message class types and defaults handling

* VM: refactor runCall and executeMessage into unified runCall

* VM: fix gas refund reset to 0 after tx finishes and fix parenthesis in runBlock receipt ternary

* VM: reimplement transient storage clear method

* VM: use VM async create method instead of constructor

* VM: unify interpreter and evm DEBUG property

Co-authored-by: Holger Drewes <[email protected]>
@codecov
Copy link

codecov bot commented May 25, 2022

Codecov Report

Merging #1910 (73a487a) into develop (66f8779) will increase coverage by 1.02%.
The diff coverage is 63.63%.

Impacted file tree graph

Flag Coverage Δ
block 86.42% <58.33%> (+1.13%) ⬆️
blockchain 83.13% <ø> (?)
client 77.75% <ø> (+<0.01%) ⬆️
common 95.52% <ø> (+0.59%) ⬆️
devp2p 82.96% <ø> (+0.13%) ⬆️
ethash 90.81% <ø> (?)
statemanager 84.24% <ø> (ø)
trie 80.14% <ø> (?)
tx 92.09% <ø> (?)
util 86.62% <70.00%> (?)
vm 81.08% <ø> (ø)

Flags with carried forward coverage won't be shown. Click here to find out more.

g11tech
g11tech previously approved these changes May 25, 2022
Copy link
Contributor

@g11tech g11tech left a comment

Choose a reason for hiding this comment

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

LGTM! 🚀

@holgerd77
Copy link
Member Author

holgerd77 commented May 26, 2022

I now had a closer look in the Block library on the feasibility/practicability of a broader application of this and realized that it would be good if we have a layer of generic error type classes in between since the goal should be, that things are sufficiently differentiated but that we do have as minimal additional work as possible along defining new types, error classes and the like.

I do have a version that looks roughly as what I would like to have, it is not completely working though since my TypeScript knowledge is too limited to get this up and running. 😜

@gabrocheleau: could you please pick this up (respectively do the next round)? I guess you will know a lot better what you are doing than me.

So, the thing is: I have now defined two first generic error type classes in Util, ValidationError (if the format validation goes wrong, e.g. 'transactionsTrie must be 32 bytes') and UsageError (if the API is used in a wrong way, e.g. 'The prevRandao parameter can only be accessed when EIP-4399 is activated').

  1. What I would like to have is that the type of the specific error class (in this case HeaderValidationError respectively HeaderValidationErrorType) is inheriting and extending the generic error type from Util (ValidationErrorType) so that in many cases it won't be necessary to add additional type definitions but one can use re-use the more generic ones. In a first step it would be great if you can realize that and "make this working", lol (tried so much but gave up). So the test here would be that you could delete block: this.errorStr(), from the first test error usage in BlockHeader and things would still work since the type is taken from ValidationErrorType.

This is just to make this work in general.

  1. In a second step I think that the HeaderValidationErrorType is generally still so generic that we likely don't want to go down that level (so to clarify: particularly in this (and likely a lot of other) specific case(s), not in general) - and - as often as possible - use the types from the more generic error types in Util. It would still be good if we keep the code we realized in 1. just as an example that this would still be working (or alternatively we can directly apply on a more fitting context, mainly for having this as reference code for this subclass error case).

It would then be good if you extend the more generic ValidationErrorType in a way that it can take either { received: string } or { objectContext: string, received: string } (there might be a better name here, so this is the generic version of the { block: string, received: string } type).

  1. I guess then we can have another look at the errors in BlockHeader, maybe also in other files. I think for these kind of errors in BlockHeader it might actually generally be sufficient (and if it is sufficient this would be highly prefered to keep things simple) to use the generic ValidationError and UsageError classes, together with the specific error codes (like WRONG_TX_TRIE_LENGTH) it very much seems to me that this differentiates enough.

I would very much assume (for now) that we likely still want to keep this two-level-structure and keep the possibility with also defining the application/domain specific error classes and types like HeaderValidationError and HeaderValidationErrorType.

  1. Then we can expand the concept from above along the whole Block package and see if things "hold" or if we still need to adjust. I think we'll see this along the way. You can also continue or at least start with this, I can also do or we share, we'll see, but generall would be cool if we can work on this together. 🙂

Whew, long post. All the thoughts I had this morning. 😋

Side quest: ah, and what I would also like to have is that we inject the package name and version number (e.g. library version: @ethereumjs/block v7.1.4, or in two entries, just to get the idea) in a as generic as possible way (so optimally: only once in the code in Util without the need of an extra argument to be passed in). This would just safe us from a lot of "what version are you on?" check backs or confusion about version numbers along community support. 😋

Ok, so far. 😁

@holgerd77
Copy link
Member Author

(have done a lot of post-submission edits, please read on site)

@gabrocheleau
Copy link
Contributor

I now had a closer look in the Block library on the feasibility/practicability of a broader application of this and realized that it would be good if we have a layer of generic error type classes in between since the goal should be, that things are sufficiently differentiated but that we do have as minimal additional work as possible along defining new types, error classes and the like.

I do have a version that looks roughly as what I would like to have, it is not completely working though since my TypeScript knowledge is too limited to get this up and running. stuck_out_tongue_winking_eye

@gabrocheleau: could you please pick this up (respectively do the next round)? I guess you will know a lot better what you are doing than me.

So, the thing is: I have now defined two first generic error type classes in Util, ValidationError (if the format validation goes wrong, e.g. 'transactionsTrie must be 32 bytes') and UsageError (if the API is used in a wrong way, e.g. 'The prevRandao parameter can only be accessed when EIP-4399 is activated').

  1. What I would like to have is that the type of the specific error class (in this case HeaderValidationError respectively HeaderValidationErrorType) is inheriting and extending the generic error type from Util (ValidationErrorType) so that in many cases it won't be necessary to add additional type definitions but one can use re-use the more generic ones. In a first step it would be great if you can realize that and "make this working", lol (tried so much but gave up). So the test here would be that you could delete block: this.errorStr(), from the first test error usage in BlockHeader and things would still work since the type is taken from ValidationErrorType.

This is just to make this work in general.

  1. In a second step I think that the HeaderValidationErrorType is generally still so generic that we likely don't want to go down that level (so to clarify: particularly in this (and likely a lot of other) specific case(s), not in general) - and - as often as possible - use the types from the more generic error types in Util. It would still be good if we keep the code we realized in 1. just as an example that this would still be working (or alternatively we can directly apply on a more fitting context, mainly for having this as reference code for this subclass error case).

It would then be good if you extend the more generic ValidationErrorType in a way that it can take either { received: string } or { objectContext: string, received: string } (there might be a better name here, so this is the generic version of the { block: string, received: string } type).

  1. I guess then we can have another look at the errors in BlockHeader, maybe also in other files. I think for these kind of errors in BlockHeader it might actually generally be sufficient (and if it is sufficient this would be highly prefered to keep things simple) to use the generic ValidationError and UsageError classes, together with the specific error codes (like WRONG_TX_TRIE_LENGTH) it very much seems to me that this differentiates enough.

I would very much assume (for now) that we likely still want to keep this two-level-structure and keep the possibility with also defining the application/domain specific error classes and types like HeaderValidationError and HeaderValidationErrorType.

  1. Then we can expand the concept from above along the whole Block package and see if things "hold" or if we still need to adjust. I think we'll see this along the way. You can also continue or at least start with this, I can also do or we share, we'll see, but generall would be cool if we can work on this together. slightly_smiling_face

Whew, long post. All the thoughts I had this morning. yum

Side quest: ah, and what I would also like to have is that we inject the package name and version number (e.g. library version: @ethereumjs/block v7.1.4, or in two entries, just to get the idea) in a as generic as possible way (so optimally: only once in the code in Util without the need of an extra argument to be passed in). This would just safe us from a lot of "what version are you on?" check backs or confusion about version numbers along community support. yum

Ok, so far. grin

Hey holger!

I think I've addressed point 1, but am not sure I understand what you mean by this:

So the test here would be that you could delete block: this.errorStr(), from the first test error usage in BlockHeader

The first part of the text led me to think that the goal was for the block package error types to inherit and extend the shape of their parent type (e.g. so that we don't have to add the received prop on every instance). I don't see how this could lead us to delete block: this.errorStr(), on line 363 of header.ts, since we've actually specifically defined that HeaderValidationError needs a block prop? Perhaps I am misunderstanding?

Also, general thought on the current approach. Do you think there would be room for simplification (mostly from the point of view of usage)? One thing I liked about the previous approach is that the type (and therefore shape) of the error was automatically inferred by the error code, so I'm wondering if we could perhaps do without the more specific HeaderValidationError class, just use the more general ValidationError class and let the ValidationErrorCode.WRONG_TX_TRIE_LENGTH code automatically adjust the shape of the errorContext prop. What do you think?

@holgerd77 holgerd77 force-pushed the lodestar-error-poc branch from 170c6bc to 92f93d9 Compare May 27, 2022 07:18
@holgerd77
Copy link
Member Author

Hi there, thanks for having a look here so quickly! 😄

The first part of the text led me to think that the goal was for the block package error types to inherit and extend the shape of their parent type (e.g. so that we don't have to add the received prop on every instance). I don't see how this could lead us to delete block: this.errorStr(), on line 363 of header.ts, since we've actually specifically defined that HeaderValidationError needs a block prop? Perhaps I am misunderstanding?

Ah no, that was explicitly not the goal ("e.g. so that we don't have to add the received prop on every instance"). If you look at the Lodestar errors like here you can see that there are various type "flavours" unionized so that one can choose one fitting from this list. I think I brought you on the wrong track with pointing to "exending" - sorry - my misunderstanding. I just wanted the additional option to use either the type flavors from ValidationErrorType or from e.g. HeaderValidationErrorType (so then this "delete block: this.errorStr()" suggestion/test also makes sense again). I found it now and used the union operator to combine the types, that was actually the point where I was stuck. 😋

Also, general thought on the current approach. Do you think there would be room for simplification (mostly from the point of view of usage)? One thing I liked about the previous approach is that the type (and therefore shape) of the error was automatically inferred by the error code, so I'm wondering if we could perhaps do without the more specific HeaderValidationError class, just use the more general ValidationError class and let the ValidationErrorCode.WRONG_TX_TRIE_LENGTH code automatically adjust the shape of the errorContext prop. What do you think?

Ah no, I wouldn't want to simplify in this direction. I wouldn't want to have this error thing as some "exact science" ("one specific type shape per error", everything automatically inferred) but rather as some toolkit - where things are sufficiently typed - but which remains enough flexibility for various loose use cases. So this would basically be the Lodestar approach structurally. I also would like to keep the suberrors like HeaderValidationErrror (or at least the possibility to define such suberrors) since I would assume that this gives a useful framing to check for a certain error class (if HeaderValidationError doSomething;).

I will continue on this this morning and roll this out to the broader Block library. 🙂 Again, thanks for jumping in so quickly!

@holgerd77
Copy link
Member Author

That said, I will radically simplify.

One already gets tired after transforming 2 errors. I will throw away the sub error level and also stick with one generic ErrorCode enum per class, substructured by simple code comments.

Will see how far this will get me. 🙂

@holgerd77
Copy link
Member Author

Ok, I've radically simplified since I noticed that this is not practicable to roll this out throughout all our libraries and maybe not wanted in general.

So I removed the two-level error structure, errors are now solely in Util. I also removed the intended local package errors.ts file and also moved the error codes to Util. These are now more generic than I originally anticipated. I think this should in many/most cases still be sufficient to "target" an error happening, at the same time this is giving additional information about what type of error actually happened.

I rolled this now out to a broader part of the BlockHeader file and this seems to hold also in a more broadly applied context. I would nevertheless stop for now, just stumbled about the thought that Emmett is still heavily working on these files, so it might actually be better to start and/or continue with another library (might generally be a good idea to get an even broader idea if this is a scalable and widely appliable approach).

@holgerd77
Copy link
Member Author

holgerd77 commented Jun 8, 2022

It has been decided to not add to the initial breaking release round, will add a "Blocked" label for now.

General concept/code structure can be picked up to be released along some follow-up minor release versions.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

10 participants