feat: generalize wallet/private key + yubihsm2 (#75)
* feat: generalize wallet/private key * fix: adjust celo tests * YubiHSM2 Support (#76) * feat: support YubiHSM2
This commit is contained in:
parent
c65497543e
commit
aa37f74c4b
|
@ -10,6 +10,68 @@ dependencies = [
|
|||
"regex",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "addr2line"
|
||||
version = "0.13.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1b6a2d3371669ab3ca9797670853d61402b03d0b4b9ebf33d677dfa720203072"
|
||||
dependencies = [
|
||||
"gimli",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "adler"
|
||||
version = "0.2.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ee2a4ec343196209d6594e19543ae87a39f96d5534d7174822a3ad825dd6ed7e"
|
||||
|
||||
[[package]]
|
||||
name = "adler32"
|
||||
version = "1.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "aae1277d39aeec15cb388266ecc24b11c80469deae6067e17a1a7aa9e5c1f234"
|
||||
|
||||
[[package]]
|
||||
name = "aead"
|
||||
version = "0.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7fc95d1bdb8e6666b2b217308eeeb09f2d6728d104be3e31916cc74d15420331"
|
||||
dependencies = [
|
||||
"generic-array",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "aes"
|
||||
version = "0.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dd2bc6d3f370b5666245ff421e231cba4353df936e26986d2918e61a8fd6aef6"
|
||||
dependencies = [
|
||||
"aes-soft",
|
||||
"aesni",
|
||||
"block-cipher",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "aes-soft"
|
||||
version = "0.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "63dd91889c49327ad7ef3b500fd1109dbd3c509a03db0d4a9ce413b79f575cb6"
|
||||
dependencies = [
|
||||
"block-cipher",
|
||||
"byteorder",
|
||||
"opaque-debug",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "aesni"
|
||||
version = "0.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0a6fe808308bb07d393e2ea47780043ec47683fcf19cf5efc8ca51c50cc8c68a"
|
||||
dependencies = [
|
||||
"block-cipher",
|
||||
"opaque-debug",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "aho-corasick"
|
||||
version = "0.7.13"
|
||||
|
@ -19,6 +81,15 @@ dependencies = [
|
|||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anomaly"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "550632e31568ae1a5f47998c3aa48563030fc49b9ec91913ca337cf64fbc5ccb"
|
||||
dependencies = [
|
||||
"backtrace",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anyhow"
|
||||
version = "1.0.32"
|
||||
|
@ -64,7 +135,7 @@ version = "1.3.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d373d78ded7d0b3fa8039375718cde0aace493f2e34fb60f51cbf567562ca801"
|
||||
dependencies = [
|
||||
"async-task 4.0.2",
|
||||
"async-task",
|
||||
"concurrent-queue",
|
||||
"fastrand",
|
||||
"futures-lite",
|
||||
|
@ -74,9 +145,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "async-global-executor"
|
||||
version = "1.2.1"
|
||||
version = "1.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5586e693d02f9b439742e9d5d68bd64d923c6861954f7d78f91001a0e152d589"
|
||||
checksum = "fefeb39da249f4c33af940b779a56723ce45809ef5c54dad84bb538d4ffb6d9e"
|
||||
dependencies = [
|
||||
"async-executor",
|
||||
"async-io",
|
||||
|
@ -87,9 +158,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "async-io"
|
||||
version = "1.1.4"
|
||||
version = "1.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "33be191d05a54ec120e4667375e2ad49fe506b846463df384460ab801c7ae5dc"
|
||||
checksum = "6e727cebd055ab2861a854f79def078c4b99ea722d54c6800a0e274389882d4c"
|
||||
dependencies = [
|
||||
"concurrent-queue",
|
||||
"fastrand",
|
||||
|
@ -114,15 +185,14 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "async-std"
|
||||
version = "1.6.4"
|
||||
version = "1.6.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3c92085acfce8b32e5b261d0b59b8f3309aee69fea421ea3f271f8b93225754f"
|
||||
checksum = "a9fa76751505e8df1c7a77762f60486f60c71bbd9b8557f4da6ad47d083732ed"
|
||||
dependencies = [
|
||||
"async-attributes",
|
||||
"async-global-executor",
|
||||
"async-io",
|
||||
"async-mutex",
|
||||
"async-task 3.0.0",
|
||||
"blocking",
|
||||
"crossbeam-utils",
|
||||
"futures-channel",
|
||||
|
@ -141,12 +211,6 @@ dependencies = [
|
|||
"wasm-bindgen-futures",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "async-task"
|
||||
version = "3.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c17772156ef2829aadc587461c7753af20b7e8db1529bc66855add962a3b35d3"
|
||||
|
||||
[[package]]
|
||||
name = "async-task"
|
||||
version = "4.0.2"
|
||||
|
@ -167,9 +231,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "async-trait"
|
||||
version = "0.1.40"
|
||||
version = "0.1.41"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "687c230d85c0a52504709705fc8a53e4a692b83a2184f03dae73e38e1e93a783"
|
||||
checksum = "b246867b8b3b6ae56035f1eb1ed557c1d8eae97f0d53696138a50fa0e3a3b8c0"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
|
@ -206,6 +270,20 @@ version = "1.0.1"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a"
|
||||
|
||||
[[package]]
|
||||
name = "backtrace"
|
||||
version = "0.3.51"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ec1931848a574faa8f7c71a12ea00453ff5effbb5f51afe7f77d7a48cace6ac1"
|
||||
dependencies = [
|
||||
"addr2line",
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"miniz_oxide",
|
||||
"object",
|
||||
"rustc-demangle",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "base64"
|
||||
version = "0.11.0"
|
||||
|
@ -276,6 +354,25 @@ dependencies = [
|
|||
"generic-array",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "block-cipher"
|
||||
version = "0.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f337a3e6da609650eb74e02bc9fac7b735049f7623ab12f2e4c719316fcc7e80"
|
||||
dependencies = [
|
||||
"generic-array",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "block-modes"
|
||||
version = "0.6.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0c9b14fd8a4739e6548d4b6018696cf991dcf8c6effd9ef9eb33b29b8a650972"
|
||||
dependencies = [
|
||||
"block-cipher",
|
||||
"block-padding",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "block-padding"
|
||||
version = "0.2.1"
|
||||
|
@ -332,6 +429,17 @@ version = "1.0.60"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ef611cc68ff783f18535d77ddd080185275713d852c4f5cbb6122c462a7a825c"
|
||||
|
||||
[[package]]
|
||||
name = "ccm"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cbba800a6a55058ecb75c7a42e3d16a715a2b1f1afa9acf07365d6ab30d62ce1"
|
||||
dependencies = [
|
||||
"aead",
|
||||
"block-cipher",
|
||||
"subtle",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "0.1.10"
|
||||
|
@ -340,17 +448,28 @@ checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822"
|
|||
|
||||
[[package]]
|
||||
name = "chrono"
|
||||
version = "0.4.18"
|
||||
version = "0.4.19"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d021fddb7bd3e734370acfa4a83f34095571d8570c039f1420d77540f68d5772"
|
||||
checksum = "670ad68c9088c2a963aaa298cb369688cf3f9465ce5e2d4ca10e6e0098a1ce73"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"num-integer",
|
||||
"num-traits",
|
||||
"serde",
|
||||
"time",
|
||||
"winapi 0.3.9",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cmac"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5220604fe5c112e2851b00da795c72cbb71bf112f2cbd532bdcfb4106eeb320b"
|
||||
dependencies = [
|
||||
"crypto-mac",
|
||||
"dbl",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "coins-ledger"
|
||||
version = "0.1.0"
|
||||
|
@ -417,6 +536,15 @@ version = "0.1.2"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8aebca1129a03dc6dc2b127edd729435bbc4a37e1d5f4d7513165089ceb02634"
|
||||
|
||||
[[package]]
|
||||
name = "crc32fast"
|
||||
version = "1.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ba125de2af0df55319f41944744ad91c71113bf74a4646efff39afe1f6842db1"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-utils"
|
||||
version = "0.7.2"
|
||||
|
@ -440,6 +568,7 @@ version = "0.9.1"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "58bcd97a54c7ca5ce2f6eb16f6bede5b0ab5f0055fedc17d2f0b4466e21671ca"
|
||||
dependencies = [
|
||||
"block-cipher",
|
||||
"generic-array",
|
||||
"subtle",
|
||||
]
|
||||
|
@ -474,6 +603,28 @@ dependencies = [
|
|||
"winapi 0.3.9",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "curve25519-dalek"
|
||||
version = "3.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c8492de420e9e60bc9a1d66e2dbb91825390b738a388606600663fc529b4b307"
|
||||
dependencies = [
|
||||
"byteorder",
|
||||
"digest",
|
||||
"rand_core",
|
||||
"subtle",
|
||||
"zeroize",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "dbl"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2735145c3b9ba15f2d7a3ae8cdafcbc8c98a7bef7f62afe9d08bd99fbf7130de"
|
||||
dependencies = [
|
||||
"generic-array",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "digest"
|
||||
version = "0.9.0"
|
||||
|
@ -491,15 +642,38 @@ checksum = "134951f4028bdadb9b84baf4232681efbf277da25144b9b0ad65df75946c422b"
|
|||
|
||||
[[package]]
|
||||
name = "ecdsa"
|
||||
version = "0.8.2"
|
||||
version = "0.8.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4a7c278cc833033b4322122e05b99dbc5a2a2a9bb2c51534ff1f4899c7802d66"
|
||||
checksum = "765a6f9c0938fef990bb7af306fe1e534721bc52a1eb918cc938c5212d6e07eb"
|
||||
dependencies = [
|
||||
"elliptic-curve",
|
||||
"hmac",
|
||||
"signature",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ed25519"
|
||||
version = "1.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "07dfc993ea376e864fe29a4099a61ca0bb994c6d7745a61bf60ddb3d64e05237"
|
||||
dependencies = [
|
||||
"signature",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ed25519-dalek"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c762bae6dcaf24c4c84667b8579785430908723d5c889f469d76a41d59cc7a9d"
|
||||
dependencies = [
|
||||
"curve25519-dalek",
|
||||
"ed25519",
|
||||
"rand",
|
||||
"serde",
|
||||
"sha2",
|
||||
"zeroize",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "either"
|
||||
version = "1.6.1"
|
||||
|
@ -657,7 +831,6 @@ dependencies = [
|
|||
"rustc-hex",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"sha2",
|
||||
"thiserror",
|
||||
"tiny-keccak 2.0.2",
|
||||
]
|
||||
|
@ -712,14 +885,18 @@ version = "0.1.3"
|
|||
dependencies = [
|
||||
"async-trait",
|
||||
"coins-ledger",
|
||||
"elliptic-curve",
|
||||
"ethers",
|
||||
"ethers-core",
|
||||
"futures-util",
|
||||
"rand",
|
||||
"rustc-hex",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"sha2",
|
||||
"thiserror",
|
||||
"tokio",
|
||||
"yubihsm",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -745,6 +922,18 @@ dependencies = [
|
|||
"subtle",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "filetime"
|
||||
version = "0.2.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3ed85775dcc68644b5c950ac06a2b23768d3bc9390464151aaf27136998dcf9e"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"redox_syscall",
|
||||
"winapi 0.3.9",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fixed-hash"
|
||||
version = "0.6.1"
|
||||
|
@ -937,6 +1126,12 @@ dependencies = [
|
|||
"wasi 0.9.0+wasi-snapshot-preview1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "gimli"
|
||||
version = "0.22.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "aaf91faf136cb47367fa430cd46e37a788775e7fa104f8b4bcb3861dc389b724"
|
||||
|
||||
[[package]]
|
||||
name = "glob"
|
||||
version = "0.3.0"
|
||||
|
@ -987,10 +1182,16 @@ dependencies = [
|
|||
]
|
||||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
version = "0.9.0"
|
||||
name = "harp"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "00d63df3d41950fb462ed38308eea019113ad1508da725bbedcd0fa5a85ef5f7"
|
||||
checksum = "60bf12c625ed5e96f81609ae4377c34e9fa3e4d1fada392404322daeace511ab"
|
||||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
version = "0.9.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d7afe4a420e3fe79967a00898cc1f4db7c8a49a9333a29f8a4bd76a253d5cd04"
|
||||
|
||||
[[package]]
|
||||
name = "hermit-abi"
|
||||
|
@ -1184,12 +1385,14 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "k256"
|
||||
version = "0.5.5"
|
||||
source = "git+https://github.com/RustCrypto/elliptic-curves#8845cc09dc55ba3fa41ffa2750d799cf6a23d405"
|
||||
version = "0.5.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "be18b15cf00d98162fc37081a5098647f41168604d96174726fb1dff59c18a22"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"ecdsa",
|
||||
"elliptic-curve",
|
||||
"sha2",
|
||||
"sha3",
|
||||
]
|
||||
|
||||
|
@ -1226,9 +1429,41 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
|
|||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.77"
|
||||
version = "0.2.78"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f2f96b10ec2560088a8e76961b00d47107b3a625fecb76dedb29ee7ccbf98235"
|
||||
checksum = "aa7087f49d294270db4e1928fc110c976cd4b9e5a16348e0a1df09afa99e6c98"
|
||||
|
||||
[[package]]
|
||||
name = "libflate"
|
||||
version = "1.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e9bac9023e1db29c084f9f8cd9d3852e5e8fddf98fb47c4964a0ea4663d95949"
|
||||
dependencies = [
|
||||
"adler32",
|
||||
"crc32fast",
|
||||
"libflate_lz77",
|
||||
"rle-decode-fast",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libflate_lz77"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3286f09f7d4926fc486334f28d8d2e6ebe4f7f9994494b6dab27ddfad2c9b11b"
|
||||
|
||||
[[package]]
|
||||
name = "libusb1-sys"
|
||||
version = "0.4.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1f02e930161703cc97c0aab3a905feb9740db03a80910f31ab0f8fa309223f39"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"libc",
|
||||
"libflate",
|
||||
"pkg-config",
|
||||
"tar",
|
||||
"vcpkg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libz-sys"
|
||||
|
@ -1279,6 +1514,16 @@ dependencies = [
|
|||
"unicase",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "miniz_oxide"
|
||||
version = "0.4.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c60c0dfe32c10b43a144bad8fc83538c52f58302c92300ea7ec7bf7b38d5a7b9"
|
||||
dependencies = [
|
||||
"adler",
|
||||
"autocfg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "mio"
|
||||
version = "0.6.22"
|
||||
|
@ -1330,9 +1575,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "nb-connect"
|
||||
version = "1.0.0"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e847c76b390f44529c2071ef06d0b52fbb4bdb04cc8987a5cfa63954c000abca"
|
||||
checksum = "701f47aeb98466d0a7fea67e2c2f667c33efa1f2e4fd7f76743aac1153196f72"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"winapi 0.3.9",
|
||||
|
@ -1391,6 +1636,12 @@ dependencies = [
|
|||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "object"
|
||||
version = "0.20.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1ab52be62400ca80aa00285d25253d7f7c437b7375c4de678f5405d3afe82ca5"
|
||||
|
||||
[[package]]
|
||||
name = "once_cell"
|
||||
version = "1.4.1"
|
||||
|
@ -1436,6 +1687,27 @@ dependencies = [
|
|||
"vcpkg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "p256"
|
||||
version = "0.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1909a5a44a469f7d21c8dec2c634721ba15379b6cad80040d16abfaa8609b37c"
|
||||
dependencies = [
|
||||
"ecdsa",
|
||||
"elliptic-curve",
|
||||
"sha2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "p384"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "51ce2b1182d022bfbe2af57aeca44fe7832356dd2d998e98e423cd9b94fae4f7"
|
||||
dependencies = [
|
||||
"ecdsa",
|
||||
"elliptic-curve",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "parity-scale-codec"
|
||||
version = "1.3.5"
|
||||
|
@ -1454,6 +1726,15 @@ version = "2.0.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "427c3892f9e783d91cc128285287e70a59e206ca452770ece88a76f7a3eddd72"
|
||||
|
||||
[[package]]
|
||||
name = "pbkdf2"
|
||||
version = "0.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7170d73bf11f39b4ce1809aabc95bf5c33564cdc16fc3200ddda17a5f6e5e48b"
|
||||
dependencies = [
|
||||
"crypto-mac",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "percent-encoding"
|
||||
version = "2.1.0"
|
||||
|
@ -1462,18 +1743,18 @@ checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e"
|
|||
|
||||
[[package]]
|
||||
name = "pin-project"
|
||||
version = "0.4.24"
|
||||
version = "0.4.25"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f48fad7cfbff853437be7cf54d7b993af21f53be7f0988cbfe4a51535aa77205"
|
||||
checksum = "2b9e280448854bd91559252582173b3bd1f8e094a0e644791c0628ca9b1f144f"
|
||||
dependencies = [
|
||||
"pin-project-internal",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pin-project-internal"
|
||||
version = "0.4.24"
|
||||
version = "0.4.25"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "24c6d293bdd3ca5a1697997854c6cf7855e43fb6a0ba1c47af57a5bcafd158ae"
|
||||
checksum = "c8c8b352676bc6a4c3d71970560b913cea444a7a921cc2e2d920225e4b91edaa"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
|
@ -1482,9 +1763,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "pin-project-lite"
|
||||
version = "0.1.8"
|
||||
version = "0.1.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "71f349a4f0e70676ffb2dbafe16d0c992382d02f0a952e3ddf584fc289dac6b3"
|
||||
checksum = "e555d9e657502182ac97b539fb3dae8b79cda19e3e4f8ffb5e8de4f18df93c95"
|
||||
|
||||
[[package]]
|
||||
name = "pin-utils"
|
||||
|
@ -1544,9 +1825,9 @@ checksum = "eba180dafb9038b050a4c280019bbedf9f2467b61e5d892dcad585bb57aadc5a"
|
|||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.23"
|
||||
version = "1.0.24"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "51ef7cd2518ead700af67bf9d1a658d90b6037d77110fd9c0445429d0ba1c6c9"
|
||||
checksum = "1e0704ee1a7e00d7bb417d0770ea303c1bccbabf0ef1667dae92b5967f5f8a71"
|
||||
dependencies = [
|
||||
"unicode-xid",
|
||||
]
|
||||
|
@ -1699,14 +1980,36 @@ dependencies = [
|
|||
]
|
||||
|
||||
[[package]]
|
||||
name = "rlp"
|
||||
version = "0.4.5"
|
||||
name = "rle-decode-fast"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4a7d3f9bed94764eac15b8f14af59fac420c236adaff743b7bcc88e265cb4345"
|
||||
checksum = "cabe4fa914dec5870285fa7f71f602645da47c486e68486d2b4ceb4a343e90ac"
|
||||
|
||||
[[package]]
|
||||
name = "rlp"
|
||||
version = "0.4.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1190dcc8c3a512f1eef5d09bb8c84c7f39e1054e174d1795482e18f5272f2e73"
|
||||
dependencies = [
|
||||
"rustc-hex",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rusb"
|
||||
version = "0.6.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "67fa037368ee577fca9ef237c5ec129084c18e7e3e5987cc611fb8b2d78cf84a"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"libusb1-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustc-demangle"
|
||||
version = "0.1.16"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4c691c0e608126e00913e33f0ccf3727d5fc84573623b8d65b2df340b5201783"
|
||||
|
||||
[[package]]
|
||||
name = "rustc-hex"
|
||||
version = "2.1.0"
|
||||
|
@ -1822,9 +2125,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "serde_json"
|
||||
version = "1.0.57"
|
||||
version = "1.0.58"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "164eacbdb13512ec2745fb09d51fd5b22b0d65ed294a1dcf7285a360c80a675c"
|
||||
checksum = "a230ea9107ca2220eea9d46de97eddcb04cd00e92d13dda78e478dd33fa82bd4"
|
||||
dependencies = [
|
||||
"itoa",
|
||||
"ryu",
|
||||
|
@ -1889,6 +2192,19 @@ checksum = "29f060a7d147e33490ec10da418795238fd7545bba241504d6b31a409f2e6210"
|
|||
dependencies = [
|
||||
"digest",
|
||||
"rand_core",
|
||||
"signature_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "signature_derive"
|
||||
version = "1.0.0-pre.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b0656c180103507cca4b82519908e813225b2f6b90b2bd59ee119f46155ae872"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"synstructure",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -1938,6 +2254,30 @@ dependencies = [
|
|||
"unicode-xid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "synstructure"
|
||||
version = "0.12.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b834f2d66f734cb897113e34aaff2f1ab4719ca946f9a7358dba8f8064148701"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"unicode-xid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tar"
|
||||
version = "0.4.30"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "489997b7557e9a43e192c527face4feacc78bfbe6eed67fd55c4c9e381cba290"
|
||||
dependencies = [
|
||||
"filetime",
|
||||
"libc",
|
||||
"redox_syscall",
|
||||
"xattr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tempfile"
|
||||
version = "3.1.0"
|
||||
|
@ -2089,20 +2429,21 @@ checksum = "e987b6bf443f4b5b3b6f38704195592cca41c5bb7aedd3c3693c7081f8289860"
|
|||
|
||||
[[package]]
|
||||
name = "tracing"
|
||||
version = "0.1.19"
|
||||
version = "0.1.21"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6d79ca061b032d6ce30c660fded31189ca0b9922bf483cd70759f13a2d86786c"
|
||||
checksum = "b0987850db3733619253fe60e17cb59b82d37c7e6c0236bb81e4d6b87c879f27"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"log",
|
||||
"pin-project-lite",
|
||||
"tracing-core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tracing-core"
|
||||
version = "0.1.16"
|
||||
version = "0.1.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5bcf46c1f1f06aeea2d6b81f3c863d0930a596c86ad1920d4e5bad6dd1d7119a"
|
||||
checksum = "f50de3927f93d202783f4513cda820ab47ef17f624b03c096e86ef00c67e6b5f"
|
||||
dependencies = [
|
||||
"lazy_static",
|
||||
]
|
||||
|
@ -2207,6 +2548,15 @@ version = "0.7.5"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "05e42f7c18b8f902290b009cde6d651262f956c98bc51bca4cd1d511c9cd85c7"
|
||||
|
||||
[[package]]
|
||||
name = "uuid"
|
||||
version = "0.8.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9fde2f6a4bea1d6e007c4ad38c6839fa71cbb63b6dbf5b595aa38dc9b1093c11"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "vcpkg"
|
||||
version = "0.2.10"
|
||||
|
@ -2424,8 +2774,67 @@ version = "0.2.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "85e60b0d1b5f99db2556934e21937020776a5d31520bf169e851ac44e6420214"
|
||||
|
||||
[[package]]
|
||||
name = "xattr"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "244c3741f4240ef46274860397c7c74e50eb23624996930e484c16679633a54c"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "yubihsm"
|
||||
version = "0.35.0-rc"
|
||||
source = "git+https://github.com/iqlusioninc/yubihsm.rs.git?branch=develop#a8fa868fbc50b8a326c46ab2b178f42e3e7a4e88"
|
||||
dependencies = [
|
||||
"aes",
|
||||
"anomaly",
|
||||
"bitflags",
|
||||
"block-modes",
|
||||
"ccm",
|
||||
"chrono",
|
||||
"cmac",
|
||||
"digest",
|
||||
"ecdsa",
|
||||
"ed25519",
|
||||
"ed25519-dalek",
|
||||
"harp",
|
||||
"hmac",
|
||||
"k256",
|
||||
"log",
|
||||
"p256",
|
||||
"p384",
|
||||
"pbkdf2",
|
||||
"rand_core",
|
||||
"rusb",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"sha2",
|
||||
"signature",
|
||||
"subtle",
|
||||
"thiserror",
|
||||
"uuid",
|
||||
"zeroize",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zeroize"
|
||||
version = "1.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "05f33972566adbd2d3588b0491eb94b98b43695c4ef897903470ede4f3f5a28a"
|
||||
dependencies = [
|
||||
"zeroize_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zeroize_derive"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c3f369ddb18862aba61aa49bf31e74d29f0f162dec753063200e1dc084345d16"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"synstructure",
|
||||
]
|
||||
|
|
|
@ -7,7 +7,7 @@ use ethers_contract::{Contract, ContractFactory};
|
|||
use ethers_core::utils::{GanacheInstance, Solc};
|
||||
use ethers_middleware::Client;
|
||||
use ethers_providers::{Http, Middleware, Provider};
|
||||
use ethers_signers::Wallet;
|
||||
use ethers_signers::LocalWallet;
|
||||
use std::{convert::TryFrom, sync::Arc, time::Duration};
|
||||
|
||||
// Note: We also provide the `abigen` macro for generating these bindings automatically
|
||||
|
@ -44,14 +44,14 @@ pub fn compile_contract(name: &str, filename: &str) -> (Abi, Bytes) {
|
|||
(contract.abi.clone(), contract.bytecode.clone())
|
||||
}
|
||||
|
||||
type HttpWallet = Client<Provider<Http>, Wallet>;
|
||||
type HttpWallet = Client<Provider<Http>, LocalWallet>;
|
||||
|
||||
/// connects the private key to http://localhost:8545
|
||||
pub fn connect(ganache: &GanacheInstance, idx: usize) -> Arc<HttpWallet> {
|
||||
let provider = Provider::<Http>::try_from(ganache.endpoint())
|
||||
.unwrap()
|
||||
.interval(Duration::from_millis(10u64));
|
||||
let wallet: Wallet = ganache.keys()[idx].clone().into();
|
||||
let wallet: LocalWallet = ganache.keys()[idx].clone().into();
|
||||
Arc::new(Client::new(provider, wallet))
|
||||
}
|
||||
|
||||
|
|
|
@ -322,14 +322,15 @@ mod eth_tests {
|
|||
assert_eq!(return_data.2, multicall_contract.address());
|
||||
assert_eq!(return_data.3, multicall_contract.address());
|
||||
|
||||
let addrs = ganache.addresses();
|
||||
// query ETH balances of multiple addresses
|
||||
// these keys haven't been used to do any tx
|
||||
// so should have 100 ETH
|
||||
multicall
|
||||
.clear_calls()
|
||||
.eth_balance_of(Address::from(&ganache.keys()[4]))
|
||||
.eth_balance_of(Address::from(&ganache.keys()[5]))
|
||||
.eth_balance_of(Address::from(&ganache.keys()[6]));
|
||||
.eth_balance_of(addrs[4])
|
||||
.eth_balance_of(addrs[5])
|
||||
.eth_balance_of(addrs[6]);
|
||||
let balances: (U256, U256, U256) = multicall.call().await.unwrap();
|
||||
assert_eq!(balances.0, U256::from(100000000000000000000u128));
|
||||
assert_eq!(balances.1, U256::from(100000000000000000000u128));
|
||||
|
@ -343,7 +344,7 @@ mod celo_tests {
|
|||
use ethers::{
|
||||
middleware::Client,
|
||||
providers::{Http, Provider},
|
||||
signers::Wallet,
|
||||
signers::LocalWallet,
|
||||
types::BlockNumber,
|
||||
};
|
||||
use std::{convert::TryFrom, sync::Arc, time::Duration};
|
||||
|
@ -359,7 +360,7 @@ mod celo_tests {
|
|||
|
||||
// Funded with https://celo.org/developers/faucet
|
||||
let wallet = "d652abb81e8c686edba621a895531b1f291289b63b5ef09a94f686a5ecdd5db1"
|
||||
.parse::<Wallet>()
|
||||
.parse::<LocalWallet>()
|
||||
.unwrap();
|
||||
|
||||
let client = Client::new(provider, wallet);
|
||||
|
|
|
@ -20,10 +20,9 @@ arrayvec = { version = "0.5.1", default-features = false }
|
|||
ecdsa = { version = "0.8.0", features = ["std"] }
|
||||
elliptic-curve = { version = "0.6.1", features = ["arithmetic"] }
|
||||
generic-array = "0.14.4"
|
||||
k256 = { git = "https://github.com/RustCrypto/elliptic-curves", version = "0.5.2", features = ["keccak256", "ecdsa"] }
|
||||
k256 = { version = "0.5.2", features = ["keccak256", "ecdsa"] }
|
||||
rand = "0.7.2"
|
||||
tiny-keccak = { version = "2.0.2", default-features = false }
|
||||
sha2 = { version = "0.9.1" }
|
||||
|
||||
# misc
|
||||
serde = { version = "1.0.110", default-features = false, features = ["derive"] }
|
||||
|
|
|
@ -13,19 +13,21 @@
|
|||
//! signing the hash of the result.
|
||||
//!
|
||||
//! ```rust
|
||||
//! use ethers::core::types::{PrivateKey, Address};
|
||||
//! # async fn foo() -> Result<(), Box<dyn std::error::Error>> {
|
||||
//! use ethers::signers::{Signer, LocalWallet};
|
||||
//!
|
||||
//! let message = "Some data";
|
||||
//! let key = PrivateKey::new(&mut rand::thread_rng());
|
||||
//! let address = Address::from(&key);
|
||||
//! let wallet = LocalWallet::new(&mut rand::thread_rng());
|
||||
//!
|
||||
//! // Sign the message
|
||||
//! let signature = key.sign(message);
|
||||
//! let signature = wallet.sign_message(message).await?;
|
||||
//!
|
||||
//! // Recover the signer from the message
|
||||
//! let recovered = signature.recover(message).unwrap();
|
||||
//! let recovered = signature.recover(message)?;
|
||||
//!
|
||||
//! assert_eq!(recovered, address);
|
||||
//! assert_eq!(recovered, wallet.address());
|
||||
//! # Ok(())
|
||||
//! # }
|
||||
//! ```
|
||||
//!
|
||||
//! ## Utilities
|
||||
|
|
|
@ -1,26 +0,0 @@
|
|||
pub type Selector = [u8; 4];
|
||||
|
||||
// Re-export common ethereum datatypes with more specific names
|
||||
|
||||
/// A transaction Hash
|
||||
pub use ethereum_types::H256 as TxHash;
|
||||
|
||||
pub use ethereum_types::{Address, Bloom, H160, H256, U128, U256, U64};
|
||||
|
||||
mod transaction;
|
||||
pub use transaction::{Transaction, TransactionReceipt, TransactionRequest};
|
||||
|
||||
mod bytes;
|
||||
pub use bytes::Bytes;
|
||||
|
||||
mod block;
|
||||
pub use block::{Block, BlockId, BlockNumber};
|
||||
|
||||
#[cfg(feature = "celo")]
|
||||
pub use block::Randomness;
|
||||
|
||||
mod log;
|
||||
pub use log::{Filter, Log, ValueOrArray};
|
||||
|
||||
mod ens;
|
||||
pub use ens::NameOrAddress;
|
|
@ -1,348 +0,0 @@
|
|||
use super::hash::Sha256Proxy;
|
||||
use crate::{
|
||||
types::{Address, Signature, TransactionRequest, H256},
|
||||
utils::{hash_message, keccak256},
|
||||
};
|
||||
|
||||
use rand::{CryptoRng, Rng};
|
||||
use rustc_hex::FromHex;
|
||||
use serde::{
|
||||
de::Error as DeserializeError,
|
||||
de::{SeqAccess, Visitor},
|
||||
ser::SerializeTuple,
|
||||
Deserialize, Deserializer, Serialize, Serializer,
|
||||
};
|
||||
use std::{fmt, ops::Deref, str::FromStr};
|
||||
|
||||
use k256::{
|
||||
ecdsa::{
|
||||
recoverable::{Id as RecoveryId, Signature as RecoverableSignature},
|
||||
signature::DigestSigner,
|
||||
SigningKey,
|
||||
},
|
||||
elliptic_curve::{error::Error as EllipticCurveError, FieldBytes},
|
||||
EncodedPoint as K256PublicKey, Secp256k1, SecretKey as K256SecretKey,
|
||||
};
|
||||
|
||||
const SECRET_KEY_SIZE: usize = 32;
|
||||
const COMPRESSED_PUBLIC_KEY_SIZE: usize = 33;
|
||||
|
||||
/// A private key on Secp256k1
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct PrivateKey(pub(super) K256SecretKey);
|
||||
|
||||
impl PartialEq for PrivateKey {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.0.to_bytes().eq(&other.0.to_bytes())
|
||||
}
|
||||
}
|
||||
|
||||
impl Serialize for PrivateKey {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer,
|
||||
{
|
||||
let mut seq = serializer.serialize_tuple(SECRET_KEY_SIZE)?;
|
||||
for e in self.0.to_bytes() {
|
||||
seq.serialize_element(&e)?;
|
||||
}
|
||||
seq.end()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de> Deserialize<'de> for PrivateKey {
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
let bytes = <[u8; SECRET_KEY_SIZE]>::deserialize(deserializer)?;
|
||||
Ok(PrivateKey(
|
||||
K256SecretKey::from_bytes(&bytes).map_err(DeserializeError::custom)?,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
impl FromStr for PrivateKey {
|
||||
type Err = EllipticCurveError;
|
||||
|
||||
fn from_str(src: &str) -> Result<PrivateKey, Self::Err> {
|
||||
let src = src
|
||||
.from_hex::<Vec<u8>>()
|
||||
.expect("invalid hex when reading PrivateKey");
|
||||
let sk = K256SecretKey::from_bytes(&src)?;
|
||||
Ok(PrivateKey(sk))
|
||||
}
|
||||
}
|
||||
|
||||
impl PrivateKey {
|
||||
pub fn new<R: Rng + CryptoRng>(rng: &mut R) -> Self {
|
||||
PrivateKey(K256SecretKey::random(rng))
|
||||
}
|
||||
|
||||
/// Sign arbitrary string data.
|
||||
///
|
||||
/// The data is UTF-8 encoded and enveloped the same way as with
|
||||
/// `hash_message`. The returned signed data's signature is in 'Electrum'
|
||||
/// notation, that is the recovery value `v` is either `27` or `28` (as
|
||||
/// opposed to the standard notation where `v` is either `0` or `1`). This
|
||||
/// is important to consider when using this signature with other crates.
|
||||
pub fn sign<S>(&self, message: S) -> Signature
|
||||
where
|
||||
S: AsRef<[u8]>,
|
||||
{
|
||||
let message = message.as_ref();
|
||||
let message_hash = hash_message(message);
|
||||
|
||||
self.sign_hash_with_eip155(message_hash, None)
|
||||
}
|
||||
|
||||
/// RLP encodes and then signs the stransaction.
|
||||
///
|
||||
/// If no chain_id is provided, then EIP-155 is not used.
|
||||
///
|
||||
/// This will return an error if called if any of the `nonce`, `gas_price` or `gas`
|
||||
/// fields are not populated.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// If `tx.to` is an ENS name. The caller MUST take care of name resolution before
|
||||
/// calling this function.
|
||||
pub fn sign_transaction(&self, tx: &TransactionRequest, chain_id: Option<u64>) -> Signature {
|
||||
let sighash = tx.sighash(chain_id);
|
||||
self.sign_hash_with_eip155(sighash, chain_id)
|
||||
}
|
||||
|
||||
fn sign_hash_with_eip155(&self, hash: H256, chain_id: Option<u64>) -> Signature {
|
||||
let signing_key = SigningKey::new(&self.0.to_bytes()).expect("invalid secret key");
|
||||
|
||||
let recoverable_sig: RecoverableSignature =
|
||||
signing_key.sign_digest(Sha256Proxy::from(hash));
|
||||
|
||||
let v = to_eip155_v(recoverable_sig.recovery_id(), chain_id);
|
||||
|
||||
let r_bytes: FieldBytes<Secp256k1> = recoverable_sig.r().into();
|
||||
let s_bytes: FieldBytes<Secp256k1> = recoverable_sig.s().into();
|
||||
let r = H256::from_slice(&r_bytes.as_slice());
|
||||
let s = H256::from_slice(&s_bytes.as_slice());
|
||||
|
||||
Signature { r, s, v }
|
||||
}
|
||||
}
|
||||
|
||||
/// Applies [EIP155](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-155.md)
|
||||
fn to_eip155_v(recovery_id: RecoveryId, chain_id: Option<u64>) -> u64 {
|
||||
let standard_v: u8 = recovery_id.into();
|
||||
if let Some(chain_id) = chain_id {
|
||||
// When signing with a chain ID, add chain replay protection.
|
||||
(standard_v as u64) + 35 + chain_id * 2
|
||||
} else {
|
||||
// Otherwise, convert to 'Electrum' notation.
|
||||
(standard_v as u64) + 27
|
||||
}
|
||||
}
|
||||
|
||||
impl Deref for PrivateKey {
|
||||
type Target = K256SecretKey;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
/// A secp256k1 Public Key
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub struct PublicKey(pub(super) K256PublicKey);
|
||||
|
||||
impl From<K256PublicKey> for PublicKey {
|
||||
/// Gets the public address of a private key.
|
||||
fn from(src: K256PublicKey) -> PublicKey {
|
||||
PublicKey(src)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&PrivateKey> for PublicKey {
|
||||
/// Gets the public address of a private key.
|
||||
fn from(src: &PrivateKey) -> PublicKey {
|
||||
let public_key = K256PublicKey::from_secret_key(src, false);
|
||||
PublicKey(public_key)
|
||||
}
|
||||
}
|
||||
|
||||
/// Gets the address of a public key.
|
||||
///
|
||||
/// The public address is defined as the low 20 bytes of the keccak hash of
|
||||
/// the public key. Note that the public key returned from the `secp256k1`
|
||||
/// crate is 65 bytes long, that is because it is prefixed by `0x04` to
|
||||
/// indicate an uncompressed public key; this first byte is ignored when
|
||||
/// computing the hash.
|
||||
impl From<&PublicKey> for Address {
|
||||
fn from(src: &PublicKey) -> Address {
|
||||
let public_key = src.0.as_bytes();
|
||||
|
||||
debug_assert_eq!(public_key[0], 0x04);
|
||||
let hash = keccak256(&public_key[1..]);
|
||||
|
||||
Address::from_slice(&hash[12..])
|
||||
}
|
||||
}
|
||||
|
||||
impl From<PublicKey> for Address {
|
||||
fn from(src: PublicKey) -> Address {
|
||||
Address::from(&src)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&PrivateKey> for Address {
|
||||
fn from(src: &PrivateKey) -> Address {
|
||||
let public_key = PublicKey::from(src);
|
||||
Address::from(&public_key)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<PrivateKey> for Address {
|
||||
fn from(src: PrivateKey) -> Address {
|
||||
Address::from(&src)
|
||||
}
|
||||
}
|
||||
|
||||
impl Serialize for PublicKey {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer,
|
||||
{
|
||||
let mut seq = serializer.serialize_tuple(COMPRESSED_PUBLIC_KEY_SIZE)?;
|
||||
|
||||
for e in self.0.compress().as_bytes().iter() {
|
||||
seq.serialize_element(e)?;
|
||||
}
|
||||
seq.end()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de> Deserialize<'de> for PublicKey {
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
struct ArrayVisitor;
|
||||
|
||||
impl<'de> Visitor<'de> for ArrayVisitor {
|
||||
type Value = PublicKey;
|
||||
|
||||
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
|
||||
formatter.write_str("a valid proof")
|
||||
}
|
||||
|
||||
fn visit_seq<S>(self, mut seq: S) -> Result<PublicKey, S::Error>
|
||||
where
|
||||
S: SeqAccess<'de>,
|
||||
{
|
||||
let mut bytes = [0u8; COMPRESSED_PUBLIC_KEY_SIZE];
|
||||
for b in &mut bytes[..] {
|
||||
*b = seq
|
||||
.next_element()?
|
||||
.ok_or_else(|| DeserializeError::custom("could not read bytes"))?;
|
||||
}
|
||||
|
||||
let pub_key = K256PublicKey::from_bytes(&bytes[..])
|
||||
.map_err(|_| DeserializeError::custom("parse pub key"))?;
|
||||
|
||||
let uncompressed_pub_key = pub_key.decompress();
|
||||
if uncompressed_pub_key.is_some().into() {
|
||||
Ok(PublicKey(uncompressed_pub_key.unwrap()))
|
||||
} else {
|
||||
Err(DeserializeError::custom("parse pub key"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
deserializer.deserialize_tuple(COMPRESSED_PUBLIC_KEY_SIZE, ArrayVisitor)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn serde() {
|
||||
for _ in 0..10 {
|
||||
let key = PrivateKey::new(&mut rand::thread_rng());
|
||||
let serialized = bincode::serialize(&key).unwrap();
|
||||
assert_eq!(serialized.as_slice(), key.0.to_bytes().as_slice());
|
||||
let de: PrivateKey = bincode::deserialize(&serialized).unwrap();
|
||||
assert_eq!(key, de);
|
||||
|
||||
let public = PublicKey::from(&key);
|
||||
println!("public = {:?}", public);
|
||||
|
||||
let serialized = bincode::serialize(&public).unwrap();
|
||||
let de: PublicKey = bincode::deserialize(&serialized).unwrap();
|
||||
assert_eq!(public, de);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(not(feature = "celo"))]
|
||||
fn signs_tx() {
|
||||
use crate::types::Address;
|
||||
// retrieved test vector from:
|
||||
// https://web3js.readthedocs.io/en/v1.2.0/web3-eth-accounts.html#eth-accounts-signtransaction
|
||||
let tx = TransactionRequest {
|
||||
from: None,
|
||||
to: Some(
|
||||
"F0109fC8DF283027b6285cc889F5aA624EaC1F55"
|
||||
.parse::<Address>()
|
||||
.unwrap()
|
||||
.into(),
|
||||
),
|
||||
value: Some(1_000_000_000.into()),
|
||||
gas: Some(2_000_000.into()),
|
||||
nonce: Some(0.into()),
|
||||
gas_price: Some(21_000_000_000u128.into()),
|
||||
data: None,
|
||||
};
|
||||
let chain_id = 1;
|
||||
|
||||
let key: PrivateKey = "4c0883a69102937d6231471b5dbb6204fe5129617082792ae468d01a3f362318"
|
||||
.parse()
|
||||
.unwrap();
|
||||
|
||||
let sig = key.sign_transaction(&tx, Some(chain_id));
|
||||
let sighash = tx.sighash(Some(chain_id));
|
||||
assert!(sig.verify(sighash, Address::from(key)).is_ok());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn key_to_address() {
|
||||
let priv_key: PrivateKey =
|
||||
"0000000000000000000000000000000000000000000000000000000000000001"
|
||||
.parse()
|
||||
.unwrap();
|
||||
let addr: Address = priv_key.into();
|
||||
assert_eq!(
|
||||
addr,
|
||||
Address::from_str("7E5F4552091A69125d5DfCb7b8C2659029395Bdf").expect("Decoding failed")
|
||||
);
|
||||
|
||||
let priv_key: PrivateKey =
|
||||
"0000000000000000000000000000000000000000000000000000000000000002"
|
||||
.parse()
|
||||
.unwrap();
|
||||
let addr: Address = priv_key.into();
|
||||
assert_eq!(
|
||||
addr,
|
||||
Address::from_str("2B5AD5c4795c026514f8317c7a215E218DcCD6cF").expect("Decoding failed")
|
||||
);
|
||||
|
||||
let priv_key: PrivateKey =
|
||||
"0000000000000000000000000000000000000000000000000000000000000003"
|
||||
.parse()
|
||||
.unwrap();
|
||||
let addr: Address = priv_key.into();
|
||||
assert_eq!(
|
||||
addr,
|
||||
Address::from_str("6813Eb9362372EEF6200f3b1dbC3f819671cBA69").expect("Decoding failed")
|
||||
);
|
||||
}
|
||||
}
|
|
@ -5,3 +5,4 @@ mod signature;
|
|||
pub use signature::Signature;
|
||||
|
||||
mod hash;
|
||||
pub use hash::Sha256Proxy;
|
||||
|
|
|
@ -1,6 +1,29 @@
|
|||
//! Ethereum related datatypes
|
||||
mod crypto;
|
||||
pub use crypto::*;
|
||||
pub type Selector = [u8; 4];
|
||||
|
||||
mod chainstate;
|
||||
pub use chainstate::*;
|
||||
// Re-export common ethereum datatypes with more specific names
|
||||
|
||||
/// A transaction Hash
|
||||
pub use ethereum_types::H256 as TxHash;
|
||||
|
||||
pub use ethereum_types::{Address, Bloom, H160, H256, U128, U256, U64};
|
||||
|
||||
mod transaction;
|
||||
pub use transaction::{Transaction, TransactionReceipt, TransactionRequest};
|
||||
|
||||
mod bytes;
|
||||
pub use bytes::Bytes;
|
||||
|
||||
mod block;
|
||||
pub use block::{Block, BlockId, BlockNumber};
|
||||
|
||||
#[cfg(feature = "celo")]
|
||||
pub use block::Randomness;
|
||||
|
||||
mod log;
|
||||
pub use log::{Filter, Log, ValueOrArray};
|
||||
|
||||
mod ens;
|
||||
pub use ens::NameOrAddress;
|
||||
|
||||
mod signature;
|
||||
pub use signature::*;
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
// Code adapted from: https://github.com/tomusdrw/rust-web3/blob/master/src/api/accounts.rs
|
||||
use crate::{
|
||||
types::{Address, PublicKey, H256},
|
||||
types::{Address, H256},
|
||||
utils::hash_message,
|
||||
};
|
||||
|
||||
|
@ -106,7 +106,10 @@ impl Signature {
|
|||
|
||||
let uncompressed_pub_key = K256PublicKey::from(&verify_key).decompress();
|
||||
if uncompressed_pub_key.is_some().into() {
|
||||
Ok(PublicKey::from(uncompressed_pub_key.unwrap()).into())
|
||||
let public_key = uncompressed_pub_key.unwrap().to_bytes();
|
||||
debug_assert_eq!(public_key[0], 0x04);
|
||||
let hash = crate::utils::keccak256(&public_key[1..]);
|
||||
Ok(Address::from_slice(&hash[12..]))
|
||||
} else {
|
||||
Err(SignatureError::RecoveryError)
|
||||
}
|
||||
|
@ -245,55 +248,17 @@ impl From<H256> for RecoveryMessage {
|
|||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::types::PrivateKey;
|
||||
|
||||
#[test]
|
||||
fn recover_signature_from_message() {
|
||||
let message = "Some data";
|
||||
let hash = hash_message(message);
|
||||
let key = PrivateKey::new(&mut rand::thread_rng());
|
||||
let address = Address::from(&key);
|
||||
|
||||
// sign a message
|
||||
let signature = key.sign(message);
|
||||
|
||||
// ecrecover via the message will hash internally
|
||||
let recovered = signature.recover(message).unwrap();
|
||||
|
||||
// if provided with a hash, it will skip hashing
|
||||
let recovered2 = signature.recover(hash).unwrap();
|
||||
|
||||
// verifies the signature is produced by `address`
|
||||
signature.verify(message, address).unwrap();
|
||||
|
||||
assert_eq!(recovered, address);
|
||||
assert_eq!(recovered2, address);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn recover_web3_signature() {
|
||||
// test vector taken from:
|
||||
// https://web3js.readthedocs.io/en/v1.2.2/web3-eth-accounts.html#sign
|
||||
let key: PrivateKey = "4c0883a69102937d6231471b5dbb6204fe5129617082792ae468d01a3f362318"
|
||||
.parse()
|
||||
.unwrap();
|
||||
let address = Address::from(&key);
|
||||
let our_signature = key.sign("Some data");
|
||||
let signature = Signature::from_str(
|
||||
"b91467e570a6466aa9e9876cbcd013baba02900b8979d43fe208a4a4f339f5fd6007e74cd82e037b800186422fc2da167c747ef045e5d18a5f5d4300f8e1a0291c"
|
||||
).expect("could not parse signature");
|
||||
assert_eq!(our_signature.recover("Some data").unwrap(), address,);
|
||||
assert_eq!(signature.recover("Some data").unwrap(), address);
|
||||
assert_eq!(our_signature, signature);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn to_vec() {
|
||||
let message = "Some data";
|
||||
let key = PrivateKey::new(&mut rand::thread_rng());
|
||||
let signature = key.sign(message);
|
||||
let serialized = signature.to_vec();
|
||||
let de = Signature::try_from(&serialized[..]).unwrap();
|
||||
assert_eq!(signature, de);
|
||||
assert_eq!(
|
||||
signature.recover("Some data").unwrap(),
|
||||
Address::from_str("2c7536E3605D9C16a7a3D7b1898e529396a65c23").unwrap()
|
||||
);
|
||||
}
|
||||
}
|
|
@ -1,4 +1,6 @@
|
|||
use crate::types::PrivateKey;
|
||||
use crate::{types::Address, utils::secret_key_to_address};
|
||||
use k256::{ecdsa::SigningKey, SecretKey as K256SecretKey};
|
||||
use rustc_hex::FromHex;
|
||||
use std::{
|
||||
io::{BufRead, BufReader},
|
||||
net::TcpListener,
|
||||
|
@ -14,16 +16,22 @@ const GANACHE_STARTUP_TIMEOUT_MILLIS: u64 = 10_000;
|
|||
/// Construct this using [`Ganache`](crate::utils::Ganache)
|
||||
pub struct GanacheInstance {
|
||||
pid: Child,
|
||||
private_keys: Vec<PrivateKey>,
|
||||
private_keys: Vec<K256SecretKey>,
|
||||
addresses: Vec<Address>,
|
||||
port: u16,
|
||||
}
|
||||
|
||||
impl GanacheInstance {
|
||||
/// Returns the private keys used to instantiate this instance
|
||||
pub fn keys(&self) -> &[PrivateKey] {
|
||||
pub fn keys(&self) -> &[K256SecretKey] {
|
||||
&self.private_keys
|
||||
}
|
||||
|
||||
/// Returns the addresses used to instantiate this instance
|
||||
pub fn addresses(&self) -> &[Address] {
|
||||
&self.addresses
|
||||
}
|
||||
|
||||
/// Returns the port of this instance
|
||||
pub fn port(&self) -> u16 {
|
||||
self.port
|
||||
|
@ -130,6 +138,7 @@ impl Ganache {
|
|||
let mut reader = BufReader::new(stdout);
|
||||
|
||||
let mut private_keys = Vec::new();
|
||||
let mut addresses = Vec::new();
|
||||
let mut is_private_key = false;
|
||||
loop {
|
||||
if start + Duration::from_millis(GANACHE_STARTUP_TIMEOUT_MILLIS) <= Instant::now() {
|
||||
|
@ -150,7 +159,11 @@ impl Ganache {
|
|||
|
||||
if is_private_key && line.starts_with('(') {
|
||||
let key_str = &line[6..line.len() - 1];
|
||||
let key: PrivateKey = key_str.parse().expect("did not get private key");
|
||||
let key_hex = key_str
|
||||
.from_hex::<Vec<u8>>()
|
||||
.expect("could not parse as hex");
|
||||
let key = K256SecretKey::from_bytes(&key_hex).expect("did not get private key");
|
||||
addresses.push(secret_key_to_address(&SigningKey::from(&key)));
|
||||
private_keys.push(key);
|
||||
}
|
||||
}
|
||||
|
@ -160,6 +173,7 @@ impl Ganache {
|
|||
GanacheInstance {
|
||||
pid: child,
|
||||
private_keys,
|
||||
addresses,
|
||||
port,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,6 +18,7 @@ pub use hash::{hash_message, id, keccak256, serialize};
|
|||
pub use rlp;
|
||||
|
||||
use crate::types::{Address, Bytes, U256};
|
||||
use k256::{ecdsa::SigningKey, EncodedPoint as K256PublicKey};
|
||||
use std::convert::TryInto;
|
||||
|
||||
/// 1 Ether = 1e18 Wei == 0x0de0b6b3a7640000 Wei
|
||||
|
@ -100,6 +101,16 @@ pub fn get_create2_address(
|
|||
Address::from(bytes)
|
||||
}
|
||||
|
||||
/// Converts a K256 SigningKey to an Ethereum Address
|
||||
pub fn secret_key_to_address(secret_key: &SigningKey) -> Address {
|
||||
// TODO: Can we do this in a better way?
|
||||
let uncompressed_pub_key = K256PublicKey::from(&secret_key.verify_key()).decompress();
|
||||
let public_key = uncompressed_pub_key.unwrap().to_bytes();
|
||||
debug_assert_eq!(public_key[0], 0x04);
|
||||
let hash = keccak256(&public_key[1..]);
|
||||
Address::from_slice(&hash[12..])
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
use ethers_signers::Signer;
|
||||
|
||||
use ethers_core::{
|
||||
types::{
|
||||
Address, BlockNumber, Bytes, NameOrAddress, Signature, Transaction, TransactionRequest,
|
||||
|
@ -7,7 +5,8 @@ use ethers_core::{
|
|||
},
|
||||
utils::keccak256,
|
||||
};
|
||||
use ethers_providers::Middleware;
|
||||
use ethers_providers::{FromErr, Middleware};
|
||||
use ethers_signers::Signer;
|
||||
|
||||
use async_trait::async_trait;
|
||||
use futures_util::{future::ok, join};
|
||||
|
@ -23,7 +22,7 @@ use thiserror::Error;
|
|||
/// ```no_run
|
||||
/// use ethers::{
|
||||
/// providers::{Middleware, Provider, Http},
|
||||
/// signers::Wallet,
|
||||
/// signers::LocalWallet,
|
||||
/// middleware::Client,
|
||||
/// types::{Address, TransactionRequest},
|
||||
/// };
|
||||
|
@ -35,7 +34,7 @@ use thiserror::Error;
|
|||
///
|
||||
/// // Transactions will be signed with the private key below and will be broadcast
|
||||
/// // via the eth_sendRawTransaction API)
|
||||
/// let wallet: Wallet = "380eb0f3d505f087e438eca80bc4df9a7faa24f868e69fc0440261a0fc0567dc"
|
||||
/// let wallet: LocalWallet = "380eb0f3d505f087e438eca80bc4df9a7faa24f868e69fc0440261a0fc0567dc"
|
||||
/// .parse()?;
|
||||
///
|
||||
/// let mut client = Client::new(provider, wallet);
|
||||
|
@ -55,7 +54,7 @@ use thiserror::Error;
|
|||
/// let receipt = client.pending_transaction(tx_hash).confirmations(6).await?;
|
||||
///
|
||||
/// // You can connect with other wallets at runtime via the `with_signer` function
|
||||
/// let wallet2: Wallet = "cd8c407233c0560f6de24bb2dc60a8b02335c959a1a17f749ce6c1ccf63d74a7"
|
||||
/// let wallet2: LocalWallet = "cd8c407233c0560f6de24bb2dc60a8b02335c959a1a17f749ce6c1ccf63d74a7"
|
||||
/// .parse()?;
|
||||
///
|
||||
/// let signed_msg2 = client.with_signer(wallet2).sign(b"hello".to_vec(), &client.address()).await?;
|
||||
|
@ -78,8 +77,6 @@ pub struct Client<M, S> {
|
|||
pub(crate) address: Address,
|
||||
}
|
||||
|
||||
use ethers_providers::FromErr;
|
||||
|
||||
impl<M: Middleware, S: Signer> FromErr<M::Error> for ClientError<M, S> {
|
||||
fn from(src: M::Error) -> ClientError<M, S> {
|
||||
ClientError::MiddlewareError(src)
|
||||
|
@ -300,7 +297,7 @@ where
|
|||
#[cfg(all(test, not(feature = "celo")))]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use ethers::{providers::Provider, signers::Wallet};
|
||||
use ethers::{providers::Provider, signers::LocalWallet};
|
||||
use rustc_hex::FromHex;
|
||||
use std::convert::TryFrom;
|
||||
|
||||
|
@ -326,7 +323,7 @@ mod tests {
|
|||
|
||||
let provider = Provider::try_from("http://localhost:8545").unwrap();
|
||||
let key = "4c0883a69102937d6231471b5dbb6204fe5129617082792ae468d01a3f362318"
|
||||
.parse::<Wallet>()
|
||||
.parse::<LocalWallet>()
|
||||
.unwrap()
|
||||
.set_chain_id(chain_id);
|
||||
let client = Client::new(provider, key);
|
||||
|
|
|
@ -9,7 +9,7 @@ use std::convert::TryFrom;
|
|||
async fn using_gas_oracle() {
|
||||
let ganache = Ganache::new().spawn();
|
||||
|
||||
let from = Address::from(ganache.keys()[0].clone());
|
||||
let from = ganache.addresses()[0];
|
||||
|
||||
// connect to the network
|
||||
let provider = Provider::<Http>::try_from(ganache.endpoint()).unwrap();
|
||||
|
|
|
@ -4,7 +4,7 @@ async fn nonce_manager() {
|
|||
use ethers_core::types::*;
|
||||
use ethers_middleware::{Client, NonceManager};
|
||||
use ethers_providers::{Http, Middleware, Provider};
|
||||
use ethers_signers::Wallet;
|
||||
use ethers_signers::LocalWallet;
|
||||
use std::convert::TryFrom;
|
||||
use std::time::Duration;
|
||||
|
||||
|
@ -14,7 +14,7 @@ async fn nonce_manager() {
|
|||
.interval(Duration::from_millis(2000u64));
|
||||
|
||||
let wallet = "59c37cb6b16fa2de30675f034c8008f890f4b2696c729d6267946d29736d73e4"
|
||||
.parse::<Wallet>()
|
||||
.parse::<LocalWallet>()
|
||||
.unwrap();
|
||||
let address = wallet.address();
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@ use ethers_providers::{Http, Middleware, Provider};
|
|||
|
||||
use ethers_core::types::TransactionRequest;
|
||||
use ethers_middleware::Client;
|
||||
use ethers_signers::Wallet;
|
||||
use ethers_signers::LocalWallet;
|
||||
use std::{convert::TryFrom, time::Duration};
|
||||
|
||||
#[tokio::test]
|
||||
|
@ -13,8 +13,8 @@ async fn send_eth() {
|
|||
let ganache = Ganache::new().spawn();
|
||||
|
||||
// this private key belongs to the above mnemonic
|
||||
let wallet: Wallet = ganache.keys()[0].clone().into();
|
||||
let wallet2: Wallet = ganache.keys()[1].clone().into();
|
||||
let wallet: LocalWallet = ganache.keys()[0].clone().into();
|
||||
let wallet2: LocalWallet = ganache.keys()[1].clone().into();
|
||||
|
||||
// connect to the network
|
||||
let provider = Provider::<Http>::try_from(ganache.endpoint())
|
||||
|
@ -52,7 +52,7 @@ async fn test_send_transaction() {
|
|||
// Funded with https://celo.org/developers/faucet
|
||||
// Please do not drain this account :)
|
||||
let wallet = "d652abb81e8c686edba621a895531b1f291289b63b5ef09a94f686a5ecdd5db1"
|
||||
.parse::<Wallet>()
|
||||
.parse::<LocalWallet>()
|
||||
.unwrap();
|
||||
let client = Client::new(provider, wallet);
|
||||
|
||||
|
|
|
@ -7,12 +7,12 @@ async fn can_stack_middlewares() {
|
|||
Client, GasOracleMiddleware, NonceManager,
|
||||
};
|
||||
use ethers_providers::{Http, Middleware, Provider};
|
||||
use ethers_signers::Wallet;
|
||||
use ethers_signers::LocalWallet;
|
||||
use std::convert::TryFrom;
|
||||
|
||||
let ganache = Ganache::new().block_time(5u64).spawn();
|
||||
let gas_oracle = GasNow::new().category(GasCategory::SafeLow);
|
||||
let signer: Wallet = ganache.keys()[0].clone().into();
|
||||
let signer: LocalWallet = ganache.keys()[0].clone().into();
|
||||
let address = signer.address();
|
||||
|
||||
// the base provider
|
||||
|
|
|
@ -20,15 +20,23 @@ futures-util = { version = "0.3.5", default-features = false }
|
|||
serde = { version = "1.0.112", default-features = false }
|
||||
|
||||
coins-ledger = { git = "https://github.com/summa-tx/bitcoins-rs", optional = true }
|
||||
rustc-hex = { version = "2.1.0", optional = true }
|
||||
rustc-hex = { version = "2.1.0" }
|
||||
async-trait = "0.1.40"
|
||||
elliptic-curve = { version = "0.6.1", features = ["arithmetic"] }
|
||||
sha2 = { version = "0.9.1" }
|
||||
rand = "0.7.3"
|
||||
|
||||
# todo: update to latest once published
|
||||
yubihsm = { git = "https://github.com/iqlusioninc/yubihsm.rs.git", branch = "develop", features = ["secp256k1", "http", "usb"], optional = true }
|
||||
|
||||
[dev-dependencies]
|
||||
ethers = { version = "0.1.3", path = "../ethers" }
|
||||
yubihsm = { git = "https://github.com/iqlusioninc/yubihsm.rs.git", branch = "develop", features = ["secp256k1", "usb", "mockhsm"] }
|
||||
|
||||
tokio = { version = "0.2.21", features = ["macros"] }
|
||||
serde_json = "1.0.55"
|
||||
|
||||
[features]
|
||||
celo = ["ethers-core/celo"]
|
||||
ledger = ["coins-ledger", "rustc-hex"]
|
||||
ledger = ["coins-ledger"]
|
||||
yubi = ["yubihsm"]
|
||||
|
|
|
@ -159,7 +159,7 @@ impl LedgerEthereum {
|
|||
let mut result = Vec::new();
|
||||
|
||||
// Iterate in 255 byte chunks
|
||||
while payload.len() > 0 {
|
||||
while !payload.is_empty() {
|
||||
let chunk_size = std::cmp::min(payload.len(), 255);
|
||||
let data = payload.drain(0..chunk_size).collect::<Vec<_>>();
|
||||
command.data = APDUData::new(&data);
|
||||
|
@ -188,10 +188,10 @@ impl LedgerEthereum {
|
|||
|
||||
let mut bytes = vec![depth as u8];
|
||||
for derivation_index in elements {
|
||||
let hardened = derivation_index.contains("'");
|
||||
let hardened = derivation_index.contains('\'');
|
||||
let mut index = derivation_index.replace("'", "").parse::<u32>().unwrap();
|
||||
if hardened {
|
||||
index = 0x80000000 | index;
|
||||
index |= 0x80000000;
|
||||
}
|
||||
|
||||
bytes.extend(&index.to_be_bytes());
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
//! Helpers for interacting with the Ethereum Ledger App
|
||||
//! [Official Docs](https://github.com/LedgerHQ/app-ethereum/blob/master/doc/ethapp.asc)
|
||||
use std::fmt;
|
||||
use thiserror::Error;
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
|
@ -9,13 +10,17 @@ pub enum DerivationType {
|
|||
Other(String),
|
||||
}
|
||||
|
||||
impl DerivationType {
|
||||
pub fn to_string(&self) -> String {
|
||||
impl fmt::Display for DerivationType {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
|
||||
write!(
|
||||
f,
|
||||
"{}",
|
||||
match self {
|
||||
DerivationType::Legacy(index) => format!("m/44'/60'/0'/{}", index),
|
||||
DerivationType::LedgerLive(index) => format!("m/44'/60'/{}'/0/0", index),
|
||||
DerivationType::Other(inner) => inner.to_owned(),
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -12,13 +12,13 @@
|
|||
//!
|
||||
//! ```no_run
|
||||
//! # use ethers::{
|
||||
//! signers::{Wallet, Signer},
|
||||
//! core::types::TransactionRequest
|
||||
//! signers::{LocalWallet, Signer},
|
||||
//! core::{k256::ecdsa::SigningKey, types::TransactionRequest},
|
||||
//! };
|
||||
//! # async fn foo() -> Result<(), Box<dyn std::error::Error>> {
|
||||
//! // instantiate the wallet
|
||||
//! let wallet = "dcf2cbdd171a21c480aa7f53d77f31bb102282b3ff099c78e3118b37348c72f7"
|
||||
//! .parse::<Wallet>()?;
|
||||
//! .parse::<LocalWallet>()?;
|
||||
//!
|
||||
//! // create a transaction
|
||||
//! let tx = TransactionRequest::new()
|
||||
|
@ -33,9 +33,18 @@
|
|||
//! signature.verify("hello world", wallet.address()).unwrap();
|
||||
//! # Ok(())
|
||||
//! # }
|
||||
// mod wallet;
|
||||
// pub use wallet::Wallet;
|
||||
mod wallet;
|
||||
pub use wallet::Wallet;
|
||||
|
||||
/// A wallet instantiated with a locally stored private key
|
||||
pub type LocalWallet = Wallet<ethers_core::k256::ecdsa::SigningKey>;
|
||||
|
||||
#[cfg(feature = "yubi")]
|
||||
/// A wallet instantiated with a YubiHSM
|
||||
pub type YubiWallet = Wallet<yubihsm::ecdsa::Signer<ethers_core::k256::Secp256k1>>;
|
||||
|
||||
#[cfg(feature = "ledger")]
|
||||
mod ledger;
|
||||
#[cfg(feature = "ledger")]
|
||||
|
@ -44,6 +53,9 @@ pub use ledger::{
|
|||
types::{DerivationType as HDPath, LedgerError},
|
||||
};
|
||||
|
||||
#[cfg(feature = "yubi")]
|
||||
pub use yubihsm;
|
||||
|
||||
use async_trait::async_trait;
|
||||
use ethers_core::types::{Address, Signature, TransactionRequest};
|
||||
use std::error::Error;
|
||||
|
@ -52,7 +64,7 @@ use std::error::Error;
|
|||
///
|
||||
/// Implement this trait to support different signing modes, e.g. Ledger, hosted etc.
|
||||
#[async_trait(?Send)]
|
||||
pub trait Signer: Send + Sync + std::fmt::Debug {
|
||||
pub trait Signer: std::fmt::Debug + Send + Sync {
|
||||
type Error: Error + Send + Sync;
|
||||
/// Signs the hash of the provided message after prefixing it
|
||||
async fn sign_message<S: Send + Sync + AsRef<[u8]>>(
|
||||
|
|
|
@ -1,141 +0,0 @@
|
|||
use crate::Signer;
|
||||
|
||||
use ethers_core::{
|
||||
k256::elliptic_curve::error::Error as K256Error,
|
||||
rand::{CryptoRng, Rng},
|
||||
types::{Address, PrivateKey, PublicKey, Signature, TransactionRequest},
|
||||
};
|
||||
|
||||
use async_trait::async_trait;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::str::FromStr;
|
||||
|
||||
/// An Ethereum private-public key pair which can be used for signing messages.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ## Signing and Verifying a message
|
||||
///
|
||||
/// The wallet can be used to produce ECDSA [`Signature`] objects, which can be
|
||||
/// then verified. Note that this uses [`hash_message`] under the hood which will
|
||||
/// prefix the message being hashed with the `Ethereum Signed Message` domain separator.
|
||||
///
|
||||
/// ```
|
||||
/// use ethers_core::rand::thread_rng;
|
||||
/// use ethers_signers::{Wallet, Signer};
|
||||
///
|
||||
/// # async fn foo() -> Result<(), Box<dyn std::error::Error>> {
|
||||
/// let wallet = Wallet::new(&mut thread_rng());
|
||||
///
|
||||
/// // Optionally, the wallet's chain id can be set, in order to use EIP-155
|
||||
/// // replay protection with different chains
|
||||
/// let wallet = wallet.set_chain_id(1337u64);
|
||||
///
|
||||
/// // The wallet can be used to sign messages
|
||||
/// let message = b"hello";
|
||||
/// let signature = wallet.sign_message(message).await?;
|
||||
/// assert_eq!(signature.recover(&message[..]).unwrap(), wallet.address());
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
///
|
||||
/// [`Signature`]: ethers_core::types::Signature
|
||||
/// [`hash_message`]: fn@ethers_core::utils::hash_message
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
pub struct Wallet {
|
||||
/// The Wallet's private Key
|
||||
private_key: PrivateKey,
|
||||
/// The Wallet's public Key
|
||||
public_key: PublicKey,
|
||||
/// The wallet's address
|
||||
address: Address,
|
||||
/// The wallet's chain id (for EIP-155), signs w/o replay protection if left unset
|
||||
chain_id: Option<u64>,
|
||||
}
|
||||
|
||||
#[async_trait(?Send)]
|
||||
impl Signer for Wallet {
|
||||
type Error = std::convert::Infallible;
|
||||
|
||||
async fn sign_message<S: Send + Sync + AsRef<[u8]>>(
|
||||
&self,
|
||||
message: S,
|
||||
) -> Result<Signature, Self::Error> {
|
||||
Ok(self.private_key.sign(message))
|
||||
}
|
||||
|
||||
async fn sign_transaction(&self, tx: &TransactionRequest) -> Result<Signature, Self::Error> {
|
||||
Ok(self.private_key.sign_transaction(tx, self.chain_id))
|
||||
}
|
||||
|
||||
fn address(&self) -> Address {
|
||||
self.address
|
||||
}
|
||||
}
|
||||
|
||||
impl Wallet {
|
||||
// TODO: Add support for mnemonic and encrypted JSON
|
||||
|
||||
/// Creates a new random keypair seeded with the provided RNG
|
||||
pub fn new<R: Rng + CryptoRng>(rng: &mut R) -> Self {
|
||||
let private_key = PrivateKey::new(rng);
|
||||
let public_key = PublicKey::from(&private_key);
|
||||
let address = Address::from(&private_key);
|
||||
|
||||
Self {
|
||||
private_key,
|
||||
public_key,
|
||||
address,
|
||||
chain_id: None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Sets the wallet's chain_id, used in conjunction with EIP-155 signing
|
||||
pub fn set_chain_id<T: Into<u64>>(mut self, chain_id: T) -> Self {
|
||||
self.chain_id = Some(chain_id.into());
|
||||
self
|
||||
}
|
||||
|
||||
/// Gets the wallet's public key
|
||||
pub fn public_key(&self) -> &PublicKey {
|
||||
&self.public_key
|
||||
}
|
||||
|
||||
/// Gets the wallet's private key
|
||||
pub fn private_key(&self) -> &PrivateKey {
|
||||
&self.private_key
|
||||
}
|
||||
|
||||
/// Gets the wallet's chain id
|
||||
pub fn chain_id(&self) -> Option<u64> {
|
||||
self.chain_id
|
||||
}
|
||||
|
||||
/// Returns the wallet's address
|
||||
// (we duplicate this method
|
||||
pub fn address(&self) -> Address {
|
||||
self.address
|
||||
}
|
||||
}
|
||||
|
||||
impl From<PrivateKey> for Wallet {
|
||||
fn from(private_key: PrivateKey) -> Self {
|
||||
let public_key = PublicKey::from(&private_key);
|
||||
let address = Address::from(&private_key);
|
||||
|
||||
Self {
|
||||
private_key,
|
||||
public_key,
|
||||
address,
|
||||
chain_id: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl FromStr for Wallet {
|
||||
type Err = K256Error;
|
||||
|
||||
fn from_str(src: &str) -> Result<Self, Self::Err> {
|
||||
Ok(PrivateKey::from_str(src)?.into())
|
||||
}
|
||||
}
|
|
@ -1,9 +1,11 @@
|
|||
//! This is a helper module used to pass the pre-hashed message for signing to the
|
||||
//! `sign_digest` methods of K256.
|
||||
use crate::types::H256;
|
||||
use elliptic_curve::consts::U64;
|
||||
use k256::ecdsa::signature::digest::{
|
||||
use ethers_core::{
|
||||
k256::ecdsa::signature::digest::{
|
||||
generic_array::GenericArray, BlockInput, Digest, FixedOutput, Output, Reset, Update,
|
||||
},
|
||||
types::H256,
|
||||
};
|
||||
|
||||
pub type Sha256Proxy = ProxyDigest<sha2::Sha256>;
|
|
@ -0,0 +1,146 @@
|
|||
mod hash;
|
||||
mod private_key;
|
||||
|
||||
#[cfg(feature = "yubihsm")]
|
||||
mod yubi;
|
||||
|
||||
use crate::Signer;
|
||||
use ethers_core::{
|
||||
k256::{
|
||||
ecdsa::{
|
||||
recoverable::{Id as RecoveryId, Signature as RecoverableSignature},
|
||||
signature::DigestSigner,
|
||||
},
|
||||
elliptic_curve::FieldBytes,
|
||||
Secp256k1,
|
||||
},
|
||||
types::{Address, Signature, TransactionRequest, H256},
|
||||
utils::hash_message,
|
||||
};
|
||||
use hash::Sha256Proxy;
|
||||
|
||||
use async_trait::async_trait;
|
||||
use std::fmt;
|
||||
|
||||
/// An Ethereum private-public key pair which can be used for signing messages.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ## Signing and Verifying a message
|
||||
///
|
||||
/// The wallet can be used to produce ECDSA [`Signature`] objects, which can be
|
||||
/// then verified. Note that this uses [`hash_message`] under the hood which will
|
||||
/// prefix the message being hashed with the `Ethereum Signed Message` domain separator.
|
||||
///
|
||||
/// ```
|
||||
/// use ethers_core::rand::thread_rng;
|
||||
/// use ethers_signers::{LocalWallet, Signer};
|
||||
///
|
||||
/// # async fn foo() -> Result<(), Box<dyn std::error::Error>> {
|
||||
/// let wallet = LocalWallet::new(&mut thread_rng());
|
||||
///
|
||||
/// // Optionally, the wallet's chain id can be set, in order to use EIP-155
|
||||
/// // replay protection with different chains
|
||||
/// let wallet = wallet.set_chain_id(1337u64);
|
||||
///
|
||||
/// // The wallet can be used to sign messages
|
||||
/// let message = b"hello";
|
||||
/// let signature = wallet.sign_message(message).await?;
|
||||
/// assert_eq!(signature.recover(&message[..]).unwrap(), wallet.address());
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
///
|
||||
/// [`Signature`]: ethers_core::types::Signature
|
||||
/// [`hash_message`]: fn@ethers_core::utils::hash_message
|
||||
pub struct Wallet<D: DigestSigner<Sha256Proxy, RecoverableSignature>> {
|
||||
/// The Wallet's private Key
|
||||
pub(crate) signer: D,
|
||||
/// The wallet's address
|
||||
pub(crate) address: Address,
|
||||
/// The wallet's chain id (for EIP-155), signs w/o replay protection if left unset
|
||||
pub(crate) chain_id: Option<u64>,
|
||||
}
|
||||
|
||||
#[async_trait(?Send)]
|
||||
impl<D: Sync + Send + DigestSigner<Sha256Proxy, RecoverableSignature>> Signer for Wallet<D> {
|
||||
type Error = std::convert::Infallible;
|
||||
|
||||
async fn sign_message<S: Send + Sync + AsRef<[u8]>>(
|
||||
&self,
|
||||
message: S,
|
||||
) -> Result<Signature, Self::Error> {
|
||||
let message = message.as_ref();
|
||||
let message_hash = hash_message(message);
|
||||
|
||||
Ok(self.sign_hash_with_eip155(message_hash, None))
|
||||
}
|
||||
|
||||
async fn sign_transaction(&self, tx: &TransactionRequest) -> Result<Signature, Self::Error> {
|
||||
let sighash = tx.sighash(self.chain_id);
|
||||
Ok(self.sign_hash_with_eip155(sighash, self.chain_id))
|
||||
}
|
||||
|
||||
fn address(&self) -> Address {
|
||||
self.address
|
||||
}
|
||||
}
|
||||
|
||||
impl<D: DigestSigner<Sha256Proxy, RecoverableSignature>> Wallet<D> {
|
||||
fn sign_hash_with_eip155(&self, hash: H256, chain_id: Option<u64>) -> Signature {
|
||||
let recoverable_sig: RecoverableSignature =
|
||||
self.signer.sign_digest(Sha256Proxy::from(hash));
|
||||
|
||||
let v = to_eip155_v(recoverable_sig.recovery_id(), chain_id);
|
||||
|
||||
let r_bytes: FieldBytes<Secp256k1> = recoverable_sig.r().into();
|
||||
let s_bytes: FieldBytes<Secp256k1> = recoverable_sig.s().into();
|
||||
let r = H256::from_slice(&r_bytes.as_slice());
|
||||
let s = H256::from_slice(&s_bytes.as_slice());
|
||||
|
||||
Signature { r, s, v }
|
||||
}
|
||||
|
||||
/// Sets the wallet's chain_id, used in conjunction with EIP-155 signing
|
||||
pub fn set_chain_id<T: Into<u64>>(mut self, chain_id: T) -> Self {
|
||||
self.chain_id = Some(chain_id.into());
|
||||
self
|
||||
}
|
||||
|
||||
/// Gets the wallet's signer
|
||||
pub fn signer(&self) -> &D {
|
||||
&self.signer
|
||||
}
|
||||
|
||||
/// Gets the wallet's chain id
|
||||
pub fn chain_id(&self) -> Option<u64> {
|
||||
self.chain_id
|
||||
}
|
||||
|
||||
/// Returns the wallet's address
|
||||
pub fn address(&self) -> Address {
|
||||
self.address
|
||||
}
|
||||
}
|
||||
|
||||
/// Applies [EIP155](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-155.md)
|
||||
fn to_eip155_v(recovery_id: RecoveryId, chain_id: Option<u64>) -> u64 {
|
||||
let standard_v: u8 = recovery_id.into();
|
||||
if let Some(chain_id) = chain_id {
|
||||
// When signing with a chain ID, add chain replay protection.
|
||||
(standard_v as u64) + 35 + chain_id * 2
|
||||
} else {
|
||||
// Otherwise, convert to 'Electrum' notation.
|
||||
(standard_v as u64) + 27
|
||||
}
|
||||
}
|
||||
|
||||
// do not log the signer
|
||||
impl<D: DigestSigner<Sha256Proxy, RecoverableSignature>> fmt::Debug for Wallet<D> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_struct("Wallet")
|
||||
.field("address", &self.address)
|
||||
.field("chain_Id", &self.chain_id)
|
||||
.finish()
|
||||
}
|
||||
}
|
|
@ -0,0 +1,188 @@
|
|||
//! Specific helper functions for loading an offline K256 Private Key stored on disk
|
||||
use super::Wallet;
|
||||
|
||||
use ethers_core::{
|
||||
k256::{
|
||||
ecdsa::SigningKey, elliptic_curve::error::Error as K256Error, EncodedPoint as K256PublicKey,
|
||||
},
|
||||
rand::{CryptoRng, Rng},
|
||||
types::Address,
|
||||
utils::keccak256,
|
||||
};
|
||||
use rustc_hex::FromHex;
|
||||
use std::str::FromStr;
|
||||
|
||||
impl Clone for Wallet<SigningKey> {
|
||||
fn clone(&self) -> Self {
|
||||
Self {
|
||||
// TODO: Can we have a better way to clone here?
|
||||
signer: SigningKey::new(&*self.signer.to_bytes()).unwrap(),
|
||||
address: self.address,
|
||||
chain_id: self.chain_id,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Wallet<SigningKey> {
|
||||
// TODO: Add support for mnemonic and encrypted JSON
|
||||
|
||||
/// Creates a new random keypair seeded with the provided RNG
|
||||
pub fn new<R: Rng + CryptoRng>(rng: &mut R) -> Self {
|
||||
let signer = SigningKey::random(rng);
|
||||
let address = key_to_address(&signer);
|
||||
Self {
|
||||
signer,
|
||||
address,
|
||||
chain_id: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn key_to_address(secret_key: &SigningKey) -> Address {
|
||||
// TODO: Can we do this in a better way?
|
||||
let uncompressed_pub_key = K256PublicKey::from(&secret_key.verify_key()).decompress();
|
||||
let public_key = uncompressed_pub_key.unwrap().to_bytes();
|
||||
debug_assert_eq!(public_key[0], 0x04);
|
||||
let hash = keccak256(&public_key[1..]);
|
||||
Address::from_slice(&hash[12..])
|
||||
}
|
||||
|
||||
impl PartialEq for Wallet<SigningKey> {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.signer.to_bytes().eq(&other.signer.to_bytes())
|
||||
&& self.address == other.address
|
||||
&& self.chain_id == other.chain_id
|
||||
}
|
||||
}
|
||||
|
||||
impl From<SigningKey> for Wallet<SigningKey> {
|
||||
fn from(signer: SigningKey) -> Self {
|
||||
let address = key_to_address(&signer);
|
||||
|
||||
Self {
|
||||
signer,
|
||||
address,
|
||||
chain_id: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
use ethers_core::k256::SecretKey as K256SecretKey;
|
||||
|
||||
impl From<K256SecretKey> for Wallet<SigningKey> {
|
||||
fn from(key: K256SecretKey) -> Self {
|
||||
let signer = SigningKey::new(&*key.to_bytes())
|
||||
.expect("private key should always be convertible to signing key");
|
||||
let address = key_to_address(&signer);
|
||||
|
||||
Self {
|
||||
signer,
|
||||
address,
|
||||
chain_id: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl FromStr for Wallet<SigningKey> {
|
||||
type Err = K256Error;
|
||||
|
||||
fn from_str(src: &str) -> Result<Self, Self::Err> {
|
||||
let src = src
|
||||
.from_hex::<Vec<u8>>()
|
||||
.expect("invalid hex when reading PrivateKey");
|
||||
let sk = SigningKey::new(&src).unwrap(); // TODO
|
||||
Ok(sk.into())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::Signer;
|
||||
|
||||
#[tokio::test]
|
||||
async fn signs_msg() {
|
||||
let message = "Some data";
|
||||
let hash = ethers_core::utils::hash_message(message);
|
||||
let key = Wallet::<SigningKey>::new(&mut rand::thread_rng());
|
||||
let address = key.address;
|
||||
|
||||
// sign a message
|
||||
let signature = key.sign_message(message).await.unwrap();
|
||||
|
||||
// ecrecover via the message will hash internally
|
||||
let recovered = signature.recover(message).unwrap();
|
||||
|
||||
// if provided with a hash, it will skip hashing
|
||||
let recovered2 = signature.recover(hash).unwrap();
|
||||
|
||||
// verifies the signature is produced by `address`
|
||||
signature.verify(message, address).unwrap();
|
||||
|
||||
assert_eq!(recovered, address);
|
||||
assert_eq!(recovered2, address);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
#[cfg(not(feature = "celo"))]
|
||||
async fn signs_tx() {
|
||||
use ethers_core::types::TransactionRequest;
|
||||
// retrieved test vector from:
|
||||
// https://web3js.readthedocs.io/en/v1.2.0/web3-eth-accounts.html#eth-accounts-signtransaction
|
||||
let tx = TransactionRequest {
|
||||
from: None,
|
||||
to: Some(
|
||||
"F0109fC8DF283027b6285cc889F5aA624EaC1F55"
|
||||
.parse::<Address>()
|
||||
.unwrap()
|
||||
.into(),
|
||||
),
|
||||
value: Some(1_000_000_000.into()),
|
||||
gas: Some(2_000_000.into()),
|
||||
nonce: Some(0.into()),
|
||||
gas_price: Some(21_000_000_000u128.into()),
|
||||
data: None,
|
||||
};
|
||||
let chain_id = 1u64;
|
||||
|
||||
let wallet: Wallet<SigningKey> =
|
||||
"4c0883a69102937d6231471b5dbb6204fe5129617082792ae468d01a3f362318"
|
||||
.parse()
|
||||
.unwrap();
|
||||
let wallet = wallet.set_chain_id(chain_id);
|
||||
|
||||
let sig = wallet.sign_transaction(&tx).await.unwrap();
|
||||
let sighash = tx.sighash(Some(chain_id));
|
||||
assert!(sig.verify(sighash, wallet.address).is_ok());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn key_to_address() {
|
||||
let wallet: Wallet<SigningKey> =
|
||||
"0000000000000000000000000000000000000000000000000000000000000001"
|
||||
.parse()
|
||||
.unwrap();
|
||||
assert_eq!(
|
||||
wallet.address,
|
||||
Address::from_str("7E5F4552091A69125d5DfCb7b8C2659029395Bdf").expect("Decoding failed")
|
||||
);
|
||||
|
||||
let wallet: Wallet<SigningKey> =
|
||||
"0000000000000000000000000000000000000000000000000000000000000002"
|
||||
.parse()
|
||||
.unwrap();
|
||||
assert_eq!(
|
||||
wallet.address,
|
||||
Address::from_str("2B5AD5c4795c026514f8317c7a215E218DcCD6cF").expect("Decoding failed")
|
||||
);
|
||||
|
||||
let wallet: Wallet<SigningKey> =
|
||||
"0000000000000000000000000000000000000000000000000000000000000003"
|
||||
.parse()
|
||||
.unwrap();
|
||||
assert_eq!(
|
||||
wallet.address,
|
||||
Address::from_str("6813Eb9362372EEF6200f3b1dbC3f819671cBA69").expect("Decoding failed")
|
||||
);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,113 @@
|
|||
//! Helpers for creating wallets for YubiHSM2
|
||||
use super::Wallet;
|
||||
use ethers_core::{k256::Secp256k1, types::Address, utils::keccak256};
|
||||
use yubihsm::{
|
||||
asymmetric::Algorithm::EcK256, ecdsa::Signer as YubiSigner, object, object::Label, Capability,
|
||||
Client, Connector, Credentials, Domain,
|
||||
};
|
||||
|
||||
impl Wallet<YubiSigner<Secp256k1>> {
|
||||
/// Connects to a yubi key's ECDSA account at the provided id
|
||||
pub fn connect(connector: Connector, credentials: Credentials, id: object::Id) -> Self {
|
||||
let client = Client::open(connector, credentials, true).unwrap();
|
||||
let signer = YubiSigner::create(client, id).unwrap();
|
||||
signer.into()
|
||||
}
|
||||
|
||||
/// Creates a new random ECDSA keypair on the yubi at the provided id
|
||||
pub fn new(
|
||||
connector: Connector,
|
||||
credentials: Credentials,
|
||||
id: object::Id,
|
||||
label: Label,
|
||||
domain: Domain,
|
||||
) -> Self {
|
||||
let client = Client::open(connector, credentials, true).unwrap();
|
||||
let id = client
|
||||
.generate_asymmetric_key(id, label, domain, Capability::SIGN_ECDSA, EcK256)
|
||||
.unwrap();
|
||||
let signer = YubiSigner::create(client, id).unwrap();
|
||||
signer.into()
|
||||
}
|
||||
|
||||
/// Uploads the provided keypair on the yubi at the provided id
|
||||
pub fn from_key(
|
||||
connector: Connector,
|
||||
credentials: Credentials,
|
||||
id: object::Id,
|
||||
label: Label,
|
||||
domain: Domain,
|
||||
key: impl Into<Vec<u8>>,
|
||||
) -> Self {
|
||||
let client = Client::open(connector, credentials, true).unwrap();
|
||||
let id = client
|
||||
.put_asymmetric_key(id, label, domain, Capability::SIGN_ECDSA, EcK256, key)
|
||||
.unwrap();
|
||||
let signer = YubiSigner::create(client, id).unwrap();
|
||||
signer.into()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<YubiSigner<Secp256k1>> for Wallet<YubiSigner<Secp256k1>> {
|
||||
fn from(signer: YubiSigner<Secp256k1>) -> Self {
|
||||
let public_key = signer.public_key().decompress().unwrap().to_bytes();
|
||||
debug_assert_eq!(public_key[0], 0x04);
|
||||
let hash = keccak256(&public_key[1..]);
|
||||
let address = Address::from_slice(&hash[12..]);
|
||||
|
||||
Self {
|
||||
signer,
|
||||
address,
|
||||
chain_id: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::Signer;
|
||||
use rustc_hex::FromHex;
|
||||
use std::str::FromStr;
|
||||
|
||||
#[tokio::test]
|
||||
async fn from_key() {
|
||||
let key = "2d8c44dc2dd2f0bea410e342885379192381e82d855b1b112f9b55544f1e0900"
|
||||
.from_hex::<Vec<u8>>()
|
||||
.unwrap();
|
||||
|
||||
let connector = yubihsm::Connector::mockhsm();
|
||||
let wallet = Wallet::from_key(
|
||||
connector,
|
||||
Credentials::default(),
|
||||
0,
|
||||
Label::from_bytes(&[]).unwrap(),
|
||||
Domain::at(1).unwrap(),
|
||||
key,
|
||||
);
|
||||
|
||||
let msg = "Some data";
|
||||
let sig = wallet.sign_message(msg).await.unwrap();
|
||||
assert_eq!(sig.recover(msg).unwrap(), wallet.address());
|
||||
assert_eq!(
|
||||
wallet.address(),
|
||||
Address::from_str("2DE2C386082Cff9b28D62E60983856CE1139eC49").unwrap()
|
||||
);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn new_key() {
|
||||
let connector = yubihsm::Connector::mockhsm();
|
||||
let wallet = Wallet::<YubiSigner<Secp256k1>>::new(
|
||||
connector,
|
||||
Credentials::default(),
|
||||
0,
|
||||
Label::from_bytes(&[]).unwrap(),
|
||||
Domain::at(1).unwrap(),
|
||||
);
|
||||
|
||||
let msg = "Some data";
|
||||
let sig = wallet.sign_message(msg).await.unwrap();
|
||||
assert_eq!(sig.recover(msg).unwrap(), wallet.address());
|
||||
}
|
||||
}
|
|
@ -44,6 +44,7 @@ providers = ["ethers-providers"]
|
|||
middleware = ["ethers-middleware"]
|
||||
signers = ["ethers-signers"]
|
||||
ledger = ["ethers-signers/ledger"]
|
||||
yubi = ["ethers-signers/yubi"]
|
||||
|
||||
[dependencies]
|
||||
ethers-contract = { version = "0.1.3", path = "../ethers-contract", optional = true }
|
||||
|
|
|
@ -24,7 +24,7 @@ async fn main() -> Result<()> {
|
|||
let ganache = Ganache::new().spawn();
|
||||
|
||||
// 3. instantiate our wallet
|
||||
let wallet: Wallet = ganache.keys()[0].clone().into();
|
||||
let wallet: LocalWallet = ganache.keys()[0].clone().into();
|
||||
|
||||
// 4. connect to the network
|
||||
let provider =
|
||||
|
|
|
@ -10,8 +10,8 @@ async fn main() -> Result<()> {
|
|||
)?;
|
||||
|
||||
// create a wallet and connect it to the provider
|
||||
let wallet =
|
||||
"dcf2cbdd171a21c480aa7f53d77f31bb102282b3ff099c78e3118b37348c72f7".parse::<Wallet>()?;
|
||||
let wallet = "dcf2cbdd171a21c480aa7f53d77f31bb102282b3ff099c78e3118b37348c72f7"
|
||||
.parse::<LocalWallet>()?;
|
||||
let client = Client::new(provider, wallet);
|
||||
|
||||
// craft the transaction
|
||||
|
|
|
@ -6,8 +6,8 @@ use std::convert::TryFrom;
|
|||
async fn main() -> Result<()> {
|
||||
let ganache = Ganache::new().spawn();
|
||||
|
||||
let wallet: Wallet = ganache.keys()[0].clone().into();
|
||||
let wallet2: Wallet = ganache.keys()[1].clone().into();
|
||||
let wallet: LocalWallet = ganache.keys()[0].clone().into();
|
||||
let wallet2: LocalWallet = ganache.keys()[1].clone().into();
|
||||
|
||||
// connect to the network
|
||||
let provider = Provider::<Http>::try_from(ganache.endpoint())?;
|
||||
|
|
|
@ -4,7 +4,7 @@ use ethers::prelude::*;
|
|||
#[tokio::main]
|
||||
async fn main() -> Result<()> {
|
||||
let message = "Some data";
|
||||
let wallet = Wallet::new(&mut rand::thread_rng());
|
||||
let wallet = LocalWallet::new(&mut rand::thread_rng());
|
||||
|
||||
// sign a message
|
||||
let signature = wallet.sign_message(message).await?;
|
||||
|
|
|
@ -0,0 +1,31 @@
|
|||
#[tokio::main]
|
||||
#[cfg(feature = "yubi")]
|
||||
async fn main() -> anyhow::Result<()> {
|
||||
use ethers::{prelude::*, utils::parse_ether};
|
||||
use yubihsm::{Connector, Credentials, UsbConfig};
|
||||
|
||||
// Connect over websockets
|
||||
let provider = Provider::new(Ws::connect("ws://localhost:8545").await?);
|
||||
|
||||
// We use USB for the example, but you can connect over HTTP as well. Refer
|
||||
// to the [YubiHSM](https://docs.rs/yubihsm/0.34.0/yubihsm/) docs for more info
|
||||
let connector = Connector::usb(&UsbConfig::default());
|
||||
// Instantiate the connection to the YubiKey. Alternatively, use the
|
||||
// `from_key` method to upload a key you already have, or the `new` method
|
||||
// to generate a new keypair.
|
||||
let wallet = YubiWallet::connect(connector, Credentials::default(), 0);
|
||||
let client = Client::new(provider, wallet);
|
||||
|
||||
// Create and broadcast a transaction (ENS enabled!)
|
||||
let tx = TransactionRequest::new()
|
||||
.to("vitalik.eth")
|
||||
.value(parse_ether(10)?);
|
||||
let tx_hash = client.send_transaction(tx, None).await?;
|
||||
|
||||
// Get the receipt
|
||||
let _receipt = client.pending_transaction(tx_hash).confirmations(3).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "yubi"))]
|
||||
fn main() {}
|
Loading…
Reference in New Issue