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

Updates for Chronos V4 #61

Closed
wants to merge 3 commits into from
Closed

Updates for Chronos V4 #61

wants to merge 3 commits into from

Conversation

gmega
Copy link
Member

@gmega gmega commented Dec 15, 2023

This PR adds minimal adjustments to ensure that nim-ethers works (i.e. compiles and the test suite passes) with Chronos V4. Changes are minimal enough that it should still work with Chronos 3.x, though that's more of a bonus than a goal.

Two of the more controversial points in this PR are:

  • the {.cast(raises: []).} in table operations which 1.6.x will otherwise (erroneously) declare as raisers of Exception. I had asked for this in nim-forum with a smaller example and Araq suggested using {.experimental: "strictEffects".} (https://forum.nim-lang.org/t/10749) which indeed fixed my example, but for reasons I can't understand it doesn't work here. The problem seems to not exist with 2.x;
  • the bump to nim 1.6.16, which I had to do to work around what appeared to be memory getting corrupted with 1.6.14. I did a lot of digging into Chronos and couldn't really find an issue there, so I'm chalking this off as a compiler issue.

@gmega gmega requested a review from markspanbroek December 15, 2023 14:26
@@ -26,10 +26,12 @@ proc raiseEstimateGasError(
parent: parent)
raise e

method provider*(signer: Signer): Provider {.base, gcsafe.} =
method provider*(signer: Signer):
Provider {.base, gcsafe, raises: [CatchableError].} =
Copy link

Choose a reason for hiding this comment

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

Hmm... I would not blanked raise CatchableError, in fact I would set this to raises: [] and only list those exceptions that are intended to propagate. The whole point of this is preventing spurious exceptions from bubbling up, otherwise there is no point in tracking exceptions at all.

Copy link

Choose a reason for hiding this comment

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

In fact I would use the push: raises [] pragma in all the files and only override this per method on an as needed basis.

Copy link
Member Author

@gmega gmega Dec 15, 2023

Choose a reason for hiding this comment

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

Yeah, I mean, as I was working through this, I was faced with two choices:

  1. do the minimal amount of changes so that the current implementation works with Chronos V4;
  2. actually track the entire exception chains and expose the exceptions that all method implementations may throw, which is what you're proposing.

(2) seems like a good idea, but it is actually sort of tricky. Without some feeling as to how the API has to be designed, I could easily replace CatchableError with EthersError pretty much everywhere, and it would compile without much gain in exception semantics. The actual narrow types for the exceptions that would make sense to expose depends on what we expect clients to want to try to recover from, so that's why I ended up going with the (1) route.

I'll give it a stab anyhow, let's see what comes out of it.

Copy link

Choose a reason for hiding this comment

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

Yeah, I understand, this gets messy pretty quickly, but I still think that push pragma with raises: [] and an explicit EthersError where required is a much better than the bare CatchableError. Also, I suspect that a lot of issues are stemming from the fact that callbacks aren't annotated with raises: [].

Copy link
Contributor

Choose a reason for hiding this comment

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

Its actually not that bad. Here's a compiling commit with {.push raises:[].}: 7973ce8

@@ -14,6 +14,14 @@ type
callbacks: Table[JsonNode, SubscriptionCallback]
SubscriptionCallback = proc(id, arguments: JsonNode) {.gcsafe, raises:[].}

# FIXME Nim 1.6.XX seems to have issues tracking exception effects and will see
Copy link

@dryajov dryajov Dec 17, 2023

Choose a reason for hiding this comment

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

Actually, this is probably the wrong way of doing it, as you noted in the forum post, annotating the callback type with a raises: [] fixes the issue, this is in fact the correct fix. Callbacks should (almost) never allow exceptions to escape.

Copy link

Choose a reason for hiding this comment

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

Also, here is a pointer on how to handle the strict effect changes in a backwards/forwards compatible way in the 1.6.0 release notes - https://github.com/narimiran/Nim/blob/f14925d3bc778bcb7eef4eb9085f08d65f4281ce/changelogs/changelog_1_6_0.md#strict-effects, should be helpful.

Copy link
Contributor

Choose a reason for hiding this comment

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

As @dryajov and araq mentioned, the correct fix is to use raises:[] in the callback definition instead of mitigateEffectsBug. The unhandled Exception being raised (and the difference between your example in the forum post and the current implementation in ethers) is that the Table uses a JsonNode as a key. As a guess, the Exception could possibly come from use of parseJson in Table's key handling when the key type is JsonNode. Here's a compiling commit that uses string as key and no mitigateEffectsBug. Note that this commit is not necessarily complete or the correct way to completely remove JsonNode as the callback table key; it's to illustrate that the issue indeed lies in the use of JsonNode as a table key: a1eb549

Copy link
Contributor

Choose a reason for hiding this comment

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

In summary, the solution would be a combination of using raises:[] in the callback definition, and changing the callback table key type.

Copy link

Choose a reason for hiding this comment

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

Note that table accessors such as get or [] will raise an exception, so this should either use the non throwing versions (getOrDefault, etc), or handle the exceptions in the callback itself.

Copy link
Member Author

@gmega gmega Jan 5, 2024

Choose a reason for hiding this comment

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

Awesome, so we have two issues that end in the same symptom (gotta love when that happens):

  1. the nim effects issue (https://forum.nim-lang.org/t/10749);
  2. using JsonNode as a table key.

I'm not sure I follow your explanation though @emizzle (and that's probably the problem :-)): is parseJson called during the hashing of JsonNode?

@dryajov yes but in this case we're writing to the table ([]=), this should not raise exceptions.

Copy link
Contributor

@emizzle emizzle left a comment

Choose a reason for hiding this comment

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

Exception handling is always super tricky!

requires "chronicles >= 0.10.3 & < 0.11.0"
requires "chronos >= 3.0.0 & < 4.0.0"
requires "chronos#head" # FIXME: change to >= 4.0.0 when chronos 4 is released
Copy link
Contributor

Choose a reason for hiding this comment

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

Would it be better to reference the specific chronos commit instead of head?

Copy link
Member Author

Choose a reason for hiding this comment

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

Yeah... I think both are bad TBH. 😄 The tradeoff I see is:

  • using a specific commit buys you stability for the nim-ethers build in the short term, but will require you to constantly bump this to keep up with the latest V4 commits;
  • using head means every time someone pushes you'll be testing against the latest pre-release of V4, and you may be able to catch issues earlier. Can be annoying to someone who just want to change unrelated stuff and all of a sudden finds themselves with a Chronos error unrelated to their change, though.

Either one sort of sucks IMHO, so I'd be fine with either.

@@ -26,10 +26,12 @@ proc raiseEstimateGasError(
parent: parent)
raise e

method provider*(signer: Signer): Provider {.base, gcsafe.} =
method provider*(signer: Signer):
Provider {.base, gcsafe, raises: [CatchableError].} =
Copy link
Contributor

Choose a reason for hiding this comment

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

Its actually not that bad. Here's a compiling commit with {.push raises:[].}: 7973ce8

@@ -14,6 +14,14 @@ type
callbacks: Table[JsonNode, SubscriptionCallback]
SubscriptionCallback = proc(id, arguments: JsonNode) {.gcsafe, raises:[].}

# FIXME Nim 1.6.XX seems to have issues tracking exception effects and will see
Copy link
Contributor

Choose a reason for hiding this comment

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

As @dryajov and araq mentioned, the correct fix is to use raises:[] in the callback definition instead of mitigateEffectsBug. The unhandled Exception being raised (and the difference between your example in the forum post and the current implementation in ethers) is that the Table uses a JsonNode as a key. As a guess, the Exception could possibly come from use of parseJson in Table's key handling when the key type is JsonNode. Here's a compiling commit that uses string as key and no mitigateEffectsBug. Note that this commit is not necessarily complete or the correct way to completely remove JsonNode as the callback table key; it's to illustrate that the issue indeed lies in the use of JsonNode as a table key: a1eb549

@@ -14,6 +14,14 @@ type
callbacks: Table[JsonNode, SubscriptionCallback]
SubscriptionCallback = proc(id, arguments: JsonNode) {.gcsafe, raises:[].}

# FIXME Nim 1.6.XX seems to have issues tracking exception effects and will see
Copy link
Contributor

Choose a reason for hiding this comment

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

In summary, the solution would be a combination of using raises:[] in the callback definition, and changing the callback table key type.

@emizzle
Copy link
Contributor

emizzle commented Feb 8, 2024

Are we ok to close this in favour of #64?

@markspanbroek markspanbroek deleted the feat/chronos-v4 branch March 12, 2024 08:28
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants