Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implementation of the Stake table contract (register function) #769

Merged
merged 60 commits into from
Dec 1, 2023
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
Show all changes
60 commits
Select commit Hold shift + click to select a range
b2043fe
Dummy implementation of IStakeTable.sol.
philippecamacho Nov 20, 2023
c824e22
Generate the inputs for testing the `register` function.
philippecamacho Nov 21, 2023
8b80058
Sign the Schnorr public key with the BLS private key
philippecamacho Nov 22, 2023
750e39b
Sign the sender address instead of the Schnorr public key...
philippecamacho Nov 23, 2023
1937c6a
Add comment
philippecamacho Nov 23, 2023
73316a4
All logic implemented except the BEANS tokens transfer.
philippecamacho Nov 24, 2023
8139895
forge install: openzeppelin-contracts
philippecamacho Nov 24, 2023
08ec2d7
Interaction with token contract and more tests.
philippecamacho Nov 24, 2023
79781f8
forge install: solmate
philippecamacho Nov 24, 2023
a985acf
Track the total amount of deposited stake.
philippecamacho Nov 24, 2023
bd4e4f3
Refactor the code for node registration & BLS key index computation.
philippecamacho Nov 27, 2023
40a2034
Replace keywaord by .
philippecamacho Nov 27, 2023
8097268
Refactor imports in
philippecamacho Nov 27, 2023
30f91ce
More refactor of imports in
philippecamacho Nov 27, 2023
b0ba4b6
Add TODO for returning ParsedG1(G2)Point.
philippecamacho Nov 27, 2023
ac5a281
Simplify the computation of .
philippecamacho Nov 27, 2023
fc86c24
Switch lines to prevent possible reentrancy attacks. Ensure only nati…
philippecamacho Nov 27, 2023
fbf96f9
Use of custom error when checking for the next registration epoch.
philippecamacho Nov 27, 2023
8a12d7a
Better name for the test contract.
philippecamacho Nov 27, 2023
f438d38
Make some contract variable public and rename them.
philippecamacho Nov 27, 2023
9251a82
Merge branch 'main' into feat/t737-stake-table-contract-impl
philippecamacho Nov 27, 2023
1d62650
Fix TODO message.
philippecamacho Nov 27, 2023
6ab57f1
Integrate with the LightClient contract.
philippecamacho Nov 27, 2023
9f5b9f2
Add custom error
philippecamacho Nov 27, 2023
b4ca915
fix: use toString(address) directly
alxiong Nov 28, 2023
9f10be5
Use of named imports.
philippecamacho Nov 29, 2023
6d90e39
Use of named imports bis
philippecamacho Nov 29, 2023
a4a17a2
Split test in order to isolate each expected behaviour.
philippecamacho Nov 29, 2023
87ed794
Use of abstract contract to avoid superfluous getter functions.
philippecamacho Nov 29, 2023
01c490d
CI: verbose output if precommit check fails
sveitser Nov 29, 2023
1c9f48d
Merge remote-tracking branch 'origin/main' into feat/t737-stake-table…
alxiong Nov 29, 2023
a1dd04d
Update bindings (on OSX)
sveitser Nov 29, 2023
9809707
CI: debug toolchain versions
sveitser Nov 29, 2023
58b5ffa
Add missing script
sveitser Nov 29, 2023
6a5953c
Use only one ERC20 token
sveitser Nov 29, 2023
aa51a16
Update contract bindings
sveitser Nov 29, 2023
36040ce
Update bindings from OSX
sveitser Nov 29, 2023
003aca6
Merge remote-tracking branch 'origin/main' into feat/t737-stake-table…
sveitser Nov 29, 2023
2783b42
Reorder the code of to save gas in case of an error.
philippecamacho Nov 29, 2023
2295635
Remove superfluous field in StakeTable test contract.
philippecamacho Nov 29, 2023
5f9711a
Rename to .
philippecamacho Nov 29, 2023
91f8df8
Define and variables locally.
philippecamacho Nov 29, 2023
df906e7
Rename to
philippecamacho Nov 29, 2023
1acd0b4
Remove useless call to vm.prank.
philippecamacho Nov 29, 2023
1d85c9f
Put vm.expectEmit and call to contract closer together.
philippecamacho Nov 29, 2023
d4893b9
Rename test functions.
philippecamacho Nov 29, 2023
db06877
Better name for happy path test.
philippecamacho Nov 29, 2023
e5165c2
Remove confusing comment.
philippecamacho Nov 29, 2023
1c99334
Call _hashBlsKey in the test in order to avoid code duplication.
philippecamacho Nov 29, 2023
ce1ebbf
Reorder the tests.
philippecamacho Nov 29, 2023
7584da6
Replace remaining ocurrences of msg.sender with exampleTokenCreator.
philippecamacho Nov 29, 2023
44cfc9e
Add fuzzing tests for checking an error is raised when the registrati…
philippecamacho Nov 29, 2023
d97df8e
Rename IStakeTable to AbstractStakeTable
philippecamacho Nov 29, 2023
3c97955
Add some documentation.
philippecamacho Nov 29, 2023
44fc0f0
Remove openzeppelin dependency.
philippecamacho Nov 30, 2023
3a3ec04
Fuzz depositAmount and validUntilEpoch in RevertWhen_UsingRestakeToke…
philippecamacho Nov 30, 2023
adb34a8
Fuzz invalid signatures.
philippecamacho Nov 30, 2023
4d63381
Improve test_RevertWhen_TransferFailed
philippecamacho Nov 30, 2023
9261d5b
Improve fuzzing for test testFuzz_RevertWhen_InvalidNextRegistrationE…
philippecamacho Nov 30, 2023
968b4a8
rename LightClientHelper->*Test to avoid binding
alxiong Dec 1, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
172 changes: 82 additions & 90 deletions contract-bindings/src/i_stake_table.rs

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions contract-bindings/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,5 +16,6 @@ pub mod i_stake_table;
pub mod plonk_verifier;
pub mod polynomial_eval;
pub mod shared_types;
pub mod stake_table;
pub mod transcript;
pub mod utils;
38 changes: 38 additions & 0 deletions contract-bindings/src/shared_types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,23 @@ pub struct G2Point {
pub y_0: ::ethers::core::types::U256,
pub y_1: ::ethers::core::types::U256,
}
///`EdOnBN254Point(uint256,uint256)`
#[derive(
Clone,
::ethers::contract::EthAbiType,
::ethers::contract::EthAbiCodec,
serde::Serialize,
serde::Deserialize,
Default,
Debug,
PartialEq,
Eq,
Hash,
)]
pub struct EdOnBN254Point {
pub x: ::ethers::core::types::U256,
pub y: ::ethers::core::types::U256,
}
///`PlonkProof((uint256,uint256),(uint256,uint256),(uint256,uint256),(uint256,uint256),(uint256,uint256),(uint256,uint256),(uint256,uint256),(uint256,uint256),(uint256,uint256),(uint256,uint256),(uint256,uint256),(uint256,uint256),(uint256,uint256),uint256,uint256,uint256,uint256,uint256,uint256,uint256,uint256,uint256,uint256)`
#[derive(
Clone,
Expand Down Expand Up @@ -107,3 +124,24 @@ pub struct VerifyingKey {
pub q_h4: G1Point,
pub q_ecc: G1Point,
}
///`Node(address,uint8,uint64,uint64,uint64,(uint256,uint256))`
#[derive(
Clone,
::ethers::contract::EthAbiType,
::ethers::contract::EthAbiCodec,
serde::Serialize,
serde::Deserialize,
Default,
Debug,
PartialEq,
Eq,
Hash,
)]
pub struct Node {
pub account: ::ethers::core::types::Address,
pub stake_type: u8,
pub balance: u64,
pub register_epoch: u64,
pub exit_epoch: u64,
pub schnorr_vk: EdOnBN254Point,
}
1,206 changes: 1,206 additions & 0 deletions contract-bindings/src/stake_table.rs

Large diffs are not rendered by default.

26 changes: 26 additions & 0 deletions contracts/rust/src/bin/diff_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,11 @@ use jf_plonk::{
testing_apis::Verifier,
transcript::{PlonkTranscript, SolidityTranscript},
};
use jf_primitives::constants::CS_ID_BLS_BN254;

use jf_primitives::pcs::prelude::{Commitment, UnivariateUniversalParams};
use jf_primitives::signatures::bls_over_bn254::KeyPair;
use jf_primitives::signatures::bls_over_bn254::Signature;
use jf_relation::{Arithmetization, Circuit, PlonkCircuit};
use num_bigint::BigUint;
use num_traits::Num;
Expand Down Expand Up @@ -76,6 +80,8 @@ enum Action {
DummyProof,
/// Test only logic
TestOnly,
/// Generate a BLS Signature
GenBLSSig,
}

#[allow(clippy::type_complexity)]
Expand Down Expand Up @@ -387,6 +393,26 @@ fn main() {
Action::TestOnly => {
println!("args: {:?}", cli.args);
}
Action::GenBLSSig => {
let mut rng = jf_utils::test_rng();

let key_pair = KeyPair::generate(&mut rng);
let vk = key_pair.ver_key();
let vk_g2_proj: G2Affine = vk.to_affine();
let msg = Bytes::from(vec![54u8, 12u8]);
let pk_x_c0 = field_to_u256::<Fq>(vk_g2_proj.x.c0);
let pk_x_c1 = field_to_u256::<Fq>(vk_g2_proj.x.c1);
let pk_y_c0 = field_to_u256::<Fq>(vk_g2_proj.y.c0);
let pk_y_c1 = field_to_u256::<Fq>(vk_g2_proj.y.c1);

let sig: Signature = key_pair.sign(&msg, CS_ID_BLS_BN254);
let sig_affine_point = sig.sigma.into_affine();
let sig_x = field_to_u256::<Fq>(sig_affine_point.x);
let sig_y = field_to_u256::<Fq>(sig_affine_point.y);

let res = (sig_x, sig_y, pk_x_c0, pk_x_c1, pk_y_c0, pk_y_c1, msg);
println!("{}", res.encode_hex());
philippecamacho marked this conversation as resolved.
Show resolved Hide resolved
}
};
}

Expand Down
112 changes: 112 additions & 0 deletions contracts/src/StakeTable.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
pragma solidity ^0.8.0;

import { BN254 } from "bn254/BN254.sol";
import { BLSSig } from "./libraries/BLSSig.sol";

import "./interfaces/IStakeTable.sol";

contract StakeTable is IStakeTable {
mapping(bytes32 keyHash => Node node) private nodesTable;
uint256[2] private totalStakeArr;
uint32 private totalNumberOfKeys;
uint256 private totalVotingStakeVal;
uint64 private numRegistrations;
uint64 private numPendingExits;

// TODO check
function hashBlsKey(BN254.G2Point calldata blsVK) private pure returns (bytes32) {
uint256 x0 = blsVK.x0;
uint256 x1 = blsVK.x1;
uint256 y0 = blsVK.y0;
uint256 y1 = blsVK.y1;
bytes32 hash = keccak256(abi.encode(x0, x1, y0, y1));
return hash;
philippecamacho marked this conversation as resolved.
Show resolved Hide resolved
}

function totalStake() external view returns (uint256, uint256) {
return (totalStakeArr[0], totalStakeArr[1]);
}

function totalKeys() external view returns (uint32) {
return totalNumberOfKeys;
}

function totalVotingStake() external view returns (uint256) {
return totalVotingStakeVal;
}

function lookupStake(BN254.G2Point calldata blsVK) external view returns (uint64) {
Node memory node = this.lookupNode(blsVK);
return node.balance;
}

function lookupNode(BN254.G2Point calldata blsVK) external view returns (Node memory) {
bytes32 hash = hashBlsKey(blsVK);
return nodesTable[hash];
}

function nextRegistrationEpoch() external view returns (uint64) {
if (numRegistrations == 0) {
return 0;
} else {
return 1;
}
}

function numPendingRegistrations() external view returns (uint64) {
return numRegistrations;
}

function nextExitEpoch() external view returns (uint64) {
if (numPendingExits == 0) {
return 0;
} else {
return 1;
}
}

function numPendingExit() external view returns (uint64) {
return numPendingExits;
}

function register(
BN254.G2Point calldata blsVK,
EdOnBN254.EdOnBN254Point calldata schnorrVK,
uint64 amount,
StakeType stakeType,
BN254.G1Point calldata blsSig,
uint64 validUntilEpoch
) external returns (bool) {
uint64 thisEpoch = 0; // TODO
Node memory node =
Node(msg.sender, stakeType, amount, thisEpoch, validUntilEpoch, schnorrVK);
// TODO Check blsSig
if (blsSig.x == 0) {
return false;
}
bytes32 hash = hashBlsKey(blsVK);
nodesTable[hash] = node;
return true; // TODO
}

function deposit(BN254.G2Point calldata blsVK, uint64 amount)
external
returns (uint64, uint64)
{
bytes32 hash = hashBlsKey(blsVK);
nodesTable[hash].balance += amount;
return (0, 0);
}

function requestExit(BN254.G2Point calldata blsVK) external returns (bool) {
bytes32 hash = hashBlsKey(blsVK);
nodesTable[hash].exitEpoch = 0;
return true;
}

function withdrawFunds(BN254.G2Point calldata blsVK) external returns (uint64) {
bytes32 hash = hashBlsKey(blsVK);
nodesTable[hash].balance = 0;
return 0;
}
}
18 changes: 9 additions & 9 deletions contracts/src/interfaces/IStakeTable.sol
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,8 @@ interface IStakeTable {
// We avoid declaring another struct even if the type info helps with readability,
// extra layer of struct introduces overhead and more gas cost.
//
/// @dev BLS verification key (used for Consensus voting in HotShot) is `BN254.G1Point`.
// `type BlsVerKey is BN254.G1Point;`
/// @dev BLS verification key (used for Consensus voting in HotShot) is `BN254.G2Point`.
// `type BlsVerKey is BN254.G2Point;`
//
/// @dev Verification key of a Schnorr signature is just a `EdOnBN254.EdOnBN254Point`
// `type SchnorrVerKey is EdOnBN254.EdOnBN254Point;`
Expand Down Expand Up @@ -56,9 +56,9 @@ interface IStakeTable {
function totalVotingStake() external view returns (uint256);

/// @notice Look up the balance of `blsVK`
function lookupStake(BN254.G1Point calldata blsVK) external view returns (uint64);
function lookupStake(BN254.G2Point calldata blsVK) external view returns (uint64);
/// @notice Look up the full `Node` state associated with `blsVK`
function lookupNode(BN254.G1Point calldata blsVK) external view returns (Node memory);
function lookupNode(BN254.G2Point calldata blsVK) external view returns (Node memory);

// === Queuing Stats ===

Expand Down Expand Up @@ -89,11 +89,11 @@ interface IStakeTable {
/// the contract only treat it as auxiliary info submitted by `blsVK`.
/// @dev `blsSig` field is necessary to prevent "rogue public-key attack".
function register(
BN254.G1Point calldata blsVK,
BN254.G2Point calldata blsVK,
EdOnBN254.EdOnBN254Point calldata schnorrVK,
uint64 amount,
StakeType stakeType,
bytes calldata blsSig,
BN254.G1Point calldata blsSig,
philippecamacho marked this conversation as resolved.
Show resolved Hide resolved
uint64 validUntilEpoch
) external returns (bool);

Expand All @@ -102,20 +102,20 @@ interface IStakeTable {
/// @param blsVK The BLS verification key
/// @param amount The amount to deposit
/// @return (newBalance, effectiveEpoch) the new balance effective at a future epoch
function deposit(BN254.G1Point calldata blsVK, uint64 amount)
function deposit(BN254.G2Point calldata blsVK, uint64 amount)
external
returns (uint64, uint64);

/// @notice Request to exit from the stake table, not immediately withdrawable!
///
/// @param blsVK The BLS verification key to exit
/// @return success status
function requestExit(BN254.G1Point calldata blsVK) external returns (bool);
function requestExit(BN254.G2Point calldata blsVK) external returns (bool);

/// @notice Withdraw from the staking pool. Transfers occur! Only successfully exited keys can
/// withdraw past their `exitEpoch`.
///
/// @param blsVK The BLS verification key to withdraw
/// @return The total amount withdrawn, equal to `Node.balance` associated with `blsVK`
function withdrawFunds(BN254.G1Point calldata blsVK) external returns (uint64);
function withdrawFunds(BN254.G2Point calldata blsVK) external returns (uint64);
}
47 changes: 47 additions & 0 deletions contracts/test/StakeTable.t.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
// SPDX-License-Identifier: Unlicensed

/* solhint-disable contract-name-camelcase, func-name-mixedcase, one-contract-per-file */

pragma solidity ^0.8.0;

// Libraries
import "forge-std/Test.sol";
import { BN254 } from "bn254/BN254.sol";
import { BLSSig } from "../src/libraries/BLSSig.sol";

// Target contract
import { StakeTable as S } from "../src/StakeTable.sol";

contract StableTable_keyRegister_Test is Test {
S public stakeTable;

function setUp() public {
stakeTable = new S();
}

/// @dev Tests the key registering process
function testKeyRegister() external {
// Generate a BLS signature in rust and read it in solidity
string[] memory cmds = new string[](2);
cmds[0] = "diff-test";
cmds[1] = "gen-bls-sig";
philippecamacho marked this conversation as resolved.
Show resolved Hide resolved

bytes memory result = vm.ffi(cmds);
(
uint256 blsSigX,
uint256 blsSigY,
uint256 blsVkx0,
uint256 blsVkx1,
uint256 blsVky0,
uint256 blsVky1,
bytes memory message
) = abi.decode(result, (uint256, uint256, uint256, uint256, uint256, uint256, bytes));

BN254.G1Point memory sig = BN254.G1Point(blsSigX, blsSigY);

// Note: (x,y) coordinates for each field component must be inverted.
BN254.G2Point memory vk = BN254.G2Point(blsVkx1, blsVkx0, blsVky1, blsVky0);

BLSSig.verifyBlsSig(message, sig, vk);
}
}