Skip to content

Commit

Permalink
[Examples/Move] Port remaining Crypto Sui Programmability Examples (#…
Browse files Browse the repository at this point in the history
…18609)

## Description

Port over the following examples from sui_programmability/examples:

- crypto/sources/ecdsa.move
- crypto/sources/groth16.move
- ~games/sources/drand_lib.move~
- ~games/sources/drand_based_lottery.move~
- ~games/sources/drand_based_scratch_card.move~
- games/sources/vdf_based_lottery.move

Modernising and cleaning them up in the process:

- Applying wrapping consistently at 100 characters, and cleaning up
comments.
- Removing unnecessary use of `entry` functions, including returning
values instead of transfering to sender in some cases.
- Using receiver functions where possible.
- Standardising file order and adding titles for sections.
- Standardising use of doc comments vs regular comments.
- Using clever errors.

This marks the final set of examples to be moved out of
sui-programmability, which will then be deleted.

## Test plan

```
sui-framework-tests$ cargo nextest run -- run_examples_move_unit_tests
```

## Stack

- #18525 
- #18526 
- #18557 
- #18558
- #18595 

---

## Release notes

Check each box that your changes affect. If none of the boxes relate to
your changes, release notes aren't required.

For each box you select, include information after the relevant heading
that describes the impact of your changes that a user might notice and
any actions they must take to implement updates.

- [ ] Protocol: 
- [ ] Nodes (Validators and Full nodes): 
- [ ] Indexer: 
- [ ] JSON-RPC: 
- [ ] GraphQL: 
- [ ] CLI: 
- [ ] Rust SDK:

---------

Co-authored-by: Ronny Roland <[email protected]>
  • Loading branch information
amnn and ronny-mysten authored Jul 12, 2024
1 parent aae6f5c commit d43a7ad
Show file tree
Hide file tree
Showing 8 changed files with 677 additions and 0 deletions.
10 changes: 10 additions & 0 deletions examples/move/crypto/ecdsa_k1/Move.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
[package]
name = "ecdsa_k1"
version = "0.0.1"
edition = "2024.beta"

[dependencies]
Sui = { local = "../../../../crates/sui-framework/packages/sui-framework" }

[addresses]
ecdsa_k1 = "0x0"
119 changes: 119 additions & 0 deletions examples/move/crypto/ecdsa_k1/sources/example.move
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
// Copyright (c) Mysten Labs, Inc.
// SPDX-License-Identifier: Apache-2.0

/// A basic ECDSA utility contract to do the following:
///
/// 1) Hash a piece of data using Keccak256, output an object with hashed data.
/// 2) Recover a Secp256k1 signature to its public key, output an
/// object with the public key.
/// 3) Verify a Secp256k1 signature, produce an event for whether it is verified.
module ecdsa_k1::example {
use sui::ecdsa_k1;
use sui::event;

// === Object Types ===

/// Object that holds the output data
public struct Output has key, store {
id: UID,
value: vector<u8>
}

// == Event Types ===

/// Event on whether the signature is verified
public struct VerifiedEvent has copy, drop {
is_verified: bool,
}

// === Public Functions ===

/// Hash the data using Keccak256, output an object with the hash to recipient.
public fun keccak256(
data: vector<u8>,
recipient: address,
ctx: &mut TxContext,
) {
let hashed = Output {
id: object::new(ctx),
value: sui::hash::keccak256(&data),
};
// Transfer an output data object holding the hashed data to the recipient.
transfer::public_transfer(hashed, recipient)
}

/// Recover the public key using the signature and message, assuming the signature was produced
/// over the Keccak256 hash of the message. Output an object with the recovered pubkey to
/// recipient.
public fun ecrecover(
signature: vector<u8>,
msg: vector<u8>,
recipient: address,
ctx: &mut TxContext,
) {
let pubkey = Output {
id: object::new(ctx),
value: ecdsa_k1::secp256k1_ecrecover(&signature, &msg, 0),
};
// Transfer an output data object holding the pubkey to the recipient.
transfer::public_transfer(pubkey, recipient)
}

/// Recover the Ethereum address using the signature and message, assuming the signature was
/// produced over the Keccak256 hash of the message. Output an object with the recovered address
/// to recipient.
public fun ecrecover_to_eth_address(
mut signature: vector<u8>,
msg: vector<u8>,
recipient: address,
ctx: &mut TxContext,
) {
// Normalize the last byte of the signature to be 0 or 1.
let v = &mut signature[64];
if (*v == 27) {
*v = 0;
} else if (*v == 28) {
*v = 1;
} else if (*v > 35) {
*v = (*v - 1) % 2;
};

// Ethereum signature is produced with Keccak256 hash of the message, so the last param is
// 0.
let pubkey = ecdsa_k1::secp256k1_ecrecover(&signature, &msg, 0);
let uncompressed = ecdsa_k1::decompress_pubkey(&pubkey);

// Take the last 64 bytes of the uncompressed pubkey.
let mut uncompressed_64 = vector[];
let mut i = 1;
while (i < 65) {
uncompressed_64.push_back(uncompressed[i]);
i = i + 1;
};

// Take the last 20 bytes of the hash of the 64-bytes uncompressed pubkey.
let hashed = sui::hash::keccak256(&uncompressed_64);
let mut addr = vector[];
let mut i = 12;
while (i < 32) {
addr.push_back(hashed[i]);
i = i + 1;
};

let addr_object = Output {
id: object::new(ctx),
value: addr,
};

// Transfer an output data object holding the address to the recipient.
transfer::public_transfer(addr_object, recipient)
}

/// Verified the secp256k1 signature using public key and message assuming Keccak was using when
/// signing. Emit an is_verified event of the verification result.
public fun secp256k1_verify(signature: vector<u8>, public_key: vector<u8>, msg: vector<u8>) {
event::emit(VerifiedEvent {
is_verified: ecdsa_k1::secp256k1_verify(&signature, &public_key, &msg, 0)
});
}
}
10 changes: 10 additions & 0 deletions examples/move/crypto/groth16/Move.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
[package]
name = "groth16"
version = "0.0.1"
edition = "2024.beta"

[dependencies]
Sui = { local = "../../../../crates/sui-framework/packages/sui-framework" }

[addresses]
groth16 = "0x0"
108 changes: 108 additions & 0 deletions examples/move/crypto/groth16/sources/example.move
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
// Copyright (c) Mysten Labs, Inc.
// SPDX-License-Identifier: Apache-2.0

/// A verifier for the Groth16 zk-SNARK over the BLS12-381 construction.
/// See https://eprint.iacr.org/2016/260.pdf for details.
module groth16::example {
use sui::bls12381;
use sui::group_ops::Element;

// === Types ===

/// A Groth16 proof.
public struct Proof has drop {
a: Element<bls12381::G1>,
b: Element<bls12381::G2>,
c: Element<bls12381::G1>,
}

/// A Groth16 verifying key used to verify a zero-knowledge proof.
public struct VerifyingKey has store, drop {
alpha: Element<bls12381::G1>,
beta: Element<bls12381::G2>,
gamma: Element<bls12381::G2>,
gamma_abc: vector<Element<bls12381::G1>>,
delta: Element<bls12381::G2>,
}

/// A prepared verifying key. This makes verification faster than using the verifying key directly.
public struct PreparedVerifyingKey has store, drop {
alpha_beta: Element<bls12381::GT>,
gamma_neg: Element<bls12381::G2>,
gamma_abc: vector<Element<bls12381::G1>>,
delta_neg: Element<bls12381::G2>,
}

// === Errors ===

#[error]
const EInvalidNumberOfPublicInputs: vector<u8> =
b"There must be one more public input than gamma_abc entries in the verifying key.";

// === Public Functions ===

/// Create a new `Proof`.
public fun create_proof(
a: Element<bls12381::G1>,
b: Element<bls12381::G2>,
c: Element<bls12381::G1>,
): Proof {
Proof { a, b, c }
}

/// Create a new `VerifyingKey`.
public fun create_verifying_key(
alpha: Element<bls12381::G1>,
beta: Element<bls12381::G2>,
gamma: Element<bls12381::G2>,
gamma_abc: vector<Element<bls12381::G1>>,
delta: Element<bls12381::G2>,
): VerifyingKey {
VerifyingKey { alpha, beta, gamma, gamma_abc, delta }
}

/// Create a PreparedVerifyingKey from a VerifyingKey. This only have to be
/// done once.
public fun prepare(vk: VerifyingKey): PreparedVerifyingKey {
PreparedVerifyingKey {
alpha_beta: bls12381::pairing(&vk.alpha, &vk.beta),
gamma_neg: bls12381::g2_neg(&vk.gamma),
gamma_abc: vk.gamma_abc,
delta_neg: bls12381::g2_neg(&vk.delta),
}
}

/// Verify a Groth16 proof with some public inputs and a verifying key.
public fun verify(
pvk: &PreparedVerifyingKey,
proof: &Proof,
public_inputs: &vector<Element<bls12381::Scalar>>,
): bool {
let prepared_inputs = prepare_inputs(&pvk.gamma_abc, public_inputs);
let mut lhs = bls12381::pairing(&proof.a, &proof.b);
lhs = bls12381::gt_add(&lhs, &bls12381::pairing(&prepared_inputs, &pvk.gamma_neg));
lhs = bls12381::gt_add(&lhs, &bls12381::pairing(&proof.c, &pvk.delta_neg));
lhs == pvk.alpha_beta
}

// === Helpers ===

fun prepare_inputs(
vk_gamma_abc: &vector<Element<bls12381::G1>>,
public_inputs: &vector<Element<bls12381::Scalar>>,
): Element<bls12381::G1> {
let length = public_inputs.length();
assert!(length + 1 == vk_gamma_abc.length(), EInvalidNumberOfPublicInputs);

let mut output = vk_gamma_abc[0];
let mut i = 0;
while (i < length) {
output = bls12381::g1_add(
&output,
&bls12381::g1_mul(&public_inputs[i], &vk_gamma_abc[i + 1]),
);
i = i + 1;
};
output
}
}
34 changes: 34 additions & 0 deletions examples/move/crypto/groth16/tests/example_tests.move
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
// Copyright (c) Mysten Labs, Inc.
// SPDX-License-Identifier: Apache-2.0

#[test_only]
module groth16::example_tests {
use sui::bls12381;
use groth16::example::{create_verifying_key, create_proof, verify};

#[test]
fun test_verification() {
let vk = create_verifying_key(
bls12381::g1_from_bytes(&x"b58cfc3b0f43d98e7dbe865af692577d52813cb62ef3c355215ec3be2a0355a1ae5da76dd3e626f8a60de1f4a8138dee"),
bls12381::g2_from_bytes(&x"9047b42915b32ef9dffe3acc0121a1450416e7f9791159f165ab0729d744da3ed82cd4822ca1d7fef35147cfd620b59b0ca09db7dff43aab6c71635ba8f86a83f058e9922e5cdacbe21d0e5e855cf1e776a61b272c12272fe526f5ba3b48d579"),
bls12381::g2_from_bytes(&x"ad7c5a6cefcae53a3fbae72662c7c04a2f8e1892cb83615a02b32c31247172b7f317489b84e72f14acaf4f3e9ed18141157c6c1464bf15d957227f75a3c550d6d27f295b41a753340c6eec47b471b2cb8664c84f3e9b725325d3fb8afc6b56d0"),
vector[
bls12381::g1_from_bytes(&x"b2c9c61ccc28e913284a47c34e60d487869ff423dd574db080d35844f9eddd2b2967141b588a35fa82a278ce39ae6b1a"),
bls12381::g1_from_bytes(&x"9026ae12d58d203b4fc5dfad4968cbf51e43632ed1a05afdcb2e380ee552b036fbefc7780afe9675bcb60201a2421b2c")
],
bls12381::g2_from_bytes(&x"b1294927d02f8e86ac57c3b832f4ecf5e03230445a9a785ac8d25cf968f48cca8881d0c439c7e8870b66567cf611da0c1734316632f39d3125c8cecca76a8661db91cbfae217547ea1fc078a24a1a31555a46765011411094ec649d42914e2f5"),
);

let public_inputs = vector[
bls12381::scalar_from_bytes(&x"46722abc81a82d01ac89c138aa01a8223cb239ceb1f02cdaad7e1815eb997ca6")
];

let proof = create_proof(
bls12381::g1_from_bytes(&x"9913bdcabdff2cf1e7dea1673c5edba5ed6435df2f2a58d6d9e624609922bfa3976a98d991db333812bf6290a590afaa"),
bls12381::g2_from_bytes(&x"b0265b35af5069593ee88626cf3ba9a0f07699510a25aec3a27048792ab83b3467d6b814d1c09c412c4dcd7656582e6607b72915081c82794ccedf643c27abace5b23a442079d8dcbd0d68dd697b8e0b699a1925a5f2c77f5237efbbbeda3bd0"),
bls12381::g1_from_bytes(&x"b1237cf48ca7aa98507e826aac336b9e24f14133de1923fffac602a1203b795b3037c4c94e7246bacee7b2757ae912e5"),
);

assert!(vk.prepare().verify(&proof, &public_inputs));
}
}
10 changes: 10 additions & 0 deletions examples/move/vdf/Move.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
[package]
name = "VDF"
version = "0.0.1"
edition = "2024.beta"

[dependencies]
Sui = { local = "../../../crates/sui-framework/packages/sui-framework" }

[addresses]
vdf = "0x0"
Loading

0 comments on commit d43a7ad

Please sign in to comment.