feat(aquavm-air,air-interpreter-signature,air-interpreter-data)!: Peer signatures (#598)

A peer signs the multiset of call results and canon results it has produced.

New field signatures, a map from peer public key to signature, is added to the interpreter data.

Signatures verification is yet to be done.
This commit is contained in:
Ivan Boldyrev 2023-05-15 22:21:57 +07:00 committed by GitHub
parent 5e7423bd06
commit f8b734abde
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
20 changed files with 1313 additions and 100 deletions

552
Cargo.lock generated
View File

@ -17,7 +17,7 @@ version = "0.7.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47"
dependencies = [
"getrandom",
"getrandom 0.2.8",
"once_cell",
"version_check",
]
@ -76,7 +76,7 @@ name = "air-interpreter-cid"
version = "0.2.0"
dependencies = [
"cid",
"multihash",
"multihash 0.18.1",
"serde",
"serde_json",
]
@ -87,6 +87,7 @@ version = "0.7.0"
dependencies = [
"air-interpreter-cid",
"air-interpreter-interface",
"air-interpreter-signatures",
"air-utils",
"aquavm-air-parser",
"newtype_derive",
@ -109,6 +110,22 @@ dependencies = [
"serde_json",
]
[[package]]
name = "air-interpreter-signatures"
version = "0.1.0"
dependencies = [
"air-interpreter-cid",
"base64ct",
"bs58",
"ed25519-dalek",
"fluence-keypair",
"rand_chacha 0.2.2",
"serde",
"serde_json",
"sha2 0.10.6",
"zeroize",
]
[[package]]
name = "air-lambda-ast"
version = "0.1.0"
@ -290,6 +307,7 @@ dependencies = [
"air-interpreter-cid",
"air-interpreter-data",
"air-interpreter-interface",
"air-interpreter-signatures",
"air-lambda-ast",
"air-lambda-parser",
"air-log-targets",
@ -304,6 +322,7 @@ dependencies = [
"csv",
"env_logger 0.7.1",
"fluence-app-service",
"fluence-keypair",
"fstrings",
"log",
"maplit",
@ -363,6 +382,12 @@ dependencies = [
"tracing",
]
[[package]]
name = "arrayref"
version = "0.3.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6b4930d2cb77ce62f89ee5d5289b4ac049559b1c45539271f5ed4fdc7db34545"
[[package]]
name = "arrayvec"
version = "0.7.2"
@ -378,6 +403,31 @@ dependencies = [
"term",
]
[[package]]
name = "asn1_der"
version = "0.6.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6fce6b6a0ffdafebd82c87e79e3f40e8d2c523e5fea5566ff6b90509bf98d638"
dependencies = [
"asn1_der_derive",
]
[[package]]
name = "asn1_der"
version = "0.7.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "155a5a185e42c6b77ac7b88a15143d930a9e9727a5b7b77eed417404ab15c247"
[[package]]
name = "asn1_der_derive"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0d0864d84b8e07b145449be9a8537db86bf9de5ce03b913214694643b4743502"
dependencies = [
"quote",
"syn 1.0.107",
]
[[package]]
name = "async-trait"
version = "0.1.66"
@ -463,6 +513,12 @@ version = "0.13.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8"
[[package]]
name = "base64ct"
version = "1.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b"
[[package]]
name = "bimap"
version = "0.6.3"
@ -499,6 +555,50 @@ version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
[[package]]
name = "blake2b_simd"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3c2f0dc9a68c6317d884f97cc36cf5a3d20ba14ce404227df55e1af708ab04bc"
dependencies = [
"arrayref",
"arrayvec",
"constant_time_eq",
]
[[package]]
name = "blake2s_simd"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6637f448b9e61dfadbdcbae9a885fadee1f3eaffb1f8d3c1965d3ade8bdfd44f"
dependencies = [
"arrayref",
"arrayvec",
"constant_time_eq",
]
[[package]]
name = "blake3"
version = "1.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "42ae2468a89544a466886840aa467a25b766499f4f04bf7d9fcd10ecee9fccef"
dependencies = [
"arrayref",
"arrayvec",
"cc",
"cfg-if",
"constant_time_eq",
]
[[package]]
name = "block-buffer"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4"
dependencies = [
"generic-array",
]
[[package]]
name = "block-buffer"
version = "0.10.3"
@ -514,6 +614,12 @@ version = "2.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cfa8873f51c92e232f9bac4065cddef41b714152812bfc5f7672ba16d6ef8cd9"
[[package]]
name = "bs58"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "771fe0050b883fcc3ea2359b1a96bcfbc090b7116eae7c3c512c7a083fdf23d3"
[[package]]
name = "bumpalo"
version = "3.12.0"
@ -532,6 +638,12 @@ version = "1.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610"
[[package]]
name = "bytes"
version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be"
[[package]]
name = "bytesize"
version = "1.1.0"
@ -577,7 +689,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "103e94d97d73504c5fa6ffb47135d5627ce5ff84a4ad37e8219103ddc291de24"
dependencies = [
"ambient-authority",
"rand",
"rand 0.8.5",
]
[[package]]
@ -677,7 +789,7 @@ checksum = "fd94671561e36e4e7de75f753f577edafb0e7c05d6e4547229fdf7938fbcd2c3"
dependencies = [
"core2",
"multibase",
"multihash",
"multihash 0.18.1",
"serde",
"unsigned-varint",
]
@ -791,6 +903,12 @@ dependencies = [
"syn 1.0.107",
]
[[package]]
name = "constant_time_eq"
version = "0.2.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "13418e745008f7349ec7e449155f419a61b92b58a99cc3616942b926825ec76b"
[[package]]
name = "core-foundation-sys"
version = "0.8.3"
@ -1063,6 +1181,16 @@ dependencies = [
"typenum",
]
[[package]]
name = "crypto-mac"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b584a330336237c1eecd3e94266efb216c56ed91225d634cb2991c5f3fd1aeab"
dependencies = [
"generic-array",
"subtle",
]
[[package]]
name = "csv"
version = "1.2.0"
@ -1094,6 +1222,19 @@ dependencies = [
"syn 1.0.107",
]
[[package]]
name = "curve25519-dalek"
version = "3.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b9fdf9972b2bd6af2d913799d9ebc165ea4d2e65878e329d9c6b372c4491b61"
dependencies = [
"byteorder",
"digest 0.9.0",
"rand_core 0.5.1",
"subtle",
"zeroize",
]
[[package]]
name = "cxx"
version = "1.0.91"
@ -1211,13 +1352,22 @@ version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "524cbf6897b527295dff137cec09ecf3a05f4fddffd7dfcd1585403449e74198"
[[package]]
name = "digest"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066"
dependencies = [
"generic-array",
]
[[package]]
name = "digest"
version = "0.10.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8168378f4e5023e7218c89c891c0fd8ecdb5e5e4f18cb78f38cf245dd021e76f"
dependencies = [
"block-buffer",
"block-buffer 0.10.3",
"crypto-common",
]
@ -1272,6 +1422,31 @@ dependencies = [
"winapi",
]
[[package]]
name = "ed25519"
version = "1.5.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "91cff35c70bba8a626e3185d8cd48cc11b5437e1a5bcd15b9b5fa3c64b6dfee7"
dependencies = [
"serde",
"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 0.7.3",
"serde",
"serde_bytes",
"sha2 0.9.9",
"zeroize",
]
[[package]]
name = "either"
version = "1.8.1"
@ -1419,6 +1594,29 @@ dependencies = [
"wast 8.0.0",
]
[[package]]
name = "fluence-keypair"
version = "0.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ab5ed0ab3b8a69317a0452fdb52e35613a8ac5e4a638b21c774ed1ae9195662b"
dependencies = [
"asn1_der 0.6.3",
"bs58",
"ed25519-dalek",
"eyre",
"lazy_static",
"libp2p-identity",
"libsecp256k1",
"multihash 0.18.1",
"rand 0.8.5",
"ring",
"serde",
"serde_bytes",
"sha2 0.10.6",
"thiserror",
"zeroize",
]
[[package]]
name = "fnv"
version = "1.0.7"
@ -1486,6 +1684,17 @@ dependencies = [
"version_check",
]
[[package]]
name = "getrandom"
version = "0.1.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce"
dependencies = [
"cfg-if",
"libc",
"wasi 0.9.0+wasi-snapshot-preview1",
]
[[package]]
name = "getrandom"
version = "0.2.8"
@ -1568,6 +1777,27 @@ version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
[[package]]
name = "hmac"
version = "0.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "126888268dcc288495a26bf004b38c5fdbb31682f992c84ceb046a1f0fe38840"
dependencies = [
"crypto-mac",
"digest 0.9.0",
]
[[package]]
name = "hmac-drbg"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "17ea0a1394df5b6574da6e0c1ade9e78868c9fb0a4e5ef4428e32da4676b85b1"
dependencies = [
"digest 0.9.0",
"generic-array",
"hmac",
]
[[package]]
name = "humantime"
version = "1.3.0"
@ -1787,6 +2017,15 @@ dependencies = [
"wasm-bindgen",
]
[[package]]
name = "keccak"
version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f6d5ed8676d904364de097082f4e7d240b571b67989ced0240f08b7f966f940"
dependencies = [
"cpufeatures",
]
[[package]]
name = "lalrpop"
version = "0.19.12"
@ -1836,6 +2075,76 @@ version = "0.2.139"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "201de327520df007757c1f0adce6e827fe8562fbc28bfd9c15571c66ca1f5f79"
[[package]]
name = "libp2p-identity"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8a8ea433ae0cea7e3315354305237b9897afe45278b2118a7a57ca744e70fd27"
dependencies = [
"asn1_der 0.7.6",
"bs58",
"ed25519-dalek",
"libsecp256k1",
"log",
"multiaddr",
"multihash 0.17.0",
"prost",
"quick-protobuf",
"rand 0.8.5",
"ring",
"sha2 0.10.6",
"thiserror",
"zeroize",
]
[[package]]
name = "libsecp256k1"
version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "95b09eff1b35ed3b33b877ced3a691fc7a481919c7e29c53c906226fcf55e2a1"
dependencies = [
"arrayref",
"base64",
"digest 0.9.0",
"hmac-drbg",
"libsecp256k1-core",
"libsecp256k1-gen-ecmult",
"libsecp256k1-gen-genmult",
"rand 0.8.5",
"serde",
"sha2 0.9.9",
"typenum",
]
[[package]]
name = "libsecp256k1-core"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5be9b9bb642d8522a44d533eab56c16c738301965504753b03ad1de3425d5451"
dependencies = [
"crunchy",
"digest 0.9.0",
"subtle",
]
[[package]]
name = "libsecp256k1-gen-ecmult"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3038c808c55c87e8a172643a7d87187fc6c4174468159cb3090659d55bcb4809"
dependencies = [
"libsecp256k1-core",
]
[[package]]
name = "libsecp256k1-gen-genmult"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3db8d6ba2cec9eacc40e6e8ccc98931840301f1006e95647ceb2dd5c3aa06f7c"
dependencies = [
"libsecp256k1-core",
]
[[package]]
name = "link-cplusplus"
version = "1.0.8"
@ -2186,6 +2495,25 @@ version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a"
[[package]]
name = "multiaddr"
version = "0.17.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2b36f567c7099511fa8612bbbb52dda2419ce0bdbacf31714e3a5ffdb766d3bd"
dependencies = [
"arrayref",
"byteorder",
"data-encoding",
"log",
"multibase",
"multihash 0.17.0",
"percent-encoding",
"serde",
"static_assertions",
"unsigned-varint",
"url",
]
[[package]]
name = "multibase"
version = "0.9.1"
@ -2197,16 +2525,33 @@ dependencies = [
"data-encoding-macro",
]
[[package]]
name = "multihash"
version = "0.17.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "835d6ff01d610179fbce3de1694d007e500bf33a7f29689838941d6bf783ae40"
dependencies = [
"core2",
"digest 0.10.6",
"multihash-derive",
"sha2 0.10.6",
"unsigned-varint",
]
[[package]]
name = "multihash"
version = "0.18.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cfd8a792c1694c6da4f68db0a9d707c72bd260994da179e6030a5dcee00bb815"
dependencies = [
"blake2b_simd",
"blake2s_simd",
"blake3",
"core2",
"digest",
"digest 0.10.6",
"multihash-derive",
"sha2",
"sha2 0.10.6",
"sha3",
"unsigned-varint",
]
@ -2349,6 +2694,12 @@ version = "11.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0ab1bc2a289d34bd04a330323ac98a1b4bc82c9d9fcb1e66b63caa84da26b575"
[[package]]
name = "opaque-debug"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5"
[[package]]
name = "os_str_bytes"
version = "6.4.1"
@ -2577,6 +2928,29 @@ dependencies = [
"unicode-ident",
]
[[package]]
name = "prost"
version = "0.11.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b82eaa1d779e9a4bc1c3217db8ffbeabaae1dca241bf70183242128d48681cd"
dependencies = [
"bytes",
"prost-derive",
]
[[package]]
name = "prost-derive"
version = "0.11.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e5d2d8d10f3c6ded6da8b05b5fb3b8a5082514344d56c9f871412d29b4e075b4"
dependencies = [
"anyhow",
"itertools",
"proc-macro2",
"quote",
"syn 1.0.107",
]
[[package]]
name = "psm"
version = "0.1.21"
@ -2592,6 +2966,15 @@ version = "1.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0"
[[package]]
name = "quick-protobuf"
version = "0.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9d6da84cc204722a989e01ba2f6e1e276e190f22263d0cb6ce8526fcdb0d2e1f"
dependencies = [
"byteorder",
]
[[package]]
name = "quote"
version = "1.0.26"
@ -2601,6 +2984,19 @@ dependencies = [
"proc-macro2",
]
[[package]]
name = "rand"
version = "0.7.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03"
dependencies = [
"getrandom 0.1.16",
"libc",
"rand_chacha 0.2.2",
"rand_core 0.5.1",
"rand_hc",
]
[[package]]
name = "rand"
version = "0.8.5"
@ -2608,8 +3004,18 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
dependencies = [
"libc",
"rand_chacha",
"rand_core",
"rand_chacha 0.3.1",
"rand_core 0.6.4",
]
[[package]]
name = "rand_chacha"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402"
dependencies = [
"ppv-lite86",
"rand_core 0.5.1",
]
[[package]]
@ -2619,7 +3025,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
dependencies = [
"ppv-lite86",
"rand_core",
"rand_core 0.6.4",
]
[[package]]
name = "rand_core"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19"
dependencies = [
"getrandom 0.1.16",
]
[[package]]
@ -2628,7 +3043,16 @@ version = "0.6.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
dependencies = [
"getrandom",
"getrandom 0.2.8",
]
[[package]]
name = "rand_hc"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c"
dependencies = [
"rand_core 0.5.1",
]
[[package]]
@ -2668,7 +3092,7 @@ version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b033d837a7cf162d7993aded9304e30a83213c648b6e389db233191f891e5c2b"
dependencies = [
"getrandom",
"getrandom 0.2.8",
"redox_syscall",
"thiserror",
]
@ -2717,6 +3141,21 @@ version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a5996294f19bd3aae0453a862ad728f60e6600695733dd5df01da90c54363a3c"
[[package]]
name = "ring"
version = "0.16.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3053cf52e236a3ed746dfc745aa9cacf1b791d846bdaf412f60a8d7d6e17c8fc"
dependencies = [
"cc",
"libc",
"once_cell",
"spin",
"untrusted",
"web-sys",
"winapi",
]
[[package]]
name = "rustc-demangle"
version = "0.1.21"
@ -2857,9 +3296,9 @@ dependencies = [
[[package]]
name = "serde_json"
version = "1.0.95"
version = "1.0.96"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d721eca97ac802aa7777b701877c8004d950fc142651367300d21c1cc0194744"
checksum = "057d394a50403bcac12672b2b18fb387ab6d289d957dab67dd201875391e52f1"
dependencies = [
"itoa",
"ryu",
@ -2894,6 +3333,19 @@ dependencies = [
"syn 1.0.107",
]
[[package]]
name = "sha2"
version = "0.9.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4d58a1e1bf39749807d89cf2d98ac2dfa0ff1cb3faa38fbb64dd88ac8013d800"
dependencies = [
"block-buffer 0.9.0",
"cfg-if",
"cpufeatures",
"digest 0.9.0",
"opaque-debug",
]
[[package]]
name = "sha2"
version = "0.10.6"
@ -2902,7 +3354,17 @@ checksum = "82e6b795fe2e3b1e845bafcb27aa35405c4d47cdfc92af5fc8d3002f76cebdc0"
dependencies = [
"cfg-if",
"cpufeatures",
"digest",
"digest 0.10.6",
]
[[package]]
name = "sha3"
version = "0.10.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "54c2bb1a323307527314a36bfb73f24febb08ce2b8a554bf4ffd6f51ad15198c"
dependencies = [
"digest 0.10.6",
"keccak",
]
[[package]]
@ -2923,6 +3385,12 @@ dependencies = [
"dirs",
]
[[package]]
name = "signature"
version = "1.6.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "74233d3b3b2f6d4b006dc19dee745e73e2a6bfb6f93607cd3b02bd5b00797d7c"
[[package]]
name = "siphasher"
version = "0.3.10"
@ -2941,12 +3409,24 @@ version = "1.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0"
[[package]]
name = "spin"
version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d"
[[package]]
name = "stable_deref_trait"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3"
[[package]]
name = "static_assertions"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
[[package]]
name = "string_cache"
version = "0.8.4"
@ -2988,6 +3468,12 @@ dependencies = [
"syn 1.0.107",
]
[[package]]
name = "subtle"
version = "2.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc"
[[package]]
name = "syn"
version = "1.0.107"
@ -3305,6 +3791,12 @@ version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d86a8dc7f45e4c1b0d30e43038c38f274e77af056aa5f74b93c2cf9eb3c1c836"
[[package]]
name = "untrusted"
version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a"
[[package]]
name = "url"
version = "2.3.1"
@ -3381,6 +3873,12 @@ dependencies = [
"syn 1.0.107",
]
[[package]]
name = "wasi"
version = "0.9.0+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519"
[[package]]
name = "wasi"
version = "0.10.0+wasi-snapshot-preview1"
@ -3600,7 +4098,7 @@ dependencies = [
"log",
"rustix 0.36.8",
"serde",
"sha2",
"sha2 0.10.6",
"toml",
"windows-sys 0.42.0",
"zstd",
@ -3709,7 +4207,7 @@ dependencies = [
"memfd",
"memoffset 0.6.5",
"paste",
"rand",
"rand 0.8.5",
"rustix 0.36.8",
"wasmtime-asm-macros",
"wasmtime-environ",
@ -4040,6 +4538,26 @@ version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09041cd90cf85f7f8b2df60c646f853b7f535ce68f85244eb6731cf89fa498ec"
[[package]]
name = "zeroize"
version = "1.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2a0956f1ba7c7909bfb66c2e9e4124ab6f6482560f6628b5aaeba39207c9aad9"
dependencies = [
"zeroize_derive",
]
[[package]]
name = "zeroize_derive"
version = "1.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.10",
]
[[package]]
name = "zstd"
version = "0.11.2+zstd.1.5.2"

View File

@ -9,6 +9,7 @@ members = [
"crates/air-lib/interpreter-cid",
"crates/air-lib/interpreter-data",
"crates/air-lib/interpreter-interface",
"crates/air-lib/interpreter-signatures",
"crates/air-lib/lambda/ast",
"crates/air-lib/lambda/parser",
"crates/air-lib/log-targets",

View File

@ -20,6 +20,7 @@ aquavm-air-parser = { version = "0.7.4", path = "../crates/air-lib/air-parser" }
air-execution-info-collector = { version = "0.7.4", path = "../crates/air-lib/execution-info-collector" }
air-interpreter-cid = { version = "0.2.0", path = "../crates/air-lib/interpreter-cid" }
air-interpreter-data = { version = "0.7.0", path = "../crates/air-lib/interpreter-data" }
air-interpreter-signatures = { version = "0.1.0", path = "../crates/air-lib/interpreter-signatures" }
air-interpreter-interface = { version = "0.13.0", path = "../crates/air-lib/interpreter-interface", default-features = false }
air-log-targets = { version = "0.1.0", path = "../crates/air-lib/log-targets" }
air-lambda-ast = { version = "0.1.0", path = "../crates/air-lib/lambda/ast" }
@ -27,6 +28,7 @@ air-lambda-parser = { version = "0.1.0", path = "../crates/air-lib/lambda/parser
air-trace-handler = { version = "0.2.0", path = "../crates/air-lib/trace-handler" }
air-utils = { version = "0.1.0", path = "../crates/air-lib/utils" }
polyplets = { version = "0.3.2", path = "../crates/air-lib/polyplets" }
fluence-keypair = { version = "0.10.1" }
serde = { version = "1.0.159", features = [ "derive", "rc" ] }
serde_json = "1.0.95"

View File

@ -21,10 +21,15 @@ use super::Scalars;
use super::Streams;
use air_execution_info_collector::InstructionTracker;
use air_interpreter_cid::CID;
use air_interpreter_data::CanonResultCidAggregate;
use air_interpreter_data::CidInfo;
use air_interpreter_data::GlobalStreamGens;
use air_interpreter_data::RestrictedStreamGens;
use air_interpreter_data::ServiceResultCidAggregate;
use air_interpreter_interface::*;
use air_interpreter_signatures::SignatureStore;
use air_interpreter_signatures::SignatureTracker;
use std::rc::Rc;
@ -69,6 +74,17 @@ pub(crate) struct ExecutionCtx<'i> {
/// CID-to-something trackers.
pub(crate) cid_state: ExecutionCidState,
/// Signatures' store.
///
/// It contains peers' signatures for verification.
pub(crate) signature_store: SignatureStore,
/// Local signatures tracker.
///
/// It gathers peers' CIDs (call results and canon results) stored in the trace either for signing (current peer's
/// CIDs) or sign verification (other peers).
pub(crate) signature_tracker: SignatureTracker,
}
impl<'i> ExecutionCtx<'i> {
@ -87,6 +103,9 @@ impl<'i> ExecutionCtx<'i> {
);
let cid_state = ExecutionCidState::from_cid_info(prev_ingredients.cid_info, current_ingredients.cid_info);
// TODO we might keep both stores and merge them only with signature info collected into SignatureTracker
let signature_store =
SignatureStore::merge(prev_ingredients.signature_store, current_ingredients.signature_store);
Self {
run_parameters,
@ -95,6 +114,7 @@ impl<'i> ExecutionCtx<'i> {
call_results,
streams,
cid_state,
signature_store,
..<_>::default()
}
}
@ -107,6 +127,14 @@ impl<'i> ExecutionCtx<'i> {
self.last_call_request_id += 1;
self.last_call_request_id
}
pub(crate) fn record_call_cid(&mut self, peer_id: impl Into<Box<str>>, cid: &CID<ServiceResultCidAggregate>) {
self.signature_tracker.register(peer_id, cid);
}
pub(crate) fn record_canon_cid(&mut self, peer_id: impl Into<Box<str>>, cid: &CID<CanonResultCidAggregate>) {
self.signature_tracker.register(peer_id, cid);
}
}
impl ExecutionCtx<'_> {
@ -134,6 +162,7 @@ pub(crate) struct ExecCtxIngredients {
pub(crate) last_call_request_id: u32,
pub(crate) restricted_streams: RestrictedStreamGens,
pub(crate) cid_info: CidInfo,
pub(crate) signature_store: SignatureStore,
}
use serde::Deserialize;

View File

@ -38,6 +38,7 @@ pub(crate) fn populate_context_from_peer_service_result<'i>(
) -> ExecutionResult<CallResult> {
match output {
CallOutputValue::Scalar(scalar) => {
let peer_id: Box<str> = tetraplet.peer_pk.as_str().into();
let service_result_agg_cid = exec_ctx
.cid_state
.insert_value(executed_result.result.clone(), tetraplet, argument_hash)
@ -45,9 +46,11 @@ pub(crate) fn populate_context_from_peer_service_result<'i>(
let executed_result = ValueAggregate::from_service_result(executed_result, service_result_agg_cid.clone());
exec_ctx.scalars.set_scalar_value(scalar.name, executed_result)?;
exec_ctx.record_call_cid(peer_id, &service_result_agg_cid);
Ok(CallResult::executed_scalar(service_result_agg_cid))
}
CallOutputValue::Stream(stream) => {
let peer_id: Box<str> = tetraplet.peer_pk.as_str().into();
let service_result_agg_cid = exec_ctx
.cid_state
.insert_value(executed_result.result.clone(), tetraplet, argument_hash)
@ -63,6 +66,7 @@ pub(crate) fn populate_context_from_peer_service_result<'i>(
stream.position,
);
let generation = exec_ctx.streams.add_stream_value(value_descriptor)?;
exec_ctx.record_call_cid(&*peer_id, &service_result_agg_cid);
Ok(CallResult::executed_stream(service_result_agg_cid, generation))
}
CallOutputValue::None => {

View File

@ -61,6 +61,7 @@ pub(super) fn handle_prev_state<'i>(
serde_json::from_value((*err_value).clone()).map_err(UncatchableError::MalformedCallServiceFailed)?;
exec_ctx.make_subgraph_incomplete();
exec_ctx.record_call_cid(&*tetraplet.peer_pk, failed_cid);
trace_ctx.meet_call_end(met_result.result);
let err_msg = call_service_failed.message;
@ -101,6 +102,8 @@ pub(super) fn handle_prev_state<'i>(
}
// this instruction's been already executed
Executed(value) => {
use air_interpreter_data::ValueRef;
let resulted_value = populate_context_from_data(
value,
tetraplet.clone(),
@ -109,6 +112,14 @@ pub(super) fn handle_prev_state<'i>(
output,
exec_ctx,
)?;
match &resulted_value {
ValueRef::Scalar(ref cid) | ValueRef::Stream { ref cid, .. } => {
exec_ctx.record_call_cid(&*tetraplet.peer_pk, cid);
}
ValueRef::Unused(_) => {}
}
let call_result = CallResult::Executed(resulted_value);
trace_ctx.meet_call_end(call_result);
@ -170,11 +181,13 @@ fn handle_service_error(
let failed_value = CallServiceFailed::new(service_result.ret_code, error_message).to_value();
let peer_id: Box<str> = tetraplet.peer_pk.as_str().into();
let service_result_agg_cid = exec_ctx
.cid_state
.insert_value(failed_value.into(), tetraplet, argument_hash)
.map_err(UncatchableError::from)?;
exec_ctx.record_call_cid(peer_id, &service_result_agg_cid);
trace_ctx.meet_call_end(Failed(service_result_agg_cid));
Err(error.into())

View File

@ -59,6 +59,8 @@ fn handle_seen_canon(
let tetraplet_cid = canon_result_agg.tetraplet.clone();
let tetraplet = exec_ctx.cid_state.get_tetraplet_by_cid(&tetraplet_cid)?;
exec_ctx.record_canon_cid(&*tetraplet.peer_pk, &canon_result_cid);
let value_cids = canon_result_agg.values.clone();
let values = value_cids
.iter()
@ -149,10 +151,11 @@ fn create_canon_stream_from_name(
.record_value(canon_value_aggregate)?)
})
.collect::<Result<_, _>>()?;
let tetraplet = canon_stream.tetraplet();
let tetraplet_cid = exec_ctx
.cid_state
.tetraplet_tracker
.record_value(canon_stream.tetraplet().clone())
.record_value(tetraplet.clone())
.map_err(UncatchableError::from)?;
let canon_result = CanonResultCidAggregate::new(tetraplet_cid, value_cids);
@ -162,6 +165,8 @@ fn create_canon_stream_from_name(
.record_value(canon_result)
.map_err(UncatchableError::from)?;
exec_ctx.record_canon_cid(&*tetraplet.peer_pk, &canon_result_cid);
let result = StreamWithSerializedView {
canon_stream,
canon_result_cid,

View File

@ -25,6 +25,7 @@ use crate::INTERPRETER_SUCCESS;
use air_interpreter_data::InterpreterData;
use air_interpreter_interface::CallRequests;
use air_utils::measure;
use fluence_keypair::KeyPair;
use std::fmt::Debug;
use std::hash::Hash;
@ -36,6 +37,7 @@ use std::rc::Rc;
pub(crate) fn from_success_result(
exec_ctx: ExecutionCtx<'_>,
trace_handler: TraceHandler,
keypair: &KeyPair,
) -> Result<InterpreterOutcome, InterpreterOutcome> {
let (ret_code, error_message) = if exec_ctx.call_results.is_empty() {
(INTERPRETER_SUCCESS, String::new())
@ -44,7 +46,7 @@ pub(crate) fn from_success_result(
(farewell_error.to_error_code(), farewell_error.to_string())
};
let outcome = populate_outcome_from_contexts(exec_ctx, trace_handler, ret_code, error_message);
let outcome = populate_outcome_from_contexts(exec_ctx, trace_handler, ret_code, error_message, keypair);
Ok(outcome)
}
@ -64,21 +66,29 @@ pub(crate) fn from_uncatchable_error(
/// Create InterpreterOutcome from supplied execution context, trace handler, and error,
/// set ret_code based on the error.
#[tracing::instrument(skip(exec_ctx, trace_handler))]
#[tracing::instrument(skip(exec_ctx, trace_handler, keypair))]
pub(crate) fn from_execution_error(
exec_ctx: ExecutionCtx<'_>,
trace_handler: TraceHandler,
error: impl ToErrorCode + ToString + Debug,
keypair: &KeyPair,
) -> InterpreterOutcome {
populate_outcome_from_contexts(exec_ctx, trace_handler, error.to_error_code(), error.to_string())
populate_outcome_from_contexts(
exec_ctx,
trace_handler,
error.to_error_code(),
error.to_string(),
keypair,
)
}
#[tracing::instrument(skip(exec_ctx, trace_handler), level = "info")]
#[tracing::instrument(skip(exec_ctx, trace_handler, keypair), level = "info")]
fn populate_outcome_from_contexts(
exec_ctx: ExecutionCtx<'_>,
mut exec_ctx: ExecutionCtx<'_>,
mut trace_handler: TraceHandler,
ret_code: i64,
error_message: String,
keypair: &KeyPair,
) -> InterpreterOutcome {
let maybe_gens = exec_ctx
.streams
@ -89,11 +99,19 @@ fn populate_outcome_from_contexts(
Err(outcome) => return outcome,
};
let current_signature = exec_ctx
.signature_tracker
.into_signature(&exec_ctx.run_parameters.current_peer_id, keypair)
.expect("siging shouldn't fail");
let current_pubkey = keypair.public();
exec_ctx.signature_store.put(current_pubkey.into(), current_signature);
let data = InterpreterData::from_execution_result(
trace_handler.into_result_trace(),
global_streams,
restricted_streams,
exec_ctx.cid_state.into(),
exec_ctx.signature_store,
exec_ctx.last_call_request_id,
semver::Version::parse(env!("CARGO_PKG_VERSION")).expect("cargo version is valid"),
);

View File

@ -53,6 +53,7 @@ pub(crate) fn prepare<'i>(
last_call_request_id: prev_data.last_call_request_id,
restricted_streams: prev_data.restricted_streams,
cid_info: prev_data.cid_info,
signature_store: prev_data.signatures,
};
let current_ingredients = ExecCtxIngredients {
@ -60,6 +61,7 @@ pub(crate) fn prepare<'i>(
last_call_request_id: current_data.last_call_request_id,
restricted_streams: current_data.restricted_streams,
cid_info: current_data.cid_info,
signature_store: current_data.signatures,
};
let exec_ctx = make_exec_ctx(prev_ingredients, current_ingredients, call_results, run_parameters)?;

View File

@ -21,6 +21,7 @@ use crate::preparation_step::PreparationDescriptor;
use air_interpreter_interface::InterpreterOutcome;
use air_interpreter_interface::RunParameters;
use air_interpreter_signatures::derive_dummy_keypair;
use air_log_targets::RUN_PARAMS;
use air_utils::measure;
@ -55,6 +56,9 @@ fn execute_air_impl(
params: RunParameters,
call_results: Vec<u8>,
) -> Result<InterpreterOutcome, InterpreterOutcome> {
// TODO STUB this is a stub key that is to be replaced by external one in other PR
let (keypair, _) = derive_dummy_keypair(&params.current_peer_id);
let PreparationDescriptor {
mut exec_ctx,
mut trace_handler,
@ -72,11 +76,17 @@ fn execute_air_impl(
tracing::Level::INFO,
"execute",
);
match exec_result {
Ok(_) => farewell::from_success_result(exec_ctx, trace_handler),
// return new collected trace in case of errors
Err(error) if error.is_catchable() => Err(farewell::from_execution_error(exec_ctx, trace_handler, error)),
// return the prev data in case of any trace errors
Err(error) => Err(farewell::from_uncatchable_error(prev_data, error)),
}
measure!(
match exec_result {
Ok(_) => farewell::from_success_result(exec_ctx, trace_handler, &keypair),
// return new collected trace in case of errors
Err(error) if error.is_catchable() => {
Err(farewell::from_execution_error(exec_ctx, trace_handler, error, &keypair))
}
// return the prev data in case of any trace errors
Err(error) => Err(farewell::from_uncatchable_error(prev_data, error)),
},
tracing::Level::INFO,
"farewell",
)
}

View File

@ -21,5 +21,6 @@ mod join_behaviour;
mod lambda;
mod misc;
mod scopes;
mod signatures;
mod streams;
mod tetraplets;

View File

@ -0,0 +1,444 @@
/*
* Copyright 2023 Fluence Labs Limited
*
* 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.
*/
use air_interpreter_signatures::{derive_dummy_keypair, SignatureTracker};
use air_test_framework::{ephemeral::PeerId, AirScriptExecutor};
use air_test_utils::prelude::*;
use air_test_utils::test_runner::TestRunParameters;
#[test]
fn test_signature_empty() {
let script = "(null)";
let init_peer_id = "init_peer_id";
let (keypair, _) = derive_dummy_keypair(init_peer_id);
let exec = AirScriptExecutor::new(
TestRunParameters::from_init_peer_id(init_peer_id),
vec![],
vec![PeerId::from(init_peer_id)].into_iter(),
script,
)
.unwrap();
let res = exec.execute_one(init_peer_id).unwrap();
assert_eq!(res.ret_code, 0, "{:?}", res);
let expected_signature: air_interpreter_signatures::Signature = keypair.sign(b"[]").unwrap().into();
let data = data_from_result(&res);
let signature = data.signatures.get(&keypair.public().into());
assert_eq!(signature, Some(&expected_signature), "{:?}", data.signatures);
}
#[test]
fn test_signature_call_var() {
let init_peer_id = "init_peer_id";
let (keypair, _) = derive_dummy_keypair(init_peer_id);
let air_script = format!(
r#"
(call "{init_peer_id}" ("" "") [] var) ; ok = "ok"
"#
);
let exec = AirScriptExecutor::simple(TestRunParameters::from_init_peer_id(init_peer_id), &air_script).unwrap();
let res = exec.execution_iter(init_peer_id).unwrap().last().unwrap();
assert_eq!(res.ret_code, 0, "{:?}", res);
let data = data_from_result(&res);
let expected_call_state = scalar!("ok", peer = init_peer_id, service = "..0");
let expected_cid = extract_service_result_cid(&expected_call_state);
let mut expected_tracker = SignatureTracker::new();
expected_tracker.register(init_peer_id, &expected_cid);
let expected_signature = expected_tracker.into_signature(init_peer_id, &keypair).unwrap();
let signature = data.signatures.get(&keypair.public().into());
assert_eq!(signature, Some(&expected_signature), "{:?}", data.signatures);
}
#[test]
fn test_signature_call_stream() {
let init_peer_id = "init_peer_id";
let air_script = format!(
r#"
(call "{init_peer_id}" ("" "") [] $var) ; ok = "ok"
"#
);
let exec = AirScriptExecutor::simple(TestRunParameters::from_init_peer_id(init_peer_id), &air_script).unwrap();
let res = exec.execution_iter(init_peer_id).unwrap().last().unwrap();
assert_eq!(res.ret_code, 0, "{:?}", res);
let data = data_from_result(&res);
let expected_call_state = stream!("ok", 0, peer = init_peer_id, service = "..0");
let expected_cid = extract_service_result_cid(&expected_call_state);
let (keypair, _) = derive_dummy_keypair(init_peer_id);
let mut expected_tracker = SignatureTracker::new();
expected_tracker.register(init_peer_id, &expected_cid);
let expected_signature = expected_tracker.into_signature(init_peer_id, &keypair).unwrap();
let signature = data.signatures.get(&keypair.public().into());
assert_eq!(signature, Some(&expected_signature), "{:?}", data.signatures);
}
#[test]
fn test_signature_call_ununsed() {
let init_peer_id = "init_peer_id";
let air_script = format!(
r#"
(call "{init_peer_id}" ("" "") []) ; ok = "ok"
"#
);
let exec = AirScriptExecutor::simple(TestRunParameters::from_init_peer_id(init_peer_id), &air_script).unwrap();
let res = exec.execution_iter(init_peer_id).unwrap().last().unwrap();
assert_eq!(res.ret_code, 0, "{:?}", res);
let data = data_from_result(&res);
let (keypair, _) = derive_dummy_keypair(init_peer_id);
let mut expected_tracker = SignatureTracker::new();
let expected_signature = expected_tracker.into_signature(init_peer_id, &keypair).unwrap();
let signature = data.signatures.get(&keypair.public().into());
assert_eq!(signature, Some(&expected_signature), "{:?}", data.signatures);
}
#[test]
fn test_signature_call_merged() {
let init_peer_id = "init_peer_id";
let other_peer_id = "other_peer_id";
let air_script = format!(
r#"
(seq
(call "{init_peer_id}" ("" "") [] x) ; ok = "res0"
(seq
(call "{other_peer_id}" ("" "") [] y) ; ok = "res1"
(call "{init_peer_id}" ("" "") [] z) ; ok = "res2"
))
"#
);
let exec = AirScriptExecutor::simple(TestRunParameters::from_init_peer_id(init_peer_id), &air_script).unwrap();
let _ = exec.execute_one(init_peer_id).unwrap();
let _ = exec.execute_one(other_peer_id).unwrap();
let res2 = exec.execute_one(init_peer_id).unwrap();
let data2 = data_from_result(&res2);
let expected_call_state0 = scalar!("res0", peer = init_peer_id, service = "..0");
let expected_cid0 = extract_service_result_cid(&expected_call_state0);
let expected_call_state2 = scalar!("res2", peer = init_peer_id, service = "..2");
let expected_cid2 = extract_service_result_cid(&expected_call_state2);
let (keypair, _) = derive_dummy_keypair(init_peer_id);
let mut expected_tracker = SignatureTracker::new();
expected_tracker.register(init_peer_id, &expected_cid0);
expected_tracker.register(init_peer_id, &expected_cid2);
let expected_signature = expected_tracker.into_signature(init_peer_id, &keypair).unwrap();
let signature = data2.signatures.get(&keypair.public().into());
assert_eq!(signature, Some(&expected_signature), "{:?}", data2.signatures);
}
#[test]
fn test_signature_call_double() {
// Test that if some CID appears twice in the call result, it is accounted twice.
let init_peer_id = "init_peer_id";
let air_script = format!(
r#"
(seq
(seq (ap 1 $s) (ap 2 $s))
(fold $s i
(seq
(call "{init_peer_id}" ("" "") [] var) ; ok = "ok"
(next i))))
"#
);
let exec = AirScriptExecutor::simple(TestRunParameters::from_init_peer_id(init_peer_id), &air_script).unwrap();
let res = exec.execution_iter(init_peer_id).unwrap().last().unwrap();
assert_eq!(res.ret_code, 0, "{:?}", res);
let data = data_from_result(&res);
let expected_call_state = scalar!("ok", peer = init_peer_id, service = "..0");
let expected_cid = extract_service_result_cid(&expected_call_state);
let (keypair, _) = derive_dummy_keypair(init_peer_id);
let mut unexpected_tracker = SignatureTracker::new();
unexpected_tracker.register(init_peer_id, &expected_cid);
let unexpected_signature = unexpected_tracker.into_signature(init_peer_id, &keypair).unwrap();
let mut expected_tracker = SignatureTracker::new();
expected_tracker.register(init_peer_id, &expected_cid);
expected_tracker.register(init_peer_id, &expected_cid);
let expected_signature = expected_tracker.into_signature(init_peer_id, &keypair).unwrap();
assert_ne!(expected_signature, unexpected_signature, "test is incorrect");
let signature = data.signatures.get(&keypair.public().into());
assert_eq!(signature, Some(&expected_signature), "{:?}", data.signatures);
}
#[test]
fn test_signature_canon_basic() {
let init_peer_id = "init_peer_id";
let (keypair, _) = derive_dummy_keypair(init_peer_id);
let air_script = format!(
r#"
(seq
(call "{init_peer_id}" ("serv" "func") [] items) ; ok = [1, 2, 3]
(seq
(fold items i
(seq
(ap i $stream)
(next i)))
(canon "{init_peer_id}" $stream #canon)))
"#
);
let exec = AirScriptExecutor::simple(TestRunParameters::from_init_peer_id(init_peer_id), &air_script).unwrap();
let last_result = exec.execution_iter(init_peer_id).unwrap().last().unwrap();
let last_data = data_from_result(&last_result);
let expected_call_result = scalar!(
json!([1, 2, 3]),
peer = init_peer_id,
service = "serv..0",
function = "func"
);
let expected_call_result_cid = extract_service_result_cid(&expected_call_result);
let expected_canon_state = canon(json!({
"tetraplet": {"peer_pk": init_peer_id, "service_id": "", "function_name": "", "json_path": ""},
"values": [{
"result": 1,
"tetraplet": {
"peer_pk": init_peer_id,
"service_id": "serv..0",
"function_name": "func",
"json_path": ".$.[0]",
},
"provenance": Provenance::service_result(expected_call_result_cid.clone()),
}, {
"result": 2,
"tetraplet": {
"peer_pk": init_peer_id,
"service_id": "serv..0",
"function_name": "func",
"json_path": ".$.[1]",
},
"provenance": Provenance::service_result(expected_call_result_cid.clone()),
}, {
"result": 3,
"tetraplet": {
"peer_pk": init_peer_id,
"service_id": "serv..0",
"function_name": "func",
"json_path": ".$.[2]",
},
"provenance": Provenance::service_result(expected_call_result_cid.clone()),
}]
}));
let expected_canon_cid = extract_canon_result_cid(&expected_canon_state);
let mut expected_tracker = SignatureTracker::new();
expected_tracker.register(init_peer_id, &expected_canon_cid);
expected_tracker.register(init_peer_id.to_owned(), &expected_call_result_cid);
let expected_signature = expected_tracker.into_signature(init_peer_id, &keypair).unwrap();
let signature = last_data.signatures.get(&keypair.public().into());
assert_eq!(signature, Some(&expected_signature), "{:?}", last_data);
}
#[test]
fn test_signature_canon_merge() {
let init_peer_id = "init_peer_id";
let other_peer_id = "other_peer_id";
let (keypair, _) = derive_dummy_keypair(init_peer_id);
let air_script = format!(
r#"
(seq
(seq
(call "{init_peer_id}" ("serv" "func") [] items) ; ok = [1, 2, 3]
(seq
(fold items i
(seq
(ap i $stream)
(next i)))
(canon "{init_peer_id}" $stream #canon)))
(seq
(call "{other_peer_id}" ("" "") []) ; ok = "ok"
(call "{init_peer_id}" ("" "") []))) ; ok = "ok"
"#
);
let exec = AirScriptExecutor::simple(TestRunParameters::from_init_peer_id(init_peer_id), &air_script).unwrap();
exec.execute_all(init_peer_id);
exec.execute_one(other_peer_id);
let last_result = exec.execution_iter(init_peer_id).unwrap().last().unwrap();
let last_data = data_from_result(&last_result);
let expected_call_result = scalar!(
json!([1, 2, 3]),
peer = init_peer_id,
service = "serv..0",
function = "func"
);
let expected_call_result_cid = extract_service_result_cid(&expected_call_result);
let expected_canon_state = canon(json!({
"tetraplet": {"peer_pk": init_peer_id, "service_id": "", "function_name": "", "json_path": ""},
"values": [{
"result": 1,
"tetraplet": {
"peer_pk": init_peer_id,
"service_id": "serv..0",
"function_name": "func",
"json_path": ".$.[0]",
},
"provenance": Provenance::service_result(expected_call_result_cid.clone()),
}, {
"result": 2,
"tetraplet": {
"peer_pk": init_peer_id,
"service_id": "serv..0",
"function_name": "func",
"json_path": ".$.[1]",
},
"provenance": Provenance::service_result(expected_call_result_cid.clone()),
}, {
"result": 3,
"tetraplet": {
"peer_pk": init_peer_id,
"service_id": "serv..0",
"function_name": "func",
"json_path": ".$.[2]",
},
"provenance": Provenance::service_result(expected_call_result_cid.clone()),
}]
}));
let expected_canon_cid = extract_canon_result_cid(&expected_canon_state);
let mut expected_tracker = SignatureTracker::new();
expected_tracker.register(init_peer_id, &expected_canon_cid);
expected_tracker.register(init_peer_id, &expected_call_result_cid);
let expected_signature = expected_tracker.into_signature(init_peer_id, &keypair).unwrap();
let signature = last_data.signatures.get(&keypair.public().into());
assert_eq!(signature, Some(&expected_signature), "{:?}", last_data);
}
#[test]
fn test_signature_canon_result() {
// this test checks that call result in canon doesn't lead to repeadted accounting of the call result
let init_peer_id = "init_peer_id";
let (keypair, _) = derive_dummy_keypair(init_peer_id);
let air_script = format!(
r#"
(seq
(seq
(call "{init_peer_id}" ("serv" "func") [] items) ; ok = [1, 2, 3]
(fold items i
(seq
(ap i $stream)
(next i))))
(seq
(call "{init_peer_id}" ("serv" "func2") [] $stream) ; ok = 42
(canon "{init_peer_id}" $stream #canon)))
"#
);
let exec = AirScriptExecutor::simple(TestRunParameters::from_init_peer_id(init_peer_id), &air_script).unwrap();
let last_result = exec.execution_iter(init_peer_id).unwrap().last().unwrap();
let last_data = data_from_result(&last_result);
let expected_call_result1 = scalar!(
json!([1, 2, 3]),
peer = init_peer_id,
service = "serv..0",
function = "func"
);
let expected_call_result_cid1 = extract_service_result_cid(&expected_call_result1);
let expected_call_result2 = stream!(
json!(42),
1,
peer = init_peer_id,
service = "serv..1",
function = "func2"
);
let expected_call_result_cid2 = extract_service_result_cid(&expected_call_result2);
let expected_canon_state = canon(json!({
"tetraplet": {"peer_pk": init_peer_id, "service_id": "", "function_name": "", "json_path": ""},
"values": [{
"result": 1,
"tetraplet": {
"peer_pk": init_peer_id,
"service_id": "serv..0",
"function_name": "func",
"json_path": ".$.[0]",
},
"provenance": Provenance::service_result(expected_call_result_cid1.clone()),
}, {
"result": 2,
"tetraplet": {
"peer_pk": init_peer_id,
"service_id": "serv..0",
"function_name": "func",
"json_path": ".$.[1]",
},
"provenance": Provenance::service_result(expected_call_result_cid1.clone()),
}, {
"result": 3,
"tetraplet": {
"peer_pk": init_peer_id,
"service_id": "serv..0",
"function_name": "func",
"json_path": ".$.[2]",
},
"provenance": Provenance::service_result(expected_call_result_cid1.clone()),
}, {
"result": 42,
"tetraplet": {
"peer_pk": init_peer_id,
"service_id": "serv..1",
"function_name": "func2",
"json_path": "",
},
"provenance": Provenance::service_result(expected_call_result_cid2.clone()),
}]
}));
let expected_canon_cid = extract_canon_result_cid(&expected_canon_state);
let mut expected_tracker = SignatureTracker::new();
expected_tracker.register(init_peer_id, &expected_call_result_cid1);
expected_tracker.register(init_peer_id, &expected_call_result_cid2);
expected_tracker.register(init_peer_id, &expected_canon_cid);
let expected_signature = expected_tracker.into_signature(init_peer_id, &keypair).unwrap();
let signature = last_data.signatures.get(&keypair.public().into());
assert_eq!(signature, Some(&expected_signature), "{:?}", last_data);
}

View File

@ -310,28 +310,20 @@ fn invalid_dst_generations() {
(ap "a" $s)
"#
);
// TODO generate data instead, as the code will be apdated on each data change
let data = json!(
{
"version": "0.6.3",
"interpreter_version": "1.1.1",
"trace": [
{"ap":
{"gens": [42,42]}
}
],
"streams": {},
"r_streams": {},
"lcid": 0,
"cid_info": {
"value_store": {},
"tetraplet_store": {},
"canon_element_store": {},
"canon_result_store": {},
"service_result_store": {}
}
});
let data: Vec<u8> = serde_json::to_vec(&data).unwrap();
let empty_data = InterpreterData::from_execution_result(
<_>::default(),
<_>::default(),
<_>::default(),
<_>::default(),
<_>::default(),
<_>::default(),
semver::Version::new(1, 1, 1),
);
let mut data_value = serde_json::to_value(&empty_data).unwrap();
data_value["trace"] = json!([{"ap": {"gens": [42, 42]}}]);
let data: Vec<u8> = serde_json::to_vec(&data_value).unwrap();
// let result = peer_vm_1.call(script, "", data, <_>::default()).unwrap();
let result = call_vm!(peer_vm_1, <_>::default(), &script, "", data);
let expected_error = UncatchableError::TraceError {

View File

@ -32,7 +32,7 @@ use serde::Serialize;
use std::fmt;
use std::marker::PhantomData;
#[derive(Clone, Serialize, Deserialize)]
#[derive(Serialize, Deserialize)]
#[serde(transparent)]
pub struct CID<T: ?Sized>(String, #[serde(skip)] PhantomData<*const T>);
@ -46,6 +46,11 @@ impl<T: ?Sized> CID<T> {
}
}
impl<T: ?Sized> Clone for CID<T> {
fn clone(&self) -> Self {
Self(self.0.clone(), self.1)
}
}
impl<T: ?Sized> fmt::Debug for CID<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_tuple("CID").field(&self.0).finish()

View File

@ -19,6 +19,7 @@ air-utils = { version = "0.1.0", path = "../utils" }
aquavm-air-parser = { version = "0.7.4", path = "../air-parser" }
air-interpreter-interface = { version = "0.13.0", path = "../interpreter-interface" }
air-interpreter-cid = { version = "0.2.0", path = "../interpreter-cid" }
air-interpreter-signatures = { version = "0.1.0", path = "../interpreter-signatures" }
polyplets = { version = "0.3.2", path = "../polyplets" }
serde = {version = "1.0.159", features = ["derive", "rc"]}

View File

@ -62,6 +62,18 @@ impl CallResult {
pub fn failed(service_result_agg_cid: Rc<CID<ServiceResultCidAggregate>>) -> CallResult {
CallResult::Failed(service_result_agg_cid)
}
pub fn get_cid(&self) -> Option<Rc<CID<ServiceResultCidAggregate>>> {
match self {
CallResult::RequestSentBy(_) => None,
CallResult::Executed(executed) => match executed {
ValueRef::Scalar(cid) => Some(cid.clone()),
ValueRef::Stream { cid, .. } => Some(cid.clone()),
ValueRef::Unused(_) => None,
},
CallResult::Failed(cid) => Some(cid.clone()),
}
}
}
impl SubTraceDesc {

View File

@ -23,6 +23,7 @@ use crate::ExecutionTrace;
use crate::JValue;
use crate::ServiceResultCidAggregate;
use air_interpreter_signatures::SignatureStore;
use air_utils::measure;
use polyplets::SecurityTetraplet;
@ -61,6 +62,12 @@ pub struct InterpreterData {
/// CID-to-somethings mappings.
pub cid_info: CidInfo,
/// Signature store.
///
/// Every peer signs call results and canon values it produced (all together), and stores the signatures
/// in this store.
pub signatures: SignatureStore,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
@ -84,6 +91,7 @@ impl InterpreterData {
last_call_request_id: 0,
restricted_streams: RestrictedStreamGens::new(),
cid_info: <_>::default(),
signatures: <_>::default(),
}
}
@ -93,6 +101,7 @@ impl InterpreterData {
streams: GlobalStreamGens,
restricted_streams: RestrictedStreamGens,
cid_info: CidInfo,
signatures: SignatureStore,
last_call_request_id: u32,
interpreter_version: semver::Version,
) -> Self {
@ -105,6 +114,7 @@ impl InterpreterData {
last_call_request_id,
restricted_streams,
cid_info,
signatures,
}
}
@ -149,50 +159,3 @@ pub struct CidInfo {
/// Map CID to a service result aggregate.
pub service_result_store: CidStore<ServiceResultCidAggregate>,
}
#[cfg(test)]
mod tests {
use super::*;
use serde::Deserialize;
use serde::Serialize;
#[test]
fn compatible_with_0_6_0_version() {
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct InterpreterData0_6_0 {
pub trace: ExecutionTrace,
#[serde(rename = "streams")] // for compatibility with versions <= 0.2.1
pub global_streams: GlobalStreamGens,
pub version: semver::Version,
#[serde(default)]
#[serde(rename = "lcid")]
pub last_call_request_id: u32,
#[serde(default)]
#[serde(rename = "r_streams")]
pub restricted_streams: RestrictedStreamGens,
pub interpreter_version: semver::Version,
pub cid_info: CidInfo,
}
// test 0.6.0 to 0.6.1 conversion
let data_0_6_0 = InterpreterData0_6_0 {
trace: ExecutionTrace::default(),
global_streams: GlobalStreamGens::default(),
version: semver::Version::new(0, 2, 0),
last_call_request_id: 0,
restricted_streams: RestrictedStreamGens::default(),
interpreter_version: semver::Version::new(0, 1, 1),
cid_info: CidInfo::default(),
};
let data_0_6_0_se = serde_json::to_vec(&data_0_6_0).unwrap();
let data_0_6_1 = serde_json::from_slice::<InterpreterData>(&data_0_6_0_se);
assert!(data_0_6_1.is_ok());
// test 0.6.1 to 0.6.0 conversion
let data_0_6_1 = InterpreterData::new(semver::Version::new(1, 1, 1));
let data_0_6_1_se = serde_json::to_vec(&data_0_6_1).unwrap();
let data_0_6_0 = serde_json::from_slice::<InterpreterData0_6_0>(&data_0_6_1_se);
assert!(data_0_6_0.is_ok());
}
}

View File

@ -0,0 +1,26 @@
[package]
name = "air-interpreter-signatures"
description = "AIR interpreter signatures util module"
version = "0.1.0"
authors = ["Fluence Labs"]
edition = "2018"
license = "Apache-2.0"
documentation = "https://docs.rs/air-interpreter-signatures"
repository = "https://github.com/fluencelabs/aquavm/tree/master/crates/air-lib/interpreter-signatures"
keywords = ["fluence", "air", "programming-language"]
categories = ["wasm"]
[dependencies]
air-interpreter-cid = { version = "0.2.0", path = "../interpreter-cid" }
fluence-keypair = "0.10.1"
base64ct = { version = "1.6.0", features = ["std"] }
serde = { version = "1.0.159", features = ["derive"] }
serde_json = "1.0.96"
zeroize = { version = "1.6.0" }
# TODO to be moved to test-utils:
ed25519-dalek = "1.0.1"
rand_chacha = "0.2.2"
sha2 = "0.10.6"
bs58 = "0.4.0"

View File

@ -0,0 +1,165 @@
/*
* Copyright 2023 Fluence Labs Limited
*
* 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.
*/
#![forbid(unsafe_code)]
#![warn(rust_2018_idioms)]
#![deny(
dead_code,
nonstandard_style,
unused_imports,
unused_mut,
unused_variables,
unused_unsafe,
unreachable_patterns
)]
use air_interpreter_cid::CID;
use fluence_keypair::error::SigningError;
use fluence_keypair::KeyPair;
use rand_chacha::rand_core::SeedableRng;
use serde::{Deserialize, Serialize};
use std::borrow::Borrow;
use std::collections::HashMap;
use std::hash::Hash;
/// An opaque serializable representation of public key.
///
/// It can be string or binary, you shouldn't care about it unless you change serialization format.
// surrent implementation uses string as it is used as a key in a JSON map
#[derive(Debug, Hash, Clone, Eq, PartialEq, PartialOrd, Deserialize, Serialize)]
#[serde(transparent)]
pub struct PublicKey(Box<str>);
impl From<fluence_keypair::PublicKey> for PublicKey {
fn from(value: fluence_keypair::PublicKey) -> Self {
Self(bs58::encode(&value.to_vec()).into_string().into())
}
}
/// An opaque serializable representation of signature key.
///
/// It can be string or binary, you shouldn't care about it unless you change serialization format.
// surrent implementation uses string as more compact in JSON representation than number array
#[derive(Debug, Hash, Clone, Eq, PartialEq, PartialOrd, Deserialize, Serialize)]
#[serde(transparent)]
pub struct Signature(Box<str>);
impl Signature {
fn new(signature: fluence_keypair::Signature) -> Self {
signature.into()
}
}
impl From<fluence_keypair::Signature> for Signature {
fn from(value: fluence_keypair::Signature) -> Self {
Self(bs58::encode(value.to_vec()).into_string().into())
}
}
#[derive(Debug, Default)]
pub struct SignatureTracker {
peer_to_cids: HashMap<Box<str>, Vec<Box<str>>>,
}
impl SignatureTracker {
pub fn new() -> Self {
Default::default()
}
pub fn register<T>(&mut self, peer_id: impl Into<Box<str>>, cid: &CID<T>) {
self.peer_to_cids
.entry(peer_id.into())
.or_default()
.push(cid.clone().into_inner().into());
}
pub fn into_signature(
&mut self,
peer_id: &str,
signer: &KeyPair,
) -> Result<Signature, SigningError> {
let mut cids = self.peer_to_cids.get(peer_id).cloned().unwrap_or_default();
cids.sort_unstable();
// TODO make pluggable serialization
// TODO it will be useful for CID too
// TODO please note that using serde::Serializer is not enough
let serialized_cids =
serde_json::to_string(&cids).expect("default serialization shouldn't fail");
signer.sign(serialized_cids.as_bytes()).map(Signature::new)
}
}
/// A dictionary-like structure that stores peer public keys and their particle data signatures.
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct SignatureStore<Key: Hash + Eq = PublicKey, Sign = Signature>(HashMap<Key, Sign>);
impl<Key: Hash + Eq, Sign> SignatureStore<Key, Sign> {
pub fn new() -> Self {
Default::default()
}
pub fn is_empty(&self) -> bool {
self.0.is_empty()
}
pub fn get<Q>(&self, peer_pk: &Q) -> Option<&Sign>
where
Key: Borrow<Q>,
Q: Hash + Eq + ?Sized,
{
self.0.get(peer_pk)
}
pub fn put(&mut self, peer_pk: Key, signature: Sign) {
self.0.insert(peer_pk, signature);
}
pub fn merge(prev: Self, _current: Self) -> Self {
// TODO STUB
prev
}
}
impl<Key: Hash + Eq, Sign> Default for SignatureStore<Key, Sign> {
fn default() -> Self {
Self(Default::default())
}
}
/// Derive fake keypair for testing proposes.
///
/// This function should be used in production, but it is yet.
/// It returns a keypair determinisitically derived from seed, and a corresponding peer ID
/// that might be useful in tests.
// Should be moved to test lib when keypair interface PR is merged.
pub fn derive_dummy_keypair(seed: &str) -> (KeyPair, String) {
use sha2::{Digest as _, Sha256};
let mut rng = {
let mut hasher = Sha256::new();
hasher.update(seed);
rand_chacha::ChaCha8Rng::from_seed(hasher.finalize().into())
};
let keypair_ed25519 = ed25519_dalek::Keypair::generate(&mut rng);
let keypair: KeyPair = KeyPair::Ed25519(keypair_ed25519.into());
let peer_id = keypair.public().to_peer_id().to_string();
(keypair, peer_id)
}

View File

@ -100,6 +100,7 @@ pub fn raw_data_from_trace(
<_>::default(),
<_>::default(),
cid_state.into(),
<_>::default(),
0,
semver::Version::new(1, 1, 1),
);
@ -121,6 +122,7 @@ pub fn raw_data_from_trace_with_canon(
canon_result_store: cid_state.canon_result_tracker.into(),
service_result_store: cid_state.service_result_agg_tracker.into(),
},
<_>::default(),
0,
semver::Version::new(1, 1, 1),
);