diff --git a/src/SequencerSetPublisher.sol b/src/SequencerSetPublisher.sol index f85386c..d5faccc 100644 --- a/src/SequencerSetPublisher.sol +++ b/src/SequencerSetPublisher.sol @@ -25,7 +25,8 @@ contract SequencerSetPublisher is using ECDSA for bytes32; using MessageHashUtils for bytes32; - mapping(uint256 height => SequencerSetUpdateWitness) public sequencerSetUpdateWitnesses; + mapping(uint256 height => SequencerSetUpdateWitness[]) private _sequencerSetUpdateWitnesses; + mapping(uint256 height => mapping(bytes32 pubkeyHash => bool)) private _hasWitness; function initialize( address initialOwner @@ -40,11 +41,18 @@ contract SequencerSetPublisher is ) external override { BtcUtils.verifyBtcSignature(witness.sigHash, witness.btcPubkey, witness.btcSig); - // Avoid double commit - if (sequencerSetUpdateWitnesses[goatHeight].sigHash != bytes32(0)) { + bytes32 pubkeyHash = keccak256(witness.btcPubkey); + if (_hasWitness[goatHeight][pubkeyHash]) { revert DoubleCommit(); } - sequencerSetUpdateWitnesses[goatHeight] = witness; + _hasWitness[goatHeight][pubkeyHash] = true; + _sequencerSetUpdateWitnesses[goatHeight].push(witness); + } + + function getSequencerSetUpdateWitnesses( + uint256 goatHeight + ) external view override returns (SequencerSetUpdateWitness[] memory) { + return _sequencerSetUpdateWitnesses[goatHeight]; } } diff --git a/src/interfaces/ISequencerSetPublisher.sol b/src/interfaces/ISequencerSetPublisher.sol index fcfc072..c690f62 100644 --- a/src/interfaces/ISequencerSetPublisher.sol +++ b/src/interfaces/ISequencerSetPublisher.sol @@ -19,4 +19,8 @@ interface ISequencerSetPublisher { uint256 goatHeight, SequencerSetUpdateWitness calldata witness ) external; + + function getSequencerSetUpdateWitnesses( + uint256 goatHeight + ) external view returns (SequencerSetUpdateWitness[] memory); } diff --git a/test/SequencerSetPublisher.t.sol b/test/SequencerSetPublisher.t.sol index edd7160..e0ecf00 100644 --- a/test/SequencerSetPublisher.t.sol +++ b/test/SequencerSetPublisher.t.sol @@ -255,4 +255,44 @@ contract SequencerSetPublisherTest is Test { keccak256("set5") ); } + + function testMultipleWitnesses() public { + uint256 height = 100; + bytes32 sigHash = keccak256("commit_multi"); + + // Witness 1 (pk=11) + run_sequencer_update_test( + batch, // contains 11 + batch, + height, + sigHash, + bytes32(0), + bytes32(0) + ); + + // Witness 2 (pk=21) + run_sequencer_update_test( + batch1, // contains 21 + batch1, + height, + sigHash, + bytes32(0), + bytes32(0) + ); + + // Check if we have 2 witnesses + ISequencerSetPublisher.SequencerSetUpdateWitness[] memory witnesses = sspublisher.getSequencerSetUpdateWitnesses(height); + assertEq(witnesses.length, 2); + + // Try adding Witness 1 again (should fail) + vm.expectRevert(ISequencerSetPublisher.DoubleCommit.selector); + run_sequencer_update_test( + batch, + batch, + height, + sigHash, + bytes32(0), + bytes32(0) + ); + } }