Skip to content

Commit

Permalink
Add message prefixes (#104)
Browse files Browse the repository at this point in the history
* feat: Add HashWithPrefix to core utils

* feat: Add message prefixes to avoid collision

* chore: Update AVS deployed anvil state

* test: Update message inclusion tests

* test: Update operator test signature and SMT roots

* chore: Update devnet rollup deployment

* fix: Fix devnet deployment script path

* feat: Add anvil state generation for rollup deployment

* chore: Update bindings

* fix: Remove unused error from Keccak256

* refactor: Rename HashWithPrefix to HashMessageWithPrefix
  • Loading branch information
Hyodar authored Apr 22, 2024
1 parent 5eaabe7 commit 9e965c1
Show file tree
Hide file tree
Showing 18 changed files with 99 additions and 60 deletions.
2 changes: 1 addition & 1 deletion contracts/bindings/SFFLRegistryRollup/binding.go

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion contracts/bindings/SFFLServiceManager/binding.go

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion contracts/bindings/SFFLTaskManager/binding.go

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
{
"addresses": {
"deployer": "0xa0Ee7A142d267C1f36714E4a8F75612F20a79720",
"sfflPauserReg": "0xb19b36b1456E65E3A6D514D3F715f204BD59f431",
"sfflProxyAdmin": "0xA15BB66138824a1c7167f5E85b957d04Dd34E468",
"sfflRegistryRollup": "0xe1Aa25618fA0c7A1CFDab5d6B456af611873b629",
"sfflRegistryRollupImpl": "0x8ce361602B935680E8DeC218b820ff5056BeB7af"
"sfflPauserReg": "0xA15BB66138824a1c7167f5E85b957d04Dd34E468",
"sfflProxyAdmin": "0x700b6A60ce7EaaEA56F065753d8dcB9653dbAD35",
"sfflRegistryRollup": "0x8ce361602B935680E8DeC218b820ff5056BeB7af",
"sfflRegistryRollupImpl": "0xb19b36b1456E65E3A6D514D3F715f204BD59f431"
},
"chainInfo": {
"chainId": 31337,
"deploymentBlock": 1
"deploymentBlock": 0
}
}
6 changes: 4 additions & 2 deletions contracts/evm/src/base/message/OperatorSetUpdate.sol
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,15 @@ library OperatorSetUpdate {
RollupOperators.Operator[] operators;
}

bytes32 internal constant OPERATOR_SET_UPDATE_HASH_PREFIX = keccak256("SFFL::OperatorSetUpdateMessage");

/**
* @notice Hashes an operator set update message
* @param message Message structured data
* @return Message hash
*/
function hashCalldata(Message calldata message) internal pure returns (bytes32) {
return keccak256(abi.encode(message));
return keccak256(abi.encode(OPERATOR_SET_UPDATE_HASH_PREFIX, keccak256(abi.encode(message))));
}

/**
Expand All @@ -33,7 +35,7 @@ library OperatorSetUpdate {
* @return Message hash
*/
function hash(Message memory message) internal pure returns (bytes32) {
return keccak256(abi.encode(message));
return keccak256(abi.encode(OPERATOR_SET_UPDATE_HASH_PREFIX, keccak256(abi.encode(message))));
}

/**
Expand Down
6 changes: 4 additions & 2 deletions contracts/evm/src/base/message/StateRootUpdate.sol
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,15 @@ library StateRootUpdate {
bytes32 stateRoot;
}

bytes32 internal constant STATE_ROOT_UPDATE_HASH_PREFIX = keccak256("SFFL::StateRootUpdateMessage");

/**
* @notice Hashes a state root update message
* @param message Message structured data
* @return Message hash
*/
function hashCalldata(Message calldata message) internal pure returns (bytes32) {
return keccak256(abi.encode(message));
return keccak256(abi.encode(STATE_ROOT_UPDATE_HASH_PREFIX, keccak256(abi.encode(message))));
}

/**
Expand All @@ -38,7 +40,7 @@ library StateRootUpdate {
* @return Message hash
*/
function hash(Message memory message) internal pure returns (bytes32) {
return keccak256(abi.encode(message));
return keccak256(abi.encode(STATE_ROOT_UPDATE_HASH_PREFIX, keccak256(abi.encode(message))));
}

/**
Expand Down
6 changes: 4 additions & 2 deletions contracts/evm/src/eth/task/Checkpoint.sol
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ library Checkpoint {
bytes32 hashOfNonSigners;
}

bytes32 internal constant TASK_RESPONSE_HASH_PREFIX = keccak256("SFFL::CheckpointTaskResponse");

/**
* @notice Hashes a checkpoint task (submission)
* @param task Checkpoint task structured data
Expand All @@ -55,7 +57,7 @@ library Checkpoint {
* @return Task response hash
*/
function hash(TaskResponse memory taskResponse) internal pure returns (bytes32) {
return keccak256(abi.encode(taskResponse));
return keccak256(abi.encode(TASK_RESPONSE_HASH_PREFIX, keccak256(abi.encode(taskResponse))));
}

/**
Expand All @@ -64,7 +66,7 @@ library Checkpoint {
* @return Task response hash
*/
function hashCalldata(TaskResponse calldata taskResponse) internal pure returns (bytes32) {
return keccak256(abi.encode(taskResponse));
return keccak256(abi.encode(TASK_RESPONSE_HASH_PREFIX, keccak256(abi.encode(taskResponse))));
}

/**
Expand Down
58 changes: 28 additions & 30 deletions contracts/evm/test/SFFLTaskManager.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -452,34 +452,32 @@ contract SFFLTaskManagerTest is TestUtils {
});

bytes32[] memory sideNodes = new bytes32[](14);
sideNodes[0] = 0xdf1be5ddc9e2322471da289fbabe89736c380cf6e7b43523662f682112c84122;
sideNodes[1] = 0xc52a4ff7f46a1a86455b3b2bb7200f9328ac27d77e3ed73910c49a61af28093f;
sideNodes[2] = 0xf3ee52fa31013a46900c15f0c2f7caf055585d1c82dc102e67c9e82437f14006;
sideNodes[3] = 0x0e71abd111484b7ffa57954f91fb6bb266052d768beb1da6ba3e9158ae237b9c;
sideNodes[4] = 0x50154a0e1869c92ed28b674c40a284def6803be6bbd788f302eabf6da6225d0e;
sideNodes[5] = 0x3d852be9a805d6e31b4d65dcb8661bb3b8ee7033217d385f5a09c57cb64e11a6;
sideNodes[6] = 0x46c274dfc5793fdd7f0325c513d39af05a2fa4f94878dfee5c369e355e073225;
sideNodes[7] = 0xb0f7d42d6b502dcad188430fc6bf76b5d6d25b9ab8cd0f3dcb483743224bf4f7;
sideNodes[8] = 0xfb52ca3c04c8357d5e0aa4f07929ac45aeeb92bd805dc711e6044671cc895a34;
sideNodes[9] = 0xe2b4750a57837a67019ff7117d4c8967735fa760c3ab3bde22ff78ee9150979f;
sideNodes[10] = 0xd62c445fc937037971e212e62ecb5a126bd9ce7c707c6f9753d59375d9c70054;
sideNodes[11] = 0x501c24e0fbf5a628107134b0c32ca165dd56b59eae552095e5a1be9c10b3c7d8;
sideNodes[12] = 0x86c2a8bbd4e626c2c25403b1ef4cbbe105e2e1fd924fb93171962dbd47a3f0c8;
sideNodes[13] = 0xb2ca769155e311bfab9526580c1de1cd2fcaf79fe2cab1833de9b9e3651459d3;

sideNodes[0] = 0xa0286b7cb830ed21a1a2189ee78ba381f6873fe4ccc22d539ab255248edf693e;
sideNodes[1] = 0x7e3cd0e7384e5a7067756b7e28b7efa849e4aaf12a1bdcb24c0ca5cbcaad8cd9;
sideNodes[2] = 0x7630a1cb55f8f353927b18d3a753089c204bc99cef47e3c16d1b2cbaf7c2d036;
sideNodes[3] = 0x3858f0f8dcfd0d3129bcff66835a6fdbf8e24afc615e8f444f467bdc232643ec;
sideNodes[4] = 0xa0639833727ebd631b230b6ac1b1420358df10de7c080c142bc3f86281c4b5a9;
sideNodes[5] = 0x8dae5c1cba3b325a7a5c1469dbd1e0dfb61c73a7ed3800b0792abc95f6225795;
sideNodes[6] = 0x0e19b18097d957f6d96dfaf452417300fec1044004d00eeae251e5e53ba7898f;
sideNodes[7] = 0x77a64d95442ac067e77c91ba17cd5e7e56846eb7b82569d428b5fbbffdfffbc2;
sideNodes[8] = 0xe29c7de383621e49406c7072ea1a4278f557729e666e679169946272532d6b8c;
sideNodes[9] = 0xbc1812f22537be7471527f0521a68b83afefaff574e84791ac70a05eff31e579;
sideNodes[10] = 0x72abfb17637bf5469c09dc4ab46565d0152bebd10e2bcf37b336028613cfb33c;

SparseMerkleTree.Proof memory proof = SparseMerkleTree.Proof({
key: message.index(),
value: message.hash(),
bitMask: 2,
bitMask: 12,
sideNodes: sideNodes,
numSideNodes: 15,
numSideNodes: 13,
nonMembershipLeafPath: bytes32(0),
nonMembershipLeafValue: bytes32(0)
});

Checkpoint.TaskResponse memory taskResponse = Checkpoint.TaskResponse({
referenceTaskIndex: 0,
stateRootUpdatesRoot: 0x0f058469e9fdee877c111cb46f6fdfd81b39985679767a9fe02092c02f3164bc,
stateRootUpdatesRoot: 0x60a11596701d4c4806d5585a092192f4773197ee4819f4b0fad16b071970e3c8,
operatorSetUpdatesRoot: keccak256(hex"f00d")
});

Expand Down Expand Up @@ -698,33 +696,33 @@ contract SFFLTaskManagerTest is TestUtils {
function test_verifyMessageInclusionState_operatorSetUpdate_Inclusion() public {
RollupOperators.Operator[] memory operators = new RollupOperators.Operator[](0);
OperatorSetUpdate.Message memory message =
OperatorSetUpdate.Message({id: 10001, timestamp: 10002, operators: operators});
OperatorSetUpdate.Message({id: 0, timestamp: 1, operators: operators});

bytes32[] memory sideNodes = new bytes32[](9);
sideNodes[0] = 0xfefddb8e58df3d27f864dfff103682cfb0cd5539cfaac9b74f3b303972cf3077;
sideNodes[1] = 0x06c35448da4779acabe2c253e9b127289d2a1a1a7c7c9a1c5cdc48542b142d94;
sideNodes[2] = 0x6a8f4cecdcc4e460552a4f448663f765957f5a8d8935d94bcd8218be6bd9a54b;
sideNodes[3] = 0x2c610282b2a7abfc7ba929033160af1886bc0ad01b64b1427ef58a7ca2842aed;
sideNodes[4] = 0x8c3afd210bf137ce930dde5fd59c6251676d44200434c583f3cd33a85430d8ec;
sideNodes[5] = 0x13c7b2839fc82b97e6d598d47a1c4d90c950fbc88d3cc3b05c74196a5c679190;
sideNodes[6] = 0x69b8cda97a7d5f1aced71bd3862712148d25032ffaeb1348a08e54701a0479ae;
sideNodes[7] = 0xfb1f58ad0c60c69bd0b0ce360492fadce902b9e50e1fe5920be1684608c800fd;
sideNodes[8] = 0x4afd3c90e3a249ad697914f9f26d735f78bf7453b6c936b4f23ce562bf2f3074;
sideNodes[0] = 0xfb6698c46f574262f6ebf78c364ee4062713b4736272dd4accdfd00062af13f1;
sideNodes[1] = 0x5592d260430874768f71103d707880731e6bc4856eb6cba7823dd42088814876;
sideNodes[2] = 0xebee5d31739018590231f08764a35919e9d434aa41ece6267999c608c925961c;
sideNodes[3] = 0x08dca9669f5189255649692a9049a77d362f611c937c05a94197aa653bd30908;
sideNodes[4] = 0x3e8bb9029380067c6df6824b876c67f1e490bf95372d0544a9bdcc790c0cba90;
sideNodes[5] = 0xc9bf81cc84cc3a173d45be52de08d757126c97535f290f84930cc42510e5d90e;
sideNodes[6] = 0xcc0dc57fdc5130c33bdd6779ec3ad71252863681510701b5544b05e822e509b2;
sideNodes[7] = 0xc02f223854592783e7b9b1a3f79fa14d29491a0d9a6783bcb1c9a3dac44c8906;
sideNodes[8] = 0xe290d048070c4c99c6d56638f5d002d3aa7a91655cae56dfbf046612f6301e55;

SparseMerkleTree.Proof memory proof = SparseMerkleTree.Proof({
key: message.index(),
value: message.hash(),
bitMask: 30,
bitMask: 0,
sideNodes: sideNodes,
numSideNodes: 13,
numSideNodes: 9,
nonMembershipLeafPath: bytes32(0),
nonMembershipLeafValue: bytes32(0)
});

Checkpoint.TaskResponse memory taskResponse = Checkpoint.TaskResponse({
referenceTaskIndex: 0,
stateRootUpdatesRoot: keccak256(hex"beef"),
operatorSetUpdatesRoot: 0xc5d91313ef50b5c1c6bad1ebba70165c0098db1b3ce9b74881356fe41ee4ac33
operatorSetUpdatesRoot: 0x28d5b47b9e30188a9cb2ece12a42ce864e7f818775ef5ca62d2691411580d428
});

assertTrue(taskManager.verifyMessageInclusionState(message, taskResponse, proof));
Expand Down
2 changes: 1 addition & 1 deletion core/types/messages/checkpoint_task.go
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ func (msg CheckpointTaskResponse) Digest() ([32]byte, error) {
return [32]byte{}, err
}

digest, err := core.Keccak256(data)
digest, err := core.HashMessageWithPrefix([]byte("SFFL::CheckpointTaskResponse"), data)
if err != nil {
return [32]byte{}, err
}
Expand Down
2 changes: 1 addition & 1 deletion core/types/messages/operator_set_update.go
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ func (msg OperatorSetUpdateMessage) Digest() ([32]byte, error) {
return [32]byte{}, err
}

digest, err := core.Keccak256(data)
digest, err := core.HashMessageWithPrefix([]byte("SFFL::OperatorSetUpdateMessage"), data)
if err != nil {
return [32]byte{}, err
}
Expand Down
2 changes: 1 addition & 1 deletion core/types/messages/state_root_update.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ func (msg StateRootUpdateMessage) Digest() ([32]byte, error) {
return [32]byte{}, err
}

digest, err := core.Keccak256(data)
digest, err := core.HashMessageWithPrefix([]byte("SFFL::StateRootUpdateMessage"), data)
if err != nil {
return [32]byte{}, err
}
Expand Down
25 changes: 22 additions & 3 deletions core/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,32 @@ import (
taskmanager "github.com/NethermindEth/near-sffl/contracts/bindings/SFFLTaskManager"
)

func Keccak256(data []byte) ([32]byte, error) {
func Keccak256(data []byte) [32]byte {
var digest [32]byte
hasher := sha3.NewLegacyKeccak256()
hasher.Write(data)
copy(digest[:], hasher.Sum(nil)[:32])

return digest, nil
return digest
}

func HashMessageWithPrefix(prefix []byte, data []byte) ([32]byte, error) {
prefixHash := Keccak256(prefix)
dataHash := Keccak256(data)

bytes32Ty, err := abi.NewType("bytes32", "", nil)
if err != nil {
return [32]byte{}, err
}

arguments := abi.Arguments{{Type: bytes32Ty}, {Type: bytes32Ty}}

bytes, err := arguments.Pack(prefixHash, dataHash)
if err != nil {
return [32]byte{}, err
}

return Keccak256(bytes), nil
}

// BINDING UTILS - conversion from contract structs to golang structs
Expand Down Expand Up @@ -56,5 +75,5 @@ func HashBNG1Point(input taskmanager.BN254G1Point) ([32]byte, error) {
return [32]byte{}, err
}

return Keccak256(bytes)
return Keccak256(bytes), nil
}
12 changes: 6 additions & 6 deletions operator/operator_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,16 +56,16 @@ func TestOperator(t *testing.T) {
}
fmt.Println("newTaskCreatedEvent", newTaskCreatedEvent)

X, ok := big.NewInt(0).SetString("16027015062938738578882736302236067956295942129658001187467262823130911146848", 10)
X, ok := big.NewInt(0).SetString("14682076405452102073294678397007301219581403230466932828034308124846926767307", 10)
assert.True(t, ok)
Y, ok := big.NewInt(0).SetString("17647202624711407226560166949876419852295410380838239126346172357046468756471", 10)
Y, ok := big.NewInt(0).SetString("21795352068006341387944991363719316176428465837889238952393688260477242359304", 10)
assert.True(t, ok)
taskResponseSignature := bls.Signature{G1Point: bls.NewG1Point(X, Y)}

stateRootUpdatesRoot, err := hex.DecodeString("c3566ef4aad0610b0d273388480d8d21f7d07151bd62c428ec3c74f0ffbebf3c")
stateRootUpdatesRoot, err := hex.DecodeString("f4356e325f801b1c6acd597d8d32d2b65d28596622a33f9ec882314f41dd4411")
assert.Nil(t, err)

operatorSetUpdatesRoot, err := hex.DecodeString("5ae69791d810e0ec17aa2ec2f67e443f3f7d380079a7e51ff70009f0533aa61e")
operatorSetUpdatesRoot, err := hex.DecodeString("a5552192acf1e92adb86a881768349fb3408f56e68a31cd17dabdf1108f1ac93")
assert.Nil(t, err)

signedTaskResponse := &messages.SignedCheckpointTaskResponse{
Expand All @@ -81,9 +81,9 @@ func TestOperator(t *testing.T) {
stateRoot, err := hex.DecodeString("04d855ea9fbfefca9069335296aaa5108fa16d36ecd200bf133a1f5b5a7f5fe2")
assert.Nil(t, err)

X, ok = big.NewInt(0).SetString("21116328994885950554238991365696504336803036592749146303103711143274485566828", 10)
X, ok = big.NewInt(0).SetString("14166665838505742237234466022950148109946898229040848081862518171991385270422", 10)
assert.True(t, ok)
Y, ok = big.NewInt(0).SetString("20497381518544159074554997034942551621756618995010334610971970630114874587485", 10)
Y, ok = big.NewInt(0).SetString("4090516448351082424065118359663770590467802349609723171049485579272418302598", 10)
assert.True(t, ok)
stateRootUpdateMessageSignature := bls.Signature{G1Point: bls.NewG1Point(X, Y)}

Expand Down

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion tests/anvil/data/genesis.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
}
}
},
"number": "9",
"number": "11",
"gasUsed": "0x0",
"parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
"config": {
Expand Down
2 changes: 1 addition & 1 deletion tests/anvil/data/rollup-avs-deployed-anvil-state.json

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion tests/anvil/deploy-avs-save-anvil-state.sh
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ cd "$parent_path"
# start an anvil instance in the background that has eigenlayer contracts deployed
anvil --load-state data/eigenlayer-deployed-anvil-state.json --dump-state data/avs-and-eigenlayer-deployed-anvil-state.json &
cd ../../contracts/evm
forge script script/SFFLDeployer.s.sol --rpc-url $RPC_URL --private-key $PRIVATE_KEY --broadcast -v
forge script script/deploy/devnet/SFFLDeployer.s.sol --rpc-url $RPC_URL --private-key $PRIVATE_KEY --broadcast -v
# save the block-number in the genesis file which we also need to restart the anvil chain at the correct block
# otherwise the indexRegistry has a quorumUpdate at a high block number, and when we restart a clean anvil (without genesis.json) file
# it starts at block 0, and so calling getOperatorListAtBlockNumber reverts because it thinks there are no quorums registered at block 0
Expand Down
16 changes: 16 additions & 0 deletions tests/anvil/deploy-rollup-avs-save-anvil-state.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
#!/bin/bash

RPC_URL=http://localhost:8545
PRIVATE_KEY=0x2a871d0798f97d79848a013d4936a73bf4cc922c825d33c1cf7073dff6d409c6

# cd to the directory of this script so that this can be run from anywhere
parent_path=$( cd "$(dirname "${BASH_SOURCE[0]}")" ; pwd -P )
cd "$parent_path"

# start an anvil instance in the background that has eigenlayer contracts deployed
anvil --dump-state data/rollup-avs-deployed-anvil-state.json &
cd ../../contracts/evm
forge script script/deploy/devnet/SFFLDeployerRollup.s.sol --rpc-url $RPC_URL --private-key $PRIVATE_KEY --broadcast -v

# kill anvil to save its state
pkill anvil

0 comments on commit 9e965c1

Please sign in to comment.