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

feat: more solidity tests #8

Merged
merged 2 commits into from
Oct 10, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 1 addition & 1 deletion chapter1/bls-multisig/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ EIP-2537 introduces a set of precompiled contracts enabling elliptic curve opera
## Implementation

### Contract
We demonstrate a simple multisignature contract [BLSMultisig](../contracts/BLSMultisig.sol) which keeps a list of signers public keys and allows executing arbitrary operations which are signed by a subset of signers. Both stored public keys and signatures can be aggregated, thus allowing for much better scalability for large numbers of signers vs ECDSA. Let's walk through the contract's code.
We demonstrate a simple multisignature contract [BLSMultisig](../contracts/src/BLSMultisig.sol) which keeps a list of signers public keys and allows executing arbitrary operations which are signed by a subset of signers. Both stored public keys and signatures can be aggregated, thus allowing for much better scalability for large numbers of signers vs ECDSA. Let's walk through the contract's code.

BLS signing operates on two curves: G1 and G2. In our case we will store public keys on G1 while signatures and messages will be on G2. To sign or verify a message consisting of arbitrary bytes, we need to firstly map the message to a point on G2. There is a commonly used [algorithm](https://datatracker.ietf.org/doc/html/rfc9380#name-hashing-to-a-finite-field) for this mapping, we are using its implementation in Solidity:

Expand Down
2 changes: 1 addition & 1 deletion chapter1/bls-multisig/python/README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Python BLS Multisig

This example demonstrates an integration of [BlsMultisig](../../contracts/BLSMultisig.sol) with Python.
This example demonstrates an integration of [BlsMultisig](../../contracts/src/BLSMultisig.sol) with Python.

## Running the example

Expand Down
2 changes: 1 addition & 1 deletion chapter1/bls-multisig/rust/README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Rust BLS Multisig

This example demonstrates an integration of [BlsMultisig](../../contracts/BLSMultisig.sol) with Rust.
This example demonstrates an integration of [BlsMultisig](../../contracts/src/BLSMultisig.sol) with Rust.

## Running the example

Expand Down
2 changes: 1 addition & 1 deletion chapter1/bls-multisig/rust/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ alloy::sol! {
#[derive(Debug, Default, PartialEq, Eq, PartialOrd, Ord)]
#[sol(rpc)]
BLSMultisig,
"../../out/BLSMultisig.sol/BLSMultisig.json"
"../../contracts/out/BLSMultisig.sol/BLSMultisig.json"
}

/// Generates `num` BLS keys and returns them as a tuple of private and public keys
Expand Down
4 changes: 2 additions & 2 deletions chapter1/contracts/src/SimpleDelegateContract.sol
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,11 @@ contract SimpleDelegateContract {
function execute(Call[] memory calls) external payable {
for (uint256 i = 0; i < calls.length; i++) {
Call memory call = calls[i];
(bool success, ) = call.to.call{value: call.value}(call.data);
(bool success,) = call.to.call{value: call.value}(call.data);
require(success, "Call failed");
emit Executed(call.to, call.value, call.data);
}
}

receive() external payable {}
}
}
94 changes: 94 additions & 0 deletions chapter1/contracts/test/BLS.t.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.23;

import {Test, console} from "forge-std/Test.sol";
import {BLS} from "../src/libraries/BLS.sol";

/// @notice A simple test demonstrating BLS signature verification.
contract BLSTest is Test {
/// @notice The generator point in G1 (P1).
BLS.G1Point G1_GENERATOR = BLS.G1Point(
BLS.Fp(
31827880280837800241567138048534752271,
88385725958748408079899006800036250932223001591707578097800747617502997169851
),
BLS.Fp(
11568204302792691131076548377920244452,
114417265404584670498511149331300188430316142484413708742216858159411894806497
)
);

/// @notice The negated generator point in G1 (-P1).
BLS.G1Point NEGATED_G1_GENERATOR = BLS.G1Point(
BLS.Fp(
31827880280837800241567138048534752271,
88385725958748408079899006800036250932223001591707578097800747617502997169851
),
BLS.Fp(
22997279242622214937712647648895181298,
46816884707101390882112958134453447585552332943769894357249934112654335001290
)
);

/// @dev Demonstrates the signing and verification of a message.
function test() public {
// Obtain the private key as a random scalar.
uint256 privateKey = vm.randomUint();

// Public key is the generator point multiplied by the private key.
BLS.G1Point memory publicKey = BLS.G1Mul(G1_GENERATOR, privateKey);

// Compute the message point by mapping message's keccak256 hash to a point in G2.
bytes memory message = "hello world";
BLS.G2Point memory messagePoint = BLS.MapFp2ToG2(BLS.Fp2(BLS.Fp(0, 0), BLS.Fp(0, uint256(keccak256(message)))));

// Obtain the signature by multiplying the message point by the private key.
BLS.G2Point memory signature = BLS.G2Mul(messagePoint, privateKey);

// Invoke the pairing check to verify the signature.
BLS.G1Point[] memory g1Points = new BLS.G1Point[](2);
g1Points[0] = NEGATED_G1_GENERATOR;
g1Points[1] = publicKey;

BLS.G2Point[] memory g2Points = new BLS.G2Point[](2);
g2Points[0] = signature;
g2Points[1] = messagePoint;

assertTrue(BLS.Pairing(g1Points, g2Points));
}

/// @dev Demonstrates the aggregation and verification of two signatures.
function testAggregated() public {
// private keys
uint256 sk1 = vm.randomUint();
uint256 sk2 = vm.randomUint();

// public keys
BLS.G1Point memory pk1 = BLS.G1Mul(G1_GENERATOR, sk1);
BLS.G1Point memory pk2 = BLS.G1Mul(G1_GENERATOR, sk2);

// Compute the message point by mapping message's keccak256 hash to a point in G2.
bytes memory message = "hello world";
BLS.G2Point memory messagePoint = BLS.MapFp2ToG2(BLS.Fp2(BLS.Fp(0, 0), BLS.Fp(0, uint256(keccak256(message)))));

// signatures
BLS.G2Point memory sig1 = BLS.G2Mul(messagePoint, sk1);
BLS.G2Point memory sig2 = BLS.G2Mul(messagePoint, sk2);

// aggregated signature
BLS.G2Point memory sig = BLS.G2Add(sig1, sig2);

// Invoke the pairing check to verify the signature.
BLS.G1Point[] memory g1Points = new BLS.G1Point[](3);
g1Points[0] = NEGATED_G1_GENERATOR;
g1Points[1] = pk1;
g1Points[2] = pk2;

BLS.G2Point[] memory g2Points = new BLS.G2Point[](3);
g2Points[0] = sig;
g2Points[1] = messagePoint;
g2Points[2] = messagePoint;

assertTrue(BLS.Pairing(g1Points, g2Points));
}
}
23 changes: 23 additions & 0 deletions chapter1/contracts/test/SimpleDelegateContract.t.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.23;

import {Test, console} from "forge-std/Test.sol";
import {SimpleDelegateContract} from "../src/SimpleDelegateContract.sol";

contract SimpleDelegateContractTest is Test {
function test() public {
address payable ALICE = payable(address(0xa11ce));
SimpleDelegateContract delegation = new SimpleDelegateContract();

// let's inject EIP-7702 delegation designation into ALICE's code to
// make it forward all calls to the deployed contract
vm.etch(ALICE, bytes.concat(hex"ef0100", abi.encodePacked(delegation)));

ALICE.call{value: 1e18}("");

SimpleDelegateContract.Call[] memory calls = new SimpleDelegateContract.Call[](1);
calls[0] = SimpleDelegateContract.Call({data: "", to: address(0), value: 1e18});

SimpleDelegateContract(ALICE).execute(calls);
}
}
23 changes: 15 additions & 8 deletions chapter1/eof/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -101,24 +101,31 @@ Contracts compiled for EOF tend to consume less gas. You can verify this yoursel

```bash
$ forge test
Ran 1 test for test/P256.t.sol:BLSTest
Ran 1 test for contracts/test/SimpleDelegateContract.t.sol:SimpleDelegateContractTest
[PASS] test() (gas: 301928)
Suite result: ok. 1 passed; 0 failed; 0 skipped; finished in 6.87ms (2.15ms CPU time)

Ran 1 test for contracts/test/P256.t.sol:BLSTest
[PASS] test() (gas: 8951)
Suite result: ok. 1 passed; 0 failed; 0 skipped; finished in 7.74ms (3.89ms CPU time)
Suite result: ok. 1 passed; 0 failed; 0 skipped; finished in 8.86ms (4.14ms CPU time)

Ran 2 tests for test/BLS.t.sol:BLSTest
Ran 2 tests for contracts/test/BLS.t.sol:BLSTest
[PASS] test() (gas: 321781)
[PASS] testAggregated() (gas: 439327)
Suite result: ok. 2 passed; 0 failed; 0 skipped; finished in 9.27ms (9.88ms CPU time)

$ forge test --eof
Ran 1 test for test/P256.t.sol:BLSTest
Ran 1 test for contracts/test/SimpleDelegateContract.t.sol:SimpleDelegateContractTest
[PASS] test() (gas: 261751)
Suite result: ok. 1 passed; 0 failed; 0 skipped; finished in 9.37ms (2.47ms CPU time)

Ran 1 test for contracts/test/P256.t.sol:BLSTest
[PASS] test() (gas: 8191)
Suite result: ok. 1 passed; 0 failed; 0 skipped; finished in 10.62ms (4.74ms CPU time)
Suite result: ok. 1 passed; 0 failed; 0 skipped; finished in 11.44ms (4.54ms CPU time)

Ran 2 tests for test/BLS.t.sol:BLSTest
Ran 2 tests for contracts/test/BLS.t.sol:BLSTest
[PASS] test() (gas: 314754)
[PASS] testAggregated() (gas: 425091)
Suite result: ok. 2 passed; 0 failed; 0 skipped; finished in 11.04ms (9.69ms CPU time)
Suite result: ok. 2 passed; 0 failed; 0 skipped; finished in 12.74ms (11.11ms CPU time)
```

As you can see, EOF-compiled contracts are 5-10% more gas efficient.
Expand Down
18 changes: 6 additions & 12 deletions chapter1/simple-7702/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,17 +67,11 @@ Note that in this over-simplified example, you’ll already see some issues e.g.

To test this delegation feature, you may use the `vm.etch` cheatcode in your tests as follows:

```bash
import {Test} from "forge-std/Test.sol";
import {SimpleDelegateContract} from "../src/SimpleDelegateContract.sol";

contract DelegationTest is Test {
function test() public {
SimpleDelegateContract delegation = new SimpleDelegateContract();
// this sets ALICE's EOA code to the deployed contract code
vm.etch(ALICE, address(delegation).code);
}
}
```solidity
// this will inject delegation designation into ALICE's code
vm.etch(ALICE, bytes.concat(hex"ef0100", abi.encodePacked(contractToDelegate)));
```

This cheat code allows you to **simulate that ALICE's account is no longer a regular EOA but a contract**(like `P256Delegation`) and then test how delegations or transactions behave from that new "smart contract" EOA.
This cheat code allows you to **simulate that ALICE's account is no longer a regular EOA but a contract**(like `P256Delegation`) and then test how delegations or transactions behave from that new "smart contract" EOA.

You can check out complete example in [SimpleDelegateContract.t.sol](../contracts/test/SimpleDelegateContract.t.sol)