diff --git a/bolt-contracts/go.mod b/bolt-contracts/go.mod new file mode 100644 index 00000000..f5b65da2 --- /dev/null +++ b/bolt-contracts/go.mod @@ -0,0 +1,5 @@ +module bolt-contracts + +go 1.22.7 + +require github.com/supranational/blst v0.3.11 diff --git a/bolt-contracts/go.sum b/bolt-contracts/go.sum new file mode 100644 index 00000000..6f41ee08 --- /dev/null +++ b/bolt-contracts/go.sum @@ -0,0 +1,2 @@ +github.com/supranational/blst v0.3.11 h1:LyU6FolezeWAhvQk0k6O/d49jqgO52MSDDfYgbeoEm4= +github.com/supranational/blst v0.3.11/go.mod h1:jZJtfjgudtNl4en1tzwPIV3KjUnQUvG3/j+w+fVonLw= diff --git a/bolt-contracts/main.go b/bolt-contracts/main.go new file mode 100644 index 00000000..527d6f3d --- /dev/null +++ b/bolt-contracts/main.go @@ -0,0 +1,41 @@ +package main + +import ( + "encoding/hex" + "fmt" + "os" + "strings" + + blst "github.com/supranational/blst/bindings/go" +) + +type blsPublicKey = blst.P1Affine + +func main() { + if len(os.Args) != 2 { + fmt.Println("Usage: pubkey_to_g1 ") + os.Exit(1) + } + + pubkey := strings.TrimPrefix(os.Args[1], "0x") + + pubkeyBytes, err := hex.DecodeString(pubkey) + if err != nil { + fmt.Println("Failed to decode pubkey:", err) + os.Exit(1) + } + + if len(pubkeyBytes) != 48 { + fmt.Println("Invalid pubkey length") + os.Exit(1) + } + + G1 := new(blsPublicKey).Uncompress(pubkeyBytes) + + serialized := G1.Serialize() + + x := serialized[0:48] + y := serialized[48:] + + fmt.Printf("0x%x,0x%x\n", x, y) +} diff --git a/bolt-contracts/script/holesky/validators/RegisterValidators.s.sol b/bolt-contracts/script/holesky/validators/RegisterValidators.s.sol index fe44aa36..ddf45337 100644 --- a/bolt-contracts/script/holesky/validators/RegisterValidators.s.sol +++ b/bolt-contracts/script/holesky/validators/RegisterValidators.s.sol @@ -9,6 +9,8 @@ import {Script, console} from "forge-std/Script.sol"; /// @notice Script to register Ethereum validators to Bolt /// @dev this script reads from the config file in /config/holesky/register_validators.json contract RegisterValidators is Script { + using BLS12381 for BLS12381.G1Point; + struct RegisterValidatorsConfig { uint128 maxCommittedGasLimit; address authorizedOperator; @@ -39,7 +41,7 @@ contract RegisterValidators is Script { return IBoltValidatorsV1(vm.parseJsonAddress(json, ".bolt.validators")); } - function _parseConfig() public view returns (RegisterValidatorsConfig memory config) { + function _parseConfig() public returns (RegisterValidatorsConfig memory config) { string memory root = vm.projectRoot(); string memory path = string.concat(root, "/config/holesky/validators.json"); string memory json = vm.readFile(path); @@ -47,33 +49,55 @@ contract RegisterValidators is Script { config.authorizedOperator = vm.parseJsonAddress(json, ".authorizedOperator"); config.maxCommittedGasLimit = uint128(vm.parseJsonUint(json, ".maxCommittedGasLimit")); - bytes[] memory pubkeysRaw = vm.parseJsonBytesArray(json, ".pubkeys"); + string[] memory pubkeysRaw = vm.parseJsonStringArray(json, ".pubkeys"); BLS12381.G1Point[] memory pubkeys = new BLS12381.G1Point[](pubkeysRaw.length); for (uint256 i = 0; i < pubkeysRaw.length; i++) { - bytes memory pubkey = pubkeysRaw[i]; - require(pubkey.length == 96, "Invalid pubkey length"); + string memory pubkey = pubkeysRaw[i]; + + string[] memory convertCmd = new string[](2); + convertCmd[0] = "./script/pubkey_to_g1_wrapper.sh"; + convertCmd[1] = pubkey; + + bytes memory output = vm.ffi(convertCmd); + string memory outputStr = string(output); + string[] memory array = vm.split(outputStr, ","); - uint256[2] memory x; - uint256[2] memory y; + uint256[2] memory x = _bytesToParts(vm.parseBytes(array[0])); + uint256[2] memory y = _bytesToParts(vm.parseBytes(array[1])); - // Assuming each coordinate is split into two 32 bytes - x[0] = uint256(bytes32(_slice(pubkey, 0, 32))); - x[1] = uint256(bytes32(_slice(pubkey, 32, 32))); - y[0] = uint256(bytes32(_slice(pubkey, 64, 32))); - y[1] = uint256(bytes32(_slice(pubkey, 96, 32))); + console.logBytes(abi.encodePacked(x)); + console.logBytes(abi.encodePacked(y)); pubkeys[i] = BLS12381.G1Point(x, y); + + console.log("Registering pubkey:", vm.toString(abi.encodePacked(pubkeys[i].compress()))); } config.pubkeys = pubkeys; } - function _slice(bytes memory data, uint256 start, uint256 length) internal pure returns (bytes memory) { - bytes memory part = new bytes(length); - for (uint256 i = 0; i < length; i++) { - part[i] = data[i + start]; + function _bytesToParts( + bytes memory data + ) public pure returns (uint256[2] memory out) { + require(data.length == 48, "Invalid data length"); + + uint256 value1; + uint256 value2; + + // Load the first 32 bytes into value1 + assembly { + value1 := mload(add(data, 32)) + } + value1 = value1 >> 128; // Clear unwanted upper bits + + // Load the next 16 bytes into value2 + assembly { + value2 := mload(add(data, 48)) } - return part; + // value2 = value2 >> 128; + + out[0] = value1; + out[1] = value2; } } diff --git a/bolt-contracts/script/pubkey_to_g1-darwin-arm64 b/bolt-contracts/script/pubkey_to_g1-darwin-arm64 new file mode 100755 index 00000000..fd51c95c Binary files /dev/null and b/bolt-contracts/script/pubkey_to_g1-darwin-arm64 differ diff --git a/bolt-contracts/script/pubkey_to_g1_wrapper.sh b/bolt-contracts/script/pubkey_to_g1_wrapper.sh new file mode 100755 index 00000000..62e53846 --- /dev/null +++ b/bolt-contracts/script/pubkey_to_g1_wrapper.sh @@ -0,0 +1,23 @@ +#!/bin/bash + +# Only works on Linux +arch=$(uname -i 2>/dev/null) || arch=$(uname -p) +if [[ $OSTYPE == linux* ]]; then + if [[ $arch == x86_64 ]]; then + ./script/pubkey_to_g1-linux-amd64 "$1" + elif [[ $arch == aarch64 ]]; then + ./script/pubkey_to_g1-linux-arm64 "$1" + else + exit 1 + fi +elif [[ $OSTYPE == darwin* ]]; then + if [[ $arch == arm* ]]; then + ./script/pubkey_to_g1-darwin-arm64 "$1" + elif [[ $arch == x86_64 ]]; then + ./script/pubkey_to_g1-darwin-amd64 "$1" + else + exit 1 + fi +else + exit 1 +fi \ No newline at end of file