Initial commit
This commit is contained in:
commit
eb14b4b706
|
@ -0,0 +1,87 @@
|
|||
version: '{build}'
|
||||
|
||||
cache:
|
||||
- x86_64-4.9.2-release-win32-seh-rt_v4-rev4.7z -> .appveyor.yml
|
||||
- i686-4.9.2-release-win32-dwarf-rt_v4-rev4.7z -> .appveyor.yml
|
||||
- Nim -> .appveyor.yml
|
||||
|
||||
matrix:
|
||||
# We always want 32 and 64-bit compilation
|
||||
fast_finish: false
|
||||
|
||||
platform:
|
||||
- x86
|
||||
- x64
|
||||
|
||||
install:
|
||||
- setlocal EnableExtensions EnableDelayedExpansion
|
||||
|
||||
- IF "%PLATFORM%" == "x86" (
|
||||
SET "MINGW_ARCHIVE=i686-4.9.2-release-win32-dwarf-rt_v4-rev4.7z" &
|
||||
SET "MINGW_URL=https://sourceforge.net/projects/mingw-w64/files/Toolchains%%20targetting%%20Win32/Personal%%20Builds/mingw-builds/4.9.2/threads-win32/dwarf/i686-4.9.2-release-win32-dwarf-rt_v4-rev4.7z" &
|
||||
SET "MINGW_DIR=mingw32"
|
||||
) ELSE (
|
||||
IF "%PLATFORM%" == "x64" (
|
||||
SET "MINGW_ARCHIVE=x86_64-4.9.2-release-win32-seh-rt_v4-rev4.7z" &
|
||||
SET "MINGW_URL=https://sourceforge.net/projects/mingw-w64/files/Toolchains%%20targetting%%20Win64/Personal%%20Builds/mingw-builds/4.9.2/threads-win32/seh/x86_64-4.9.2-release-win32-seh-rt_v4-rev4.7z" &
|
||||
SET "MINGW_DIR=mingw64"
|
||||
) else (
|
||||
echo "Unknown platform"
|
||||
)
|
||||
)
|
||||
|
||||
- SET PATH=%CD%\%MINGW_DIR%\bin;%CD%\Nim\bin;%PATH%
|
||||
|
||||
# Unpack mingw
|
||||
- IF NOT EXIST "%MINGW_ARCHIVE%" appveyor DownloadFile "%MINGW_URL%" -FileName "%MINGW_ARCHIVE%"
|
||||
- 7z x -y "%MINGW_ARCHIVE%" > nul
|
||||
|
||||
# build nim from our own branch - this to avoid the day-to-day churn and
|
||||
# regressions of the fast-paced Nim development while maintaining the
|
||||
# flexibility to apply patches
|
||||
- SET "NEED_REBUILD="
|
||||
|
||||
- IF NOT EXIST "Nim\\.git\\" (
|
||||
git clone https://github.com/status-im/Nim.git
|
||||
) ELSE (
|
||||
( cd Nim ) &
|
||||
( git pull ) &
|
||||
( cd .. )
|
||||
)
|
||||
|
||||
# Rebuild Nim if HEAD has moved or if we don't yet have a cached version
|
||||
- IF NOT EXIST "Nim\\ver.txt" (
|
||||
SET NEED_REBUILD=1
|
||||
) ELSE (
|
||||
( CD Nim ) &
|
||||
( git rev-parse HEAD > ..\\cur_ver.txt ) &
|
||||
( fc ver.txt ..\\cur_ver.txt > nul ) &
|
||||
( IF NOT ERRORLEVEL == 0 SET NEED_REBUILD=1 ) &
|
||||
( cd .. )
|
||||
)
|
||||
|
||||
- IF NOT EXIST "Nim\\bin\\nim.exe" SET NEED_REBUILD=1
|
||||
- IF NOT EXIST "Nim\\bin\\nimble.exe" SET NEED_REBUILD=1
|
||||
|
||||
# after building nim, wipe csources to save on cache space
|
||||
- IF DEFINED NEED_REBUILD (
|
||||
cd Nim &
|
||||
( IF EXIST "csources" rmdir /s /q csources ) &
|
||||
git clone --depth 1 https://github.com/nim-lang/csources &
|
||||
cd csources &
|
||||
( IF "%PLATFORM%" == "x64" ( build64.bat ) else ( build.bat ) ) &
|
||||
cd .. &
|
||||
bin\nim c koch &
|
||||
koch boot -d:release &
|
||||
koch nimble &
|
||||
git rev-parse HEAD > ver.txt &
|
||||
rmdir /s /q csources
|
||||
)
|
||||
|
||||
build_script:
|
||||
- cd C:\projects\%APPVEYOR_PROJECT_SLUG%
|
||||
- nimble install -y
|
||||
test_script:
|
||||
- nimble test
|
||||
|
||||
deploy: off
|
|
@ -0,0 +1,11 @@
|
|||
nimcache/
|
||||
|
||||
# Executables shall be put in an ignored build/ directory
|
||||
# Ignore dynamic, static libs and libtool archive files
|
||||
build/
|
||||
*.so
|
||||
*.dylib
|
||||
*.a
|
||||
*.la
|
||||
*.exe
|
||||
*.dll
|
|
@ -0,0 +1,39 @@
|
|||
language: c # or other C/C++ variants
|
||||
|
||||
sudo: false
|
||||
|
||||
# https://docs.travis-ci.com/user/caching/
|
||||
#
|
||||
# Caching the whole nim folder is better than relying on ccache - this way, we
|
||||
# skip the expensive bootstrap process and linking
|
||||
cache:
|
||||
directories:
|
||||
- nim
|
||||
|
||||
os:
|
||||
- linux
|
||||
- osx
|
||||
|
||||
install:
|
||||
# build nim from our own branch - this to avoid the day-to-day churn and
|
||||
# regressions of the fast-paced Nim development while maintaining the
|
||||
# flexibility to apply patches
|
||||
#
|
||||
# check version of remote branch
|
||||
- "export NIMVER=$(git ls-remote https://github.com/status-im/nim.git HEAD | cut -f 1)"
|
||||
|
||||
# after building nim, wipe csources to save on cache space
|
||||
- "{ [ -f nim/$NIMVER/bin/nim ] && [ -f nim/$NIMVER/bin/nimble ] ; } ||
|
||||
{ rm -rf nim ;
|
||||
mkdir -p nim ;
|
||||
git clone --depth=1 https://github.com/status-im/nim.git nim/$NIMVER ;
|
||||
cd nim/$NIMVER ;
|
||||
sh build_all.sh ;
|
||||
rm -rf csources ;
|
||||
cd ../.. ;
|
||||
}"
|
||||
- "export PATH=$PWD/nim/$NIMVER/bin:$PATH"
|
||||
|
||||
script:
|
||||
- nimble install -y
|
||||
- nimble test
|
|
@ -0,0 +1,205 @@
|
|||
web3 is licensed under the Apache License version 2
|
||||
Copyright (c) 2018 Status Research & Development GmbH
|
||||
-----------------------------------------------------
|
||||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright 2018 Status Research & Development GmbH
|
||||
|
||||
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.
|
|
@ -0,0 +1,25 @@
|
|||
web3 is licensed under the MIT License
|
||||
Copyright (c) 2018 Status Research & Development GmbH
|
||||
-----------------------------------------------------
|
||||
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2018 Status Research & Development GmbH
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
|
@ -0,0 +1,26 @@
|
|||
# web3
|
||||
|
||||
[![Build Status (Travis)](https://img.shields.io/travis/status-im/nim-web3/master.svg?label=Linux%20/%20macOS "Linux/macOS build status (Travis)")](https://travis-ci.org/status-im/nim-web3)
|
||||
[![Windows build status (Appveyor)](https://img.shields.io/appveyor/ci/nimbus/nim-web3/master.svg?label=Windows "Windows build status (Appveyor)")](https://ci.appveyor.com/project/nimbus/nim-web3)
|
||||
[![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT)
|
||||
[![License: Apache](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://opensource.org/licenses/Apache-2.0)
|
||||
![Stability: experimental](https://img.shields.io/badge/stability-experimental-orange.svg)
|
||||
|
||||
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
|
||||
```
|
||||
|
||||
## License
|
||||
|
||||
Licensed and distributed under either of
|
||||
|
||||
* MIT license: [LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT
|
||||
* Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) 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.
|
||||
|
|
@ -0,0 +1,63 @@
|
|||
## This module contains signatures for the Ethereum client RPCs.
|
||||
## The signatures are not imported directly, but read and processed with parseStmt,
|
||||
## then a procedure body is generated to marshal native Nim parameters to json and visa versa.
|
||||
import json, stint, ethtypes
|
||||
|
||||
proc web3_clientVersion(): string
|
||||
proc web3_sha3(data: string): string
|
||||
proc net_version(): string
|
||||
proc net_peerCount(): int
|
||||
proc net_listening(): bool
|
||||
proc eth_protocolVersion(): string
|
||||
proc eth_syncing(): JsonNode
|
||||
proc eth_coinbase(): string
|
||||
proc eth_mining(): bool
|
||||
proc eth_hashrate(): int
|
||||
proc eth_gasPrice(): int64
|
||||
proc eth_accounts(): seq[array[20, byte]]
|
||||
proc eth_blockNumber(): int
|
||||
proc eth_getBalance(data: array[20, byte], quantityTag: string): int
|
||||
proc eth_getStorageAt(data: array[20, byte], quantity: int, quantityTag: string): seq[byte]
|
||||
proc eth_getTransactionCount(data: array[20, byte], quantityTag: string)
|
||||
proc eth_getBlockTransactionCountByHash(data: array[32, byte])
|
||||
proc eth_getBlockTransactionCountByNumber(quantityTag: string)
|
||||
proc eth_getUncleCountByBlockHash(data: array[32, byte])
|
||||
proc eth_getUncleCountByBlockNumber(quantityTag: string)
|
||||
proc eth_getCode(data: array[20, byte], quantityTag: string): seq[byte]
|
||||
proc eth_sign(data: array[20, byte], message: seq[byte]): seq[byte]
|
||||
proc eth_sendTransaction(obj: EthSend): UInt256
|
||||
proc eth_sendRawTransaction(data: string, quantityTag: int): UInt256
|
||||
proc eth_call(call: EthCall, quantityTag: string): UInt256
|
||||
proc eth_estimateGas(call: EthCall, quantityTag: string): UInt256
|
||||
proc eth_getBlockByHash(data: array[32, byte], fullTransactions: bool): BlockObject
|
||||
proc eth_getBlockByNumber(quantityTag: string, fullTransactions: bool): BlockObject
|
||||
proc eth_getTransactionByHash(data: Uint256): TransactionObject
|
||||
proc eth_getTransactionByBlockHashAndIndex(data: UInt256, quantity: int): TransactionObject
|
||||
proc eth_getTransactionByBlockNumberAndIndex(quantityTag: string, quantity: int): TransactionObject
|
||||
proc eth_getTransactionReceipt(data: UInt256): ReceiptObject
|
||||
proc eth_getUncleByBlockHashAndIndex(data: UInt256, quantity: int64): BlockObject
|
||||
proc eth_getUncleByBlockNumberAndIndex(quantityTag: string, quantity: int64): BlockObject
|
||||
proc eth_getCompilers(): seq[string]
|
||||
proc eth_compileLLL(): seq[byte]
|
||||
proc eth_compileSolidity(): seq[byte]
|
||||
proc eth_compileSerpent(): seq[byte]
|
||||
proc eth_newFilter(filterOptions: FilterOptions): int
|
||||
proc eth_newBlockFilter(): int
|
||||
proc eth_newPendingTransactionFilter(): int
|
||||
proc eth_uninstallFilter(filterId: int): bool
|
||||
proc eth_getFilterChanges(filterId: int): seq[LogObject]
|
||||
proc eth_getFilterLogs(filterId: int): seq[LogObject]
|
||||
proc eth_getLogs(filterOptions: FilterOptions): seq[LogObject]
|
||||
proc eth_getWork(): seq[UInt256]
|
||||
proc eth_submitWork(nonce: int64, powHash: Uint256, mixDigest: Uint256): bool
|
||||
proc eth_submitHashrate(hashRate: UInt256, id: Uint256): bool
|
||||
proc shh_post(): string
|
||||
proc shh_version(message: WhisperPost): bool
|
||||
proc shh_newIdentity(): array[60, byte]
|
||||
proc shh_hasIdentity(identity: array[60, byte]): bool
|
||||
proc shh_newGroup(): array[60, byte]
|
||||
proc shh_addToGroup(identity: array[60, byte]): bool
|
||||
proc shh_newFilter(filterOptions: FilterOptions, to: array[60, byte], topics: seq[UInt256]): int
|
||||
proc shh_uninstallFilter(id: int): bool
|
||||
proc shh_getFilterChanges(id: int): seq[WhisperMessage]
|
||||
proc shh_getMessages(id: int): seq[WhisperMessage]
|
|
@ -0,0 +1,159 @@
|
|||
type
|
||||
HexQuantityStr* = distinct string
|
||||
HexDataStr* = distinct string
|
||||
|
||||
# Hex validation
|
||||
|
||||
template stripLeadingZeros(value: string): string =
|
||||
var cidx = 0
|
||||
# ignore the last character so we retain '0' on zero value
|
||||
while cidx < value.len - 1 and value[cidx] == '0':
|
||||
cidx.inc
|
||||
value[cidx .. ^1]
|
||||
|
||||
proc encodeQuantity*(value: SomeUnsignedInt): string =
|
||||
var hValue = value.toHex.stripLeadingZeros
|
||||
result = "0x" & hValue
|
||||
|
||||
func hasHexHeader*(value: string): bool =
|
||||
if value != "" and value[0] == '0' and value[1] in {'x', 'X'} and value.len > 2: true
|
||||
else: false
|
||||
|
||||
template hasHexHeader*(value: HexDataStr|HexQuantityStr): bool =
|
||||
value.string.hasHexHeader
|
||||
|
||||
func isHexChar*(c: char): bool =
|
||||
if c notin {'0'..'9'} and
|
||||
c notin {'a'..'f'} and
|
||||
c notin {'A'..'F'}: false
|
||||
else: true
|
||||
|
||||
proc validate*(value: HexQuantityStr): bool =
|
||||
template strVal: untyped = value.string
|
||||
if not value.hasHexHeader:
|
||||
return false
|
||||
# No leading zeros
|
||||
if strVal[2] == '0': return false
|
||||
for i in 2..<strVal.len:
|
||||
let c = strVal[i]
|
||||
if not c.isHexChar:
|
||||
return false
|
||||
return true
|
||||
|
||||
proc validate*(value: HexDataStr): bool =
|
||||
template strVal: untyped = value.string
|
||||
if not value.hasHexHeader:
|
||||
return false
|
||||
# Leading zeros are allowed
|
||||
for i in 2..<strVal.len:
|
||||
let c = strVal[i]
|
||||
if not c.isHexChar:
|
||||
return false
|
||||
# Must be even number of digits
|
||||
if strVal.len mod 2 != 0: return false
|
||||
return true
|
||||
|
||||
# Initialisation
|
||||
|
||||
template hexDataStr*(value: string): HexDataStr = value.HexDataStr
|
||||
template hexQuantityStr*(value: string): HexQuantityStr = value.HexQuantityStr
|
||||
|
||||
# Converters
|
||||
|
||||
import json
|
||||
from json_rpc/rpcserver import expect
|
||||
|
||||
proc `%`*(value: HexDataStr): JsonNode =
|
||||
if not value.validate:
|
||||
raise newException(ValueError, "HexDataStr: Invalid hex for Ethereum: " & value.string)
|
||||
else:
|
||||
result = %(value.string)
|
||||
|
||||
proc `%`*(value: HexQuantityStr): JsonNode =
|
||||
if not value.validate:
|
||||
raise newException(ValueError, "HexQuantityStr: Invalid hex for Ethereum: " & value.string)
|
||||
else:
|
||||
result = %(value.string)
|
||||
|
||||
proc fromJson*(n: JsonNode, argName: string, result: var HexDataStr) =
|
||||
# Note that '0x' is stripped after validation
|
||||
n.kind.expect(JString, argName)
|
||||
let hexStr = n.getStr()
|
||||
if not hexStr.hexDataStr.validate:
|
||||
raise newException(ValueError, "Parameter \"" & argName & "\" value is not valid as a Ethereum data \"" & hexStr & "\"")
|
||||
result = hexStr[2..hexStr.high].hexDataStr
|
||||
|
||||
proc fromJson*(n: JsonNode, argName: string, result: var HexQuantityStr) =
|
||||
# Note that '0x' is stripped after validation
|
||||
n.kind.expect(JString, argName)
|
||||
let hexStr = n.getStr()
|
||||
if not hexStr.hexQuantityStr.validate:
|
||||
raise newException(ValueError, "Parameter \"" & argName & "\" value is not valid as an Ethereum hex quantity \"" & hexStr & "\"")
|
||||
result = hexStr[2..hexStr.high].hexQuantityStr
|
||||
|
||||
# testing
|
||||
|
||||
when isMainModule:
|
||||
import unittest
|
||||
suite "Hex quantity":
|
||||
test "Empty string":
|
||||
expect ValueError:
|
||||
let
|
||||
source = ""
|
||||
x = hexQuantityStr source
|
||||
check %x == %source
|
||||
test "Even length":
|
||||
let
|
||||
source = "0x123"
|
||||
x = hexQuantityStr source
|
||||
check %x == %source
|
||||
test "Odd length":
|
||||
let
|
||||
source = "0x123"
|
||||
x = hexQuantityStr"0x123"
|
||||
check %x == %source
|
||||
test "Missing header":
|
||||
expect ValueError:
|
||||
let
|
||||
source = "1234"
|
||||
x = hexQuantityStr source
|
||||
check %x != %source
|
||||
expect ValueError:
|
||||
let
|
||||
source = "01234"
|
||||
x = hexQuantityStr source
|
||||
check %x != %source
|
||||
expect ValueError:
|
||||
let
|
||||
source = "x1234"
|
||||
x = hexQuantityStr source
|
||||
check %x != %source
|
||||
|
||||
suite "Hex data":
|
||||
test "Even length":
|
||||
let
|
||||
source = "0x1234"
|
||||
x = hexDataStr source
|
||||
check %x == %source
|
||||
test "Odd length":
|
||||
expect ValueError:
|
||||
let
|
||||
source = "0x123"
|
||||
x = hexDataStr source
|
||||
check %x != %source
|
||||
test "Missing header":
|
||||
expect ValueError:
|
||||
let
|
||||
source = "1234"
|
||||
x = hexDataStr source
|
||||
check %x != %source
|
||||
expect ValueError:
|
||||
let
|
||||
source = "01234"
|
||||
x = hexDataStr source
|
||||
check %x != %source
|
||||
expect ValueError:
|
||||
let
|
||||
source = "x1234"
|
||||
x = hexDataStr source
|
||||
check %x != %source
|
|
@ -0,0 +1,453 @@
|
|||
import
|
||||
strutils, json,
|
||||
nimcrypto, stint,
|
||||
ethtypes, ethhexstrings, stintjson, json_rpc/rpcserver
|
||||
|
||||
#[
|
||||
For details on available RPC calls, see: https://github.com/ethereum/wiki/wiki/JSON-RPC
|
||||
Note that many of the calls return hashes and even 'ints' as hex strings.
|
||||
This module will likely have to be split into smaller sections for ease of use.
|
||||
|
||||
Information:
|
||||
Default block parameter: https://github.com/ethereum/wiki/wiki/JSON-RPC#the-default-block-parameter
|
||||
|
||||
Parameter types
|
||||
Changes might be required for parameter types.
|
||||
For example:
|
||||
* String might be more appropriate than seq[byte], for example for addresses, although would need length constraints.
|
||||
* Int return values might actually be more hex string than int.
|
||||
* array[32, byte] could be UInt256 or Int256, but default to UInt256.
|
||||
* EthTypes such as BlockObject and TransactionObject might be better as existing Nimbus objects if present.
|
||||
|
||||
NOTE:
|
||||
* as `from` is a keyword, this has been replaced with `source` for variable names. TODO: Related - eplace `to` with `dest`?
|
||||
|
||||
TODO:
|
||||
* Some values can be returned as different types (eg, int or bool)
|
||||
* Currently implemented as variant types, but server macros need to support
|
||||
initialisation of these types before any use as `kind` can only be
|
||||
specified once without invoking `reset`.
|
||||
]#
|
||||
|
||||
proc addEthRpcs*(server: RpcServer) =
|
||||
server.rpc("web3_clientVersion") do() -> string:
|
||||
## Returns the current client version.
|
||||
result = "Nimbus-RPC-Test"
|
||||
|
||||
server.rpc("web3_sha3") do(data: HexDataStr) -> HexDataStr:
|
||||
## Returns Keccak-256 (not the standardized SHA3-256) of the given data.
|
||||
##
|
||||
## data: the data to convert into a SHA3 hash.
|
||||
## Returns the SHA3 result of the given string.
|
||||
# TODO: Capture error on malformed input
|
||||
var rawData: seq[byte]
|
||||
rawData = data.string.fromHex
|
||||
# data will have 0x prefix
|
||||
result = hexDataStr "0x" & $keccak_256.digest(rawData)
|
||||
|
||||
server.rpc("net_version") do() -> string:
|
||||
## Returns string of the current network id:
|
||||
## "1": Ethereum Mainnet
|
||||
## "2": Morden Testnet (deprecated)
|
||||
## "3": Ropsten Testnet
|
||||
## "4": Rinkeby Testnet
|
||||
## "42": Kovan Testnet
|
||||
#[ Note, See:
|
||||
https://github.com/ethereum/interfaces/issues/6
|
||||
https://github.com/ethereum/EIPs/issues/611
|
||||
]#
|
||||
result = ""
|
||||
|
||||
server.rpc("net_listening") do() -> bool:
|
||||
## Returns boolean true when listening, otherwise false.
|
||||
result = true
|
||||
|
||||
server.rpc("net_peerCount") do() -> int:
|
||||
## Returns integer of the number of connected peers.
|
||||
discard
|
||||
|
||||
server.rpc("eth_protocolVersion") do() -> string:
|
||||
## Returns string of the current ethereum protocol version.
|
||||
discard
|
||||
|
||||
server.rpc("eth_syncing") do() -> JsonNode:
|
||||
## Returns SyncObject or false when not syncing.
|
||||
var
|
||||
res: JsonNode
|
||||
sync: SyncObject
|
||||
if true: res = %sync
|
||||
else: res = newJBool(false)
|
||||
result = res
|
||||
|
||||
server.rpc("eth_coinbase") do() -> string:
|
||||
## Returns the current coinbase address.
|
||||
result = ""
|
||||
|
||||
server.rpc("eth_mining") do() -> bool:
|
||||
## Returns true of the client is mining, otherwise false.
|
||||
discard
|
||||
|
||||
server.rpc("eth_hashrate") do() -> int:
|
||||
## Returns the number of hashes per second that the node is mining with.
|
||||
discard
|
||||
|
||||
server.rpc("eth_gasPrice") do() -> int64:
|
||||
## Returns an integer of the current gas price in wei.
|
||||
discard
|
||||
|
||||
server.rpc("eth_accounts") do() -> seq[array[20, byte]]:
|
||||
## Returns a list of addresses owned by client.
|
||||
# TODO: this might be easier to use as seq[string]
|
||||
# This is what's expected: "result": ["0x407d73d8a49eeb85d32cf465507dd71d507100c1"]
|
||||
discard
|
||||
|
||||
server.rpc("eth_blockNumber") do() -> int:
|
||||
## Returns integer of the current block number the client is on.
|
||||
discard
|
||||
|
||||
server.rpc("eth_getBalance") do(data: array[20, byte], quantityTag: string) -> int:
|
||||
## Returns the balance of the account of given address.
|
||||
##
|
||||
## data: address to check for balance.
|
||||
## quantityTag: integer block number, or the string "latest", "earliest" or "pending", see the default block parameter.
|
||||
## Returns integer of the current balance in wei.
|
||||
discard
|
||||
|
||||
server.rpc("eth_getStorageAt") do(data: array[20, byte], quantity: int, quantityTag: string) -> seq[byte]:
|
||||
## Returns the value from a storage position at a given address.
|
||||
##
|
||||
## data: address of the storage.
|
||||
## quantity: integer of the position in the storage.
|
||||
## quantityTag: integer block number, or the string "latest", "earliest" or "pending", see the default block parameter.
|
||||
## Returns: the value at this storage position.
|
||||
# TODO: More appropriate return type?
|
||||
# For more details, see: https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_getstorageat
|
||||
result = @[]
|
||||
|
||||
server.rpc("eth_getTransactionCount") do(data: array[20, byte], quantityTag: string):
|
||||
## Returns the number of transactions sent from an address.
|
||||
##
|
||||
## data: address.
|
||||
## quantityTag: integer block number, or the string "latest", "earliest" or "pending", see the default block parameter.
|
||||
## Returns integer of the number of transactions send from this address.
|
||||
discard
|
||||
|
||||
server.rpc("eth_getBlockTransactionCountByHash") do(data: array[32, byte]) -> int:
|
||||
## Returns the number of transactions in a block from a block matching the given block hash.
|
||||
##
|
||||
## data: hash of a block
|
||||
## Returns integer of the number of transactions in this block.
|
||||
discard
|
||||
|
||||
server.rpc("eth_getBlockTransactionCountByNumber") do(quantityTag: string) -> int:
|
||||
## Returns the number of transactions in a block matching the given block number.
|
||||
##
|
||||
## data: integer of a block number, or the string "earliest", "latest" or "pending", as in the default block parameter.
|
||||
## Returns integer of the number of transactions in this block.
|
||||
discard
|
||||
|
||||
server.rpc("eth_getUncleCountByBlockHash") do(data: array[32, byte]):
|
||||
## Returns the number of uncles in a block from a block matching the given block hash.
|
||||
##
|
||||
## data: hash of a block.
|
||||
## Returns integer of the number of uncles in this block.
|
||||
discard
|
||||
|
||||
server.rpc("eth_getUncleCountByBlockNumber") do(quantityTag: string):
|
||||
## Returns the number of uncles in a block from a block matching the given block number.
|
||||
##
|
||||
## quantityTag: integer of a block number, or the string "latest", "earliest" or "pending", see the default block parameter.
|
||||
## Returns integer of uncles in this block.
|
||||
discard
|
||||
|
||||
server.rpc("eth_getCode") do(data: array[20, byte], quantityTag: string) -> seq[byte]:
|
||||
## Returns code at a given address.
|
||||
##
|
||||
## data: address
|
||||
## quantityTag: integer block number, or the string "latest", "earliest" or "pending", see the default block parameter.
|
||||
## Returns the code from the given address.
|
||||
result = @[]
|
||||
|
||||
server.rpc("eth_sign") do(data: array[20, byte], message: seq[byte]) -> seq[byte]:
|
||||
## The sign method calculates an Ethereum specific signature with: sign(keccak256("\x19Ethereum Signed Message:\n" + len(message) + message))).
|
||||
## By adding a prefix to the message makes the calculated signature recognisable as an Ethereum specific signature.
|
||||
## This prevents misuse where a malicious DApp can sign arbitrary data (e.g. transaction) and use the signature to impersonate the victim.
|
||||
## Note the address to sign with must be unlocked.
|
||||
##
|
||||
## data: address.
|
||||
## message: message to sign.
|
||||
## Returns signature.
|
||||
discard
|
||||
|
||||
server.rpc("eth_sendTransaction") do(obj: EthSend) -> UInt256:
|
||||
## Creates new message call transaction or a contract creation, if the data field contains code.
|
||||
##
|
||||
## obj: the transaction object.
|
||||
## Returns the transaction hash, or the zero hash if the transaction is not yet available.
|
||||
## Note: Use eth_getTransactionReceipt to get the contract address, after the transaction was mined, when you created a contract.
|
||||
discard
|
||||
|
||||
server.rpc("eth_sendRawTransaction") do(data: string, quantityTag: int) -> UInt256: # TODO: string or array of byte?
|
||||
## Creates new message call transaction or a contract creation for signed transactions.
|
||||
##
|
||||
## data: the signed transaction data.
|
||||
## Returns the transaction hash, or the zero hash if the transaction is not yet available.
|
||||
## Note: Use eth_getTransactionReceipt to get the contract address, after the transaction was mined, when you created a contract.
|
||||
discard
|
||||
|
||||
server.rpc("eth_call") do(call: EthCall, quantityTag: string) -> UInt256:
|
||||
## Executes a new message call immediately without creating a transaction on the block chain.
|
||||
##
|
||||
## call: the transaction call object.
|
||||
## quantityTag: integer block number, or the string "latest", "earliest" or "pending", see the default block parameter.
|
||||
## Returns the return value of executed contract.
|
||||
# TODO: Should return value be UInt256 or seq[byte] or string?
|
||||
discard
|
||||
|
||||
server.rpc("eth_estimateGas") do(call: EthCall, quantityTag: string) -> UInt256: # TODO: Int or U/Int256?
|
||||
## Generates and returns an estimate of how much gas is necessary to allow the transaction to complete.
|
||||
## The transaction will not be added to the blockchain. Note that the estimate may be significantly more than
|
||||
## the amount of gas actually used by the transaction, for a variety of reasons including EVM mechanics and node performance.
|
||||
##
|
||||
## call: the transaction call object.
|
||||
## quantityTag: integer block number, or the string "latest", "earliest" or "pending", see the default block parameter.
|
||||
## Returns the amount of gas used.
|
||||
discard
|
||||
|
||||
server.rpc("eth_getBlockByHash") do(data: array[32, byte], fullTransactions: bool) -> BlockObject:
|
||||
## Returns information about a block by hash.
|
||||
##
|
||||
## data: Hash of a block.
|
||||
## fullTransactions: If true it returns the full transaction objects, if false only the hashes of the transactions.
|
||||
## Returns BlockObject or nil when no block was found.
|
||||
discard
|
||||
|
||||
server.rpc("eth_getBlockByNumber") do(quantityTag: string, fullTransactions: bool) -> BlockObject:
|
||||
## Returns information about a block by block number.
|
||||
##
|
||||
## quantityTag: integer of a block number, or the string "earliest", "latest" or "pending", as in the default block parameter.
|
||||
## fullTransactions: If true it returns the full transaction objects, if false only the hashes of the transactions.
|
||||
## Returns BlockObject or nil when no block was found.
|
||||
discard
|
||||
|
||||
server.rpc("eth_getTransactionByHash") do(data: Uint256) -> TransactionObject:
|
||||
## Returns the information about a transaction requested by transaction hash.
|
||||
##
|
||||
## data: hash of a transaction.
|
||||
## Returns requested transaction information.
|
||||
discard
|
||||
|
||||
server.rpc("eth_getTransactionByBlockHashAndIndex") do(data: UInt256, quantity: int) -> TransactionObject:
|
||||
## Returns information about a transaction by block hash and transaction index position.
|
||||
##
|
||||
## data: hash of a block.
|
||||
## quantity: integer of the transaction index position.
|
||||
## Returns requested transaction information.
|
||||
discard
|
||||
|
||||
server.rpc("eth_getTransactionByBlockNumberAndIndex") do(quantityTag: string, quantity: int) -> TransactionObject:
|
||||
## Returns information about a transaction by block number and transaction index position.
|
||||
##
|
||||
## quantityTag: a block number, or the string "earliest", "latest" or "pending", as in the default block parameter.
|
||||
## quantity: the transaction index position.
|
||||
discard
|
||||
|
||||
server.rpc("eth_getTransactionReceipt") do(data: UInt256) -> ReceiptObject:
|
||||
## Returns the receipt of a transaction by transaction hash.
|
||||
##
|
||||
## data: hash of a transaction.
|
||||
## Returns transaction receipt.
|
||||
discard
|
||||
|
||||
server.rpc("eth_getUncleByBlockHashAndIndex") do(data: UInt256, quantity: int64) -> BlockObject:
|
||||
## Returns information about a uncle of a block by hash and uncle index position.
|
||||
##
|
||||
## data: hash a block.
|
||||
## quantity: the uncle's index position.
|
||||
## Returns BlockObject or nil when no block was found.
|
||||
discard
|
||||
|
||||
server.rpc("eth_getUncleByBlockNumberAndIndex") do(quantityTag: string, quantity: int64) -> BlockObject:
|
||||
# Returns information about a uncle of a block by number and uncle index position.
|
||||
##
|
||||
## quantityTag: a block number, or the string "earliest", "latest" or "pending", as in the default block parameter.
|
||||
## quantity: the uncle's index position.
|
||||
## Returns BlockObject or nil when no block was found.
|
||||
discard
|
||||
|
||||
server.rpc("eth_getCompilers") do() -> seq[string]:
|
||||
## Returns a list of available compilers in the client.
|
||||
##
|
||||
## Returns a list of available compilers.
|
||||
result = @[]
|
||||
|
||||
server.rpc("eth_compileSolidity") do(sourceCode: string) -> seq[byte]:
|
||||
## Returns compiled solidity code.
|
||||
##
|
||||
## sourceCode: source code as string.
|
||||
## Returns compiles source code.
|
||||
result = @[]
|
||||
|
||||
server.rpc("eth_compileLLL") do(sourceCode: string) -> seq[byte]:
|
||||
## Returns compiled LLL code.
|
||||
##
|
||||
## sourceCode: source code as string.
|
||||
## Returns compiles source code.
|
||||
result = @[]
|
||||
|
||||
server.rpc("eth_compileSerpent") do(sourceCode: string) -> seq[byte]:
|
||||
## Returns compiled serpent code.
|
||||
##
|
||||
## sourceCode: source code as string.
|
||||
## Returns compiles source code.
|
||||
result = @[]
|
||||
|
||||
server.rpc("eth_newFilter") do(filterOptions: FilterOptions) -> int:
|
||||
## Creates a filter object, based on filter options, to notify when the state changes (logs).
|
||||
## To check if the state has changed, call eth_getFilterChanges.
|
||||
## Topics are order-dependent. A transaction with a log with topics [A, B] will be matched by the following topic filters:
|
||||
## [] "anything"
|
||||
## [A] "A in first position (and anything after)"
|
||||
## [null, B] "anything in first position AND B in second position (and anything after)"
|
||||
## [A, B] "A in first position AND B in second position (and anything after)"
|
||||
## [[A, B], [A, B]] "(A OR B) in first position AND (A OR B) in second position (and anything after)"
|
||||
##
|
||||
## filterOptions: settings for this filter.
|
||||
## Returns integer filter id.
|
||||
discard
|
||||
|
||||
server.rpc("eth_newBlockFilter") do() -> int:
|
||||
## Creates a filter in the node, to notify when a new block arrives.
|
||||
## To check if the state has changed, call eth_getFilterChanges.
|
||||
##
|
||||
## Returns integer filter id.
|
||||
discard
|
||||
|
||||
server.rpc("eth_newPendingTransactionFilter") do() -> int:
|
||||
## Creates a filter in the node, to notify when a new block arrives.
|
||||
## To check if the state has changed, call eth_getFilterChanges.
|
||||
##
|
||||
## Returns integer filter id.
|
||||
discard
|
||||
|
||||
server.rpc("eth_uninstallFilter") do(filterId: int) -> bool:
|
||||
## Uninstalls a filter with given id. Should always be called when watch is no longer needed.
|
||||
## Additonally Filters timeout when they aren't requested with eth_getFilterChanges for a period of time.
|
||||
##
|
||||
## filterId: The filter id.
|
||||
## Returns true if the filter was successfully uninstalled, otherwise false.
|
||||
discard
|
||||
|
||||
server.rpc("eth_getFilterChanges") do(filterId: int) -> seq[LogObject]:
|
||||
## Polling method for a filter, which returns an list of logs which occurred since last poll.
|
||||
##
|
||||
## filterId: the filter id.
|
||||
result = @[]
|
||||
|
||||
server.rpc("eth_getFilterLogs") do(filterId: int) -> seq[LogObject]:
|
||||
## filterId: the filter id.
|
||||
## Returns a list of all logs matching filter with given id.
|
||||
result = @[]
|
||||
|
||||
server.rpc("eth_getLogs") do(filterOptions: FilterOptions) -> seq[LogObject]:
|
||||
## filterOptions: settings for this filter.
|
||||
## Returns a list of all logs matching a given filter object.
|
||||
result = @[]
|
||||
|
||||
server.rpc("eth_getWork") do() -> seq[UInt256]:
|
||||
## Returns the hash of the current block, the seedHash, and the boundary condition to be met ("target").
|
||||
## Returned list has the following properties:
|
||||
## DATA, 32 Bytes - current block header pow-hash.
|
||||
## DATA, 32 Bytes - the seed hash used for the DAG.
|
||||
## DATA, 32 Bytes - the boundary condition ("target"), 2^256 / difficulty.
|
||||
result = @[]
|
||||
|
||||
server.rpc("eth_submitWork") do(nonce: int64, powHash: Uint256, mixDigest: Uint256) -> bool:
|
||||
## Used for submitting a proof-of-work solution.
|
||||
##
|
||||
## nonce: the nonce found.
|
||||
## headerPow: the header's pow-hash.
|
||||
## mixDigest: the mix digest.
|
||||
## Returns true if the provided solution is valid, otherwise false.
|
||||
discard
|
||||
|
||||
server.rpc("eth_submitHashrate") do(hashRate: UInt256, id: Uint256) -> bool:
|
||||
## Used for submitting mining hashrate.
|
||||
##
|
||||
## hashRate: a hexadecimal string representation (32 bytes) of the hash rate.
|
||||
## id: a random hexadecimal(32 bytes) ID identifying the client.
|
||||
## Returns true if submitting went through succesfully and false otherwise.
|
||||
discard
|
||||
|
||||
server.rpc("shh_version") do() -> string:
|
||||
## Returns string of the current whisper protocol version.
|
||||
discard
|
||||
|
||||
server.rpc("shh_post") do(message: WhisperPost) -> bool:
|
||||
## Sends a whisper message.
|
||||
##
|
||||
## message: Whisper message to post.
|
||||
## Returns true if the message was send, otherwise false.
|
||||
discard
|
||||
|
||||
server.rpc("shh_newIdentity") do() -> array[60, byte]:
|
||||
## Creates new whisper identity in the client.
|
||||
##
|
||||
## Returns the address of the new identiy.
|
||||
discard
|
||||
|
||||
server.rpc("shh_hasIdentity") do(identity: array[60, byte]) -> bool:
|
||||
## Checks if the client holds the private keys for a given identity.
|
||||
##
|
||||
## identity: the identity address to check.
|
||||
## Returns true if the client holds the privatekey for that identity, otherwise false.
|
||||
discard
|
||||
|
||||
server.rpc("shh_newGroup") do() -> array[60, byte]:
|
||||
## (?) - This has no description information in the RPC wiki.
|
||||
##
|
||||
## Returns the address of the new group. (?)
|
||||
discard
|
||||
|
||||
server.rpc("shh_addToGroup") do(identity: array[60, byte]) -> bool:
|
||||
## (?) - This has no description information in the RPC wiki.
|
||||
##
|
||||
## identity: the identity address to add to a group (?).
|
||||
## Returns true if the identity was successfully added to the group, otherwise false (?).
|
||||
discard
|
||||
|
||||
server.rpc("shh_newFilter") do(filterOptions: FilterOptions, to: array[60, byte], topics: seq[UInt256]) -> int: # TODO: Is topic of right type?
|
||||
## Creates filter to notify, when client receives whisper message matching the filter options.
|
||||
##
|
||||
## filterOptions: The filter options:
|
||||
## to: DATA, 60 Bytes - (optional) identity of the receiver. When present it will try to decrypt any incoming message if the client holds the private key to this identity.
|
||||
## topics: Array of DATA - list of DATA topics which the incoming message's topics should match. You can use the following combinations:
|
||||
## [A, B] = A && B
|
||||
## [A, [B, C]] = A && (B || C)
|
||||
## [null, A, B] = ANYTHING && A && B null works as a wildcard
|
||||
## Returns the newly created filter.
|
||||
discard
|
||||
|
||||
server.rpc("shh_uninstallFilter") do(id: int) -> bool:
|
||||
## Uninstalls a filter with given id.
|
||||
## Should always be called when watch is no longer needed.
|
||||
## Additonally Filters timeout when they aren't requested with shh_getFilterChanges for a period of time.
|
||||
##
|
||||
## id: the filter id.
|
||||
## Returns true if the filter was successfully uninstalled, otherwise false.
|
||||
discard
|
||||
|
||||
server.rpc("shh_getFilterChanges") do(id: int) -> seq[WhisperMessage]:
|
||||
## Polling method for whisper filters. Returns new messages since the last call of this method.
|
||||
## Note: calling the shh_getMessages method, will reset the buffer for this method, so that you won't receive duplicate messages.
|
||||
##
|
||||
## id: the filter id.
|
||||
discard
|
||||
|
||||
server.rpc("shh_getMessages") do(id: int) -> seq[WhisperMessage]:
|
||||
## Get all messages matching a filter. Unlike shh_getFilterChanges this returns all messages.
|
||||
##
|
||||
## id: the filter id.
|
||||
## Returns a list of messages received since last poll.
|
||||
discard
|
||||
|
|
@ -0,0 +1,171 @@
|
|||
import options, json
|
||||
import stint
|
||||
|
||||
type
|
||||
SyncObject* = object
|
||||
startingBlock*: int
|
||||
currentBlock*: int
|
||||
highestBlock*: int
|
||||
|
||||
EthSend* = object
|
||||
source*: array[20, byte] # the address the transaction is send from.
|
||||
to*: Option[array[20, byte]] # (optional when creating new contract) the address the transaction is directed to.
|
||||
gas*: Option[int] # (optional, default: 90000) integer of the gas provided for the transaction execution. It will return unused gas.
|
||||
gasPrice*: Option[int] # (optional, default: To-Be-Determined) integer of the gasPrice used for each paid gas.
|
||||
value*: Option[int] # (optional) integer of the value sent with this transaction.
|
||||
data*: string # the compiled code of a contract OR the hash of the invoked method signature and encoded parameters. For details see Ethereum Contract ABI.
|
||||
nonce*: Option[int] # (optional) integer of a nonce. This allows to overwrite your own pending transactions that use the same nonce
|
||||
#EthSend* = object
|
||||
# source*: array[20, byte] # the address the transaction is send from.
|
||||
# to*: array[20, byte] # (optional when creating new contract) the address the transaction is directed to.
|
||||
# gas*: int # (optional, default: 90000) integer of the gas provided for the transaction execution. It will return unused gas.
|
||||
# gasPrice*: int # (optional, default: To-Be-Determined) integer of the gasPrice used for each paid gas.
|
||||
# value*: int # (optional) integer of the value sent with this transaction.
|
||||
# data*: string # the compiled code of a contract OR the hash of the invoked method signature and encoded parameters. For details see Ethereum Contract ABI.
|
||||
# nonce*: int # (optional) integer of a nonce. This allows to overwrite your own pending transactions that use the same nonce
|
||||
|
||||
EthCall* = object
|
||||
source*: array[20, byte] # (optional) The address the transaction is send from.
|
||||
to*: array[20, byte] # The address the transaction is directed to.
|
||||
gas*: int # (optional) Integer of the gas provided for the transaction execution. eth_call consumes zero gas, but this parameter may be needed by some executions.
|
||||
gasPrice*: int # (optional) Integer of the gasPrice used for each paid gas.
|
||||
value*: int # (optional) Integer of the value sent with this transaction.
|
||||
data*: int # (optional) Hash of the method signature and encoded parameters. For details see Ethereum Contract ABI.
|
||||
|
||||
## A block object, or null when no block was found
|
||||
BlockObject* = ref object
|
||||
number*: int # the block number. null when its pending block.
|
||||
hash*: UInt256 # hash of the block. null when its pending block.
|
||||
parentHash*: UInt256 # hash of the parent block.
|
||||
nonce*: int64 # hash of the generated proof-of-work. null when its pending block.
|
||||
sha3Uncles*: UInt256 # SHA3 of the uncles data in the block.
|
||||
logsBloom*: array[256, byte] # the bloom filter for the logs of the block. null when its pending block.
|
||||
transactionsRoot*: UInt256 # the root of the transaction trie of the block.
|
||||
stateRoot*: UInt256 # the root of the final state trie of the block.
|
||||
receiptsRoot*: UInt256 # the root of the receipts trie of the block.
|
||||
miner*: array[20, byte] # the address of the beneficiary to whom the mining rewards were given.
|
||||
difficulty*: int # integer of the difficulty for this block.
|
||||
totalDifficulty*: int # integer of the total difficulty of the chain until this block.
|
||||
extraData*: string # the "extra data" field of this block.
|
||||
size*: int # integer the size of this block in bytes.
|
||||
gasLimit*: int # the maximum gas allowed in this block.
|
||||
gasUsed*: int # the total used gas by all transactions in this block.
|
||||
timestamp*: int # the unix timestamp for when the block was collated.
|
||||
transactions*: seq[Uint256] # list of transaction objects, or 32 Bytes transaction hashes depending on the last given parameter.
|
||||
uncles*: seq[Uint256] # list of uncle hashes.
|
||||
|
||||
TransactionObject* = object # A transaction object, or null when no transaction was found:
|
||||
hash*: UInt256 # hash of the transaction.
|
||||
nonce*: int64 # TODO: Is int? the number of transactions made by the sender prior to this one.
|
||||
blockHash*: UInt256 # hash of the block where this transaction was in. null when its pending.
|
||||
blockNumber*: int64 # block number where this transaction was in. null when its pending.
|
||||
transactionIndex*: int64 # integer of the transactions index position in the block. null when its pending.
|
||||
source*: array[20, byte] # address of the sender.
|
||||
to*: array[20, byte] # address of the receiver. null when its a contract creation transaction.
|
||||
value*: int64 # value transferred in Wei.
|
||||
gasPrice*: int64 # gas price provided by the sender in Wei.
|
||||
gas*: int64 # gas provided by the sender.
|
||||
input*: seq[byte] # the data send along with the transaction.
|
||||
|
||||
ReceiptKind* = enum rkRoot, rkStatus
|
||||
ReceiptObject* = object
|
||||
# A transaction receipt object, or null when no receipt was found:
|
||||
transactionHash*: UInt256 # hash of the transaction.
|
||||
transactionIndex*: int # integer of the transactions index position in the block.
|
||||
blockHash*: UInt256 # hash of the block where this transaction was in.
|
||||
blockNumber*: int # block number where this transaction was in.
|
||||
cumulativeGasUsed*: int # the total amount of gas used when this transaction was executed in the block.
|
||||
gasUsed*: int # the amount of gas used by this specific transaction alone.
|
||||
contractAddress*: array[20, byte] # the contract address created, if the transaction was a contract creation, otherwise null.
|
||||
logs*: seq[string] # TODO: See Wiki for details. list of log objects, which this transaction generated.
|
||||
logsBloom*: array[256, byte] # bloom filter for light clients to quickly retrieve related logs.
|
||||
# TODO:
|
||||
#case kind*: ReceiptKind
|
||||
#of rkRoot: root*: UInt256 # post-transaction stateroot (pre Byzantium).
|
||||
#of rkStatus: status*: int # 1 = success, 0 = failure.
|
||||
|
||||
FilterDataKind* = enum fkItem, fkList
|
||||
FilterData* = object
|
||||
# Difficult to process variant objects in input data, as kind is immutable.
|
||||
# TODO: This might need more work to handle "or" options
|
||||
kind*: FilterDataKind
|
||||
items*: seq[FilterData]
|
||||
item*: UInt256
|
||||
# TODO: I don't think this will work as input, need only one value that is either UInt256 or seq[UInt256]
|
||||
|
||||
FilterOptions* = object
|
||||
fromBlock*: string # (optional, default: "latest") integer block number, or "latest" for the last mined block or "pending", "earliest" for not yet mined transactions.
|
||||
toBlock*: string # (optional, default: "latest") integer block number, or "latest" for the last mined block or "pending", "earliest" for not yet mined transactions.
|
||||
address*: seq[array[20, byte]] # (optional) contract address or a list of addresses from which logs should originate.
|
||||
topics*: seq[FilterData] # (optional) list of DATA topics. Topics are order-dependent. Each topic can also be a list of DATA with "or" options.
|
||||
|
||||
LogObject* = object
|
||||
removed*: bool # true when the log was removed, due to a chain reorganization. false if its a valid log.
|
||||
logIndex*: int # integer of the log index position in the block. null when its pending log.
|
||||
transactionIndex*: ref int # integer of the transactions index position log was created from. null when its pending log.
|
||||
transactionHash*: UInt256 # hash of the transactions this log was created from. null when its pending log.
|
||||
blockHash*: ref UInt256 # hash of the block where this log was in. null when its pending. null when its pending log.
|
||||
blockNumber*: ref int64 # the block number where this log was in. null when its pending. null when its pending log.
|
||||
address*: array[20, byte] # address from which this log originated.
|
||||
data*: seq[UInt256] # contains one or more 32 Bytes non-indexed arguments of the log.
|
||||
topics*: array[4, UInt256] # array of 0 to 4 32 Bytes DATA of indexed log arguments.
|
||||
# (In solidity: The first topic is the hash of the signature of the event.
|
||||
# (e.g. Deposit(address,bytes32,uint256)), except you declared the event with the anonymous specifier.)
|
||||
|
||||
WhisperPost* = object
|
||||
# The whisper post object:
|
||||
source*: array[60, byte] # (optional) the identity of the sender.
|
||||
to*: array[60, byte] # (optional) the identity of the receiver. When present whisper will encrypt the message so that only the receiver can decrypt it.
|
||||
topics*: seq[UInt256] # TODO: Correct type? list of DATA topics, for the receiver to identify messages.
|
||||
payload*: UInt256 # TODO: Correct type - maybe string? the payload of the message.
|
||||
priority*: int # integer of the priority in a rang from ... (?).
|
||||
ttl*: int # integer of the time to live in seconds.
|
||||
|
||||
WhisperMessage* = object
|
||||
# (?) are from the RPC Wiki, indicating uncertainty in type format.
|
||||
hash*: UInt256 # (?) the hash of the message.
|
||||
source*: array[60, byte] # the sender of the message, if a sender was specified.
|
||||
to*: array[60, byte] # the receiver of the message, if a receiver was specified.
|
||||
expiry*: int # integer of the time in seconds when this message should expire (?).
|
||||
ttl*: int # integer of the time the message should float in the system in seconds (?).
|
||||
sent*: int # integer of the unix timestamp when the message was sent.
|
||||
topics*: seq[UInt256] # list of DATA topics the message contained.
|
||||
payload*: string # TODO: Correct type? the payload of the message.
|
||||
workProved*: int # integer of the work this message required before it was send (?).
|
||||
|
||||
# EthSend* = object
|
||||
# source*: array[20, byte] # the address the transaction is send from.
|
||||
# to*: Option[array[20, byte]] # (optional when creating new contract) the address the transaction is directed to.
|
||||
# gas*: Option[int] # (optional, default: 90000) integer of the gas provided for the transaction execution. It will return unused gas.
|
||||
# gasPrice*: Option[int] # (optional, default: To-Be-Determined) integer of the gasPrice used for each paid gas.
|
||||
# value*: Option[int] # (optional) integer of the value sent with this transaction.
|
||||
# data*: string # the compiled code of a contract OR the hash of the invoked method signature and encoded parameters. For details see Ethereum Contract ABI.
|
||||
# nonce*: Option[int] # (optional) integer of a nonce. This allows to overwrite your own pending transactions that use the same nonce
|
||||
|
||||
var x: array[20, byte] = [1.byte, 2, 3, 4, 5, 6, 7, 0xab, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20]
|
||||
|
||||
const hchars = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f']
|
||||
proc toStr*[N: static[int]](x: array[N, byte]): string =
|
||||
result = newString(2 + N*2)
|
||||
result[0] = '0'
|
||||
result[1] = 'x'
|
||||
for i, c in x:
|
||||
result[2 + i*2] = hchars[(c and 0xf0) shr 4]
|
||||
result[2 + i*2 + 1] = hchars[c and 0x0f]
|
||||
|
||||
proc `%`*(x: EthSend): JsonNode =
|
||||
result = newJobject()
|
||||
result["from"] = %x.source.toStr
|
||||
if x.to.isSome:
|
||||
result["to"] = %x.to.unsafeGet.toStr
|
||||
if x.gas.isSome:
|
||||
result["gas"] = %x.gas.unsafeGet
|
||||
if x.gasPrice.isSome:
|
||||
result["gasPrice"] = %x.gasPrice.unsafeGet
|
||||
if x.value.isSome:
|
||||
result["value"] = %x.value.unsafeGet
|
||||
result["data"] = %x.data
|
||||
if x.nonce.isSome:
|
||||
result["nonce"] = %x.nonce.unsafeGet
|
||||
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
import json, stint
|
||||
from json_rpc/rpcserver import expect
|
||||
|
||||
template stintStr(n: UInt256|Int256): JsonNode =
|
||||
var s = n.toHex
|
||||
if s.len mod 2 != 0: s = "0" & s
|
||||
s = "0x" & s
|
||||
%s
|
||||
|
||||
proc `%`*(n: UInt256): JsonNode = n.stintStr
|
||||
|
||||
proc `%`*(n: Int256): JsonNode = n.stintStr
|
||||
|
||||
# allows UInt256 to be passed as a json string
|
||||
proc fromJson*(n: JsonNode, argName: string, result: var UInt256) =
|
||||
# expects base 16 string, starting with "0x"
|
||||
n.kind.expect(JString, argName)
|
||||
let hexStr = n.getStr()
|
||||
if hexStr.len > 64 + 2: # including "0x"
|
||||
raise newException(ValueError, "Parameter \"" & argName & "\" value too long for UInt256: " & $hexStr.len)
|
||||
result = hexStr.parse(StUint[256], 16) # TODO: Handle errors
|
||||
|
||||
# allows ref UInt256 to be passed as a json string
|
||||
proc fromJson*(n: JsonNode, argName: string, result: var ref UInt256) =
|
||||
# expects base 16 string, starting with "0x"
|
||||
n.kind.expect(JString, argName)
|
||||
let hexStr = n.getStr()
|
||||
if hexStr.len > 64 + 2: # including "0x"
|
||||
raise newException(ValueError, "Parameter \"" & argName & "\" value too long for UInt256: " & $hexStr.len)
|
||||
new result
|
||||
result[] = hexStr.parse(StUint[256], 16) # TODO: Handle errors
|
||||
|
|
@ -0,0 +1,336 @@
|
|||
import macros, strutils, options, math, json
|
||||
from os import getCurrentDir, DirSep
|
||||
import
|
||||
nimcrypto, stint, httputils, chronicles, asyncdispatch2, json_rpc/rpcclient
|
||||
import ethtypes, ethprocs, stintjson, ethhexstrings
|
||||
|
||||
template sourceDir: string = currentSourcePath.rsplit(DirSep, 1)[0]
|
||||
|
||||
## Generate client convenience marshalling wrappers from forward declarations
|
||||
createRpcSigs(RpcHttpClient, sourceDir & DirSep & "ethcallsigs.nim")
|
||||
|
||||
type
|
||||
Web3 = object
|
||||
eth*: RpcHttpClient
|
||||
|
||||
proc initWeb3*(address: string, port: int): Web3 =
|
||||
## Just creates a simple dummy wrapper object for now. Functionality should
|
||||
## increase as the web3 interface is fleshed out.
|
||||
var client = newRpcHttpClient()
|
||||
client.httpMethod(MethodPost)
|
||||
|
||||
waitFor client.connect(address, Port(port))
|
||||
result.eth = client
|
||||
|
||||
func encode[bits: static[int]](x: Stuint[bits]): string =
|
||||
## Encodes a `Stuint` to a textual representation for use in the JsonRPC
|
||||
## `sendTransaction` call.
|
||||
'0'.repeat((256 - bits) div 4) & x.dumpHex
|
||||
|
||||
func encode[bits: static[int]](x: Stint[bits]): string =
|
||||
## Encodes a `Stint` to a textual representation for use in the JsonRPC
|
||||
## `sendTransaction` call.
|
||||
if x.isNegative:
|
||||
'f'.repeat((256 - bits) div 4) & x.dumpHex
|
||||
else:
|
||||
'0'.repeat((256 - bits) div 4) & x.dumpHex
|
||||
|
||||
macro makeTypeEnum(): untyped =
|
||||
## This macro creates all the various types of Solidity contracts and maps
|
||||
## them to the type used for their encoding. It also creates an enum to
|
||||
## identify these types in the contract signatures, along with encoder
|
||||
## functions used in the generated procedures.
|
||||
result = newStmtList()
|
||||
var fields: seq[NimNode]
|
||||
var lastpow2: int
|
||||
for i in countdown(256, 8, 8):
|
||||
let
|
||||
identUint = newIdentNode("Uint" & $i)
|
||||
identInt = newIdentNode("Int" & $i)
|
||||
if ceil(log2(i.float)) == floor(log2(i.float)):
|
||||
lastpow2 = i
|
||||
result.add quote do:
|
||||
type
|
||||
`identUint`* = Stuint[`lastpow2`]
|
||||
`identInt`* = Stint[`lastpow2`]
|
||||
fields.add ident("Uint" & $i)
|
||||
fields.add ident("Int" & $i)
|
||||
let
|
||||
identAddress = ident("Address")
|
||||
identUint = ident("Uint")
|
||||
identInt = ident("Int")
|
||||
identBool = ident("Bool")
|
||||
result.add quote do:
|
||||
type
|
||||
`identAddress`* = Uint160
|
||||
`identUint`* = Uint256
|
||||
`identInt`* = Int256
|
||||
`identBool`* = distinct Int256
|
||||
func encode*(x: `identBool`): string = encode(Int256(x))
|
||||
fields.add [
|
||||
identAddress,
|
||||
identUint,
|
||||
identInt,
|
||||
identBool
|
||||
]
|
||||
for m in countup(8, 256, 8):
|
||||
let
|
||||
identInt = ident("Int" & $m)
|
||||
identUint = ident("Uint" & $m)
|
||||
identFixed = ident "Fixed" & $m
|
||||
identUfixed = ident "Ufixed" & $m
|
||||
identT = ident "T"
|
||||
result.add quote do:
|
||||
# Fixed stuff is not actually implemented yet, these procedures don't
|
||||
# do what they are supposed to.
|
||||
type
|
||||
`identFixed`[N: static[int]] = distinct `identInt`
|
||||
`identUfixed`[N: static[int]] = distinct `identUint`
|
||||
|
||||
func to*(x: `identInt`, `identT`: typedesc[`identFixed`]): `identT` =
|
||||
T(x)
|
||||
|
||||
func to*(x: `identUint`, `identT`: typedesc[`identUfixed`]): `identT` =
|
||||
T(x)
|
||||
|
||||
func encode*[N: static[int]](x: `identFixed`[N]): string =
|
||||
encode(`identInt`(x) * (10 ^ N).to(`identInt`))
|
||||
|
||||
func encode*[N: static[int]](x: `identUfixed`[N]): string =
|
||||
encode(`identUint`(x) * (10 ^ N).to(`identUint`))
|
||||
|
||||
fields.add ident("Fixed" & $m)
|
||||
fields.add ident("Ufixed" & $m)
|
||||
let
|
||||
identFixed = ident("Fixed")
|
||||
identUfixed = ident("Ufixed")
|
||||
fields.add identFixed
|
||||
fields.add identUfixed
|
||||
result.add quote do:
|
||||
type
|
||||
`identFixed` = distinct Int128
|
||||
`identUfixed` = distinct Uint128
|
||||
for i in 1..32:
|
||||
let
|
||||
identBytes = ident("Bytes" & $i)
|
||||
identResult = ident "result"
|
||||
fields.add identBytes
|
||||
result.add quote do:
|
||||
type
|
||||
`identBytes` = array[0..(`i`-1), byte]
|
||||
func encode(x: `identBytes`): string =
|
||||
`identResult` = ""
|
||||
for y in x:
|
||||
`identResult` &= y.toHex.toLower
|
||||
`identResult` &= "00".repeat(32 - x.len)
|
||||
|
||||
fields.add [
|
||||
ident("Function"),
|
||||
ident("Bytes"),
|
||||
ident("String")
|
||||
]
|
||||
result.add quote do:
|
||||
type
|
||||
Bytes = seq[byte]
|
||||
result.add newEnum(ident "FieldKind", fields, public = true, pure = true)
|
||||
echo result.repr
|
||||
|
||||
makeTypeEnum()
|
||||
|
||||
type
|
||||
InterfaceObjectKind = enum
|
||||
function, constructor, event
|
||||
MutabilityKind = enum
|
||||
pure, view, nonpayable, payable
|
||||
FunctionInputOutput = object
|
||||
name: string
|
||||
kind: FieldKind
|
||||
EventInput = object
|
||||
name: string
|
||||
kind: FieldKind
|
||||
indexed: bool
|
||||
FunctionObject = object
|
||||
name: string
|
||||
stateMutability: MutabilityKind
|
||||
inputs: seq[FunctionInputOutput]
|
||||
outputs: seq[FunctionInputOutput]
|
||||
ConstructorObject = object
|
||||
stateMutability: MutabilityKind
|
||||
inputs: seq[FunctionInputOutput]
|
||||
outputs: seq[FunctionInputOutput]
|
||||
EventObject = object
|
||||
name: string
|
||||
inputs: seq[EventInput]
|
||||
anonymous: bool
|
||||
|
||||
InterfaceObject = object
|
||||
case kind: InterfaceObjectKind
|
||||
of function: functionObject: FunctionObject
|
||||
of constructor: constructorObject: ConstructorObject
|
||||
of event: eventObject: EventObject
|
||||
|
||||
proc getMethodSignature(function: FunctionObject): string =
|
||||
var signature = function.name & "("
|
||||
for i, input in function.inputs:
|
||||
signature.add(
|
||||
case input.kind:
|
||||
of FieldKind.Uint: "uint256"
|
||||
of FieldKind.Int: "int256"
|
||||
else: ($input.kind).toLower
|
||||
)
|
||||
if i != function.inputs.high:
|
||||
signature.add ","
|
||||
signature.add ")"
|
||||
return signature
|
||||
|
||||
proc parseContract(body: NimNode): seq[InterfaceObject] =
|
||||
proc parseOutputs(outputNode: NimNode): seq[FunctionInputOutput] =
|
||||
if outputNode.kind == nnkIdent:
|
||||
result.add FunctionInputOutput(
|
||||
name: "",
|
||||
kind: parseEnum[FieldKind]($outputNode.ident)
|
||||
)
|
||||
proc parseInputs(inputNodes: NimNode): seq[FunctionInputOutput] =
|
||||
for i in 1..<inputNodes.len:
|
||||
let input = inputNodes[i]
|
||||
if input.kind == nnkIdentDefs:
|
||||
result.add FunctionInputOutput(
|
||||
name: $input[0].ident,
|
||||
kind: parseEnum[FieldKind]($input[1].ident)
|
||||
)
|
||||
proc parseEventInputs(inputNodes: NimNode): seq[EventInput] =
|
||||
for i in 1..<inputNodes.len:
|
||||
let input = inputNodes[i]
|
||||
if input.kind == nnkIdentDefs:
|
||||
case input[1].kind:
|
||||
of nnkIdent:
|
||||
result.add EventInput(
|
||||
name: $input[0].ident,
|
||||
kind: parseEnum[FieldKind]($input[1].ident),
|
||||
indexed: false
|
||||
)
|
||||
of nnkBracketExpr:
|
||||
doAssert($input[1][0].ident == "indexed",
|
||||
"Only `indexed` is allowed as option for event inputs")
|
||||
result.add EventInput(
|
||||
name: $input[0].ident,
|
||||
kind: parseEnum[FieldKind]($input[1][1].ident),
|
||||
indexed: true
|
||||
)
|
||||
else:
|
||||
doAssert(false,
|
||||
"Can't have anything but ident or bracket expression here")
|
||||
echo body.treeRepr
|
||||
var
|
||||
constructor: Option[ConstructorObject]
|
||||
functions: seq[FunctionObject]
|
||||
events: seq[EventObject]
|
||||
for procdef in body:
|
||||
doAssert(procdef.kind == nnkProcDef,
|
||||
"Contracts can only be built with procedures")
|
||||
let
|
||||
isconstructor = procdef[4].findChild(it.ident == !"constructor") != nil
|
||||
isevent = procdef[4].findChild(it.ident == !"event") != nil
|
||||
doAssert(not (isconstructor and constructor.isSome),
|
||||
"Contract can only have a single constructor")
|
||||
doAssert(not (isconstructor and isevent),
|
||||
"Can't be both event and constructor")
|
||||
if not isevent:
|
||||
let
|
||||
ispure = procdef[4].findChild(it.ident == !"pure") != nil
|
||||
isview = procdef[4].findChild(it.ident == !"view") != nil
|
||||
ispayable = procdef[4].findChild(it.ident == !"payable") != nil
|
||||
doAssert(not (ispure and isview),
|
||||
"can't be both `pure` and `view`")
|
||||
doAssert(not ((ispure or isview) and ispayable),
|
||||
"can't be both `pure` or `view` while being `payable`")
|
||||
if isconstructor:
|
||||
constructor = some(ConstructorObject(
|
||||
stateMutability: if ispure: pure elif isview: view elif ispayable: payable else: nonpayable,
|
||||
inputs: parseInputs(procdef[3]),
|
||||
outputs: parseOutputs(procdef[3][0])
|
||||
))
|
||||
else:
|
||||
functions.add FunctionObject(
|
||||
name: $procdef[0].ident,
|
||||
stateMutability: if ispure: pure elif isview: view elif ispayable: payable else: nonpayable,
|
||||
inputs: parseInputs(procdef[3]),
|
||||
outputs: parseOutputs(procdef[3][0])
|
||||
)
|
||||
else:
|
||||
let isanonymous = procdef[4].findChild(it.ident == !"anonymous") != nil
|
||||
doAssert(procdef[3][0].kind == nnkEmpty,
|
||||
"Events can't have return values")
|
||||
events.add EventObject(
|
||||
name: $procdef[0].ident,
|
||||
inputs: parseEventInputs(procdef[3]),
|
||||
anonymous: isanonymous
|
||||
)
|
||||
echo constructor
|
||||
echo functions
|
||||
echo events
|
||||
if constructor.isSome:
|
||||
result.add InterfaceObject(kind: InterfaceObjectKind.constructor, constructorObject: constructor.unsafeGet)
|
||||
for function in functions:
|
||||
result.add InterfaceObject(kind: InterfaceObjectKind.function, functionObject: function)
|
||||
for event in events:
|
||||
result.add InterfaceObject(kind: InterfaceObjectKind.event, eventObject: event)
|
||||
|
||||
macro contract(cname: untyped, body: untyped): untyped =
|
||||
var objects = parseContract(body)
|
||||
result = newStmtList()
|
||||
for obj in objects:
|
||||
if obj.kind == function:
|
||||
let
|
||||
signature = getMethodSignature(obj.functionObject)
|
||||
procName = ident obj.functionObject.name
|
||||
var encodedParams = newLit("")
|
||||
for input in obj.functionObject.inputs:
|
||||
encodedParams = nnkInfix.newTree(
|
||||
ident "&",
|
||||
encodedParams,
|
||||
nnkCall.newTree(ident "encode", ident input.name)
|
||||
)
|
||||
# This is not the final output, but the signature printed out here can
|
||||
# be used in sendTransaction to execute the method of the contract.
|
||||
var procDef = quote do:
|
||||
proc `procName`() =
|
||||
echo "0x" & ($keccak_256.digest(`signature`))[0..<8].toLower & `encodedParams`
|
||||
for input in obj.functionObject.inputs:
|
||||
procDef[3].add nnkIdentDefs.newTree(
|
||||
ident input.name,
|
||||
ident $input.kind,
|
||||
newEmptyNode()
|
||||
)
|
||||
result.add procDef
|
||||
let test = "InterfaceObject(kind: InterfaceObjectKind.constructor, constructorObqect: " & $objects[0] & ")"
|
||||
echo result.repr
|
||||
|
||||
contract(Test):
|
||||
proc sendCoin(receiver: Address, amount: Uint): Bool
|
||||
|
||||
# This call will generate the `cc.data` part to call that contract method in the code below
|
||||
sendCoin(fromHex(Stuint[256], "e375b6fb6d0bf0d86707884f3952fee3977251fe"), 600.to(Stuint[256]))
|
||||
|
||||
# Set up a JsonRPC call to send a transaction
|
||||
# The idea here is to let the Web3 object contain the RPC calls, then allow the
|
||||
# above DSL to create helpers to create the EthSend object and perform the
|
||||
# transaction. The current idea is to make all this reduce to something like:
|
||||
# var
|
||||
# w3 = initWeb3("127.0.0.1", 8545)
|
||||
# myContract = contract:
|
||||
# <DSL>
|
||||
# myContract.sender("0x780bc7b4055941c2cb0ee10510e3fc837eb093c1").sendCoin(
|
||||
# fromHex(Stuint[256], "e375b6fb6d0bf0d86707884f3952fee3977251fe"),
|
||||
# 600.to(Stuint[256])
|
||||
# )
|
||||
# If the address of the contract on the chain should be part of the DSL or
|
||||
# dynamically registered is still not decided.
|
||||
var cc: EthSend
|
||||
cc.source = [0x78.byte, 0x0b, 0xc7, 0xb4, 0x05, 0x59, 0x41, 0xc2, 0xcb, 0x0e, 0xe1, 0x05, 0x10, 0xe3, 0xfc, 0x83, 0x7e, 0xb0, 0x93, 0xc1]
|
||||
cc.to = some([0x0a.byte, 0x78, 0xc0, 0x8F, 0x31, 0x4E, 0xB2, 0x5A, 0x35, 0x1B, 0xfB, 0xA9, 0x03,0x21, 0xa6, 0x96, 0x04, 0x74, 0xbD, 0x79])
|
||||
cc.data = "0x90b98a11000000000000000000000000e375b6fb6d0bf0d86707884f3952fee3977251FE0000000000000000000000000000000000000000000000000000000000000258"
|
||||
|
||||
var w3 = initWeb3("127.0.0.1", 8545)
|
||||
let response = waitFor w3.eth.eth_sendTransaction(cc)
|
||||
echo response
|
|
@ -0,0 +1,16 @@
|
|||
# web3
|
||||
# Copyright (c) 2018 Status Research & Development GmbH
|
||||
# Licensed and distributed under either of
|
||||
# * MIT license: [LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT
|
||||
# * Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) 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.
|
||||
|
||||
import unittest,
|
||||
../src/web3
|
||||
|
||||
suite "Your first test suite":
|
||||
test "Your first test":
|
||||
block: # independant block of subtest
|
||||
discard
|
||||
block:
|
||||
discard
|
|
@ -0,0 +1,29 @@
|
|||
packageName = "web3"
|
||||
version = "0.0.1"
|
||||
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"
|
||||
srcDir = "src"
|
||||
bin = @["web3"]
|
||||
|
||||
### Dependencies
|
||||
requires "nim >= 0.18.0"
|
||||
requires "nimcrypto"
|
||||
requires "stint"
|
||||
requires "httputils"
|
||||
requires "chronicles"
|
||||
requires "asyncdispatch2"
|
||||
requires "json_rpc"
|
||||
|
||||
### Helper functions
|
||||
proc test(name: string, defaultLang = "c") =
|
||||
# TODO, don't forget to change defaultLang to `cpp` if the project requires C++
|
||||
if not dirExists "build":
|
||||
mkDir "build"
|
||||
--run
|
||||
switch("out", ("./build/" & name))
|
||||
setCommand defaultLang, "tests/" & name & ".nim"
|
||||
|
||||
### tasks
|
||||
task test, "Run all tests":
|
||||
test "all_tests"
|
Loading…
Reference in New Issue