Skip to content

Commit

Permalink
Bandersnatch library (#33)
Browse files Browse the repository at this point in the history
* add ark-ec-vrfs

* rm ark-ec-vrfs

* init

* change to cbindgen

* fix

* fix

* fix

* fix name

* fix clean

* fix setup

* fix setup

* fix

* swift wrapper with tests

* fix lint

* fix ci

* fix access

* include_bytes
  • Loading branch information
qiweiii authored Jul 23, 2024
1 parent e7dc080 commit b69a407
Show file tree
Hide file tree
Showing 18 changed files with 1,956 additions and 6 deletions.
31 changes: 27 additions & 4 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,8 @@ DerivedData/
.netrc
.vscode/settings.json

# rust build
target

# static lib files
.lib/
7 changes: 5 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand Down
5 changes: 5 additions & 0 deletions Utils/Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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"]),
Expand All @@ -40,6 +41,10 @@ let package = Package(
name: "blst",
path: "Sources"
),
.systemLibrary(
name: "bandersnatch_vrfs",
path: "Sources"
),
.testTarget(
name: "UtilsTests",
dependencies: [
Expand Down
180 changes: 180 additions & 0 deletions Utils/Sources/Utils/Bandersnatch.swift
Original file line number Diff line number Diff line change
@@ -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<Data32, BandersnatchError>
{
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))!)
}
}
Loading

0 comments on commit b69a407

Please sign in to comment.