diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 9282e82..9aebfdc 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -1,6 +1,10 @@ name: test -on: workflow_dispatch +on: + pull_request: + merge_group: + push: + branches: [main] env: FOUNDRY_PROFILE: ci @@ -27,8 +31,3 @@ jobs: forge --version forge build --sizes id: build - - - name: Run Forge tests - run: | - forge test -vvv - id: test diff --git a/README.md b/README.md index 9265b45..4978644 100644 --- a/README.md +++ b/README.md @@ -1,66 +1,35 @@ -## Foundry +## forge-alphanet -**Foundry is a blazing fast, portable and modular toolkit for Ethereum application development written in Rust.** +Set of solidity utilities to ease deployment and usage of applications on +[AlphaNet]. -Foundry consists of: +### BLS library -- **Forge**: Ethereum testing framework (like Truffle, Hardhat and DappTools). -- **Cast**: Swiss army knife for interacting with EVM smart contracts, sending transactions and getting chain data. -- **Anvil**: Local Ethereum node, akin to Ganache, Hardhat Network. -- **Chisel**: Fast, utilitarian, and verbose solidity REPL. +Functions to allow calling each of the BLS precompiles defined in [EIP-2537] +without the low level details. -## Documentation +For example, this is how the library can be used from a solidity smart contract: +```solidity +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.25; -https://book.getfoundry.sh/ +import "/path/to/forge-alphanet/src/libraries/BLS.sol"; -## Usage +contract BLSExample { + event OperationResult(bool success, bytes result); -### Build + // Function to perform a BLS12-381 G1 addition with error handling + function performG1Add(bytes memory input) public { + (bool success, bytes memory output) = BLS.G1Add(input); -```shell -$ forge build + if (!success) { + emit OperationResult(false, ""); + } else { + emit OperationResult(true, output); + } + } +} ``` -### Test - -```shell -$ forge test -``` - -### Format - -```shell -$ forge fmt -``` - -### Gas Snapshots - -```shell -$ forge snapshot -``` - -### Anvil - -```shell -$ anvil -``` - -### Deploy - -```shell -$ forge script script/Counter.s.sol:CounterScript --rpc-url --private-key -``` - -### Cast - -```shell -$ cast -``` - -### Help - -```shell -$ forge --help -$ anvil --help -$ cast --help -``` +[AlphaNet]: https://github.com/paradigmxyz/alphanet +[EIP-2537]: https://eips.ethereum.org/EIPS/eip-2537 diff --git a/patch b/patch deleted file mode 100644 index 8a48908..0000000 --- a/patch +++ /dev/null @@ -1,338 +0,0 @@ -diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml -index 9282e82..9aebfdc 100644 ---- a/.github/workflows/test.yml -+++ b/.github/workflows/test.yml -@@ -1,6 +1,10 @@ - name: test - --on: workflow_dispatch -+on: -+ pull_request: -+ merge_group: -+ push: -+ branches: [main] - - env: - FOUNDRY_PROFILE: ci -@@ -27,8 +31,3 @@ jobs: - forge --version - forge build --sizes - id: build -- -- - name: Run Forge tests -- run: | -- forge test -vvv -- id: test -diff --git a/.gitmodules b/.gitmodules -new file mode 100644 -index 0000000..888d42d ---- /dev/null -+++ b/.gitmodules -@@ -0,0 +1,3 @@ -+[submodule "lib/forge-std"] -+ path = lib/forge-std -+ url = https://github.com/foundry-rs/forge-std -diff --git a/README.md b/README.md -index 9265b45..4978644 100644 ---- a/README.md -+++ b/README.md -@@ -1,66 +1,35 @@ --## Foundry -+## forge-alphanet - --**Foundry is a blazing fast, portable and modular toolkit for Ethereum application development written in Rust.** -+Set of solidity utilities to ease deployment and usage of applications on -+[AlphaNet]. - --Foundry consists of: -+### BLS library - --- **Forge**: Ethereum testing framework (like Truffle, Hardhat and DappTools). --- **Cast**: Swiss army knife for interacting with EVM smart contracts, sending transactions and getting chain data. --- **Anvil**: Local Ethereum node, akin to Ganache, Hardhat Network. --- **Chisel**: Fast, utilitarian, and verbose solidity REPL. -+Functions to allow calling each of the BLS precompiles defined in [EIP-2537] -+without the low level details. - --## Documentation -+For example, this is how the library can be used from a solidity smart contract: -+```solidity -+// SPDX-License-Identifier: MIT -+pragma solidity ^0.8.25; - --https://book.getfoundry.sh/ -+import "/path/to/forge-alphanet/src/libraries/BLS.sol"; - --## Usage -+contract BLSExample { -+ event OperationResult(bool success, bytes result); - --### Build -+ // Function to perform a BLS12-381 G1 addition with error handling -+ function performG1Add(bytes memory input) public { -+ (bool success, bytes memory output) = BLS.G1Add(input); - --```shell --$ forge build -+ if (!success) { -+ emit OperationResult(false, ""); -+ } else { -+ emit OperationResult(true, output); -+ } -+ } -+} - ``` - --### Test -- --```shell --$ forge test --``` -- --### Format -- --```shell --$ forge fmt --``` -- --### Gas Snapshots -- --```shell --$ forge snapshot --``` -- --### Anvil -- --```shell --$ anvil --``` -- --### Deploy -- --```shell --$ forge script script/Counter.s.sol:CounterScript --rpc-url --private-key --``` -- --### Cast -- --```shell --$ cast --``` -- --### Help -- --```shell --$ forge --help --$ anvil --help --$ cast --help --``` -+[AlphaNet]: https://github.com/paradigmxyz/alphanet -+[EIP-2537]: https://eips.ethereum.org/EIPS/eip-2537 -diff --git a/lib/forge-std b/lib/forge-std -new file mode 160000 -index 0000000..bb4ceea ---- /dev/null -+++ b/lib/forge-std -@@ -0,0 +1 @@ -+Subproject commit bb4ceea94d6f10eeb5b41dc2391c6c8bf8e734ef -diff --git a/script/Counter.s.sol b/script/Counter.s.sol -deleted file mode 100644 -index df9ee8b..0000000 ---- a/script/Counter.s.sol -+++ /dev/null -@@ -1,12 +0,0 @@ --// SPDX-License-Identifier: UNLICENSED --pragma solidity ^0.8.13; -- --import {Script, console} from "forge-std/Script.sol"; -- --contract CounterScript is Script { -- function setUp() public {} -- -- function run() public { -- vm.broadcast(); -- } --} -diff --git a/src/Counter.sol b/src/Counter.sol -deleted file mode 100644 -index aded799..0000000 ---- a/src/Counter.sol -+++ /dev/null -@@ -1,14 +0,0 @@ --// SPDX-License-Identifier: UNLICENSED --pragma solidity ^0.8.13; -- --contract Counter { -- uint256 public number; -- -- function setNumber(uint256 newNumber) public { -- number = newNumber; -- } -- -- function increment() public { -- number++; -- } --} -diff --git a/src/libraries/BLS.sol b/src/libraries/BLS.sol -new file mode 100644 -index 0000000..f2d126f ---- /dev/null -+++ b/src/libraries/BLS.sol -@@ -0,0 +1,127 @@ -+// SPDX-License-Identifier: MIT -+pragma solidity ^0.8.25; -+ -+library BLS { -+ // Precompile addresses come from the BLS addresses submodule in AlphaNet, see: -+ // -+ -+ // Output sizes come from the ABI definition for each of the BLS operations -+ // in EIP-2537, see: -+ // G1ADD output size allocation -+ uint256 constant G1ADD_OUTPUT_SIZE = 128; -+ // G1MUL output size allocation -+ uint256 constant G1MUL_OUTPUT_SIZE = 128; -+ // G1MULTIEXP output size allocation -+ uint256 constant G1MULTIEXP_OUTPUT_SIZE = 128; -+ // G2ADD output size allocation -+ uint256 constant G2ADD_OUTPUT_SIZE = 256; -+ // G2MUL output size allocation -+ uint256 constant G2MUL_OUTPUT_SIZE = 256; -+ // G2MULTIEXP output size allocation -+ uint256 constant G2MULTIEXP_OUTPUT_SIZE = 256; -+ // PAIRING output size allocation -+ uint256 constant PAIRING_OUTPUT_SIZE = 32; -+ // MAP_FP_TO_G1 output size allocation -+ uint256 constant MAP_FP_TO_G1_OUTPUT_SIZE = 128; -+ // MAP_FP2_TO_G2 output size allocation -+ uint256 constant MAP_FP2_TO_G2_OUTPUT_SIZE = 256; -+ -+ // Function to perform G1ADD operation -+ function G1Add(bytes memory input) internal view returns (bool, bytes memory) { -+ bytes memory output = new bytes(G1ADD_OUTPUT_SIZE); -+ bool success; -+ assembly { -+ // G1ADD address is 0x0b -+ success := staticcall(gas(), 0x0b, add(input, 0x20), mload(input), output, G1ADD_OUTPUT_SIZE) -+ } -+ return (success, output); -+ } -+ -+ // Function to perform G1MUL operation -+ function G1Mul(bytes memory input) internal view returns (bool, bytes memory) { -+ bytes memory output = new bytes(G1MUL_OUTPUT_SIZE); -+ bool success; -+ assembly { -+ // G1MUL address is 0x0c -+ success := staticcall(gas(), 0x0c, add(input, 0x20), mload(input), output, G1MUL_OUTPUT_SIZE) -+ } -+ return (success, output); -+ } -+ -+ // Function to perform G1MULTIEXP operation -+ function G1MultiExp(bytes memory input) internal view returns (bool, bytes memory) { -+ bytes memory output = new bytes(G1MULTIEXP_OUTPUT_SIZE); -+ bool success; -+ assembly { -+ // G1MULTIEXP address is 0x0d -+ success := staticcall(gas(), 0x0d, add(input, 0x20), mload(input), output, G1MULTIEXP_OUTPUT_SIZE) -+ } -+ return (success, output); -+ } -+ -+ // Function to perform G2ADD operation -+ function G2Add(bytes memory input) internal view returns (bool, bytes memory) { -+ bytes memory output = new bytes(G2ADD_OUTPUT_SIZE); -+ bool success; -+ assembly { -+ // G2ADD address is 0x0e -+ success := staticcall(gas(), 0x0e, add(input, 0x20), mload(input), output, G2ADD_OUTPUT_SIZE) -+ } -+ return (success, output); -+ } -+ -+ // Function to perform G2MUL operation -+ function G2Mul(bytes memory input) internal view returns (bool, bytes memory) { -+ bytes memory output = new bytes(G2MUL_OUTPUT_SIZE); -+ bool success; -+ assembly { -+ // G2MUL address is 0x0f -+ success := staticcall(gas(), 0x0f, add(input, 0x20), mload(input), output, G2MUL_OUTPUT_SIZE) -+ } -+ return (success, output); -+ } -+ -+ // Function to perform G2MULTIEXP operation -+ function G2MultiExp(bytes memory input) internal view returns (bool, bytes memory) { -+ bytes memory output = new bytes(G2MULTIEXP_OUTPUT_SIZE); -+ bool success; -+ assembly { -+ // G2MULTIEXP address is 0x10 -+ success := staticcall(gas(), 0x10, add(input, 0x20), mload(input), output, G2MULTIEXP_OUTPUT_SIZE) -+ } -+ return (success, output); -+ } -+ -+ // Function to perform PAIRING operation -+ function Pairing(bytes memory input) internal view returns (bool, bytes memory) { -+ bytes memory output = new bytes(PAIRING_OUTPUT_SIZE); -+ bool success; -+ assembly { -+ // PAIRING address is 0x11 -+ success := staticcall(gas(), 0x11, add(input, 0x20), mload(input), output, PAIRING_OUTPUT_SIZE) -+ } -+ return (success, output); -+ } -+ -+ // Function to perform MAP_FP_TO_G1 operation -+ function MapFpToG1(bytes memory input) internal view returns (bool, bytes memory) { -+ bytes memory output = new bytes(MAP_FP_TO_G1_OUTPUT_SIZE); -+ bool success; -+ assembly { -+ // MAP_FP_TO_G1 address is 0x12 -+ success := staticcall(gas(), 0x12, add(input, 0x20), mload(input), output, MAP_FP_TO_G1_OUTPUT_SIZE) -+ } -+ return (success, output); -+ } -+ -+ // Function to perform MAP_FP2_TO_G2 operation -+ function MapFp2ToG2(bytes memory input) internal view returns (bool, bytes memory) { -+ bytes memory output = new bytes(MAP_FP2_TO_G2_OUTPUT_SIZE); -+ bool success; -+ assembly { -+ // MAP_FP2_TO_G2 address is 0x13 -+ success := staticcall(gas(), 0x13, add(input, 0x20), mload(input), output, MAP_FP2_TO_G2_OUTPUT_SIZE) -+ } -+ return (success, output); -+ } -+} -diff --git a/test/Counter.t.sol b/test/Counter.t.sol -deleted file mode 100644 -index 54b724f..0000000 ---- a/test/Counter.t.sol -+++ /dev/null -@@ -1,24 +0,0 @@ --// SPDX-License-Identifier: UNLICENSED --pragma solidity ^0.8.13; -- --import {Test, console} from "forge-std/Test.sol"; --import {Counter} from "../src/Counter.sol"; -- --contract CounterTest is Test { -- Counter public counter; -- -- function setUp() public { -- counter = new Counter(); -- counter.setNumber(0); -- } -- -- function test_Increment() public { -- counter.increment(); -- assertEq(counter.number(), 1); -- } -- -- function testFuzz_SetNumber(uint256 x) public { -- counter.setNumber(x); -- assertEq(counter.number(), x); -- } --} diff --git a/script/Counter.s.sol b/script/Counter.s.sol deleted file mode 100644 index df9ee8b..0000000 --- a/script/Counter.s.sol +++ /dev/null @@ -1,12 +0,0 @@ -// SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.13; - -import {Script, console} from "forge-std/Script.sol"; - -contract CounterScript is Script { - function setUp() public {} - - function run() public { - vm.broadcast(); - } -} diff --git a/src/Counter.sol b/src/Counter.sol deleted file mode 100644 index aded799..0000000 --- a/src/Counter.sol +++ /dev/null @@ -1,14 +0,0 @@ -// SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.13; - -contract Counter { - uint256 public number; - - function setNumber(uint256 newNumber) public { - number = newNumber; - } - - function increment() public { - number++; - } -} diff --git a/src/libraries/BLS.sol b/src/libraries/BLS.sol new file mode 100644 index 0000000..f2d126f --- /dev/null +++ b/src/libraries/BLS.sol @@ -0,0 +1,127 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.25; + +library BLS { + // Precompile addresses come from the BLS addresses submodule in AlphaNet, see: + // + + // Output sizes come from the ABI definition for each of the BLS operations + // in EIP-2537, see: + // G1ADD output size allocation + uint256 constant G1ADD_OUTPUT_SIZE = 128; + // G1MUL output size allocation + uint256 constant G1MUL_OUTPUT_SIZE = 128; + // G1MULTIEXP output size allocation + uint256 constant G1MULTIEXP_OUTPUT_SIZE = 128; + // G2ADD output size allocation + uint256 constant G2ADD_OUTPUT_SIZE = 256; + // G2MUL output size allocation + uint256 constant G2MUL_OUTPUT_SIZE = 256; + // G2MULTIEXP output size allocation + uint256 constant G2MULTIEXP_OUTPUT_SIZE = 256; + // PAIRING output size allocation + uint256 constant PAIRING_OUTPUT_SIZE = 32; + // MAP_FP_TO_G1 output size allocation + uint256 constant MAP_FP_TO_G1_OUTPUT_SIZE = 128; + // MAP_FP2_TO_G2 output size allocation + uint256 constant MAP_FP2_TO_G2_OUTPUT_SIZE = 256; + + // Function to perform G1ADD operation + function G1Add(bytes memory input) internal view returns (bool, bytes memory) { + bytes memory output = new bytes(G1ADD_OUTPUT_SIZE); + bool success; + assembly { + // G1ADD address is 0x0b + success := staticcall(gas(), 0x0b, add(input, 0x20), mload(input), output, G1ADD_OUTPUT_SIZE) + } + return (success, output); + } + + // Function to perform G1MUL operation + function G1Mul(bytes memory input) internal view returns (bool, bytes memory) { + bytes memory output = new bytes(G1MUL_OUTPUT_SIZE); + bool success; + assembly { + // G1MUL address is 0x0c + success := staticcall(gas(), 0x0c, add(input, 0x20), mload(input), output, G1MUL_OUTPUT_SIZE) + } + return (success, output); + } + + // Function to perform G1MULTIEXP operation + function G1MultiExp(bytes memory input) internal view returns (bool, bytes memory) { + bytes memory output = new bytes(G1MULTIEXP_OUTPUT_SIZE); + bool success; + assembly { + // G1MULTIEXP address is 0x0d + success := staticcall(gas(), 0x0d, add(input, 0x20), mload(input), output, G1MULTIEXP_OUTPUT_SIZE) + } + return (success, output); + } + + // Function to perform G2ADD operation + function G2Add(bytes memory input) internal view returns (bool, bytes memory) { + bytes memory output = new bytes(G2ADD_OUTPUT_SIZE); + bool success; + assembly { + // G2ADD address is 0x0e + success := staticcall(gas(), 0x0e, add(input, 0x20), mload(input), output, G2ADD_OUTPUT_SIZE) + } + return (success, output); + } + + // Function to perform G2MUL operation + function G2Mul(bytes memory input) internal view returns (bool, bytes memory) { + bytes memory output = new bytes(G2MUL_OUTPUT_SIZE); + bool success; + assembly { + // G2MUL address is 0x0f + success := staticcall(gas(), 0x0f, add(input, 0x20), mload(input), output, G2MUL_OUTPUT_SIZE) + } + return (success, output); + } + + // Function to perform G2MULTIEXP operation + function G2MultiExp(bytes memory input) internal view returns (bool, bytes memory) { + bytes memory output = new bytes(G2MULTIEXP_OUTPUT_SIZE); + bool success; + assembly { + // G2MULTIEXP address is 0x10 + success := staticcall(gas(), 0x10, add(input, 0x20), mload(input), output, G2MULTIEXP_OUTPUT_SIZE) + } + return (success, output); + } + + // Function to perform PAIRING operation + function Pairing(bytes memory input) internal view returns (bool, bytes memory) { + bytes memory output = new bytes(PAIRING_OUTPUT_SIZE); + bool success; + assembly { + // PAIRING address is 0x11 + success := staticcall(gas(), 0x11, add(input, 0x20), mload(input), output, PAIRING_OUTPUT_SIZE) + } + return (success, output); + } + + // Function to perform MAP_FP_TO_G1 operation + function MapFpToG1(bytes memory input) internal view returns (bool, bytes memory) { + bytes memory output = new bytes(MAP_FP_TO_G1_OUTPUT_SIZE); + bool success; + assembly { + // MAP_FP_TO_G1 address is 0x12 + success := staticcall(gas(), 0x12, add(input, 0x20), mload(input), output, MAP_FP_TO_G1_OUTPUT_SIZE) + } + return (success, output); + } + + // Function to perform MAP_FP2_TO_G2 operation + function MapFp2ToG2(bytes memory input) internal view returns (bool, bytes memory) { + bytes memory output = new bytes(MAP_FP2_TO_G2_OUTPUT_SIZE); + bool success; + assembly { + // MAP_FP2_TO_G2 address is 0x13 + success := staticcall(gas(), 0x13, add(input, 0x20), mload(input), output, MAP_FP2_TO_G2_OUTPUT_SIZE) + } + return (success, output); + } +} diff --git a/test/Counter.t.sol b/test/Counter.t.sol deleted file mode 100644 index 54b724f..0000000 --- a/test/Counter.t.sol +++ /dev/null @@ -1,24 +0,0 @@ -// SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.13; - -import {Test, console} from "forge-std/Test.sol"; -import {Counter} from "../src/Counter.sol"; - -contract CounterTest is Test { - Counter public counter; - - function setUp() public { - counter = new Counter(); - counter.setNumber(0); - } - - function test_Increment() public { - counter.increment(); - assertEq(counter.number(), 1); - } - - function testFuzz_SetNumber(uint256 x) public { - counter.setNumber(x); - assertEq(counter.number(), x); - } -}