Skip to content

Commit

Permalink
Bugfix/nully values (#61)
Browse files Browse the repository at this point in the history
* Fix problem with contract out of assets

* Add nully value json test

* Fix bug with passing null to a few functions

* Typesafe U256 parsing

* Update readme for the web3 to include runner info

* Cleanup commits

Strip out unused JSON

Update comment

Remove echo

Added DynamicBytes test

More correct naming

Remove one extra double check

* Add specific object tests

* Ensure we cover different status types

* Add header comments

* Cleanup

* Add more tests

* Cleanup

* Revert docs

* Nimpretty file

* Fix issue in base stew

* v0.2.4

* Fix test

---------

Co-authored-by: jangko <[email protected]>
  • Loading branch information
tavurth and jangko authored Dec 13, 2023
1 parent 195c8c6 commit 23c06ca
Show file tree
Hide file tree
Showing 14 changed files with 162 additions and 27 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,4 @@ node_modules
nohup.out
hardhat.config.js
package-lock.json
test_null_conversion
11 changes: 9 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,18 +10,25 @@ The humble beginnings of a Nim library similar to web3.[js|py]
## Installation

You can install the developement version of the library through nimble with the following command

```
nimble install https://github.com/status-im/nim-web3@#master
```

## Development

You should first run `./simulator.sh` which runs `ganache-cli`

This creates a local simulated Ethereum network on your local machine and the tests will use this for their E2E processing

## License

Licensed and distributed under either of

* MIT license: [LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT
- MIT license: [LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT

or

* Apache License, Version 2.0, ([LICENSE-APACHEv2](LICENSE-APACHEv2) or http://www.apache.org/licenses/LICENSE-2.0)
- Apache License, Version 2.0, ([LICENSE-APACHEv2](LICENSE-APACHEv2) or http://www.apache.org/licenses/LICENSE-2.0)

at your option. This file may not be copied, modified, or distributed except according to those terms.
12 changes: 12 additions & 0 deletions simulator.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
#!/usr/bin/env bash

# NOTE: Requires nodemon (https://github.com/remy/nodemon)
# npm i -g nodemon

# Watch all nim files for changes
# When a file change is detected we will restart ganache-cli
# This ensures that our deposit contracts have enough ETH as
# it seems like some of the tests do not properly initialise
# their contracts at this time. (state persists across runs)

nodemon --ext '.nim' --watch tests --watch web3 --exec "ganache-cli"
1 change: 1 addition & 0 deletions tests/all_tests.nim
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
{. warning[UnusedImport]:off .}

import
test_null_conversion,
test_primitives,
test_contracts,
test_deposit_contract,
Expand Down
6 changes: 5 additions & 1 deletion tests/test_deposit_contract.nim
Original file line number Diff line number Diff line change
Expand Up @@ -69,4 +69,8 @@ suite "Deposit contract":
echo "hash_tree_root: ", await ns.get_deposit_root().call()
await web3.close()

waitFor test()
try:
waitFor test()
except CatchableError as err:
echo "Failed to process deposit contract", err.msg
fail()
109 changes: 109 additions & 0 deletions tests/test_null_conversion.nim
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
import
std/[json, strutils],
pkg/unittest2,
stint,
json_rpc/jsonmarshal,
../web3,
../web3/[conversions, eth_api_types, engine_api_types]

template should_be_value_error(input: string, value: untyped): void =
expect ValueError:
fromJson(%input, "", value)

template should_not_error(input: string, value: untyped): void =
fromJson(%input, "", value)

suite "Null conversion":
var resAddress: Address
var resDynamicBytes: DynamicBytes[32]
var resFixedBytes: FixedBytes[5]
var resQuantity: Quantity
var resRlpEncodedBytes: RlpEncodedBytes
var resTypedTransaction: TypedTransaction
var resUInt256: UInt256
var resUInt256Ref: ref UInt256

## Covers the converters which can be found in web3/conversions.nim
## Ensure that when passing a nully value they respond with a ValueError
test "passing null values to normal convertors":
should_be_value_error("null", resAddress)
should_be_value_error("null", resDynamicBytes)
should_be_value_error("null", resFixedBytes)
should_be_value_error("null", resQuantity)
should_be_value_error("null", resRlpEncodedBytes)
should_be_value_error("null", resTypedTransaction)
should_be_value_error("null", resUInt256)
should_be_value_error("null", resUInt256Ref)

test "passing empty values to normal convertors":
should_be_value_error("", resAddress)
should_be_value_error("", resDynamicBytes)
should_be_value_error("", resFixedBytes)
should_be_value_error("", resQuantity)
should_be_value_error("", resRlpEncodedBytes)
should_be_value_error("", resTypedTransaction)
should_be_value_error("", resUInt256)
should_be_value_error("", resUInt256Ref)

test "passing invalid hex (0x) values to normal convertors":
should_be_value_error("0x", resAddress)
should_be_value_error("0x", resDynamicBytes)
should_be_value_error("0x", resFixedBytes)
should_be_value_error("0x", resQuantity)
should_be_value_error("0x", resUInt256)
should_be_value_error("0x", resUInt256Ref)

test "passing hex (0x) values to normal convertors":
should_not_error("0x", resRlpEncodedBytes)
should_not_error("0x", resTypedTransaction)

test "passing malformed hex (0x_) values to normal convertors":
should_be_value_error("0x_", resAddress)
should_be_value_error("0x_", resDynamicBytes)
should_be_value_error("0x_", resFixedBytes)
should_be_value_error("0x_", resQuantity)
should_be_value_error("0x_", resRlpEncodedBytes)
should_be_value_error("0x_", resTypedTransaction)
should_be_value_error("0x_", resUInt256)
should_be_value_error("0x_", resUInt256Ref)

## Covering the web3/engine_api_types
##
## NOTE: These will be transformed by the fromJson imported from
## nim-json-rpc/json_rpc/jsonmarshal
test "passing nully values to specific convertors":

let payloadAttributesV1 = """{ "timestamp": {item}, "prevRandao": {item}, "suggestedFeeRecipient": {item} }"""
let forkchoiceStateV1 = """{ "status": {item}, "safeBlockHash": {item}, "finalizedBlockHash": {item} }"""
let forkchoiceUpdatedResponse = """{ "payloadStatus": {item}, "payloadId": {item} }"""
let transitionConfigurationV1 = """{ "terminalTotalDifficulty": {item}, "terminalBlockHash": {item}, "terminalBlockNumber": {item} }"""

var resPayloadAttributesV1: PayloadAttributesV1
var resForkchoiceStateV1: ForkchoiceStateV1
var resForkchoiceUpdatedResponse: ForkchoiceUpdatedResponse
var resTransitionConfigurationV1: TransitionConfigurationV1

for item in @["null", "\"\"", "\"0x\"", "\"0x_\"", ""]:
template format(str: string): string =
str.replace("{item}", item)

should_be_value_error(payloadAttributesV1.format(), resPayloadAttributesV1)
should_be_value_error(forkchoiceStateV1.format(), resForkchoiceStateV1)
should_be_value_error(forkchoiceUpdatedResponse.format(), resForkchoiceUpdatedResponse)
should_be_value_error(transitionConfigurationV1.format(), resTransitionConfigurationV1)


## If different status types can have branching logic
## we should cover each status type with different null ops
test "passing nully values to specific status types":

var resPayloadStatusV1: PayloadStatusV1

for status_type in PayloadExecutionStatus:
let payloadStatusV1 = """{
"status": "status_name",
"latestValidHash": null,
"validationError": null
}""".replace("status_name", $status_type)

should_be_value_error(payloadStatusV1, resPayloadStatusV1)
6 changes: 5 additions & 1 deletion tests/test_signed_tx.nim
Original file line number Diff line number Diff line change
Expand Up @@ -105,4 +105,8 @@ suite "Signed transactions":
assert(n == 5.u256)
await web3.close()

waitFor test()
try:
waitFor test()
except CatchableError as err:
echo "Failed to send signed tx", err.msg
fail()
5 changes: 2 additions & 3 deletions web3.nim
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,14 @@
# those terms.

import
std/[options, math, json, tables, uri, strformat]
std/[options, json, tables, uri, strformat]

from os import DirSep, AltSep
from eth/common/eth_types import ChainId

import
stint, httputils, chronicles, chronos, nimcrypto/keccak,
stint, httputils, chronos,
json_rpc/[rpcclient, jsonmarshal], stew/byteutils, eth/keys,

chronos/apps/http/httpclient,
web3/[eth_api_types, conversions, ethhexstrings, transaction_signing, encoding, contract_dsl]

Expand Down
11 changes: 5 additions & 6 deletions web3.nimble
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,11 @@
# This file may not be copied, modified, or distributed except according to
# those terms.

mode = ScriptMode.Verbose

version = "0.2.2"
author = "Status Research & Development GmbH"
description = "This is the humble begginings of library similar to web3.[js|py]"
license = "MIT or Apache License 2.0"
mode = ScriptMode.Verbose
version = "0.2.4"
author = "Status Research & Development GmbH"
description = "This is the humble begginings of library similar to web3.[js|py]"
license = "MIT or Apache License 2.0"

### Dependencies
requires "nim >= 1.6.0"
Expand Down
6 changes: 0 additions & 6 deletions web3/contract_dsl.nim
Original file line number Diff line number Diff line change
Expand Up @@ -175,12 +175,6 @@ proc parseContract(body: NimNode): seq[InterfaceObject] =
macro contract*(cname: untyped, body: untyped): untyped =
var objects = parseContract(body)
result = newStmtList()
let
address = ident "address"
client = ident "client"
receipt = genSym(nskForVar)
receiver = ident "receiver"
eventListener = ident "eventListener"
result.add quote do:
type
`cname`* = object
Expand Down
10 changes: 8 additions & 2 deletions web3/conversions.nim
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ template invalidQuantityPrefix(s: string): bool =

func `%`*(n: Int256|UInt256): JsonNode = %("0x" & n.toHex)


# allows UInt256 to be passed as a json string
func fromJson*(n: JsonNode, argName: string, result: var UInt256) =
# expects base 16 string, starting with "0x"
Expand All @@ -43,7 +44,7 @@ func fromJson*(n: JsonNode, argName: string, result: var UInt256) =
raise newException(ValueError, "Parameter \"" & argName & "\" value too long for UInt256: " & $hexStr.len)
if hexStr.invalidQuantityPrefix:
raise newException(ValueError, "Parameter \"" & argName & "\" value has invalid leading 0")
result = hexStr.parse(StUint[256], 16) # TODO: Handle errors
result = hexStr.parse(StUint[256], 16) # TODO Add error checking

# allows ref UInt256 to be passed as a json string
func fromJson*(n: JsonNode, argName: string, result: var ref UInt256) =
Expand All @@ -55,13 +56,18 @@ func fromJson*(n: JsonNode, argName: string, result: var ref UInt256) =
if hexStr.invalidQuantityPrefix:
raise newException(ValueError, "Parameter \"" & argName & "\" value has invalid leading 0")
new result
result[] = hexStr.parse(StUint[256], 16) # TODO: Handle errors
result[] = hexStr.parse(StUint[256], 16) # TODO Add error checking

func bytesFromJson(n: JsonNode, argName: string, result: var openArray[byte]) =
n.kind.expect(JString, argName)
let hexStr = n.getStr()

if not ("0x" in hexStr):
raise newException(ValueError, "Parameter \"" & argName & "\" is not a hexadecimal string")

if hexStr.len != result.len * 2 + 2: # including "0x"
raise newException(ValueError, "Parameter \"" & argName & "\" value wrong length: " & $hexStr.len)

hexToByteArray(hexStr, result)

func fromJson*[N](n: JsonNode, argName: string, result: var FixedBytes[N])
Expand Down
1 change: 0 additions & 1 deletion web3/encoding.nim
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,5 @@ func encode*(x: tuple): seq[byte] =
inc i

# Obsolete
from stew/byteutils import hexToSeqByte
func decode*(input: string, offset: int, to: var DynamicBytes): int {.inline, deprecated: "Use decode(openarray[byte], ...) instead".} =
decode(hexToSeqByte(input), 0, offset div 2, to) * 2
8 changes: 4 additions & 4 deletions web3/engine_api_types.nim
Original file line number Diff line number Diff line change
Expand Up @@ -172,10 +172,10 @@ type

# https://github.com/ethereum/execution-apis/blob/v1.0.0-beta.3/src/engine/paris.md#payloadstatusv1
PayloadExecutionStatus* {.pure.} = enum
syncing = "SYNCING"
valid = "VALID"
invalid = "INVALID"
accepted = "ACCEPTED"
syncing = "SYNCING"
valid = "VALID"
invalid = "INVALID"
accepted = "ACCEPTED"
invalid_block_hash = "INVALID_BLOCK_HASH"

PayloadStatusV1* = object
Expand Down
2 changes: 1 addition & 1 deletion web3/transaction_signing.nim
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@

import
options,
eth_api_types, stew/byteutils, stint,
eth_api_types, stint,
eth/[common, keys, rlp], eth/common/transaction

func signTransaction(tr: var Transaction, pk: PrivateKey) =
Expand Down

0 comments on commit 23c06ca

Please sign in to comment.