diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 456c341d..d79895ad 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -42,18 +42,41 @@ jobs: key: ${{ runner.os }}-spm-${{ hashFiles('**/Package.resolved') }} restore-keys: | ${{ runner.os }}-spm- - - name: Cache static lib files + - name: Cache Cargo uses: actions/cache@v4 with: - path: .lib - key: ${{ runner.os }}-libs-${{ steps.blst-commit-hash.outputs.commit-hash }} + path: | + ~/.cargo/bin/ + ~/.cargo/registry/index/ + ~/.cargo/registry/cache/ + ~/.cargo/git/db/ + Utils/Sources/bandersnatch/target/ + key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }} + - name: Cache blst static lib + uses: actions/cache@v4 + with: + path: .lib/libblst.a + key: ${{ runner.os }}-libs-blst-${{ steps.blst-commit-hash.outputs.commit-hash }} + restore-keys: | + ${{ runner.os }}-libs-blst + - name: Cache bandersnatch_vrfs static lib + uses: actions/cache@v4 + with: + path: .lib/libbandersnatch_vrfs.a + key: ${{ runner.os }}-libs-libbandersnatch-${{ hashFiles('Utils/Sources/bandersnatch/**') }} restore-keys: | - ${{ runner.os }}-libs- + ${{ runner.os }}-libs-libbandersnatch - name: Setup Swift uses: SwiftyLab/setup-swift@latest with: swift-version: '6.0' development: true + - name: Setup Rust + uses: dtolnay/rust-toolchain@nightly + with: + components: rustfmt + - name: Check rust format + run: cargo +nightly fmt --all --manifest-path Utils/Sources/bandersnatch/Cargo.toml -- --check - name: Build run: make build - name: Test diff --git a/.gitignore b/.gitignore index e1c849fa..a1e56cda 100644 --- a/.gitignore +++ b/.gitignore @@ -8,5 +8,8 @@ DerivedData/ .netrc .vscode/settings.json +# rust build +target + # static lib files .lib/ diff --git a/Makefile b/Makefile index 0c0821b2..b038169e 100644 --- a/Makefile +++ b/Makefile @@ -8,11 +8,14 @@ default: build githooks: .git/hooks/pre-commit .PHONY: deps -deps: .lib/libblst.a +deps: .lib/libblst.a .lib/libbandersnatch_vrfs.a .lib/libblst.a: ./scripts/blst.sh +.lib/libbandersnatch_vrfs.a: $(wildcard Utils/Sources/bandersnatch/src/*) + ./scripts/bandersnatch.sh + .PHONY: test test: githooks deps ./scripts/run.sh test @@ -32,7 +35,7 @@ resolve: githooks .PHONY: clean clean: ./scripts/run.sh package clean - rm Utils/Sources/blst/lib/libblst.a + rm -f .lib/*.a .PHONY: lint lint: githooks diff --git a/Utils/Package.swift b/Utils/Package.swift index 6c631524..d09d3977 100644 --- a/Utils/Package.swift +++ b/Utils/Package.swift @@ -31,6 +31,7 @@ let package = Package( .product(name: "Blake2", package: "Blake2.swift"), .product(name: "Crypto", package: "swift-crypto"), "blst", + "bandersnatch_vrfs", ], linkerSettings: [ .unsafeFlags(["-L../.lib"]), @@ -40,6 +41,10 @@ let package = Package( name: "blst", path: "Sources" ), + .systemLibrary( + name: "bandersnatch_vrfs", + path: "Sources" + ), .testTarget( name: "UtilsTests", dependencies: [ diff --git a/Utils/Sources/Utils/Bandersnatch.swift b/Utils/Sources/Utils/Bandersnatch.swift new file mode 100644 index 00000000..3ea2772b --- /dev/null +++ b/Utils/Sources/Utils/Bandersnatch.swift @@ -0,0 +1,180 @@ +import bandersnatch_vrfs +import Foundation + +public enum BandersnatchError: Error { + case createSecretFailed + case deserializePubKeyFailed + case generatePubKeyFailed + case createProverFailed + case createVerifierFailed + case ringVRFSignFailed + case ietfVRFSignFailed + case verifyRingVrfFailed + case verifyIetfVrfFailed +} + +extension Data { + init(cSecret: CSecret) { + let tuple = cSecret._0 + // a short way to convert (UInt8, UInt8, ...) to [UInt8, UInt8, ...] + let array: [UInt8] = Swift.withUnsafeBytes(of: tuple) { + Array($0.bindMemory(to: UInt8.self)) + } + self.init(array) + } + + init(cPublic: CPublic) { + let tuple = cPublic._0 + let array: [UInt8] = Swift.withUnsafeBytes(of: tuple) { + Array($0.bindMemory(to: UInt8.self)) + } + self.init(array) + } +} + +extension CPublic { + private static func deserialize(data: Data) throws -> CPublic { + let cPublicPtr = public_deserialize_compressed([UInt8](data), UInt(data.count)) + guard let cPublicPtr else { + throw BandersnatchError.deserializePubKeyFailed + } + return CPublic(_0: cPublicPtr.pointee._0) + } + + init(data: Data) throws { + self = try CPublic.deserialize(data: data) + } + + init(data32: Data32) throws { + self = try CPublic.deserialize(data: data32.data) + } +} + +public struct Bandersnatch { + public let secret: Data96 + public let publicKey: Data32 + + public init(seed: Data) throws { + let seedBytes = [UInt8](seed) + let secretPtr = secret_new_from_seed(seedBytes, UInt(seed.count)) + guard let secretPtr else { + throw BandersnatchError.createSecretFailed + } + + secret = Data96(Data(cSecret: secretPtr.pointee))! + + let publicPtr = secret_get_public(secretPtr) + guard let publicPtr else { + throw BandersnatchError.generatePubKeyFailed + } + + publicKey = Data32(Data(cPublic: publicPtr.pointee))! + } +} + +public class Prover { + private var prover: OpaquePointer + + /// init with a set of bandersnatch public keys and provider index + public init(ring: [Data32], proverIdx: UInt) throws { + var success = false + let cPublicArr = try ring.map { try CPublic(data32: $0) } + prover = prover_new(cPublicArr, UInt(ring.count), proverIdx, &success) + if !success { + throw BandersnatchError.createProverFailed + } + } + + deinit { + prover_free(prover) + } + + /// Anonymous VRF signature. + /// + /// Used for tickets submission. + public func ringVRFSign(vrfInputData: Data, auxData: Data) throws -> Data784 { + var output = [UInt8](repeating: 0, count: 784) + let success = prover_ring_vrf_sign( + &output, prover, [UInt8](vrfInputData), UInt(vrfInputData.count), [UInt8](auxData), + UInt(auxData.count) + ) + if !success { + throw BandersnatchError.ringVRFSignFailed + } + return Data784(Data(output))! + } + + /// Non-Anonymous VRF signature. + /// + /// Used for ticket claiming during block production. + /// Not used with Safrole test vectors. + public func ietfVRFSign(vrfInputData: Data, auxData: Data) throws -> Data96 { + var output = [UInt8](repeating: 0, count: 96) + let success = prover_ietf_vrf_sign( + &output, prover, [UInt8](vrfInputData), UInt(vrfInputData.count), [UInt8](auxData), + UInt(auxData.count) + ) + if !success { + throw BandersnatchError.ietfVRFSignFailed + } + return Data96(Data(output))! + } +} + +public class Verifier { + private var verifier: OpaquePointer + + public init(ring: [Data32]) throws { + var success = false + let cPublicArr = try ring.map { try CPublic(data32: $0) } + verifier = verifier_new(cPublicArr, UInt(ring.count), &success) + if !success { + throw BandersnatchError.createVerifierFailed + } + } + + deinit { + verifier_free(verifier) + } + + /// Anonymous VRF signature verification. + /// + /// Used for tickets verification. + /// + /// On success returns the VRF output hash. + public func ringVRFVerify(vrfInputData: Data, auxData: Data, signature: Data) -> Result< + Data32, BandersnatchError + > { + var output = [UInt8](repeating: 0, count: 32) + let success = verifier_ring_vrf_verify( + &output, verifier, [UInt8](vrfInputData), UInt(vrfInputData.count), [UInt8](auxData), + UInt(auxData.count), [UInt8](signature), UInt(signature.count) + ) + if !success { + return .failure(.verifyRingVrfFailed) + } + return .success(Data32(Data(output))!) + } + + /// Non-Anonymous VRF signature verification. + /// + /// Used for ticket claim verification during block import. + /// Not used with Safrole test vectors. + /// + /// On success returns the VRF output hash. + public func ietfVRFVerify( + vrfInputData: Data, auxData: Data, signature: Data, signerKeyIndex: UInt + ) + -> Result + { + var output = [UInt8](repeating: 0, count: 32) + let success = verifier_ietf_vrf_verify( + &output, verifier, [UInt8](vrfInputData), UInt(vrfInputData.count), [UInt8](auxData), + UInt(auxData.count), [UInt8](signature), UInt(signature.count), signerKeyIndex + ) + if !success { + return .failure(.verifyIetfVrfFailed) + } + return .success(Data32(Data(output))!) + } +} diff --git a/Utils/Sources/bandersnatch/Cargo.lock b/Utils/Sources/bandersnatch/Cargo.lock new file mode 100644 index 00000000..20b11143 --- /dev/null +++ b/Utils/Sources/bandersnatch/Cargo.lock @@ -0,0 +1,1012 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "ahash" +version = "0.8.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" +dependencies = [ + "cfg-if", + "once_cell", + "version_check", + "zerocopy", +] + +[[package]] +name = "ark-bls12-381" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c775f0d12169cba7aae4caeb547bb6a50781c7449a8aa53793827c9ec4abf488" +dependencies = [ + "ark-ec", + "ark-ff", + "ark-serialize", + "ark-std", +] + +[[package]] +name = "ark-ec" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "defd9a439d56ac24968cca0571f598a61bc8c55f71d50a89cda591cb750670ba" +dependencies = [ + "ark-ff", + "ark-poly", + "ark-serialize", + "ark-std", + "derivative", + "hashbrown 0.13.2", + "itertools", + "num-traits", + "rayon", + "zeroize", +] + +[[package]] +name = "ark-ec-vrfs" +version = "0.1.0" +source = "git+https://github.com/davxy/ark-ec-vrfs#ced8b9ccdb6f67de2c2cbacf92b0515f9d359ba4" +dependencies = [ + "ark-bls12-381", + "ark-ec", + "ark-ed-on-bls12-381-bandersnatch", + "ark-ff", + "ark-serialize", + "ark-std", + "digest", + "fflonk", + "merlin", + "rand_chacha", + "rayon", + "ring", + "sha2", + "zeroize", +] + +[[package]] +name = "ark-ed-on-bls12-381-bandersnatch" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9cde0f2aa063a2a5c28d39b47761aa102bda7c13c84fc118a61b87c7b2f785c" +dependencies = [ + "ark-bls12-381", + "ark-ec", + "ark-ff", + "ark-std", +] + +[[package]] +name = "ark-ff" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec847af850f44ad29048935519032c33da8aa03340876d351dfab5660d2966ba" +dependencies = [ + "ark-ff-asm", + "ark-ff-macros", + "ark-serialize", + "ark-std", + "derivative", + "digest", + "itertools", + "num-bigint", + "num-traits", + "paste", + "rayon", + "rustc_version", + "zeroize", +] + +[[package]] +name = "ark-ff-asm" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ed4aa4fe255d0bc6d79373f7e31d2ea147bcf486cba1be5ba7ea85abdb92348" +dependencies = [ + "quote", + "syn 1.0.109", +] + +[[package]] +name = "ark-ff-macros" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7abe79b0e4288889c4574159ab790824d0033b9fdcb2a112a3182fac2e514565" +dependencies = [ + "num-bigint", + "num-traits", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "ark-poly" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d320bfc44ee185d899ccbadfa8bc31aab923ce1558716e1997a1e74057fe86bf" +dependencies = [ + "ark-ff", + "ark-serialize", + "ark-std", + "derivative", + "hashbrown 0.13.2", + "rayon", +] + +[[package]] +name = "ark-serialize" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "adb7b85a02b83d2f22f89bd5cac66c9c89474240cb6207cb1efc16d098e822a5" +dependencies = [ + "ark-serialize-derive", + "ark-std", + "digest", + "num-bigint", +] + +[[package]] +name = "ark-serialize-derive" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae3281bc6d0fd7e549af32b52511e1302185bd688fd3359fa36423346ff682ea" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "ark-std" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94893f1e0c6eeab764ade8dc4c0db24caf4fe7cbbaafc0eba0a9030f447b5185" +dependencies = [ + "num-traits", + "rand", + "rayon", +] + +[[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.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711" + +[[package]] +name = "atty" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" +dependencies = [ + "hermit-abi", + "libc", + "winapi", +] + +[[package]] +name = "autocfg" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" + +[[package]] +name = "bandersnatch-vrfs" +version = "0.1.0" +dependencies = [ + "ark-ec-vrfs", + "cbindgen", + "hex", +] + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bitflags" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" + +[[package]] +name = "blake2" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46502ad458c9a52b69d4d4d32775c788b7a1b85e8bc9d482d92250fc0e3f8efe" +dependencies = [ + "digest", +] + +[[package]] +name = "blake2b_simd" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23285ad32269793932e830392f2fe2f83e26488fd3ec778883a93c8323735780" +dependencies = [ + "arrayref", + "arrayvec", + "constant_time_eq", +] + +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + +[[package]] +name = "cbindgen" +version = "0.24.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b922faaf31122819ec80c4047cc684c6979a087366c069611e33649bf98e18d" +dependencies = [ + "clap", + "heck", + "indexmap", + "log", + "proc-macro2", + "quote", + "serde", + "serde_json", + "syn 1.0.109", + "tempfile", + "toml", +] + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "clap" +version = "3.2.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ea181bf566f71cb9a5d17a59e1871af638180a18fb0035c92ae62b705207123" +dependencies = [ + "atty", + "bitflags 1.3.2", + "clap_lex", + "indexmap", + "strsim", + "termcolor", + "textwrap", +] + +[[package]] +name = "clap_lex" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2850f2f5a82cbf437dd5af4d49848fbdfc27c157c3d010345776f952765261c5" +dependencies = [ + "os_str_bytes", +] + +[[package]] +name = "common" +version = "0.1.0" +source = "git+https://github.com/davxy/ring-proof?branch=extended#1d89434da560faec43dd27a2f94947f3ebd07d4d" +dependencies = [ + "ark-ec", + "ark-ff", + "ark-poly", + "ark-serialize", + "ark-std", + "blake2b_simd", + "fflonk", + "getrandom_or_panic", + "merlin", + "rand_chacha", + "rayon", +] + +[[package]] +name = "constant_time_eq" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7144d30dcf0fafbce74250a3963025d8d52177934239851c917d29f1df280c2" + +[[package]] +name = "cpufeatures" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53fe5e26ff1b7aef8bca9c6080520cfb8d9333c7568e1829cef191a9723e5504" +dependencies = [ + "libc", +] + +[[package]] +name = "crossbeam-deque" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "613f8cc01fe9cf1a3eb3d7f488fd2fa8388403e97039e2f73692932e291a770d" +dependencies = [ + "crossbeam-epoch", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80" + +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "derivative" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "crypto-common", + "subtle", +] + +[[package]] +name = "either" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" + +[[package]] +name = "errno" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba" +dependencies = [ + "libc", + "windows-sys", +] + +[[package]] +name = "fastrand" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fc0510504f03c51ada170672ac806f1f105a88aa97a5281117e1ddc3368e51a" + +[[package]] +name = "fflonk" +version = "0.1.0" +source = "git+https://github.com/w3f/fflonk#1e854f35e9a65d08b11a86291405cdc95baa0a35" +dependencies = [ + "ark-ec", + "ark-ff", + "ark-poly", + "ark-serialize", + "ark-std", + "merlin", + "rayon", +] + +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "getrandom" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "getrandom_or_panic" +version = "0.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ea1015b5a70616b688dc230cfe50c8af89d972cb132d5a622814d29773b10b9" +dependencies = [ + "rand", + "rand_core", +] + +[[package]] +name = "hashbrown" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" + +[[package]] +name = "hashbrown" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e" +dependencies = [ + "ahash", +] + +[[package]] +name = "heck" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" + +[[package]] +name = "hermit-abi" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" +dependencies = [ + "libc", +] + +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + +[[package]] +name = "indexmap" +version = "1.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" +dependencies = [ + "autocfg", + "hashbrown 0.12.3", +] + +[[package]] +name = "itertools" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" + +[[package]] +name = "keccak" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ecc2af9a1119c51f12a14607e783cb977bde58bc069ff0c3da1095e635d70654" +dependencies = [ + "cpufeatures", +] + +[[package]] +name = "libc" +version = "0.2.155" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c" + +[[package]] +name = "linux-raw-sys" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" + +[[package]] +name = "log" +version = "0.4.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" + +[[package]] +name = "merlin" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "58c38e2799fc0978b65dfff8023ec7843e2330bb462f19198840b34b6582397d" +dependencies = [ + "byteorder", + "keccak", + "rand_core", + "zeroize", +] + +[[package]] +name = "num-bigint" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9" +dependencies = [ + "num-integer", + "num-traits", +] + +[[package]] +name = "num-integer" +version = "0.1.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" +dependencies = [ + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", +] + +[[package]] +name = "once_cell" +version = "1.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" + +[[package]] +name = "os_str_bytes" +version = "6.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2355d85b9a3786f481747ced0e0ff2ba35213a1f9bd406ed906554d7af805a1" + +[[package]] +name = "paste" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" + +[[package]] +name = "ppv-lite86" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" + +[[package]] +name = "proc-macro2" +version = "1.0.86" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom", +] + +[[package]] +name = "rayon" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa" +dependencies = [ + "either", + "rayon-core", +] + +[[package]] +name = "rayon-core" +version = "1.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2" +dependencies = [ + "crossbeam-deque", + "crossbeam-utils", +] + +[[package]] +name = "ring" +version = "0.1.0" +source = "git+https://github.com/davxy/ring-proof?branch=extended#1d89434da560faec43dd27a2f94947f3ebd07d4d" +dependencies = [ + "ark-ec", + "ark-ff", + "ark-poly", + "ark-serialize", + "ark-std", + "blake2", + "common", + "fflonk", + "merlin", + "rayon", +] + +[[package]] +name = "rustc_version" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" +dependencies = [ + "semver", +] + +[[package]] +name = "rustix" +version = "0.38.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f" +dependencies = [ + "bitflags 2.6.0", + "errno", + "libc", + "linux-raw-sys", + "windows-sys", +] + +[[package]] +name = "ryu" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" + +[[package]] +name = "semver" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b" + +[[package]] +name = "serde" +version = "1.0.204" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc76f558e0cbb2a839d37354c575f1dc3fdc6546b5be373ba43d95f231bf7c12" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.204" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0cd7e117be63d3c3678776753929474f3b04a43a080c744d6b0ae2a8c28e222" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.69", +] + +[[package]] +name = "serde_json" +version = "1.0.120" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e0d21c9a8cae1235ad58a00c11cb40d4b1e5c784f1ef2c537876ed6ffd8b7c5" +dependencies = [ + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "sha2" +version = "0.10.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[package]] +name = "strsim" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" + +[[package]] +name = "subtle" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" + +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "2.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "201fcda3845c23e8212cd466bfebf0bd20694490fc0356ae8e428e0824a915a6" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "tempfile" +version = "3.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85b77fafb263dd9d05cbeac119526425676db3784113aa9295c88498cbf8bff1" +dependencies = [ + "cfg-if", + "fastrand", + "rustix", + "windows-sys", +] + +[[package]] +name = "termcolor" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "textwrap" +version = "0.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23d434d3f8967a09480fb04132ebe0a3e088c173e6d0ee7897abbdf4eab0f8b9" + +[[package]] +name = "toml" +version = "0.5.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4f7f0dd8d50a853a531c426359045b1998f04219d88799810762cd4ad314234" +dependencies = [ + "serde", +] + +[[package]] +name = "typenum" +version = "1.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" + +[[package]] +name = "unicode-ident" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" + +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-util" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d4cc384e1e73b93bafa6fb4f1df8c41695c8a91cf9c4c64358067d15a7b6c6b" +dependencies = [ + "windows-sys", +] + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_gnullvm", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + +[[package]] +name = "zerocopy" +version = "0.7.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.7.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.69", +] + +[[package]] +name = "zeroize" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" +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.69", +] diff --git a/Utils/Sources/bandersnatch/Cargo.toml b/Utils/Sources/bandersnatch/Cargo.toml new file mode 100644 index 00000000..aea8334c --- /dev/null +++ b/Utils/Sources/bandersnatch/Cargo.toml @@ -0,0 +1,19 @@ +[package] +name = "bandersnatch-vrfs" +version = "0.1.0" +edition = "2021" + +[lib] +crate-type = ["staticlib"] + +[build-dependencies] +cbindgen = "0.24.0" + +[dependencies] +ark-ec-vrfs = { git = "https://github.com/davxy/ark-ec-vrfs", default-features = false, features = [ + "bandersnatch", + "ring", + "parallel", + "test-vectors", +] } +hex = "0.4.3" diff --git a/Utils/Sources/bandersnatch/bindings.h b/Utils/Sources/bandersnatch/bindings.h new file mode 100644 index 00000000..f90c3282 --- /dev/null +++ b/Utils/Sources/bandersnatch/bindings.h @@ -0,0 +1,84 @@ +/* Warning, this file is auto generated by cbindgen. Don't modify this manually. */ + +#include +#include +#include +#include + +typedef struct Public Public; +typedef struct Secret Secret; + + +typedef struct Prover Prover; + +typedef struct Verifier Verifier; + +typedef struct CPublic { + uint8_t _0[32]; +} CPublic; + +typedef struct CSecret { + uint8_t _0[96]; +} CSecret; + +struct CPublic *public_deserialize_compressed(const uint8_t *data, uintptr_t len); + +struct CSecret *secret_new_from_seed(const uint8_t *seed, uintptr_t seed_len); + +const struct CPublic *secret_get_public(const struct CSecret *secret); + +struct Prover *prover_new(const struct CPublic *ring, + uintptr_t ring_len, + uintptr_t prover_idx, + bool *success); + +void prover_free(struct Prover *prover); + +/** + * out is 784 bytes + */ +bool prover_ring_vrf_sign(uint8_t *out, + const struct Prover *prover, + const uint8_t *vrf_input_data, + uintptr_t vrf_input_len, + const uint8_t *aux_data, + uintptr_t aux_data_len); + +/** + * out is 96 bytes + */ +bool prover_ietf_vrf_sign(uint8_t *out, + const struct Prover *prover, + const uint8_t *vrf_input_data, + uintptr_t vrf_input_len, + const uint8_t *aux_data, + uintptr_t aux_data_len); + +struct Verifier *verifier_new(const struct CPublic *ring, uintptr_t ring_len, bool *success); + +void verifier_free(struct Verifier *verifier); + +/** + * out is 32 bytes + */ +bool verifier_ring_vrf_verify(uint8_t *out, + const struct Verifier *verifier, + const uint8_t *vrf_input_data, + uintptr_t vrf_input_len, + const uint8_t *aux_data, + uintptr_t aux_data_len, + const uint8_t *signature, + uintptr_t signature_len); + +/** + * out is 32 bytes + */ +bool verifier_ietf_vrf_verify(uint8_t *out, + const struct Verifier *verifier, + const uint8_t *vrf_input_data, + uintptr_t vrf_input_len, + const uint8_t *aux_data, + uintptr_t aux_data_len, + const uint8_t *signature, + uintptr_t signature_len, + uintptr_t signer_key_index); diff --git a/Utils/Sources/bandersnatch/build.rs b/Utils/Sources/bandersnatch/build.rs new file mode 100644 index 00000000..7ec4f969 --- /dev/null +++ b/Utils/Sources/bandersnatch/build.rs @@ -0,0 +1,14 @@ +extern crate cbindgen; + +fn main() { + let crate_dir = "./"; + + let config = cbindgen::Config::from_file("cbindgen.toml").unwrap(); + + cbindgen::Builder::new() + .with_crate(crate_dir) + .with_config(config) + .generate() + .expect("Unable to generate bindings") + .write_to_file("bindings.h"); +} diff --git a/Utils/Sources/bandersnatch/cbindgen.toml b/Utils/Sources/bandersnatch/cbindgen.toml new file mode 100644 index 00000000..71895049 --- /dev/null +++ b/Utils/Sources/bandersnatch/cbindgen.toml @@ -0,0 +1,9 @@ +language = "C" + +after_includes = """ + +typedef struct Public Public; +typedef struct Secret Secret; +""" + +autogen_warning = "/* Warning, this file is auto generated by cbindgen. Don't modify this manually. */" diff --git a/Utils/Sources/bandersnatch/data/zcash-srs-2-11-uncompressed.bin b/Utils/Sources/bandersnatch/data/zcash-srs-2-11-uncompressed.bin new file mode 100644 index 00000000..2cb8c31e Binary files /dev/null and b/Utils/Sources/bandersnatch/data/zcash-srs-2-11-uncompressed.bin differ diff --git a/Utils/Sources/bandersnatch/src/bandersnatch_vrfs.rs b/Utils/Sources/bandersnatch/src/bandersnatch_vrfs.rs new file mode 100644 index 00000000..3bc0130d --- /dev/null +++ b/Utils/Sources/bandersnatch/src/bandersnatch_vrfs.rs @@ -0,0 +1,212 @@ +// Code copied and modified based on: https://github.com/davxy/bandersnatch-vrfs-spec/blob/470d836ae5c8ee9509892f90cf3eebf21ddf55c2/example/src/main.rs +// Changes: made RING_SIZE configurable, and add attributes for cbindgen + +use ark_ec_vrfs::suites::bandersnatch::edwards as bandersnatch; +use ark_ec_vrfs::{prelude::ark_serialize, suites::bandersnatch::edwards::RingContext}; +use bandersnatch::{IetfProof, Input, Output, Public, RingProof, Secret}; + +use ark_serialize::{CanonicalDeserialize, CanonicalSerialize}; + +const RING_SIZE_DEFAULT: usize = 6; + +// This is the IETF `Prove` procedure output as described in section 2.2 +// of the Bandersnatch VRFs specification +#[derive(CanonicalSerialize, CanonicalDeserialize)] +pub struct IetfVrfSignature { + output: Output, + proof: IetfProof, +} + +// This is the IETF `Prove` procedure output as described in section 4.2 +// of the Bandersnatch VRFs specification +#[derive(CanonicalSerialize, CanonicalDeserialize)] +pub struct RingVrfSignature { + output: Output, + // This contains both the Pedersen proof and actual ring proof. + proof: RingProof, +} + +// "Static" ring context data +fn ring_context() -> &'static RingContext { + use std::sync::OnceLock; + static RING_CTX: OnceLock = OnceLock::new(); + let ring_size: usize = std::env::var("RING_SIZE").map_or(RING_SIZE_DEFAULT, |s| { + s.parse().unwrap_or_else(|_| RING_SIZE_DEFAULT) + }); + RING_CTX.get_or_init(|| { + use bandersnatch::PcsParams; + let buf: &'static [u8] = include_bytes!("../data/zcash-srs-2-11-uncompressed.bin"); + let pcs_params = PcsParams::deserialize_uncompressed_unchecked(&mut &buf[..]).unwrap(); + RingContext::from_srs(ring_size, pcs_params).unwrap() + }) +} + +// Construct VRF Input Point from arbitrary data (section 1.2) +fn vrf_input_point(vrf_input_data: &[u8]) -> Input { + let point = + ::data_to_point(vrf_input_data) + .unwrap(); + Input::from(point) +} + +// Prover actor. +pub struct Prover { + pub prover_idx: usize, + pub secret: Secret, + pub ring: Vec, +} + +impl Prover { + pub fn new(ring: Vec, prover_idx: usize) -> Self { + Self { + prover_idx, + secret: Secret::from_seed(&prover_idx.to_le_bytes()), + ring, + } + } + + /// Anonymous VRF signature. + /// + /// Used for tickets submission. + pub fn ring_vrf_sign(&self, vrf_input_data: &[u8], aux_data: &[u8]) -> Vec { + use ark_ec_vrfs::ring::Prover as _; + + let input = vrf_input_point(vrf_input_data); + let output = self.secret.output(input); + + // Backend currently requires the wrapped type (plain affine points) + let pts: Vec<_> = self.ring.iter().map(|pk| pk.0).collect(); + + // Proof construction + let ring_ctx = ring_context(); + let prover_key = ring_ctx.prover_key(&pts); + let prover = ring_ctx.prover(prover_key, self.prover_idx); + let proof = self.secret.prove(input, output, aux_data, &prover); + + // Output and Ring Proof bundled together (as per section 2.2) + let signature = RingVrfSignature { output, proof }; + let mut buf = Vec::new(); + signature.serialize_compressed(&mut buf).unwrap(); + buf + } + + /// Non-Anonymous VRF signature. + /// + /// Used for ticket claiming during block production. + /// Not used with Safrole test vectors. + pub fn ietf_vrf_sign(&self, vrf_input_data: &[u8], aux_data: &[u8]) -> Vec { + use ark_ec_vrfs::ietf::Prover as _; + + let input = vrf_input_point(vrf_input_data); + let output = self.secret.output(input); + + let proof = self.secret.prove(input, output, aux_data); + + // Output and IETF Proof bundled together (as per section 2.2) + let signature = IetfVrfSignature { output, proof }; + let mut buf = Vec::new(); + signature.serialize_compressed(&mut buf).unwrap(); + buf + } +} + +/// cbindgen:ignore +pub type RingCommitment = ark_ec_vrfs::ring::RingCommitment; + +// Verifier actor. +pub struct Verifier { + pub commitment: RingCommitment, + pub ring: Vec, +} + +impl Verifier { + pub fn new(ring: Vec) -> Self { + // Backend currently requires the wrapped type (plain affine points) + let pts: Vec<_> = ring.iter().map(|pk| pk.0).collect(); + let verifier_key = ring_context().verifier_key(&pts); + let commitment = verifier_key.commitment(); + Self { ring, commitment } + } + + /// Anonymous VRF signature verification. + /// + /// Used for tickets verification. + /// + /// On success returns the VRF output hash. + pub fn ring_vrf_verify( + &self, + vrf_input_data: &[u8], + aux_data: &[u8], + signature: &[u8], + ) -> Result<[u8; 32], ()> { + use ark_ec_vrfs::ring::prelude::fflonk::pcs::PcsParams; + use ark_ec_vrfs::ring::Verifier as _; + use bandersnatch::VerifierKey; + + let signature = RingVrfSignature::deserialize_compressed(signature).unwrap(); + + let input = vrf_input_point(vrf_input_data); + let output = signature.output; + + let ring_ctx = ring_context(); + + // The verifier key is reconstructed from the commitment and the constant + // verifier key component of the SRS in order to verify some proof. + // As an alternative we can construct the verifier key using the + // RingContext::verifier_key() method, but is more expensive. + // In other words, we prefer computing the commitment once, when the keyset changes. + let verifier_key = VerifierKey::from_commitment_and_kzg_vk( + self.commitment.clone(), + ring_ctx.pcs_params.raw_vk(), + ); + let verifier = ring_ctx.verifier(verifier_key); + if Public::verify(input, output, aux_data, &signature.proof, &verifier).is_err() { + println!("Ring signature verification failure"); + return Err(()); + } + println!("Ring signature verified"); + + // This truncated hash is the actual value used as ticket-id/score in JAM + let vrf_output_hash: [u8; 32] = output.hash()[..32].try_into().unwrap(); + println!(" vrf-output-hash: {}", hex::encode(vrf_output_hash)); + Ok(vrf_output_hash) + } + + /// Non-Anonymous VRF signature verification. + /// + /// Used for ticket claim verification during block import. + /// Not used with Safrole test vectors. + /// + /// On success returns the VRF output hash. + pub fn ietf_vrf_verify( + &self, + vrf_input_data: &[u8], + aux_data: &[u8], + signature: &[u8], + signer_key_index: usize, + ) -> Result<[u8; 32], ()> { + use ark_ec_vrfs::ietf::Verifier as _; + + let signature = IetfVrfSignature::deserialize_compressed(signature).unwrap(); + + let input = vrf_input_point(vrf_input_data); + let output = signature.output; + + let public = &self.ring[signer_key_index]; + if public + .verify(input, output, aux_data, &signature.proof) + .is_err() + { + println!("Ring signature verification failure"); + return Err(()); + } + println!("Ietf signature verified"); + + // This is the actual value used as ticket-id/score + // NOTE: as far as vrf_input_data is the same, this matches the one produced + // using the ring-vrf (regardless of aux_data). + let vrf_output_hash: [u8; 32] = output.hash()[..32].try_into().unwrap(); + println!(" vrf-output-hash: {}", hex::encode(vrf_output_hash)); + Ok(vrf_output_hash) + } +} diff --git a/Utils/Sources/bandersnatch/src/ffi.rs b/Utils/Sources/bandersnatch/src/ffi.rs new file mode 100644 index 00000000..c9f955f6 --- /dev/null +++ b/Utils/Sources/bandersnatch/src/ffi.rs @@ -0,0 +1,318 @@ +use std::io::Cursor; +use std::ptr; + +use ark_ec_vrfs::{prelude::ark_serialize, suites::bandersnatch::edwards as bandersnatch}; +use ark_serialize::{CanonicalDeserialize, CanonicalSerialize}; +use bandersnatch::{Public, Secret}; + +use crate::bandersnatch_vrfs::{Prover, Verifier}; + +// MARK: Public + +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct CPublic([u8; 32]); + +impl From for Public { + fn from(c_public: CPublic) -> Self { + Public::deserialize_compressed(&c_public.0[..]).expect("CPublic to Public failed") + } +} + +impl From for CPublic { + fn from(public: Public) -> Self { + let mut buffer = Vec::with_capacity(32); + let mut cursor = Cursor::new(&mut buffer); + public + .serialize_compressed(&mut cursor) + .expect("Public to CPublic failed"); + + let mut c_public_bytes = [0u8; 32]; + c_public_bytes.copy_from_slice(&buffer); + CPublic(c_public_bytes) + } +} + +#[no_mangle] +pub extern "C" fn public_deserialize_compressed(data: *const u8, len: usize) -> *mut CPublic { + if data.is_null() { + std::ptr::null_mut() + } else { + let slice = unsafe { std::slice::from_raw_parts(data, len) }; + + match Public::deserialize_compressed(slice) { + Ok(public) => Box::into_raw(Box::new(public.into())), + Err(_) => std::ptr::null_mut(), + } + } +} + +// MARK: Secret + +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct CSecret([u8; 96]); + +impl From for Secret { + fn from(c_secret: CSecret) -> Self { + Secret::deserialize_compressed(&c_secret.0[..]).expect("CSecret to Secret failed") + } +} + +impl From for CSecret { + fn from(secret: Secret) -> Self { + let mut buffer = Vec::with_capacity(96); + let mut cursor = Cursor::new(&mut buffer); + secret + .serialize_compressed(&mut cursor) + .expect("Secret to CSecret failed"); + + let mut c_secret_bytes = [0u8; 96]; + c_secret_bytes.copy_from_slice(&buffer); + CSecret(c_secret_bytes) + } +} + +#[no_mangle] +pub extern "C" fn secret_new_from_seed(seed: *const u8, seed_len: usize) -> *mut CSecret { + if seed.is_null() { + std::ptr::null_mut() + } else { + let seed_bytes = unsafe { std::slice::from_raw_parts(seed, seed_len) }; + + let secret = Secret::from_seed(seed_bytes); + + Box::into_raw(Box::new(secret.into())) + } +} + +#[no_mangle] +pub extern "C" fn secret_get_public(secret: *const CSecret) -> *const CPublic { + if secret.is_null() { + std::ptr::null() + } else { + let secret = unsafe { &*secret }; + + let public = Into::::into(*secret).public(); + + Box::into_raw(Box::new(public.into())) + } +} + +// MARK: Prover + +#[no_mangle] +pub extern "C" fn prover_new( + ring: *const CPublic, + ring_len: usize, + prover_idx: usize, + success: *mut bool, +) -> *mut Prover { + if ring.is_null() || success.is_null() { + unsafe { *success = false }; + std::ptr::null_mut() + } else { + let ring_slice_c = unsafe { std::slice::from_raw_parts(ring, ring_len) }; + let ring_vec = ring_slice_c.iter().map(|&cp| cp.into()).collect(); + let prover = Prover::new(ring_vec, prover_idx); + let boxed_prover = Box::new(prover); + unsafe { *success = true }; + Box::into_raw(boxed_prover) + } +} + +#[no_mangle] +pub extern "C" fn prover_free(prover: *mut Prover) { + if !prover.is_null() { + // drop the `Prover` and deallocate the memory + unsafe { + let _ = Box::from_raw(prover); + }; + } +} + +/// out is 784 bytes +#[no_mangle] +pub extern "C" fn prover_ring_vrf_sign( + out: *mut u8, + prover: *const Prover, + vrf_input_data: *const u8, + vrf_input_len: usize, + aux_data: *const u8, + aux_data_len: usize, +) -> bool { + if prover.is_null() + || vrf_input_data.is_null() + || aux_data.is_null() + || vrf_input_len == 0 + || out.is_null() + { + return false; + } + + let vrf_input_slice = unsafe { std::slice::from_raw_parts(vrf_input_data, vrf_input_len) }; + let aux_data_slice = unsafe { std::slice::from_raw_parts(aux_data, aux_data_len) }; + let prover = unsafe { &*prover }; + + let result = prover.ring_vrf_sign(vrf_input_slice, aux_data_slice); + if result.len() != 784 { + return false; + } + unsafe { + ptr::copy_nonoverlapping(result.as_ptr(), out, result.len()); + } + true +} + +/// out is 96 bytes +#[no_mangle] +pub extern "C" fn prover_ietf_vrf_sign( + out: *mut u8, + prover: *const Prover, + vrf_input_data: *const u8, + vrf_input_len: usize, + aux_data: *const u8, + aux_data_len: usize, +) -> bool { + if prover.is_null() + || vrf_input_data.is_null() + || aux_data.is_null() + || vrf_input_len == 0 + || out.is_null() + { + return false; + } + + let vrf_input_slice = unsafe { std::slice::from_raw_parts(vrf_input_data, vrf_input_len) }; + let aux_data_slice = unsafe { std::slice::from_raw_parts(aux_data, aux_data_len) }; + let prover = unsafe { &*prover }; + + let result = prover.ietf_vrf_sign(vrf_input_slice, aux_data_slice); + if result.len() != 96 { + return false; + } + unsafe { + ptr::copy_nonoverlapping(result.as_ptr(), out, result.len()); + } + true +} + +// MARK: Verifier + +#[no_mangle] +pub extern "C" fn verifier_new( + ring: *const CPublic, + ring_len: usize, + success: *mut bool, +) -> *mut Verifier { + if ring.is_null() || success.is_null() { + unsafe { *success = false }; + std::ptr::null_mut() + } else { + let ring_slice_c = unsafe { std::slice::from_raw_parts(ring, ring_len) }; + let ring_vec = ring_slice_c.iter().map(|&cp| cp.into()).collect(); + let verifier = Verifier::new(ring_vec); + unsafe { *success = true }; + let boxed_verifier = Box::new(verifier); + Box::into_raw(boxed_verifier) + } +} + +#[no_mangle] +pub extern "C" fn verifier_free(verifier: *mut Verifier) { + if !verifier.is_null() { + // drop the `Verifier` and deallocate the memory + unsafe { + let _ = Box::from_raw(verifier); + }; + } +} + +/// out is 32 bytes +#[no_mangle] +pub extern "C" fn verifier_ring_vrf_verify( + out: *mut u8, + verifier: *const Verifier, + vrf_input_data: *const u8, + vrf_input_len: usize, + aux_data: *const u8, + aux_data_len: usize, + signature: *const u8, + signature_len: usize, +) -> bool { + if verifier.is_null() + || vrf_input_data.is_null() + || aux_data.is_null() + || signature.is_null() + || vrf_input_len == 0 + || signature_len == 0 + || out.is_null() + { + return false; + } + + let vrf_input_slice = unsafe { std::slice::from_raw_parts(vrf_input_data, vrf_input_len) }; + let aux_data_slice = unsafe { std::slice::from_raw_parts(aux_data, aux_data_len) }; + let signature_slice = unsafe { std::slice::from_raw_parts(signature, signature_len) }; + + let verifier = unsafe { &*verifier }; + + let result_array = + match verifier.ring_vrf_verify(vrf_input_slice, aux_data_slice, signature_slice) { + Ok(array) => array, + Err(_) => return false, + }; + + unsafe { + std::ptr::copy_nonoverlapping(result_array.as_ptr(), out, result_array.len()); + } + + true +} + +/// out is 32 bytes +#[no_mangle] +pub extern "C" fn verifier_ietf_vrf_verify( + out: *mut u8, + verifier: *const Verifier, + vrf_input_data: *const u8, + vrf_input_len: usize, + aux_data: *const u8, + aux_data_len: usize, + signature: *const u8, + signature_len: usize, + signer_key_index: usize, +) -> bool { + if verifier.is_null() + || vrf_input_data.is_null() + || aux_data.is_null() + || signature.is_null() + || vrf_input_len == 0 + || signature_len == 0 + || out.is_null() + { + return false; + } + + let vrf_input_slice = unsafe { std::slice::from_raw_parts(vrf_input_data, vrf_input_len) }; + let aux_data_slice = unsafe { std::slice::from_raw_parts(aux_data, aux_data_len) }; + let signature_slice = unsafe { std::slice::from_raw_parts(signature, signature_len) }; + + let verifier = unsafe { &*verifier }; + + let result_array = match verifier.ietf_vrf_verify( + vrf_input_slice, + aux_data_slice, + signature_slice, + signer_key_index, + ) { + Ok(array) => array, + Err(_) => return false, + }; + + unsafe { + std::ptr::copy_nonoverlapping(result_array.as_ptr(), out, result_array.len()); + } + + true +} diff --git a/Utils/Sources/bandersnatch/src/lib.rs b/Utils/Sources/bandersnatch/src/lib.rs new file mode 100644 index 00000000..14bc5cc0 --- /dev/null +++ b/Utils/Sources/bandersnatch/src/lib.rs @@ -0,0 +1,4 @@ +mod bandersnatch_vrfs; + +pub mod ffi; +pub use ffi::*; diff --git a/Utils/Sources/module.modulemap b/Utils/Sources/module.modulemap index be2b8124..5d9cdaa4 100644 --- a/Utils/Sources/module.modulemap +++ b/Utils/Sources/module.modulemap @@ -2,3 +2,8 @@ module blst { header "blst/bindings/blst.h" link "blst" } + +module bandersnatch_vrfs { + header "bandersnatch/bindings.h" + link "bandersnatch_vrfs" +} diff --git a/Utils/Tests/UtilsTests/BandersnatchTest.swift b/Utils/Tests/UtilsTests/BandersnatchTest.swift new file mode 100644 index 00000000..75400ca6 --- /dev/null +++ b/Utils/Tests/UtilsTests/BandersnatchTest.swift @@ -0,0 +1,40 @@ +import Foundation +import Testing + +@testable import Utils + +@Suite struct BandersnatchTests { + @Test func ringVrfVerifyWorks() throws { + let ringHexStrings = [ + "5e465beb01dbafe160ce8216047f2155dd0569f058afd52dcea601025a8d161d", + "3d5e5a51aab2b048f8686ecd79712a80e3265a114cc73f14bdb2a59233fb66d0", + "aa2b95f7572875b0d0f186552ae745ba8222fc0b5bd456554bfe51c68938f8bc", + "7f6190116d118d643a98878e294ccf62b509e214299931aad8ff9764181a4e33", + "48e5fcdce10e0b64ec4eebd0d9211c7bac2f27ce54bca6f7776ff6fee86ab3e3", + "f16e5352840afb47e206b5c89f560f2611835855cf2e6ebad1acc9520a72591d", + ] + + let ringData: [Data32] = ringHexStrings.compactMap { Data32(Data(fromHexString: $0)!) } + + var vrfInputData = Data("jam_ticket_seal".utf8) + let eta2Hex = "bb30a42c1e62f0afda5f0a4e8a562f7a13a24cea00ee81917b86b89e801314aa" + let eta2Bytes = Data(fromHexString: eta2Hex)! + vrfInputData.append(eta2Bytes) + vrfInputData.append(1) + + let auxData = Data() + + let signatureHex = + // swiftlint:disable:next line_length + "b342bf8f6fa69c745daad2e99c92929b1da2b840f67e5e8015ac22dd1076343ea95c5bb4b69c197bfdc1b7d2f484fe455fb19bba7e8d17fcaf309ba5814bf54f3a74d75b408da8d3b99bf07f7cde373e4fd757061b1c99e0aac4847f1e393e892b566c14a7f8643a5d976ced0a18d12e32c660d59c66c271332138269cb0fe9c2462d5b3c1a6e9f5ed330ff0d70f64218010ff337b0b69b531f916c67ec564097cd842306df1b4b44534c95ff4efb73b17a14476057fdf8678683b251dc78b0b94712179345c794b6bd99aa54b564933651aee88c93b648e91a613c87bc3f445fff571452241e03e7d03151600a6ee259051a23086b408adec7c112dd94bd8123cf0bed88fddac46b7f891f34c29f13bf883771725aa234d398b13c39fd2a871894f1b1e2dbc7fffbc9c65c49d1e9fd5ee0da133bef363d4ebebe63de2b50328b5d7e020303499d55c07cae617091e33a1ee72ba1b65f940852e93e2905fdf577adcf62be9c74ebda9af59d3f11bece8996773f392a2b35693a45a5a042d88a3dc816b689fe596762d4ea7c6024da713304f56dc928be6e8048c651766952b6c40d0f48afc067ca7cbd77763a2d4f11e88e16033b3343f39bf519fe734db8a139d148ccead4331817d46cf469befa64ae153b5923869144dfa669da36171c20e1f757ed5231fa5a08827d83f7b478ddfb44c9bceb5c6c920b8761ff1e3edb03de48fb55884351f0ac5a7a1805b9b6c49c0529deb97e994deaf2dfd008825e8704cdc04b621f316b505fde26ab71b31af7becbc1154f9979e43e135d35720b93b367bedbe6c6182bb6ed99051f28a3ad6d348ba5b178e3ea0ec0bb4a03fe36604a9eeb609857f8334d3b4b34867361ed2ff9163acd9a27fa20303abe9fc29f2d6c921a8ee779f7f77d940b48bc4fce70a58eed83a206fb7db4c1c7ebe7658603495bb40f6a581dd9e235ba0583165b1569052f8fb4a3e604f2dd74ad84531c6b96723c867b06b6fdd1c4ba150cf9080aa6bbf44cc29041090973d56913b9dc755960371568ef1cf03f127fe8eca209db5d18829f5bfb5826f98833e3f42472b47fad995a9a8bb0e41a1df45ead20285a8" + let signatureBytes = Data(fromHexString: signatureHex)! + + // verifier + let verifier = try Verifier(ring: ringData) + let verifyRes = verifier.ringVRFVerify( + vrfInputData: vrfInputData, auxData: auxData, signature: signatureBytes + ) + let outputHashData = try verifyRes.get() + #expect(outputHashData != nil) + } +} diff --git a/scripts/bandersnatch.sh b/scripts/bandersnatch.sh new file mode 100755 index 00000000..b2b742e0 --- /dev/null +++ b/scripts/bandersnatch.sh @@ -0,0 +1,17 @@ +#!/usr/bin/env bash + +set -e + + +# Setup bandersnatch vrf c binding +CWD=$(pwd) + +mkdir -p .lib + +cd Utils/Sources/bandersnatch || { echo "directory not found"; exit 1; } + +cargo build --lib + +cp target/debug/libbandersnatch_vrfs.a ${CWD}/.lib + +echo "Setup bandersnatch successfully." diff --git a/scripts/blst.sh b/scripts/blst.sh index efc20977..675968fe 100755 --- a/scripts/blst.sh +++ b/scripts/blst.sh @@ -1,5 +1,7 @@ #!/usr/bin/env bash +set -e + # Setup blst C module CWD=$(pwd)