diff --git a/contracts/BidderRegistry.sol b/contracts/BidderRegistry.sol index db2dfd9..14e16ad 100644 --- a/contracts/BidderRegistry.sol +++ b/contracts/BidderRegistry.sol @@ -4,6 +4,7 @@ pragma solidity ^0.8.15; import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol"; import {ReentrancyGuard} from "@openzeppelin/contracts/security/ReentrancyGuard.sol"; import {IBidderRegistry} from "./interfaces/IBidderRegistry.sol"; +import {IBlockTracker} from "./interfaces/IBlockTracker.sol"; /// @title Bidder Registry /// @author Kartik Chopra @@ -16,8 +17,8 @@ contract BidderRegistry is IBidderRegistry, Ownable, ReentrancyGuard { /// @dev Fee percent that would be taken by protocol when provider is slashed uint16 public feePercent; - /// @dev Minimum prepay required for registration - uint256 public minAllowance; + /// @dev Minimum deposit required for registration + uint256 public minDeposit; /// @dev Amount assigned to feeRecipient uint256 public feeRecipientAmount; @@ -28,14 +29,17 @@ contract BidderRegistry is IBidderRegistry, Ownable, ReentrancyGuard { /// @dev Address of the pre-confirmations contract address public preConfirmationsContract; + /// @dev Block tracker contract + IBlockTracker public blockTrackerContract; + /// @dev Fee recipient address public feeRecipient; /// @dev Mapping for if bidder is registered mapping(address => bool) public bidderRegistered; - /// @dev Mapping from bidder addresses to their prepayed amount - mapping(address => uint256) public bidderPrepaidBalances; + // Mapping from bidder addresses and window numbers to their locked funds + mapping(address => mapping(uint256 => uint256)) public lockedFunds; /// @dev Mapping from bidder addresses to their locked amount based on bidID (commitmentDigest) mapping(bytes32 => BidState) public BidPayment; @@ -43,11 +47,36 @@ contract BidderRegistry is IBidderRegistry, Ownable, ReentrancyGuard { /// @dev Amount assigned to bidders mapping(address => uint256) public providerAmount; - /// @dev Event emitted when a bidder is registered with their prepayed amount - event BidderRegistered(address indexed bidder, uint256 prepaidAmount); - - /// @dev Event emitted when funds are retrieved from a bidder's prepay - event FundsRetrieved(bytes32 indexed commitmentDigest, uint256 amount); + /// @dev Event emitted when a bidder is registered with their deposited amount + event BidderRegistered( + address indexed bidder, + uint256 depositedAmount, + uint256 windowNumber + ); + + /// @dev Event emitted when funds are retrieved from a bidder's deposit + event FundsRetrieved( + bytes32 indexed commitmentDigest, + address indexed bidder, + uint256 window, + uint256 amount + ); + + /// @dev Event emitted when funds are retrieved from a bidder's deposit + event FundsRewarded( + bytes32 indexed commitmentDigest, + address indexed bidder, + address indexed provider, + uint256 window, + uint256 amount + ); + + /// @dev Event emitted when a bidder withdraws their deposit + event BidderWithdrawal( + address indexed bidder, + uint256 window, + uint256 amount + ); /** * @dev Fallback function to revert all calls, ensuring no unintended interactions. @@ -57,29 +86,31 @@ contract BidderRegistry is IBidderRegistry, Ownable, ReentrancyGuard { } /** - * @dev Receive function registers bidders and takes their prepay - * Should be removed from here in case the prepay function becomes more complex + * @dev Receive function registers bidders and takes their deposit + * Should be removed from here in case the deposit function becomes more complex */ receive() external payable { - prepay(); + revert("Invalid call"); } /** - * @dev Constructor to initialize the contract with a minimum prepay requirement. - * @param _minAllowance The minimum prepay required for bidder registration. + * @dev Constructor to initialize the contract with a minimum deposit requirement. + * @param _minDeposit The minimum deposit required for bidder registration. * @param _feeRecipient The address that receives fee * @param _feePercent The fee percentage for protocol * @param _owner Owner of the contract, explicitly needed since contract is deployed w/ create2 factory. */ constructor( - uint256 _minAllowance, + uint256 _minDeposit, address _feeRecipient, uint16 _feePercent, - address _owner + address _owner, + address _blockTracker ) { - minAllowance = _minAllowance; + minDeposit = _minDeposit; feeRecipient = _feeRecipient; feePercent = _feePercent; + blockTrackerContract = IBlockTracker(_blockTracker); _transferOwnership(_owner); } @@ -111,67 +142,67 @@ contract BidderRegistry is IBidderRegistry, Ownable, ReentrancyGuard { /** * @dev Get the amount assigned to a provider. */ - function getProviderAmount(address provider) external view returns (uint256) { + function getProviderAmount( + address provider + ) external view returns (uint256) { return providerAmount[provider]; } - + /** * @dev Get the amount assigned to the fee recipient (treasury). */ - function getFeeRecipientAmount() external onlyOwner view returns (uint256) { + function getFeeRecipientAmount() external view onlyOwner returns (uint256) { return feeRecipientAmount; } /** - * @dev Internal function for bidder registration and staking. + * @dev Deposit for a specific window. + * @param window The window for which the deposit is being made. */ - function prepay() public payable { - require(msg.value >= minAllowance, "Insufficient prepay"); + function depositForSpecificWindow(uint256 window) external payable { + require(msg.value >= minDeposit, "Insufficient deposit"); - bidderPrepaidBalances[msg.sender] += msg.value; bidderRegistered[msg.sender] = true; + lockedFunds[msg.sender][window] += msg.value; - emit BidderRegistered(msg.sender, bidderPrepaidBalances[msg.sender]); + emit BidderRegistered(msg.sender, lockedFunds[msg.sender][window], window); } /** - * @dev Check the prepay of a bidder. + * @dev Check the deposit of a bidder. * @param bidder The address of the bidder. - * @return The prepayed amount for the bidder. + * @return The deposited amount for the bidder. */ - function getAllowance(address bidder) external view returns (uint256) { - return bidderPrepaidBalances[bidder]; - } - - function LockBidFunds(bytes32 commitmentDigest, uint64 bid, address bidder) external onlyPreConfirmationEngine(){ - BidState memory bidState = BidPayment[commitmentDigest]; - if (bidState.state == State.Undefined) { - BidPayment[commitmentDigest] = BidState({ - bidAmt: bid, - state: State.PreConfirmed, - bidder: bidder - }); - bidderPrepaidBalances[bidder] -= bid; - } + function getDeposit( + address bidder, + uint256 window + ) external view returns (uint256) { + return lockedFunds[bidder][window]; } /** - * @dev Retrieve funds from a bidder's prepay (only callable by the pre-confirmations contract). + * @dev Retrieve funds from a bidder's deposit (only callable by the pre-confirmations contract). * @dev reenterancy not necessary but still putting here for precaution - * @param commitmentDigest is the Bid ID that allows us to identify the bid, and prepayment + * @param commitmentDigest is the Bid ID that allows us to identify the bid, and deposit * @param provider The address to transfer the retrieved funds to. */ function retrieveFunds( + uint256 windowToSettle, bytes32 commitmentDigest, address payable provider, uint256 residualBidPercentAfterDecay ) external nonReentrant onlyPreConfirmationEngine { - BidState memory bidState = BidPayment[commitmentDigest]; - require(bidState.state == State.PreConfirmed, "The bid was not preconfirmed"); - uint256 decayedAmt = ( bidState.bidAmt * residualBidPercentAfterDecay * PRECISION) / PERCENT; + require( + bidState.state == State.PreConfirmed, + "The bid was not preconfirmed" + ); + uint256 decayedAmt = (bidState.bidAmt * + residualBidPercentAfterDecay * + PRECISION) / PERCENT; - uint256 feeAmt = (decayedAmt * uint256(feePercent) * PRECISION) / PERCENT; + uint256 feeAmt = (decayedAmt * uint256(feePercent) * PRECISION) / + PERCENT; uint256 amtMinusFeeAndDecay = decayedAmt - feeAmt; if (feeRecipient != address(0)) { @@ -183,30 +214,68 @@ contract BidderRegistry is IBidderRegistry, Ownable, ReentrancyGuard { providerAmount[provider] += amtMinusFeeAndDecay; // Ensures the bidder gets back the bid amount - decayed reward given to provider and protocol - bidderPrepaidBalances[bidState.bidder] += bidState.bidAmt - decayedAmt; + lockedFunds[bidState.bidder][windowToSettle] += + bidState.bidAmt - + decayedAmt; BidPayment[commitmentDigest].state = State.Withdrawn; BidPayment[commitmentDigest].bidAmt = 0; - emit FundsRetrieved(commitmentDigest, decayedAmt); + emit FundsRewarded( + commitmentDigest, + bidState.bidder, + provider, + windowToSettle, + decayedAmt + ); } /** - * @dev Return funds to a bidder's prepay (only callable by the pre-confirmations contract). + * @dev Return funds to a bidder's deposit (only callable by the pre-confirmations contract). * @dev reenterancy not necessary but still putting here for precaution - * @param bidID is the Bid ID that allows us to identify the bid, and prepayment + * @param bidID is the Bid ID that allows us to identify the bid, and deposit */ - function unlockFunds(bytes32 bidID) external nonReentrant onlyPreConfirmationEngine() { + function unlockFunds(uint256 window, bytes32 bidID) external nonReentrant onlyPreConfirmationEngine() { BidState memory bidState = BidPayment[bidID]; require(bidState.state == State.PreConfirmed, "The bid was not preconfirmed"); uint256 amt = bidState.bidAmt; - bidderPrepaidBalances[bidState.bidder] += amt; - + lockedFunds[bidState.bidder][window] += amt; BidPayment[bidID].state = State.Withdrawn; BidPayment[bidID].bidAmt = 0; - - emit FundsRetrieved(bidID, amt); + + emit FundsRetrieved(bidID, bidState.bidder, window, amt); + } + + /** + * @dev Open a bid (only callable by the pre-confirmations contract). + * @param commitmentDigest is the Bid ID that allows us to identify the bid, and deposit + * @param bid The bid amount. + * @param bidder The address of the bidder. + */ + function OpenBid( + bytes32 commitmentDigest, + uint64 bid, + address bidder, + uint64 blockNumber + ) external onlyPreConfirmationEngine { + BidState memory bidState = BidPayment[commitmentDigest]; + if (bidState.state == State.Undefined) { + uint256 currentWindow = blockTrackerContract.getWindowFromBlockNumber(blockNumber); + // @todo delete this, when oracle will do the calculation + // bidder cannot bid more than allowed for the round + uint256 numberOfRounds = blockTrackerContract.getBlocksPerWindow(); + uint256 windowAmount = lockedFunds[bidder][currentWindow] / numberOfRounds; + if (windowAmount < bid) { + bid = uint64(windowAmount); + } + BidPayment[commitmentDigest] = BidState({ + state: State.PreConfirmed, + bidder: bidder, + bidAmt: bid + }); + lockedFunds[bidder][currentWindow] -= bid; + } } /** @@ -227,6 +296,9 @@ contract BidderRegistry is IBidderRegistry, Ownable, ReentrancyGuard { feePercent = newFeePercent; } + /** + * @dev Withdraw funds to the fee recipient. + */ function withdrawFeeRecipientAmount() external nonReentrant { uint256 amount = feeRecipientAmount; feeRecipientAmount = 0; @@ -235,6 +307,10 @@ contract BidderRegistry is IBidderRegistry, Ownable, ReentrancyGuard { require(successFee, "couldn't transfer to fee Recipient"); } + /** + * @dev Withdraw funds to the provider. + * @param provider The address of the provider. + */ function withdrawProviderAmount( address payable provider ) external nonReentrant { @@ -246,16 +322,38 @@ contract BidderRegistry is IBidderRegistry, Ownable, ReentrancyGuard { require(success, "couldn't transfer to provider"); } - function withdrawPrepaidAmount(address payable bidder) external nonReentrant { - uint256 prepaidAmount = bidderPrepaidBalances[bidder]; - bidderPrepaidBalances[bidder] = 0; - require(msg.sender == bidder, "only bidder can unprepay"); - require(prepaidAmount > 0, "bidder prepaid Amount is zero"); + /** + * @dev Withdraw funds to the bidder. + * @param bidder The address of the bidder. + */ + function withdrawBidderAmountFromWindow( + address payable bidder, + uint256 window + ) external nonReentrant { + require( + msg.sender == bidder, + "only bidder can withdraw funds from window" + ); + uint256 currentWindow = blockTrackerContract.getCurrentWindow(); + // withdraw is enabled only when closed and settled + require( + window < currentWindow, + "funds can only be withdrawn after the window is settled" + ); + uint256 amount = lockedFunds[bidder][window]; + lockedFunds[bidder][window] = 0; + require(amount > 0, "bidder Amount is zero"); + + (bool success, ) = bidder.call{value: amount}(""); + require(success, "couldn't transfer to bidder"); - (bool success, ) = bidder.call{value: prepaidAmount}(""); - require(success, "couldn't transfer prepay to bidder"); + emit BidderWithdrawal(bidder, window, amount); } + /** + * @dev Withdraw protocol fee. + * @param bidder The address of the bidder. + */ function withdrawProtocolFee( address payable bidder ) external onlyOwner nonReentrant { @@ -264,6 +362,6 @@ contract BidderRegistry is IBidderRegistry, Ownable, ReentrancyGuard { require(_protocolFeeAmount > 0, "insufficient protocol fee amount"); (bool success, ) = bidder.call{value: _protocolFeeAmount}(""); - require(success, "couldn't transfer prepay to bidder"); + require(success, "couldn't transfer deposit to bidder"); } } diff --git a/contracts/BlockTracker.sol b/contracts/BlockTracker.sol new file mode 100644 index 0000000..a39316a --- /dev/null +++ b/contracts/BlockTracker.sol @@ -0,0 +1,149 @@ +pragma solidity ^0.8.20; + +import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol"; + +/** + * @title BlockTracker + * @dev A contract that tracks Ethereum blocks and their winners. + */ +contract BlockTracker is Ownable { + /// @dev Event emitted when a new L1 block is tracked. + event NewL1Block( + uint256 indexed blockNumber, + address indexed winner, + uint256 indexed window + ); + + /// @dev Event emitted when a new window is created. + event NewWindow(uint256 indexed window); + + /// @dev Event emitted when the number of blocks per window is updated. + event NewBlocksPerWindow(uint256 blocksPerWindow); + + uint256 public currentWindow = 1; + uint256 public blocksPerWindow = 64; + + // Mapping from block number to the winner's address + mapping(uint256 => address) public blockWinners; + + /// @dev Maps builder names to their respective Ethereum addresses. + mapping(string => address) public blockBuilderNameToAddress; + + /** + * @dev Initializes the BlockTracker contract with the specified owner. + * @param _owner The address of the contract owner. + */ + constructor(address _owner) Ownable() { + _transferOwnership(_owner); + } + + /** + * @dev Retrieves the current window number. + * @return The current window number. + */ + function getCurrentWindow() external view returns (uint256) { + return currentWindow; + } + + /** + * @dev Allows the owner to add a new builder address. + * @param builderName The name of the block builder as it appears on extra data. + * @param builderAddress The Ethereum address of the builder. + */ + function addBuilderAddress( + string memory builderName, + address builderAddress + ) external onlyOwner { + blockBuilderNameToAddress[builderName] = builderAddress; + } + + /** + * @dev Returns the builder's address corresponding to the given name. + * @param builderNameGrafiti The name (or graffiti) of the block builder. + */ + function getBuilder( + string calldata builderNameGrafiti + ) external view returns (address) { + return blockBuilderNameToAddress[builderNameGrafiti]; + } + + /** + * @dev Returns the window number corresponding to a given block number. + * @param blockNumber The block number. + * @return The window number. + */ + function getWindowFromBlockNumber(uint256 blockNumber) external view returns (uint256) { + return (blockNumber - 1) / blocksPerWindow + 1; + } + + /** + * @dev Returns the number of blocks per window. + * @return The number of blocks per window. + */ + function getBlocksPerWindow() external view returns (uint256) { + return blocksPerWindow; + } + + /** + * @dev Sets the number of blocks per window. + * @param _blocksPerWindow The new number of blocks per window. + */ + function setBlocksPerWindow(uint256 _blocksPerWindow) external onlyOwner { + blocksPerWindow = _blocksPerWindow; + + emit NewBlocksPerWindow(blocksPerWindow); + } + + /** + * @dev Records a new L1 block and its winner. + * @param _blockNumber The number of the new L1 block. + * @param _winnerGraffiti The graffiti of the winner of the new L1 block. + */ + function recordL1Block( + uint256 _blockNumber, + string calldata _winnerGraffiti + ) external onlyOwner { + address _winner = blockBuilderNameToAddress[_winnerGraffiti]; + recordBlockWinner(_blockNumber, _winner); + uint256 newWindow = (_blockNumber - 1) / blocksPerWindow + 1; + if (newWindow > currentWindow) { + // We've entered a new window + currentWindow = newWindow; + emit NewWindow(currentWindow); + } + emit NewL1Block(_blockNumber, _winner, currentWindow); + } + + // Function to record a new block winner + function recordBlockWinner(uint256 blockNumber, address winner) internal { + // Check if the block number is valid (not 0) + require(blockNumber != 0, "Invalid block number"); + + // Check if the winner address is valid (not the zero address) + require(winner != address(0), "Invalid winner address"); + + blockWinners[blockNumber] = winner; + } + + // Function to get the winner of a specific block + function getBlockWinner( + uint256 blockNumber + ) external view returns (address) { + return blockWinners[blockNumber]; + } + + /** + * @dev Fallback function to revert all calls, ensuring no unintended interactions. + */ + fallback() external payable { + revert("Invalid call"); + } + + /** + * @dev Receive function is disabled for this contract to prevent unintended interactions. + * Should be removed from here in case the registerAndStake function becomes more complex + */ + receive() external payable { + revert("Invalid call"); + } +} diff --git a/contracts/Oracle.sol b/contracts/Oracle.sol index 7559e14..825e2b7 100644 --- a/contracts/Oracle.sol +++ b/contracts/Oracle.sol @@ -4,9 +4,9 @@ pragma solidity ^0.8.20; import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol"; import {PreConfCommitmentStore} from "./PreConfirmations.sol"; import {IProviderRegistry} from "./interfaces/IProviderRegistry.sol"; -import {IPreConfCommitmentStore} from './interfaces/IPreConfirmations.sol'; -import {IBidderRegistry} from './interfaces/IBidderRegistry.sol'; - +import {IPreConfCommitmentStore} from "./interfaces/IPreConfirmations.sol"; +import {IBidderRegistry} from "./interfaces/IBidderRegistry.sol"; +import {IBlockTracker} from "./interfaces/IBlockTracker.sol"; /// @title Oracle Contract /// @author Kartik Chopra @@ -20,16 +20,6 @@ contract Oracle is Ownable { /// @dev Maps builder names to their respective Ethereum addresses. mapping(string => address) public blockBuilderNameToAddress; - /// @dev Stores the block number that is next in line to be requested. - uint256 public nextRequestedBlockNumber; - - /** - * @dev Returns the next block number that is set to be requested. - */ - function getNextRequestedBlockNumber() external view returns (uint256) { - return nextRequestedBlockNumber; - } - // To shutup the compiler /// @dev Empty receive function to silence compiler warnings about missing payable functions. receive() external payable { @@ -46,90 +36,62 @@ contract Oracle is Ownable { /// @dev Reference to the PreConfCommitmentStore contract interface. IPreConfCommitmentStore private preConfContract; + /// @dev Reference to the BlockTracker contract interface. + IBlockTracker private blockTrackerContract; /** * @dev Constructor to initialize the contract with a PreConfirmations contract. * @param _preConfContract The address of the pre-confirmations contract. - * @param _nextRequestedBlockNumber The next block number to be requested. * @param _owner Owner of the contract, explicitly needed since contract is deployed with create2 factory. */ constructor( address _preConfContract, - uint256 _nextRequestedBlockNumber, + address _blockTrackerContract, address _owner ) Ownable() { preConfContract = IPreConfCommitmentStore(_preConfContract); - nextRequestedBlockNumber = _nextRequestedBlockNumber; + blockTrackerContract = IBlockTracker(_blockTrackerContract); _transferOwnership(_owner); } /// @dev Event emitted when a commitment is processed. event CommitmentProcessed(bytes32 commitmentHash, bool isSlash); - /** - * @dev Allows the owner to add a new builder address. - * @param builderName The name of the block builder as it appears on extra data. - * @param builderAddress The Ethereum address of the builder. - */ - function addBuilderAddress(string memory builderName, address builderAddress) external onlyOwner { - blockBuilderNameToAddress[builderName] = builderAddress; - } - - /** - * @dev Returns the builder's address corresponding to the given name. - * @param builderNameGrafiti The name (or graffiti) of the block builder. - */ - function getBuilder(string calldata builderNameGrafiti) external view returns (address) { - return blockBuilderNameToAddress[builderNameGrafiti]; - } - // Function to receive and process the block data (this would be automated in a real-world scenario) /** * @dev Processes a builder's commitment for a specific block number. * @param commitmentIndex The id of the commitment in the PreConfCommitmentStore. * @param blockNumber The block number to be processed. - * @param blockBuilderName The name of the block builder. + * @param builder The address of the builder. * @param isSlash Determines whether the commitment should be slashed or rewarded. */ function processBuilderCommitmentForBlockNumber( bytes32 commitmentIndex, uint256 blockNumber, - string calldata blockBuilderName, + address builder, bool isSlash, uint256 residualBidPercentAfterDecay ) external onlyOwner { - // Check graffiti against registered builder IDs - address builder = blockBuilderNameToAddress[blockBuilderName]; - require(residualBidPercentAfterDecay <= 100, "Residual bid after decay cannot be greater than 100 percent"); - IPreConfCommitmentStore.PreConfCommitment memory commitment = preConfContract.getCommitment(commitmentIndex); - if (commitment.commiter == builder && commitment.blockNumber == blockNumber) { - processCommitment(commitmentIndex, isSlash, residualBidPercentAfterDecay); - } - - } - - /** - * @dev Sets the next block number to be requested. - * @param newBlockNumber The new block number to be set. - */ - function setNextBlock(uint64 newBlockNumber) external onlyOwner { - nextRequestedBlockNumber = newBlockNumber; - } - - /** - * @dev Increments the `nextRequestedBlockNumber` by one. - */ - function moveToNextBlock() external onlyOwner { - nextRequestedBlockNumber++; - } - - /** - * @dev unlocks funds to the bidders assosciated with BidIDs in the input array. - * @param bidIDs The array of BidIDs to unlock funds for. - */ - function unlockFunds(bytes32[] memory bidIDs) external onlyOwner { - for (uint256 i = 0; i < bidIDs.length; i++) { - preConfContract.unlockBidFunds(bidIDs[i]); + address winner = blockTrackerContract.getBlockWinner(blockNumber); + require( + winner == builder, + "Builder is not the winner of the block" + ); + require( + residualBidPercentAfterDecay <= 100, + "Residual bid after decay cannot be greater than 100 percent" + ); + IPreConfCommitmentStore.PreConfCommitment + memory commitment = preConfContract.getCommitment(commitmentIndex); + if ( + commitment.commiter == builder && + commitment.blockNumber == blockNumber + ) { + processCommitment( + commitmentIndex, + isSlash, + residualBidPercentAfterDecay + ); } } @@ -138,11 +100,21 @@ contract Oracle is Ownable { * @param commitmentIndex The id of the commitment to be processed. * @param isSlash Determines if the commitment should be slashed or rewarded. */ - function processCommitment(bytes32 commitmentIndex, bool isSlash, uint256 residualBidPercentAfterDecay) private { + function processCommitment( + bytes32 commitmentIndex, + bool isSlash, + uint256 residualBidPercentAfterDecay + ) private { if (isSlash) { - preConfContract.initiateSlash(commitmentIndex, residualBidPercentAfterDecay); + preConfContract.initiateSlash( + commitmentIndex, + residualBidPercentAfterDecay + ); } else { - preConfContract.initiateReward(commitmentIndex, residualBidPercentAfterDecay); + preConfContract.initiateReward( + commitmentIndex, + residualBidPercentAfterDecay + ); } // Emit an event that a commitment has been processed emit CommitmentProcessed(commitmentIndex, isSlash); diff --git a/contracts/PreConfirmations.sol b/contracts/PreConfirmations.sol index 2b655c4..b4f7621 100644 --- a/contracts/PreConfirmations.sol +++ b/contracts/PreConfirmations.sol @@ -6,10 +6,9 @@ import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol"; import {IProviderRegistry} from "./interfaces/IProviderRegistry.sol"; import {IBidderRegistry} from "./interfaces/IBidderRegistry.sol"; +import {IBlockTracker} from "./interfaces/IBlockTracker.sol"; import {ECDSA} from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; -import "forge-std/console.sol"; - /** * @title PreConfCommitmentStore - A contract for managing preconfirmation commitments and bids. * @notice This contract allows bidders to make precommitments and bids and provides a mechanism for the oracle to verify and process them. @@ -20,12 +19,14 @@ contract PreConfCommitmentStore is Ownable { /// @dev EIP-712 Type Hash for preconfirmation commitment bytes32 public constant EIP712_COMMITMENT_TYPEHASH = keccak256( - "PreConfCommitment(string txnHash,uint64 bid,uint64 blockNumber,uint64 decayStartTimeStamp,uint64 decayEndTimeStamp,string bidHash,string signature)" + "PreConfCommitment(string txnHash,uint64 bid,uint64 blockNumber,uint64 decayStartTimeStamp,uint64 decayEndTimeStamp,bytes32 bidHash,string signature,string sharedSecretKey)" ); /// @dev EIP-712 Type Hash for preconfirmation bid bytes32 public constant EIP712_BID_TYPEHASH = - keccak256("PreConfBid(string txnHash,uint64 bid,uint64 blockNumber,uint64 decayStartTimeStamp,uint64 decayEndTimeStamp)"); + keccak256( + "PreConfBid(string txnHash,uint64 bid,uint64 blockNumber,uint64 decayStartTimeStamp,uint64 decayEndTimeStamp)" + ); /// @dev commitment counter uint256 public commitmentCount; @@ -48,12 +49,17 @@ contract PreConfCommitmentStore is Ownable { /// @dev Address of bidderRegistry IBidderRegistry public bidderRegistry; + /// @dev Address of blockTracker + IBlockTracker public blockTracker; + /// @dev Mapping from provider to commitments count mapping(address => uint256) public commitmentsCount; - /// @dev Mapping from address to commitmentss list + /// @dev Mapping from address to commitments list mapping(address => bytes32[]) public providerCommitments; + mapping(address => bytes32[]) public providerEncryptedCommitments; + /// @dev Mapping for blocknumber to list of hash of commitments mapping(uint256 => bytes32[]) public blockCommitments; @@ -61,6 +67,8 @@ contract PreConfCommitmentStore is Ownable { /// @dev Only stores valid commitments mapping(bytes32 => PreConfCommitment) public commitments; + mapping(bytes32 => EncrPreConfCommitment) public encryptedCommitments; + /// @dev Struct for all the information around preconfirmations commitment struct PreConfCommitment { bool commitmentUsed; @@ -76,8 +84,45 @@ contract PreConfCommitmentStore is Ownable { bytes bidSignature; bytes commitmentSignature; uint256 blockCommitedAt; + bytes sharedSecretKey; + } + + /// @dev Event to log successful commitment storage + event CommitmentStored( + bytes32 indexed commitmentIndex, + address bidder, + address commiter, + uint64 bid, + uint64 blockNumber, + bytes32 bidHash, + uint64 decayStartTimeStamp, + uint64 decayEndTimeStamp, + string txnHash, + bytes32 commitmentHash, + bytes bidSignature, + bytes commitmentSignature, + uint256 blockCommitedAt, + bytes sharedSecretKey + ); + + /// @dev Struct for all the information around encrypted preconfirmations commitment + struct EncrPreConfCommitment { + bool commitmentUsed; + address commiter; + bytes32 commitmentDigest; + bytes commitmentSignature; + uint256 blockCommitedAt; } + /// @dev Event to log successful encrypted commitment storage + event EncryptedCommitmentStored( + bytes32 indexed commitmentIndex, + address commiter, + bytes32 commitmentDigest, + bytes commitmentSignature, + uint256 blockCommitedAt + ); + /// @dev Event to log successful verifications event SignatureVerified( address indexed signer, @@ -112,16 +157,19 @@ contract PreConfCommitmentStore is Ownable { * @dev Initializes the contract with the specified registry addresses, oracle, name, and version. * @param _providerRegistry The address of the provider registry. * @param _bidderRegistry The address of the bidder registry. + * @param _blockTracker The address of the block tracker. * @param _oracle The address of the oracle. * @param _owner Owner of the contract, explicitly needed since contract is deployed w/ create2 factory. */ constructor( address _providerRegistry, address _bidderRegistry, - address _oracle, + address _blockTracker, + address _oracle, address _owner ) { oracle = _oracle; + blockTracker = IBlockTracker(_blockTracker); providerRegistry = IProviderRegistry(_providerRegistry); bidderRegistry = IBidderRegistry(_bidderRegistry); _transferOwnership(_owner); @@ -189,7 +237,8 @@ contract PreConfCommitmentStore is Ownable { uint64 _decayStartTimeStamp, uint64 _decayEndTimeStamp, bytes32 _bidHash, - string memory _bidSignature + string memory _bidSignature, + string memory _sharedSecretKey ) public view returns (bytes32) { return ECDSA.toTypedDataHash( @@ -205,13 +254,13 @@ contract PreConfCommitmentStore is Ownable { keccak256( abi.encodePacked(_bytes32ToHexString(_bidHash)) ), - keccak256(abi.encodePacked(_bidSignature)) + keccak256(abi.encodePacked(_bidSignature)), + keccak256(abi.encodePacked(_sharedSecretKey)) ) ) ); } - /** * @dev Internal function to verify a bid * @param bid bid id. @@ -220,7 +269,6 @@ contract PreConfCommitmentStore is Ownable { * @param bidSignature bid signature. * @return messageDigest returns the bid hash for given bid id. * @return recoveredAddress the address from the bid hash. - * @return stake the stake amount of the address for bid id bidder. */ function verifyBid( uint64 bid, @@ -232,12 +280,16 @@ contract PreConfCommitmentStore is Ownable { ) public view - returns (bytes32 messageDigest, address recoveredAddress, uint256 stake) + returns (bytes32 messageDigest, address recoveredAddress) { - messageDigest = getBidHash(txnHash, bid, blockNumber, decayStartTimeStamp, decayEndTimeStamp); + messageDigest = getBidHash( + txnHash, + bid, + blockNumber, + decayStartTimeStamp, + decayEndTimeStamp + ); recoveredAddress = messageDigest.recover(bidSignature); - stake = bidderRegistry.getAllowance(recoveredAddress); - require(stake > (10 * bid), "Invalid bid"); } /** @@ -259,12 +311,9 @@ contract PreConfCommitmentStore is Ownable { uint64 decayEndTimeStamp, bytes32 bidHash, bytes memory bidSignature, - bytes memory commitmentSignature - ) - public - view - returns (bytes32 preConfHash, address commiterAddress) - { + bytes memory commitmentSignature, + bytes memory sharedSecretKey + ) public view returns (bytes32 preConfHash, address commiterAddress) { preConfHash = getPreConfHash( txnHash, bid, @@ -272,42 +321,72 @@ contract PreConfCommitmentStore is Ownable { decayStartTimeStamp, decayEndTimeStamp, bidHash, - _bytesToHexString(bidSignature) + _bytesToHexString(bidSignature), + _bytesToHexString(sharedSecretKey) ); commiterAddress = preConfHash.recover(commitmentSignature); } + /** + * @dev Get the index of a commitment. + * @param commitment The commitment to get the index for. + * @return The index of the commitment. + */ function getCommitmentIndex( PreConfCommitment memory commitment - ) public pure returns (bytes32){ - return keccak256( - abi.encodePacked( - commitment.commitmentHash, - commitment.commitmentSignature - ) - ); + ) public pure returns (bytes32) { + return + keccak256( + abi.encodePacked( + commitment.commitmentHash, + commitment.commitmentSignature + ) + ); } /** - * @dev Store a commitment. - * @param bid The bid amount. - * @param blockNumber The block number. - * @param txnHash The transaction hash. - * @param bidSignature The signature of the bid. - * @param commitmentSignature The signature of the commitment. - * @return commitmentIndex The index of the stored commitment + * @dev Get the index of an encrypted commitment. + * @param commitment The commitment to get the index for. + * @return The index of the commitment. */ - function storeCommitment( + function getEncryptedCommitmentIndex( + EncrPreConfCommitment memory commitment + ) public pure returns (bytes32) { + return + keccak256( + abi.encodePacked( + commitment.commitmentDigest, + commitment.commitmentSignature + ) + ); + } + + /** + @dev Open a commitment + @param encryptedCommitmentIndex The index of the encrypted commitment + @param bid The bid amount + @param blockNumber The block number + @param txnHash The transaction hash + @param decayStartTimeStamp The start time of the decay + @param decayEndTimeStamp The end time of the decay + @param bidSignature The signature of the bid + @param commitmentSignature The signature of the commitment + @param sharedSecretKey The shared secret key + @return commitmentIndex The index of the stored commitment + */ + function openCommitment( + bytes32 encryptedCommitmentIndex, uint64 bid, uint64 blockNumber, string memory txnHash, uint64 decayStartTimeStamp, uint64 decayEndTimeStamp, bytes calldata bidSignature, - bytes memory commitmentSignature + bytes memory commitmentSignature, + bytes memory sharedSecretKey ) public returns (bytes32 commitmentIndex) { - (bytes32 bHash, address bidderAddress, uint256 stake) = verifyBid( + (bytes32 bHash, address bidderAddress) = verifyBid( bid, blockNumber, decayStartTimeStamp, @@ -324,15 +403,30 @@ contract PreConfCommitmentStore is Ownable { decayStartTimeStamp, decayEndTimeStamp, bHash, - _bytesToHexString(bidSignature) + _bytesToHexString(bidSignature), + _bytesToHexString(sharedSecretKey) ); + EncrPreConfCommitment memory encryptedCommitment = encryptedCommitments[encryptedCommitmentIndex]; + require( + !encryptedCommitment.commitmentUsed, + "Commitment already used" + ); + + require(encryptedCommitment.commitmentDigest == commitmentDigest, "Invalid commitment digest"); - address commiterAddress = commitmentDigest.recover(commitmentSignature); + address commiterAddress = commitmentDigest.recover( + commitmentSignature + ); - require(stake > (10 * bid), "Stake too low"); - require(decayStartTimeStamp < decayEndTimeStamp, "Invalid decay time"); + require( + decayStartTimeStamp < decayEndTimeStamp, + "Invalid decay time" + ); - PreConfCommitment memory newCommitment = PreConfCommitment( + address winner = blockTracker.getBlockWinner(blockNumber); + require(msg.sender == winner || msg.sender == bidderAddress, "Caller is not a winner provider or bidder"); + + PreConfCommitment memory newCommitment = PreConfCommitment( false, bidderAddress, commiterAddress, @@ -345,7 +439,8 @@ contract PreConfCommitmentStore is Ownable { commitmentDigest, bidSignature, commitmentSignature, - block.number + encryptedCommitment.blockCommitedAt, + sharedSecretKey ); commitmentIndex = getCommitmentIndex(newCommitment); @@ -353,45 +448,101 @@ contract PreConfCommitmentStore is Ownable { // Store commitment commitments[commitmentIndex] = newCommitment; + // Mark the encrypted commitment as used + encryptedCommitments[encryptedCommitmentIndex].commitmentUsed = true; + // Push pointers to other mappings providerCommitments[commiterAddress].push(commitmentIndex); blockCommitments[blockNumber].push(commitmentIndex); - + + bidderRegistry.OpenBid(commitmentDigest, bid, bidderAddress, blockNumber); + + emit CommitmentStored( + commitmentIndex, + bidderAddress, + commiterAddress, + bid, + blockNumber, + bHash, + decayStartTimeStamp, + decayEndTimeStamp, + txnHash, + commitmentDigest, + bidSignature, + commitmentSignature, + encryptedCommitment.blockCommitedAt, + sharedSecretKey + ); + } + + return commitmentIndex; + } + + /** + * @dev Store an encrypted commitment. + * @param commitmentDigest The digest of the commitment. + * @param commitmentSignature The signature of the commitment. + * @return commitmentIndex The index of the stored commitment + */ + function storeEncryptedCommitment( + bytes32 commitmentDigest, + bytes memory commitmentSignature + ) public returns (bytes32 commitmentIndex) { + { + address commiterAddress = commitmentDigest.recover( + commitmentSignature + ); + + EncrPreConfCommitment memory newCommitment = EncrPreConfCommitment( + false, + commiterAddress, + commitmentDigest, + commitmentSignature, + block.number + ); + + commitmentIndex = getEncryptedCommitmentIndex(newCommitment); + + // Store commitment + encryptedCommitments[commitmentIndex] = newCommitment; + + // Push pointers to other mappings + providerEncryptedCommitments[commiterAddress].push(commitmentIndex); + commitmentCount++; commitmentsCount[commiterAddress] += 1; - // Check if Bid has bid-amt stored - bidderRegistry.LockBidFunds(commitmentDigest, bid, bidderAddress); - + emit EncryptedCommitmentStored( + commitmentIndex, + commiterAddress, + commitmentDigest, + commitmentSignature, + block.number + ); } return commitmentIndex; } - /** + /** * @dev Retrieves the list of commitments for a given committer. * @param commiter The address of the committer. * @return A list of PreConfCommitment structures for the specified committer. */ - function getCommitmentsByCommitter(address commiter) - public - view - returns (bytes32[] memory) - { + function getCommitmentsByCommitter( + address commiter + ) public view returns (bytes32[] memory) { return providerCommitments[commiter]; } - - /** + /** * @dev Retrieves the list of commitments for a given block number. - * @param blockNumber The block number. - * @return A list of indexes referencing preconfimration structures for the specified block number. - */ - function getCommitmentsByBlockNumber(uint256 blockNumber) - public - view - returns (bytes32[] memory) - { + * @param blockNumber The block number. + * @return A list of indexes referencing preconfimration structures for the specified block number. + */ + function getCommitmentsByBlockNumber( + uint256 blockNumber + ) public view returns (bytes32[] memory) { return blockCommitments[blockNumber]; } @@ -400,8 +551,9 @@ contract PreConfCommitmentStore is Ownable { * @param commitmentIndex The index of the commitment. * @return txnHash The transaction hash. */ - function getTxnHashFromCommitment(bytes32 commitmentIndex) public view returns (string memory txnHash) - { + function getTxnHashFromCommitment( + bytes32 commitmentIndex + ) public view returns (string memory txnHash) { return commitments[commitmentIndex].txnHash; } @@ -416,17 +568,33 @@ contract PreConfCommitmentStore is Ownable { return commitments[commitmentIndex]; } + /** + * @dev Get a commitments' enclosed transaction by its commitmentIndex. + * @param commitmentIndex The index of the commitment. + * @return txnHash The transaction hash. + */ + function getEncryptedCommitment( + bytes32 commitmentIndex + ) public view returns (EncrPreConfCommitment memory) { + return encryptedCommitments[commitmentIndex]; + } + /** * @dev Initiate a slash for a commitment. * @param commitmentIndex The hash of the commitment to be slashed. */ - function initiateSlash(bytes32 commitmentIndex, uint256 residualBidPercentAfterDecay) public onlyOracle { + function initiateSlash( + bytes32 commitmentIndex, + uint256 residualBidPercentAfterDecay + ) public onlyOracle { PreConfCommitment memory commitment = commitments[commitmentIndex]; require( !commitments[commitmentIndex].commitmentUsed, "Commitment already used" ); + uint256 windowToSettle = blockTracker.getWindowFromBlockNumber(commitment.blockNumber); + // Mark this commitment as used to prevent replays commitments[commitmentIndex].commitmentUsed = true; commitmentsCount[commitment.commiter] -= 1; @@ -438,33 +606,31 @@ contract PreConfCommitmentStore is Ownable { residualBidPercentAfterDecay ); - bidderRegistry.unlockFunds(commitment.commitmentHash); + bidderRegistry.unlockFunds(windowToSettle, commitment.commitmentHash); } - /** - * @dev Initiate a return of funds for a bid that was not slashed. - * @param commitmentDigest The hash of the bid to be unlocked. - */ - function unlockBidFunds(bytes32 commitmentDigest) public onlyOracle { - bidderRegistry.unlockFunds(commitmentDigest); - } - /** * @dev Initiate a reward for a commitment. * @param commitmentIndex The hash of the commitment to be rewarded. */ - function initiateReward(bytes32 commitmentIndex, uint256 residualBidPercentAfterDecay) public onlyOracle { + function initiateReward( + bytes32 commitmentIndex, + uint256 residualBidPercentAfterDecay + ) public onlyOracle { PreConfCommitment memory commitment = commitments[commitmentIndex]; require( !commitments[commitmentIndex].commitmentUsed, "Commitment already used" ); + uint256 windowToSettle = blockTracker.getWindowFromBlockNumber(commitment.blockNumber); + // Mark this commitment as used to prevent replays commitments[commitmentIndex].commitmentUsed = true; commitmentsCount[commitment.commiter] -= 1; bidderRegistry.retrieveFunds( + windowToSettle, commitment.commitmentHash, payable(commitment.commiter), residualBidPercentAfterDecay @@ -493,7 +659,9 @@ contract PreConfCommitmentStore is Ownable { * @dev Updates the address of the bidder registry. * @param newBidderRegistry The new bidder registry address. */ - function updateBidderRegistry(address newBidderRegistry) external onlyOwner { + function updateBidderRegistry( + address newBidderRegistry + ) external onlyOwner { bidderRegistry = IBidderRegistry(newBidderRegistry); } diff --git a/contracts/ProviderRegistry.sol b/contracts/ProviderRegistry.sol index 5619795..f04547f 100644 --- a/contracts/ProviderRegistry.sol +++ b/contracts/ProviderRegistry.sol @@ -47,8 +47,6 @@ contract ProviderRegistry is IProviderRegistry, Ownable, ReentrancyGuard { /// @dev Event for slashing funds event FundsSlashed(address indexed provider, uint256 amount); - /// @dev Event for rewarding funds - event FundsRewarded(address indexed provider, uint256 amount); /** * @dev Fallback function to revert all calls, ensuring no unintended interactions. @@ -115,7 +113,6 @@ contract ProviderRegistry is IProviderRegistry, Ownable, ReentrancyGuard { function registerAndStake() public payable { require(!providerRegistered[msg.sender], "Provider already registered"); require(msg.value >= minStake, "Insufficient stake"); - providerStakes[msg.sender] = msg.value; providerRegistered[msg.sender] = true; @@ -147,6 +144,7 @@ contract ProviderRegistry is IProviderRegistry, Ownable, ReentrancyGuard { * @param amt The amount to slash from the provider's stake. * @param provider The address of the provider. * @param bidder The address to transfer the slashed funds to. + * @param residualBidPercentAfterDecay The residual bid percent after decay. */ function slash( uint256 amt, @@ -188,12 +186,19 @@ contract ProviderRegistry is IProviderRegistry, Ownable, ReentrancyGuard { feePercent = newFeePercent; } + /** + * @dev Reward funds to the fee receipt. + */ function withdrawFeeRecipientAmount() external nonReentrant { feeRecipientAmount = 0; (bool successFee, ) = feeRecipient.call{value: feeRecipientAmount}(""); require(successFee, "Couldn't transfer to fee Recipient"); } + /** + * @dev Withdraw funds to the bidder. + * @param bidder The address of the bidder. + */ function withdrawBidderAmount(address bidder) external nonReentrant { require(bidderAmount[bidder] > 0, "Bidder Amount is zero"); @@ -203,6 +208,10 @@ contract ProviderRegistry is IProviderRegistry, Ownable, ReentrancyGuard { require(success, "Couldn't transfer to bidder"); } + /** + * @dev Withdraw staked amount for the provider. + * @param provider The address of the provider. + */ function withdrawStakedAmount( address payable provider ) external nonReentrant { diff --git a/contracts/interfaces/IBidderRegistry.sol b/contracts/interfaces/IBidderRegistry.sol index 0b4e1b9..1fbee9c 100644 --- a/contracts/interfaces/IBidderRegistry.sol +++ b/contracts/interfaces/IBidderRegistry.sol @@ -25,17 +25,19 @@ interface IBidderRegistry { Withdrawn } - function prepay() external payable; - function LockBidFunds(bytes32 commitmentDigest, uint64 bid, address bidder) external; + function OpenBid(bytes32 commitmentDigest, uint64 bid, address bidder, uint64 blockNumber) external; - function getAllowance(address bidder) external view returns (uint256); + function getDeposit(address bidder, uint256 window) external view returns (uint256); + + function depositForSpecificWindow(uint256 window) external payable; function retrieveFunds( + uint256 windowToSettle, bytes32 commitmentDigest, address payable provider, uint256 residualBidPercentAfterDecay ) external; - function unlockFunds(bytes32 bidID) external; + function unlockFunds(uint256 windowToSettle, bytes32 bidID) external; } diff --git a/contracts/interfaces/IBlockTracker.sol b/contracts/interfaces/IBlockTracker.sol new file mode 100644 index 0000000..5ba780f --- /dev/null +++ b/contracts/interfaces/IBlockTracker.sol @@ -0,0 +1,51 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.20; + +/// @title IBlockTracker interface for BlockTracker contract +interface IBlockTracker { + /// @notice Retrieves the builder's address corresponding to the given name. + /// @param builderNameGrafiti The name of the block builder. + /// @return The Ethereum address of the builder. + function getBuilder(string calldata builderNameGrafiti) external view returns (address); + + /// @notice Gets the current window number. + /// @return The current window number. + function getCurrentWindow() external view returns (uint256); + + /// @notice Gets the window number for a given block number. + /// @param blockNumber The block number. + /// @return The window number. + function getWindowFromBlockNumber(uint256 blockNumber) external view returns (uint256); + + /// @notice Retrieves the number of blocks per window. + /// @return The number of blocks per window. + function getBlocksPerWindow() external view returns (uint256); + + /// @notice Sets the number of blocks per window. + /// @param _blocksPerWindow The new number of blocks per window. + /// @dev Only callable by the owner. + function setBlocksPerWindow(uint256 _blocksPerWindow) external; + + /// @notice Retrieves the winner of a specific L1 block. + /// @param _blockNumber The block number of the L1 block. + /// @return The address of the winner of the L1 block. + function getBlockWinner(uint256 _blockNumber) external view returns (address); + + /// @notice Records a new L1 block with its winner. + /// @param _blockNumber The block number of the new L1 block. + /// @param _winnerGrafitti The graffiti of the winner of the new L1 block. + function recordL1Block(uint256 _blockNumber, string calldata _winnerGrafitti) external; + + /// @notice Emitted when a new L1 block is recorded. + /// @param blockNumber The block number of the new L1 block. + /// @param winner The address of the winner of the new L1 block. + event NewL1Block(uint256 indexed blockNumber, address indexed winner); + + /// @notice Emitted when entering a new window. + /// @param window The new window number. + event NewWindow(uint256 indexed window); + + /// @notice Emitted when the number of blocks per window is updated. + /// @param blocksPerWindow The new number of blocks per window. + event NewBlocksPerWindow(uint256 blocksPerWindow); +} diff --git a/contracts/interfaces/IPreConfirmations.sol b/contracts/interfaces/IPreConfirmations.sol index ec5448e..ca89fe8 100644 --- a/contracts/interfaces/IPreConfirmations.sol +++ b/contracts/interfaces/IPreConfirmations.sol @@ -25,7 +25,13 @@ interface IPreConfCommitmentStore { uint256 blockCommitedAt; } - + struct EncrPreConfCommitment { + bool commitmentUsed; + address commiter; + bytes32 commitmentDigest; + bytes commitmentSignature; + uint256 blockCommitedAt; + } event SignatureVerified( address indexed signer, @@ -61,12 +67,19 @@ interface IPreConfCommitmentStore { bytes calldata bidSignature ) external view returns (bytes32 messageDigest, address recoveredAddress, uint256 stake); - function storeCommitment( + function openCommitment( + bytes32 encryptedCommitmentIndex, uint64 bid, uint64 blockNumber, string memory txnHash, string memory commitmentHash, bytes calldata bidSignature, + bytes memory commitmentSignature, + bytes memory sharedSecretKey + ) external returns (uint256); + + function storeEncryptedCommitment( + bytes32 commitmentDigest, bytes memory commitmentSignature ) external returns (uint256); @@ -75,11 +88,13 @@ interface IPreConfCommitmentStore { function getCommitment(bytes32 commitmentIndex) external view returns (PreConfCommitment memory); + function getEncryptedCommitment(bytes32 commitmentIndex) external view returns (EncrPreConfCommitment memory); + function initiateSlash(bytes32 commitmentIndex, uint256 residualDecayedBid) external; function initiateReward(bytes32 commitmentIndex, uint256 residualDecayedBid) external; - function unlockBidFunds(bytes32 commitmentDigest) external; + function unlockBidFunds(uint256 windowToSettle, bytes32 commitmentDigest) external; function updateOracle(address newOracle) external; diff --git a/package.json b/package.json index ad52614..901df3a 100644 --- a/package.json +++ b/package.json @@ -19,6 +19,7 @@ "typechain": "^8.2.0" }, "dependencies": { + "@openzeppelin/contracts": "^5.0.2", "solc": "^0.8.20", "solidity-docgen": "^0.6.0-beta.36" } diff --git a/scripts/DeployScripts.s.sol b/scripts/DeployScripts.s.sol index 5a0e08c..6373259 100644 --- a/scripts/DeployScripts.s.sol +++ b/scripts/DeployScripts.s.sol @@ -6,6 +6,7 @@ import "contracts/ProviderRegistry.sol"; import "contracts/PreConfirmations.sol"; import "contracts/Oracle.sol"; import "contracts/Whitelist.sol"; +import "contracts/BlockTracker.sol"; // Deploy scripts should inherit this contract if they deploy using create2 deterministic addrs. contract Create2Deployer { @@ -48,13 +49,17 @@ contract DeployScript is Script, Create2Deployer { // Forge deploy with salt uses create2 proxy from https://github.com/primevprotocol/deterministic-deployment-proxy bytes32 salt = 0x8989000000000000000000000000000000000000000000000000000000000000; - BidderRegistry bidderRegistry = new BidderRegistry{salt: salt}(minStake, feeRecipient, feePercent, msg.sender); + BlockTracker blockTracker = new BlockTracker{salt: salt}(msg.sender); + console.log("BlockTracker deployed to:", address(blockTracker)); + blockTracker.setBlocksPerWindow(10); + console.log("BlockTracker updated with blocksPerWindow:", 10); + BidderRegistry bidderRegistry = new BidderRegistry{salt: salt}(minStake, feeRecipient, feePercent, msg.sender, address(blockTracker)); console.log("BidderRegistry deployed to:", address(bidderRegistry)); ProviderRegistry providerRegistry = new ProviderRegistry{salt: salt}(minStake, feeRecipient, feePercent, msg.sender); console.log("ProviderRegistry deployed to:", address(providerRegistry)); - PreConfCommitmentStore preConfCommitmentStore = new PreConfCommitmentStore{salt: salt}(address(providerRegistry), address(bidderRegistry), feeRecipient, msg.sender); + PreConfCommitmentStore preConfCommitmentStore = new PreConfCommitmentStore{salt: salt}(address(providerRegistry), address(bidderRegistry), address(blockTracker), feeRecipient, msg.sender); console.log("PreConfCommitmentStore deployed to:", address(preConfCommitmentStore)); providerRegistry.setPreconfirmationsContract(address(preConfCommitmentStore)); @@ -63,7 +68,7 @@ contract DeployScript is Script, Create2Deployer { bidderRegistry.setPreconfirmationsContract(address(preConfCommitmentStore)); console.log("BidderRegistry updated with PreConfCommitmentStore address:", address(preConfCommitmentStore)); - Oracle oracle = new Oracle{salt: salt}(address(preConfCommitmentStore), nextRequestedBlockNumber, msg.sender); + Oracle oracle = new Oracle{salt: salt}(address(preConfCommitmentStore), address(blockTracker), msg.sender); console.log("Oracle deployed to:", address(oracle)); preConfCommitmentStore.updateOracle(address(oracle)); diff --git a/test/BidderRegistryTest.sol b/test/BidderRegistryTest.sol index 62b77e3..55e269a 100644 --- a/test/BidderRegistryTest.sol +++ b/test/BidderRegistryTest.sol @@ -3,6 +3,7 @@ pragma solidity ^0.8.20; import "forge-std/Test.sol"; import {BidderRegistry} from "../contracts/BidderRegistry.sol"; +import {BlockTracker} from "../contracts/BlockTracker.sol"; contract BidderRegistryTest is Test { uint256 testNumber; @@ -11,25 +12,26 @@ contract BidderRegistryTest is Test { uint256 internal minStake; address internal bidder; address internal feeRecipient; + BlockTracker internal blockTracker; /// @dev Event emitted when a bidder is registered with their staked amount - event BidderRegistered(address indexed bidder, uint256 stakedAmount); + event BidderRegistered(address indexed bidder, uint256 stakedAmount, uint256 windowNumber); function setUp() public { testNumber = 42; feePercent = 10; minStake = 1e18 wei; feeRecipient = vm.addr(9); - - bidderRegistry = new BidderRegistry(minStake, feeRecipient, feePercent, address(this)); + blockTracker = new BlockTracker(address(this)); + bidderRegistry = new BidderRegistry(minStake, feeRecipient, feePercent, address(this), address(blockTracker)); bidder = vm.addr(1); - vm.deal(bidder, 100 ether); - vm.deal(address(this), 100 ether); + vm.deal(bidder, 1000 ether); + vm.deal(address(this), 1000 ether); } function test_VerifyInitialContractState() public { - assertEq(bidderRegistry.minAllowance(), 1e18 wei); + assertEq(bidderRegistry.minDeposit(), 1e18 wei); assertEq(bidderRegistry.feeRecipient(), feeRecipient); assertEq(bidderRegistry.feePercent(), feePercent); assertEq(bidderRegistry.preConfirmationsContract(), address(0)); @@ -39,39 +41,46 @@ contract BidderRegistryTest is Test { function testFail_BidderStakeAndRegisterMinStake() public { vm.prank(bidder); vm.expectRevert(bytes("")); - bidderRegistry.prepay{value: 1 wei}(); + bidderRegistry.depositForSpecificWindow{value: 1 wei}(2); } function test_BidderStakeAndRegister() public { + uint256 currentWindow = blockTracker.getCurrentWindow(); + uint256 nextWindow = currentWindow + 1; + vm.startPrank(bidder); vm.expectEmit(true, false, false, true); - emit BidderRegistered(bidder, 1 ether); + emit BidderRegistered(bidder, 1 ether, nextWindow); - bidderRegistry.prepay{value: 1 ether}(); + bidderRegistry.depositForSpecificWindow{value: 1 ether}(nextWindow); bool isBidderRegistered = bidderRegistry.bidderRegistered(bidder); assertEq(isBidderRegistered, true); - uint256 bidderStakeStored = bidderRegistry.getAllowance(bidder); + uint256 bidderStakeStored = bidderRegistry.getDeposit(bidder, nextWindow); assertEq(bidderStakeStored, 1 ether); + // For the second deposit, calculate the new next window + currentWindow = blockTracker.getCurrentWindow(); + nextWindow = currentWindow + 1; + vm.expectEmit(true, false, false, true); - emit BidderRegistered(bidder, 2 ether); + emit BidderRegistered(bidder, 2 ether, nextWindow); - bidderRegistry.prepay{value: 1 ether}(); + bidderRegistry.depositForSpecificWindow{value: 1 ether}(nextWindow); - uint256 bidderStakeStored2 = bidderRegistry.getAllowance(bidder); + uint256 bidderStakeStored2 = bidderRegistry.getDeposit(bidder, nextWindow); assertEq(bidderStakeStored2, 2 ether); } function testFail_BidderStakeAndRegisterAlreadyRegistered() public { vm.prank(bidder); - bidderRegistry.prepay{value: 2e18 wei}(); + bidderRegistry.depositForSpecificWindow{value: 2e18 wei}(2); vm.expectRevert(bytes("")); - bidderRegistry.prepay{value: 1 wei}(); + bidderRegistry.depositForSpecificWindow{value: 1 wei}(2); } function testFail_receive() public { @@ -131,18 +140,27 @@ contract BidderRegistryTest is Test { function test_shouldRetrieveFunds() public { bytes32 bidID = keccak256("1234"); bidderRegistry.setPreconfirmationsContract(address(this)); + uint256 currentWindow = blockTracker.getCurrentWindow(); + uint256 nextWindow = currentWindow + 1; + vm.prank(bidder); - bidderRegistry.prepay{value: 2 ether}(); + bidderRegistry.depositForSpecificWindow{value: 64 ether}(nextWindow); address provider = vm.addr(4); - bidderRegistry.LockBidFunds(bidID, 1 ether, bidder); - bidderRegistry.retrieveFunds(bidID, payable(provider),100); + uint64 blockNumber = 66; + blockTracker.addBuilderAddress("test", provider); + blockTracker.recordL1Block(blockNumber, "test"); + + bidderRegistry.OpenBid(bidID, 1 ether, bidder, blockNumber); + + bidderRegistry.retrieveFunds(nextWindow, bidID, payable(provider),100); uint256 providerAmount = bidderRegistry.providerAmount(provider); uint256 feeRecipientAmount = bidderRegistry.feeRecipientAmount(); assertEq(providerAmount, 900000000000000000); assertEq(feeRecipientAmount, 100000000000000000); assertEq(bidderRegistry.getFeeRecipientAmount(), 100000000000000000); - assertEq(bidderRegistry.bidderPrepaidBalances(bidder), 1 ether); + + assertEq(bidderRegistry.lockedFunds(bidder, nextWindow), 63 ether); } function test_shouldRetrieveFundsWithoutFeeRecipient() public { @@ -152,12 +170,18 @@ contract BidderRegistryTest is Test { bidderRegistry.setNewFeeRecipient(address(0)); bidderRegistry.setPreconfirmationsContract(address(this)); + uint256 currentWindow = blockTracker.getCurrentWindow(); + uint256 nextWindow = currentWindow + 1; vm.prank(bidder); - bidderRegistry.prepay{value: 2 ether}(); + bidderRegistry.depositForSpecificWindow{value: 64 ether}(nextWindow); + address provider = vm.addr(4); + uint64 blockNumber = 66; + blockTracker.addBuilderAddress("test", provider); + blockTracker.recordL1Block(blockNumber, "test"); bytes32 bidID = keccak256("1234"); - bidderRegistry.LockBidFunds(bidID, 1 ether, bidder); - bidderRegistry.retrieveFunds(bidID, payable(provider),100); + bidderRegistry.OpenBid(bidID, 1 ether, bidder, blockNumber); + bidderRegistry.retrieveFunds(nextWindow, bidID, payable(provider), 100); uint256 feerecipientValueAfter = bidderRegistry.feeRecipientAmount(); uint256 providerAmount = bidderRegistry.providerAmount(provider); @@ -165,17 +189,20 @@ contract BidderRegistryTest is Test { assertEq(providerAmount, 900000000000000000); assertEq(feerecipientValueAfter, feerecipientValueBefore); - assertEq(bidderRegistry.bidderPrepaidBalances(bidder), 1 ether); + assertEq(bidderRegistry.lockedFunds(bidder, nextWindow), 63 ether); } function testFail_shouldRetrieveFundsNotPreConf() public { vm.prank(bidder); - bidderRegistry.prepay{value: 2 ether}(); + uint256 currentWindow = blockTracker.getCurrentWindow(); + uint256 nextWindow = currentWindow + 1; + uint64 blockNumber = 66; + bidderRegistry.depositForSpecificWindow{value: 2 ether}(nextWindow); address provider = vm.addr(4); vm.expectRevert(bytes("")); bytes32 bidID = keccak256("1234"); - bidderRegistry.LockBidFunds(bidID, 1 ether, bidder); - bidderRegistry.retrieveFunds(bidID, payable(provider),100); + bidderRegistry.OpenBid(bidID, 1 ether, bidder, blockNumber); + bidderRegistry.retrieveFunds(nextWindow, bidID, payable(provider),100); } function testFail_shouldRetrieveFundsGreaterThanStake() public { @@ -183,25 +210,34 @@ contract BidderRegistryTest is Test { bidderRegistry.setPreconfirmationsContract(address(this)); vm.prank(bidder); - bidderRegistry.prepay{value: 2 ether}(); + uint256 currentWindow = blockTracker.getCurrentWindow(); + uint256 nextWindow = currentWindow + 1; + uint64 blockNumber = 66; + bidderRegistry.depositForSpecificWindow{value: 2 ether}(nextWindow); address provider = vm.addr(4); vm.expectRevert(bytes("")); vm.prank(address(this)); bytes32 bidID = keccak256("1234"); - bidderRegistry.LockBidFunds(bidID, 3 ether, bidder); - bidderRegistry.retrieveFunds(bidID, payable(provider),100); + bidderRegistry.OpenBid(bidID, 3 ether, bidder, blockNumber); + bidderRegistry.retrieveFunds(nextWindow, bidID, payable(provider),100); } function test_withdrawFeeRecipientAmount() public { bidderRegistry.setPreconfirmationsContract(address(this)); + uint256 currentWindow = blockTracker.getCurrentWindow(); + uint256 nextWindow = currentWindow + 1; vm.prank(bidder); - bidderRegistry.prepay{value: 2 ether}(); + bidderRegistry.depositForSpecificWindow{value: 64 ether}(nextWindow); address provider = vm.addr(4); uint256 balanceBefore = feeRecipient.balance; bytes32 bidID = keccak256("1234"); - bidderRegistry.LockBidFunds(bidID, 1 ether, bidder); - bidderRegistry.retrieveFunds(bidID, payable(provider),100); + uint64 blockNumber = 66; + blockTracker.addBuilderAddress("test", provider); + blockTracker.recordL1Block(blockNumber, "test"); + + bidderRegistry.OpenBid(bidID, 1 ether, bidder, blockNumber); + bidderRegistry.retrieveFunds(nextWindow, bidID, payable(provider),100); bidderRegistry.withdrawFeeRecipientAmount(); uint256 balanceAfter = feeRecipient.balance; assertEq(balanceAfter - balanceBefore, 100000000000000000); @@ -216,13 +252,20 @@ contract BidderRegistryTest is Test { function test_withdrawProviderAmount() public { bidderRegistry.setPreconfirmationsContract(address(this)); + uint256 currentWindow = blockTracker.getCurrentWindow(); + uint256 nextWindow = currentWindow + 1; vm.prank(bidder); - bidderRegistry.prepay{value: 5 ether}(); + bidderRegistry.depositForSpecificWindow{value: 128 ether}(nextWindow); address provider = vm.addr(4); uint256 balanceBefore = address(provider).balance; bytes32 bidID = keccak256("1234"); - bidderRegistry.LockBidFunds(bidID, 2 ether, bidder); - bidderRegistry.retrieveFunds(bidID, payable(provider), 100); + uint64 blockNumber = 66; + blockTracker.addBuilderAddress("test", provider); + blockTracker.recordL1Block(blockNumber, "test"); + + bidderRegistry.OpenBid(bidID, 2 ether, bidder, blockNumber); + + bidderRegistry.retrieveFunds(nextWindow, bidID, payable(provider), 100); bidderRegistry.withdrawProviderAmount(payable(provider)); uint256 balanceAfter = address(provider).balance; assertEq(balanceAfter - balanceBefore, 1800000000000000000); @@ -231,47 +274,30 @@ contract BidderRegistryTest is Test { function testFail_withdrawProviderAmount() public { bidderRegistry.setPreconfirmationsContract(address(this)); + uint256 currentWindow = blockTracker.getCurrentWindow(); + uint256 nextWindow = currentWindow + 1; vm.prank(bidder); - bidderRegistry.prepay{value: 5 ether}(); + bidderRegistry.depositForSpecificWindow{value: 5 ether}(nextWindow); address provider = vm.addr(4); bidderRegistry.withdrawProviderAmount(payable(provider)); } - function test_withdrawStakedAmount() public { - bidderRegistry.setPreconfirmationsContract(address(this)); - vm.prank(bidder); - bidderRegistry.prepay{value: 5 ether}(); - uint256 balanceBefore = address(bidder).balance; - vm.prank(bidder); - bidderRegistry.withdrawPrepaidAmount(payable(bidder)); - uint256 balanceAfter = address(bidder).balance; - assertEq(balanceAfter - balanceBefore, 5 ether); - assertEq(bidderRegistry.bidderPrepaidBalances(bidder), 0); - } - - function testFail_withdrawStakedAmountNotOwner() public { - bidderRegistry.setPreconfirmationsContract(address(this)); - vm.prank(bidder); - bidderRegistry.prepay{value: 5 ether}(); - bidderRegistry.withdrawPrepaidAmount(payable(bidder)); - } - - function testFail_withdrawStakedAmountStakeZero() public { - bidderRegistry.setPreconfirmationsContract(address(this)); - vm.prank(bidder); - bidderRegistry.withdrawPrepaidAmount(payable(bidder)); - } - function test_withdrawProtocolFee() public { address provider = vm.addr(4); bidderRegistry.setPreconfirmationsContract(address(this)); bidderRegistry.setNewFeeRecipient(address(0)); + uint256 currentWindow = blockTracker.getCurrentWindow(); + uint256 nextWindow = currentWindow + 1; vm.prank(bidder); - bidderRegistry.prepay{value: 5 ether}(); + bidderRegistry.depositForSpecificWindow{value: 128 ether}(nextWindow); uint256 balanceBefore = address(bidder).balance; bytes32 bidID = keccak256("1234"); - bidderRegistry.LockBidFunds(bidID, 2 ether, bidder); - bidderRegistry.retrieveFunds(bidID, payable(provider), 100); + uint64 blockNumber = 66; + blockTracker.addBuilderAddress("test", provider); + blockTracker.recordL1Block(blockNumber, "test"); + + bidderRegistry.OpenBid(bidID, 2 ether, bidder, blockNumber); + bidderRegistry.retrieveFunds(nextWindow, bidID, payable(provider), 100); vm.prank(bidderRegistry.owner()); bidderRegistry.withdrawProtocolFee(payable(address(bidder))); uint256 balanceAfter = address(bidder).balance; @@ -283,7 +309,9 @@ contract BidderRegistryTest is Test { bidderRegistry.setPreconfirmationsContract(address(this)); bidderRegistry.setNewFeeRecipient(address(0)); vm.prank(bidder); - bidderRegistry.prepay{value: 5 ether}(); + uint256 currentWindow = blockTracker.getCurrentWindow(); + uint256 nextWindow = currentWindow + 1; + bidderRegistry.depositForSpecificWindow{value: 5 ether}(nextWindow); vm.prank(bidderRegistry.owner()); bidderRegistry.withdrawProtocolFee(payable(address(bidder))); } @@ -292,7 +320,9 @@ contract BidderRegistryTest is Test { bidderRegistry.setPreconfirmationsContract(address(this)); bidderRegistry.setNewFeeRecipient(address(0)); vm.prank(bidder); - bidderRegistry.prepay{value: 5 ether}(); + uint256 currentWindow = blockTracker.getCurrentWindow(); + uint256 nextWindow = currentWindow + 1; + bidderRegistry.depositForSpecificWindow{value: 5 ether}(nextWindow); bidderRegistry.withdrawProtocolFee(payable(address(bidder))); } } diff --git a/test/OracleTest.sol b/test/OracleTest.sol index d64b92a..8ae465e 100644 --- a/test/OracleTest.sol +++ b/test/OracleTest.sol @@ -7,6 +7,7 @@ import "../contracts/PreConfirmations.sol"; import "../contracts/interfaces/IPreConfirmations.sol"; import "../contracts/ProviderRegistry.sol"; import "../contracts/BidderRegistry.sol"; +import "../contracts/BlockTracker.sol"; contract OracleTest is Test { address internal owner; @@ -20,7 +21,9 @@ contract OracleTest is Test { uint256 testNumber; uint64 testNumber2; BidderRegistry internal bidderRegistry; + BlockTracker internal blockTracker; TestCommitment internal _testCommitmentAliceBob; + bytes internal sharedSecretKey; struct TestCommitment { uint64 bid; @@ -38,12 +41,12 @@ contract OracleTest is Test { event BlockDataRequested(uint256 blockNumber); event BlockDataReceived(string[] txnList, uint256 blockNumber, string blockBuilderName); event CommitmentProcessed(bytes32 commitmentHash, bool isSlash); - event FundsRetrieved(bytes32 indexed commitmentDigest, uint256 amount); + event FundsRetrieved(bytes32 indexed commitmentDigest, uint256 window, uint256 amount); function setUp() public { testNumber = 2; testNumber2 = 2; - + sharedSecretKey = abi.encodePacked(keccak256("0xsecret")); _testCommitmentAliceBob = TestCommitment( 2, 2, @@ -66,21 +69,23 @@ contract OracleTest is Test { feePercent, address(this) ); - bidderRegistry = new BidderRegistry(minStake, feeRecipient, feePercent, address(this)); + address ownerInstance = 0x6d503Fd50142C7C469C7c6B64794B55bfa6883f3; + blockTracker = new BlockTracker(ownerInstance); + bidderRegistry = new BidderRegistry(minStake, feeRecipient, feePercent, address(this), address(blockTracker)); preConfCommitmentStore = new PreConfCommitmentStore( address(providerRegistry), // Provider Registry address(bidderRegistry), // User Registry + address(blockTracker), // Block Tracker feeRecipient, // Oracle address(this) // Owner ); - address ownerInstance = 0x6d503Fd50142C7C469C7c6B64794B55bfa6883f3; vm.deal(ownerInstance, 5 ether); vm.startPrank(ownerInstance); - bidderRegistry.prepay{value: 2 ether}(); + uint256 window = blockTracker.getCurrentWindow(); + bidderRegistry.depositForSpecificWindow{value: 2 ether}(window+1); - oracle = new Oracle(address(preConfCommitmentStore), 2, ownerInstance); - oracle.addBuilderAddress("mev builder", ownerInstance); + oracle = new Oracle(address(preConfCommitmentStore), address(blockTracker), ownerInstance); vm.stopPrank(); preConfCommitmentStore.updateOracle(address(oracle)); @@ -89,82 +94,83 @@ contract OracleTest is Test { } - function test_MultipleBlockBuildersRegistred() public { - vm.startPrank(0x6d503Fd50142C7C469C7c6B64794B55bfa6883f3); - (address builder1,) = makeAddrAndKey("k builder"); - (address builder2,) = makeAddrAndKey("primev builder"); - (address builder3,) = makeAddrAndKey("titan builder"); - (address builder4,) = makeAddrAndKey("zk builder"); - - - oracle.addBuilderAddress("k builder", builder1); - oracle.addBuilderAddress("primev builder", builder2); - oracle.addBuilderAddress("titan builder", builder3); - oracle.addBuilderAddress("zk builder", builder4); - - assertEq(oracle.blockBuilderNameToAddress("k builder"), builder1); - assertEq(oracle.blockBuilderNameToAddress("primev builder"), builder2); - assertEq(oracle.blockBuilderNameToAddress("titan builder"), builder3); - assertEq(oracle.blockBuilderNameToAddress("zk builder"), builder4); - } - - function test_builderUnidentified() public { - vm.startPrank(0x6d503Fd50142C7C469C7c6B64794B55bfa6883f3); - // Unregistered Builder - (address bidder, uint256 bidderPk) = makeAddrAndKey("alice"); - (address provider, uint256 providerPk) = makeAddrAndKey("bob"); - - (address builder3,) = makeAddrAndKey("titan builder"); - (address builder4,) = makeAddrAndKey("zk builder"); - - oracle.addBuilderAddress("titan builder", builder3); - oracle.addBuilderAddress("zk builder", builder4); - - assertEq(oracle.blockBuilderNameToAddress("titan builder"), builder3); - assertEq(oracle.blockBuilderNameToAddress("zk builder"), builder4); - vm.stopPrank(); - - vm.deal(bidder, 1000 ether); - vm.deal(provider, 1000 ether); - - vm.startPrank(bidder); - bidderRegistry.prepay{value: 250 ether }(); - vm.stopPrank(); - - vm.startPrank(provider); - providerRegistry.registerAndStake{value: 250 ether}(); - vm.stopPrank(); - - bytes32 commitmentIndex = constructAndStoreCommitment( - _testCommitmentAliceBob.bid, - _testCommitmentAliceBob.blockNumber, - _testCommitmentAliceBob.txnHash, - _testCommitmentAliceBob.decayStartTimestamp, - _testCommitmentAliceBob.decayEndTimestamp, - bidderPk, - providerPk - ); - - string[] memory txnList = new string[](1); - txnList[0] = string(abi.encodePacked(keccak256("0xkartik"))); - vm.startPrank(0x6d503Fd50142C7C469C7c6B64794B55bfa6883f3); - oracle.processBuilderCommitmentForBlockNumber(commitmentIndex, _testCommitmentAliceBob.blockNumber, "k builder", false, 50); - vm.stopPrank(); - assertEq(bidderRegistry.getProviderAmount(provider), 0); - assertEq(providerRegistry.checkStake(provider), 250 ether); - } + // function test_MultipleBlockBuildersRegistred() public { + // vm.startPrank(0x6d503Fd50142C7C469C7c6B64794B55bfa6883f3); + // (address builder1,) = makeAddrAndKey("k builder"); + // (address builder2,) = makeAddrAndKey("primev builder"); + // (address builder3,) = makeAddrAndKey("titan builder"); + // (address builder4,) = makeAddrAndKey("zk builder"); + + + // oracle.addBuilderAddress("k builder", builder1); + // oracle.addBuilderAddress("primev builder", builder2); + // oracle.addBuilderAddress("titan builder", builder3); + // oracle.addBuilderAddress("zk builder", builder4); + + // assertEq(oracle.blockBuilderNameToAddress("k builder"), builder1); + // assertEq(oracle.blockBuilderNameToAddress("primev builder"), builder2); + // assertEq(oracle.blockBuilderNameToAddress("titan builder"), builder3); + // assertEq(oracle.blockBuilderNameToAddress("zk builder"), builder4); + // } + + // function test_builderUnidentified() public { + // vm.startPrank(0x6d503Fd50142C7C469C7c6B64794B55bfa6883f3); + // // Unregistered Builder + // (address bidder, uint256 bidderPk) = makeAddrAndKey("alice"); + // (address provider, uint256 providerPk) = makeAddrAndKey("bob"); + + // (address builder3,) = makeAddrAndKey("titan builder"); + // (address builder4,) = makeAddrAndKey("zk builder"); + + // oracle.addBuilderAddress("titan builder", builder3); + // oracle.addBuilderAddress("zk builder", builder4); + + // assertEq(oracle.blockBuilderNameToAddress("titan builder"), builder3); + // assertEq(oracle.blockBuilderNameToAddress("zk builder"), builder4); + // vm.stopPrank(); + + // vm.deal(bidder, 1000 ether); + // vm.deal(provider, 1000 ether); + + // vm.startPrank(bidder); + // bidderRegistry.prepay{value: 250 ether }(); + // vm.stopPrank(); + + // vm.startPrank(provider); + // providerRegistry.registerAndStake{value: 250 ether}(); + // vm.stopPrank(); + + // bytes32 commitmentIndex = constructAndStoreCommitment( + // _testCommitmentAliceBob.bid, + // _testCommitmentAliceBob.blockNumber, + // _testCommitmentAliceBob.txnHash, + // _testCommitmentAliceBob.decayStartTimestamp, + // _testCommitmentAliceBob.decayEndTimestamp, + // bidderPk, + // providerPk, + // provider + // ); + + // string[] memory txnList = new string[](1); + // txnList[0] = string(abi.encodePacked(keccak256("0xkartik"))); + // vm.startPrank(0x6d503Fd50142C7C469C7c6B64794B55bfa6883f3); + // oracle.processBuilderCommitmentForBlockNumber(commitmentIndex, _testCommitmentAliceBob.blockNumber, "k builder", false, 50); + // vm.stopPrank(); + // assertEq(bidderRegistry.getProviderAmount(provider), 0); + // assertEq(providerRegistry.checkStake(provider), 250 ether); + // } function test_process_commitment_payment_payout() public { string memory txn = "0x6d9c53ad81249775f8c082b11ac293b2e19194ff791bd1c4fd37683310e90d08"; - uint64 blockNumber = 200; + uint64 blockNumber = 66; uint64 bid = 2; - string memory blockBuilderName = "kartik builder"; (address bidder, uint256 bidderPk) = makeAddrAndKey("alice"); (address provider, uint256 providerPk) = makeAddrAndKey("kartik"); vm.deal(bidder, 200000 ether); vm.startPrank(bidder); - bidderRegistry.prepay{value: 250 ether }(); + uint256 window = blockTracker.getCurrentWindow(); + bidderRegistry.depositForSpecificWindow{value: 250 ether}(window+1); vm.stopPrank(); vm.deal(provider, 200000 ether); @@ -172,12 +178,11 @@ contract OracleTest is Test { providerRegistry.registerAndStake{value: 250 ether}(); vm.stopPrank(); - bytes32 index = constructAndStoreCommitment(bid, blockNumber, txn, 10, 20, bidderPk, providerPk); + bytes32 index = constructAndStoreCommitment(bid, blockNumber, txn, 10, 20, bidderPk, providerPk, provider); vm.startPrank(address(0x6d503Fd50142C7C469C7c6B64794B55bfa6883f3)); - oracle.addBuilderAddress(blockBuilderName, provider); - oracle.processBuilderCommitmentForBlockNumber(index, blockNumber, blockBuilderName, false, 50); + oracle.processBuilderCommitmentForBlockNumber(index, blockNumber, provider, false, 50); vm.stopPrank(); assertEq(bidderRegistry.getProviderAmount(provider), bid*(50)/100); @@ -188,13 +193,13 @@ contract OracleTest is Test { string memory txn = "0x6d9c53ad81249775f8c082b11ac293b2e19194ff791bd1c4fd37683310e90d08"; uint64 blockNumber = 200; uint64 bid = 200; - string memory blockBuilderName = "kartik builder"; (address bidder, uint256 bidderPk) = makeAddrAndKey("alice"); (address provider, uint256 providerPk) = makeAddrAndKey("kartik"); vm.deal(bidder, 200000 ether); vm.startPrank(bidder); - bidderRegistry.prepay{value: 250 ether }(); + uint256 window = blockTracker.getCurrentWindow(); + bidderRegistry.depositForSpecificWindow{value: 250 ether}(window+1); vm.stopPrank(); vm.deal(provider, 200000 ether); @@ -202,14 +207,13 @@ contract OracleTest is Test { providerRegistry.registerAndStake{value: 250 ether}(); vm.stopPrank(); - bytes32 index = constructAndStoreCommitment(bid, blockNumber, txn, 10, 20, bidderPk, providerPk); + bytes32 index = constructAndStoreCommitment(bid, blockNumber, txn, 10, 20, bidderPk, providerPk, provider); vm.startPrank(address(0x6d503Fd50142C7C469C7c6B64794B55bfa6883f3)); - oracle.addBuilderAddress(blockBuilderName, provider); vm.expectEmit(true, false, false, true); emit CommitmentProcessed(index, true); - oracle.processBuilderCommitmentForBlockNumber(index, blockNumber, blockBuilderName, true,50); + oracle.processBuilderCommitmentForBlockNumber(index, blockNumber, provider, true,50); vm.stopPrank(); assertEq(providerRegistry.checkStake(provider) + ((bid * 50)/100), 250 ether); } @@ -218,9 +222,8 @@ contract OracleTest is Test { function test_process_commitment_slash_and_reward() public { string memory txn1 = "0x6d9c53ad81249775f8c082b11ac293b2e19194ff791bd1c4fd37683310e90d08"; string memory txn2 = "0x6d9c53ad81249775f8c082b11ac293b2e19194ff791bd1c4fd37683310e90d09"; - uint64 blockNumber = 201; + uint64 blockNumber = 66; uint64 bid = 100; - string memory blockBuilderName = "kartik builder"; (address bidder, uint256 bidderPk) = makeAddrAndKey("alice"); (address provider, uint256 providerPk) = makeAddrAndKey("kartik"); @@ -228,7 +231,8 @@ contract OracleTest is Test { vm.deal(bidder, 200000 ether); vm.startPrank(bidder); - bidderRegistry.prepay{value: 250 ether }(); + uint256 window = blockTracker.getCurrentWindow(); + bidderRegistry.depositForSpecificWindow{value: 250 ether}(window+1); vm.stopPrank(); vm.deal(provider, 200000 ether); @@ -236,19 +240,18 @@ contract OracleTest is Test { providerRegistry.registerAndStake{value: 250 ether}(); vm.stopPrank(); - bytes32 index1 = constructAndStoreCommitment(bid, blockNumber, txn1, 10, 20, bidderPk, providerPk); - bytes32 index2 = constructAndStoreCommitment(bid, blockNumber, txn2, 10, 20, bidderPk, providerPk); + bytes32 index1 = constructAndStoreCommitment(bid, blockNumber, txn1, 10, 20, bidderPk, providerPk, provider); + bytes32 index2 = constructAndStoreCommitment(bid, blockNumber, txn2, 10, 20, bidderPk, providerPk, provider); vm.startPrank(address(0x6d503Fd50142C7C469C7c6B64794B55bfa6883f3)); - oracle.addBuilderAddress(blockBuilderName, provider); vm.expectEmit(true, false, false, true); emit CommitmentProcessed(index1, true); - oracle.processBuilderCommitmentForBlockNumber(index1, blockNumber, blockBuilderName, true,100); + oracle.processBuilderCommitmentForBlockNumber(index1, blockNumber, provider, true,100); vm.expectEmit(true, false, false, true); emit CommitmentProcessed(index2, false); - oracle.processBuilderCommitmentForBlockNumber(index2, blockNumber, blockBuilderName, false,50); + oracle.processBuilderCommitmentForBlockNumber(index2, blockNumber, provider, false,50); vm.stopPrank(); assertEq(providerRegistry.checkStake(provider), 250 ether - bid); assertEq(bidderRegistry.getProviderAmount(provider), (bid * (100 - feePercent) /100) * residualAfterDecay /100 ); @@ -262,13 +265,13 @@ contract OracleTest is Test { string memory txn4 = "0x6d9c53ad81249775f8c082b11ac293b2e19194ff791bd1c4fd37683310e90d11"; uint64 blockNumber = 201; uint64 bid = 5; - string memory blockBuilderName = "kartik builder"; (address bidder, uint256 bidderPk) = makeAddrAndKey("alice"); (address provider, uint256 providerPk) = makeAddrAndKey("kartik"); vm.deal(bidder, 200000 ether); + uint256 window = blockTracker.getWindowFromBlockNumber(blockNumber); vm.startPrank(bidder); - bidderRegistry.prepay{value: 250 ether }(); + bidderRegistry.depositForSpecificWindow{value: 250 ether}(window); vm.stopPrank(); vm.deal(provider, 200000 ether); @@ -276,46 +279,46 @@ contract OracleTest is Test { providerRegistry.registerAndStake{value: 250 ether}(); vm.stopPrank(); - bytes32 index1 = constructAndStoreCommitment(bid, blockNumber, txn1, 10, 20, bidderPk, providerPk); - bytes32 index2 = constructAndStoreCommitment(bid, blockNumber, txn2, 10, 20, bidderPk, providerPk); - bytes32 index3 = constructAndStoreCommitment(bid, blockNumber, txn3, 10, 20, bidderPk, providerPk); - bytes32 index4 = constructAndStoreCommitment(bid, blockNumber, txn4, 10, 20, bidderPk, providerPk); + bytes32 index1 = constructAndStoreCommitment(bid, blockNumber, txn1, 10, 20, bidderPk, providerPk, provider); + bytes32 index2 = constructAndStoreCommitment(bid, blockNumber, txn2, 10, 20, bidderPk, providerPk, provider); + bytes32 index3 = constructAndStoreCommitment(bid, blockNumber, txn3, 10, 20, bidderPk, providerPk, provider); + bytes32 index4 = constructAndStoreCommitment(bid, blockNumber, txn4, 10, 20, bidderPk, providerPk, provider); vm.startPrank(address(0x6d503Fd50142C7C469C7c6B64794B55bfa6883f3)); - oracle.addBuilderAddress(blockBuilderName, provider); vm.expectEmit(true, false, false, true); emit CommitmentProcessed(index1, true); - oracle.processBuilderCommitmentForBlockNumber(index1, blockNumber, blockBuilderName, true,100); + oracle.processBuilderCommitmentForBlockNumber(index1, blockNumber, provider, true,100); vm.expectEmit(true, false, false, true); emit CommitmentProcessed(index2, true); - oracle.processBuilderCommitmentForBlockNumber(index2, blockNumber, blockBuilderName, true,100); + oracle.processBuilderCommitmentForBlockNumber(index2, blockNumber, provider, true,100); vm.expectEmit(true, false, false, true); emit CommitmentProcessed(index3, true); - oracle.processBuilderCommitmentForBlockNumber(index3, blockNumber, blockBuilderName, true,100); + oracle.processBuilderCommitmentForBlockNumber(index3, blockNumber, provider, true,100); vm.expectEmit(true, false, false, true); emit CommitmentProcessed(index4, true); - oracle.processBuilderCommitmentForBlockNumber(index4, blockNumber, blockBuilderName, true,100); + oracle.processBuilderCommitmentForBlockNumber(index4, blockNumber, provider, true,100); vm.stopPrank(); assertEq(providerRegistry.checkStake(provider), 250 ether - bid*4); assertEq(bidderRegistry.getProviderAmount(provider), 0); } function test_process_commitment_reward_multiple() public { - string memory txn1 = "0x6d9c53ad81249775f8c082b11ac293b2e19194ff791bd1c4fd37683310e90d08"; - string memory txn2 = "0x6d9c53ad81249775f8c082b11ac293b2e19194ff791bd1c4fd37683310e90d09"; - string memory txn3 = "0x6d9c53ad81249775f8c082b11ac293b2e19194ff791bd1c4fd37683310e90d10"; - string memory txn4 = "0x6d9c53ad81249775f8c082b11ac293b2e19194ff791bd1c4fd37683310e90d11"; - uint64 blockNumber = 201; + string[] memory txnHashes = new string[](4); + txnHashes[0] = "0x6d9c53ad81249775f8c082b11ac293b2e19194ff791bd1c4fd37683310e90d08"; + txnHashes[1] = "0x6d9c53ad81249775f8c082b11ac293b2e19194ff791bd1c4fd37683310e90d09"; + txnHashes[2] = "0x6d9c53ad81249775f8c082b11ac293b2e19194ff791bd1c4fd37683310e90d10"; + txnHashes[3] = "0x6d9c53ad81249775f8c082b11ac293b2e19194ff791bd1c4fd37683310e90d11"; + uint64 blockNumber = 66; uint64 bid = 5; - string memory blockBuilderName = "kartik builder"; (address bidder, uint256 bidderPk) = makeAddrAndKey("alice"); (address provider, uint256 providerPk) = makeAddrAndKey("kartik"); vm.deal(bidder, 200000 ether); + uint256 window = blockTracker.getCurrentWindow(); vm.startPrank(bidder); - bidderRegistry.prepay{value: 250 ether }(); + bidderRegistry.depositForSpecificWindow{value: 250 ether}(window+1); vm.stopPrank(); vm.deal(provider, 200000 ether); @@ -323,70 +326,53 @@ contract OracleTest is Test { providerRegistry.registerAndStake{value: 250 ether}(); vm.stopPrank(); - bytes32 index1 = constructAndStoreCommitment(bid, blockNumber, txn1, 10, 20, bidderPk, providerPk); - assertEq(bidderRegistry.bidderPrepaidBalances(bidder), 250 ether - bid); - bytes32 index2 = constructAndStoreCommitment(bid, blockNumber, txn2, 10, 20, bidderPk, providerPk); - assertEq(bidderRegistry.bidderPrepaidBalances(bidder), 250 ether - 2*bid); - bytes32 index3 = constructAndStoreCommitment(bid, blockNumber, txn3, 10, 20, bidderPk, providerPk); - assertEq(bidderRegistry.bidderPrepaidBalances(bidder), 250 ether - 3*bid); - bytes32 index4 = constructAndStoreCommitment(bid, blockNumber, txn4, 10, 20, bidderPk, providerPk); - assertEq(bidderRegistry.bidderPrepaidBalances(bidder), 250 ether - 4*bid); + bytes32[] memory commitments = new bytes32[](4); + bytes[] memory bidSignatures = new bytes[](4); + bytes[] memory commitmentSignatures = new bytes[](4); + for (uint i = 0; i < commitments.length; i++) { + (commitments[i], bidSignatures[i], commitmentSignatures[i]) = constructAndStoreEncryptedCommitment( + bid, + blockNumber, + txnHashes[i], + 10, + 20, + bidderPk, + providerPk + ); + } - vm.startPrank(address(0x6d503Fd50142C7C469C7c6B64794B55bfa6883f3)); - oracle.addBuilderAddress(blockBuilderName, provider); + vm.startPrank(0x6d503Fd50142C7C469C7c6B64794B55bfa6883f3); + blockTracker.addBuilderAddress("test", provider); + blockTracker.recordL1Block(blockNumber, "test"); + vm.stopPrank(); - vm.expectEmit(true, false, false, true); - emit CommitmentProcessed(index1, false); - oracle.processBuilderCommitmentForBlockNumber(index1, blockNumber, blockBuilderName, false,100); - vm.expectEmit(true, false, false, true); - emit CommitmentProcessed(index2, false); - oracle.processBuilderCommitmentForBlockNumber(index2, blockNumber, blockBuilderName, false,100); - vm.expectEmit(true, false, false, true); - emit CommitmentProcessed(index3, false); - oracle.processBuilderCommitmentForBlockNumber(index3, blockNumber, blockBuilderName, false,100); - vm.expectEmit(true, false, false, true); - emit CommitmentProcessed(index4, false); - oracle.processBuilderCommitmentForBlockNumber(index4, blockNumber, blockBuilderName, false,100); + for (uint i = 0; i < commitments.length; i++) { + vm.startPrank(provider); + preConfCommitmentStore.openCommitment( + commitments[i], + bid, + blockNumber, + txnHashes[i], + 10, + 20, + bidSignatures[i], + commitmentSignatures[i], + sharedSecretKey + ); + vm.stopPrank(); + } + + vm.startPrank(address(0x6d503Fd50142C7C469C7c6B64794B55bfa6883f3)); + for (uint i = 0; i < commitments.length; i++) { + vm.expectEmit(true, false, false, true); + emit CommitmentProcessed(commitments[i], false); + oracle.processBuilderCommitmentForBlockNumber(commitments[i], blockNumber, provider, false,100); + } vm.stopPrank(); assertEq(providerRegistry.checkStake(provider), 250 ether); assertEq(bidderRegistry.getProviderAmount(provider), 4*bid); } - - function test_process_commitment_and_return() public { - string memory txn = "0x6d9c53ad81249775f8c082b11ac293b2e19194ff791bd1c4fd37683310e90d08"; - uint64 blockNumber = 200; - uint64 bid = 2; - (address bidder, uint256 bidderPk) = makeAddrAndKey("alice"); - (address provider, uint256 providerPk) = makeAddrAndKey("kartik"); - - vm.deal(bidder, 200000 ether); - vm.startPrank(bidder); - bidderRegistry.prepay{value: 250 ether }(); - vm.stopPrank(); - - vm.deal(provider, 200000 ether); - vm.startPrank(provider); - providerRegistry.registerAndStake{value: 250 ether}(); - vm.stopPrank(); - - bytes32 index = constructAndStoreCommitment(bid, blockNumber, txn, 10, 20, bidderPk, providerPk); - PreConfCommitmentStore.PreConfCommitment memory commitment = preConfCommitmentStore.getCommitment(index); - - vm.startPrank(address(0x6d503Fd50142C7C469C7c6B64794B55bfa6883f3)); - bytes32[] memory commitments = new bytes32[](1); - commitments[0] = commitment.commitmentHash; - - vm.expectEmit(true, false, false, true); - emit FundsRetrieved(commitment.commitmentHash, bid); - oracle.unlockFunds(commitments); - - - assertEq(providerRegistry.checkStake(provider) , 250 ether); - assertEq(bidderRegistry.bidderPrepaidBalances(bidder), 250 ether); - } - - /** constructAndStoreCommitment is a helper function to construct and store a commitment */ @@ -397,7 +383,8 @@ contract OracleTest is Test { uint64 decayStartTimestamp, uint64 decayEndTimestamp, uint256 bidderPk, - uint256 signerPk + uint256 signerPk, + address provider ) public returns (bytes32 commitmentIndex) { bytes32 bidHash = preConfCommitmentStore.getBidHash( txnHash, @@ -418,24 +405,80 @@ contract OracleTest is Test { decayStartTimestamp, decayEndTimestamp, bidHash, - _bytesToHexString(bidSignature) + _bytesToHexString(bidSignature), + _bytesToHexString(sharedSecretKey) ); (v,r,s) = vm.sign(signerPk, commitmentHash); bytes memory commitmentSignature = abi.encodePacked(r, s, v); - commitmentIndex = preConfCommitmentStore.storeCommitment( + bytes32 encryptedCommitmentIndex = preConfCommitmentStore.storeEncryptedCommitment( + commitmentHash, + commitmentSignature + ); + vm.startPrank(0x6d503Fd50142C7C469C7c6B64794B55bfa6883f3); + blockTracker.addBuilderAddress("test", provider); + blockTracker.recordL1Block(blockNumber, "test"); + vm.stopPrank(); + vm.startPrank(provider); + commitmentIndex = preConfCommitmentStore.openCommitment( + encryptedCommitmentIndex, bid, blockNumber, txnHash, decayStartTimestamp, decayEndTimestamp, bidSignature, - commitmentSignature + commitmentSignature, + sharedSecretKey ); + vm.stopPrank(); return commitmentIndex; } + + function constructAndStoreEncryptedCommitment( + uint64 bid, + uint64 blockNumber, + string memory txnHash, + uint64 decayStartTimestamp, + uint64 decayEndTimestamp, + uint256 bidderPk, + uint256 signerPk + ) public returns (bytes32 commitmentIndex, bytes memory bidSignature, bytes memory commitmentSignature) { + bytes32 bidHash = preConfCommitmentStore.getBidHash( + txnHash, + bid, + blockNumber, + decayStartTimestamp, + decayEndTimestamp + ); + + + (uint8 v,bytes32 r, bytes32 s) = vm.sign(bidderPk, bidHash); + bidSignature = abi.encodePacked(r, s, v); + + bytes32 commitmentHash = preConfCommitmentStore.getPreConfHash( + txnHash, + bid, + blockNumber, + decayStartTimestamp, + decayEndTimestamp, + bidHash, + _bytesToHexString(bidSignature), + _bytesToHexString(sharedSecretKey) + ); + + (v,r,s) = vm.sign(signerPk, commitmentHash); + commitmentSignature = abi.encodePacked(r, s, v); + + bytes32 encryptedCommitmentIndex = preConfCommitmentStore.storeEncryptedCommitment( + commitmentHash, + commitmentSignature + ); + + return (encryptedCommitmentIndex, bidSignature, commitmentSignature); + } function _bytesToHexString( diff --git a/test/PreConfirmationConfTest.sol b/test/PreConfirmationConfTest.sol index 34ffc8e..e98a9ae 100644 --- a/test/PreConfirmationConfTest.sol +++ b/test/PreConfirmationConfTest.sol @@ -3,13 +3,13 @@ pragma solidity ^0.8.20; import "forge-std/Test.sol"; - import {PreConfCommitmentStore} from "../contracts/PreConfirmations.sol"; import "../contracts/ProviderRegistry.sol"; import "../contracts/BidderRegistry.sol"; +import "../contracts/BlockTracker.sol"; +import "forge-std/console.sol"; contract TestPreConfCommitmentStore is Test { - struct TestCommitment { uint64 bid; uint64 blockNumber; @@ -20,6 +20,7 @@ contract TestPreConfCommitmentStore is Test { bytes32 commitmentDigest; bytes bidSignature; bytes commitmentSignature; + bytes sharedSecretKey; } TestCommitment internal _testCommitmentAliceBob; @@ -29,6 +30,7 @@ contract TestPreConfCommitmentStore is Test { address internal provider; address internal feeRecipient; ProviderRegistry internal providerRegistry; + BlockTracker internal blockTracker; BidderRegistry internal bidderRegistry; @@ -40,9 +42,10 @@ contract TestPreConfCommitmentStore is Test { 10, 20, 0xa0327970258c49b922969af74d60299a648c50f69a2d98d6ab43f32f64ac2100, - 0x54c118e537dd7cf63b5388a5fc8322f0286a978265d0338b108a8ca9d155dccc, + 0x65618f8f9e46b8f0790c621ca2989cfe4c949594a4a3a81261baa682e8883840, hex"876c1216c232828be9fabb14981c8788cebdf6ed66e563c4a2ccc82a577d052543207aeeb158a32d8977736797ae250c63ef69a82cd85b727da21e20d030fb311b", - hex"ec0f11f77a9e96bb9c2345f031a5d12dca8d01de8a2e957cf635be14802f9ad01c6183688f0c2672639e90cc2dce0662d9bea3337306ca7d4b56dd80326aaa231b" + hex"bfea9167927707ae7586ed3bba8565999f8b7ad874b2dd4f175caf81084c0d0a17f9599daf5b3f2773757408aa4b44875c95df0f4150cfb295f95273e1fefdd01b", + bytes("0xsecret") ); feePercent = 10; @@ -54,17 +57,26 @@ contract TestPreConfCommitmentStore is Test { feePercent, address(this) ); - - bidderRegistry = new BidderRegistry(minStake, feeRecipient, feePercent, address(this)); + blockTracker = new BlockTracker(address(this)); + bidderRegistry = new BidderRegistry( + minStake, + feeRecipient, + feePercent, + address(this), + address(blockTracker) + ); preConfCommitmentStore = new PreConfCommitmentStore( address(providerRegistry), // Provider Registry address(bidderRegistry), // User Registry + address(blockTracker), // Block Tracker feeRecipient, // Oracle address(this) // Owner ); - bidderRegistry.setPreconfirmationsContract(address(preConfCommitmentStore)); + bidderRegistry.setPreconfirmationsContract( + address(preConfCommitmentStore) + ); } function test_Initialize() public { @@ -79,45 +91,41 @@ contract TestPreConfCommitmentStore is Test { ); } - function test_CreateCommitment() public { - bytes32 bidHash = preConfCommitmentStore.getBidHash( - _testCommitmentAliceBob.txnHash, - _testCommitmentAliceBob.bid, - _testCommitmentAliceBob.blockNumber, - _testCommitmentAliceBob.decayStartTimestamp, - _testCommitmentAliceBob.decayEndTimestamp + function test_storeEncryptedCommitment() public { + // Step 1: Prepare the commitment information and signature + bytes32 commitmentDigest = keccak256( + abi.encodePacked("commitment data") ); - (address bidder, uint256 bidderPk) = makeAddrAndKey("alice"); - // Wallet memory kartik = vm.createWallet('test wallet'); - (uint8 v,bytes32 r, bytes32 s) = vm.sign(bidderPk, bidHash); - bytes memory signature = abi.encodePacked(r, s, v); - - vm.deal(bidder, 200000 ether); - vm.prank(bidder); - bidderRegistry.prepay{value: 1e18 wei}(); - - (bytes32 digest, address recoveredAddress, uint256 stake) = preConfCommitmentStore.verifyBid( - _testCommitmentAliceBob.bid, - _testCommitmentAliceBob.blockNumber, - _testCommitmentAliceBob.decayStartTimestamp, - _testCommitmentAliceBob.decayEndTimestamp, - _testCommitmentAliceBob.txnHash, - signature); - - assertEq(stake, 1e18 wei); - assertEq(bidder, recoveredAddress); - assertEq(digest, bidHash); - - preConfCommitmentStore.storeCommitment( - _testCommitmentAliceBob.bid, - _testCommitmentAliceBob.blockNumber, - _testCommitmentAliceBob.txnHash, - _testCommitmentAliceBob.decayStartTimestamp, - _testCommitmentAliceBob.decayEndTimestamp, - signature, - _testCommitmentAliceBob.commitmentSignature + (address committer, uint256 committerPk) = makeAddrAndKey("committer"); + (uint8 v, bytes32 r, bytes32 s) = vm.sign( + committerPk, + commitmentDigest ); + bytes memory commitmentSignature = abi.encodePacked(r, s, v); + + // Optional: Ensure the committer has enough ETH if needed for the operation + vm.deal(committer, 1 ether); + vm.prank(committer); + + // Step 2: Store the commitment + bytes32 commitmentIndex = preConfCommitmentStore + .storeEncryptedCommitment(commitmentDigest, commitmentSignature); + + // Step 3: Verify the results + // a. Check that the commitment index is correctly generated and not zero + assert(commitmentIndex != bytes32(0)); + + // b. Retrieve the commitment by index and verify its properties + PreConfCommitmentStore.EncrPreConfCommitment + memory commitment = preConfCommitmentStore.getEncryptedCommitment( + commitmentIndex + ); + // c. Assertions to verify the stored commitment matches the input + assertEq(commitment.commitmentUsed, false); + assertEq(commitment.commiter, committer); + assertEq(commitment.commitmentDigest, commitmentDigest); + assertEq(commitment.commitmentSignature, commitmentSignature); } function test_UpdateOracle() public { @@ -135,7 +143,10 @@ contract TestPreConfCommitmentStore is Test { function test_UpdateBidderRegistry() public { preConfCommitmentStore.updateBidderRegistry(feeRecipient); - assertEq(address(preConfCommitmentStore.bidderRegistry()), feeRecipient); + assertEq( + address(preConfCommitmentStore.bidderRegistry()), + feeRecipient + ); } function test_GetBidHash() public { @@ -146,16 +157,12 @@ contract TestPreConfCommitmentStore is Test { _testCommitmentAliceBob.decayStartTimestamp, _testCommitmentAliceBob.decayEndTimestamp ); - assertEq( - bidHash, - _testCommitmentAliceBob.bidDigest - ); + assertEq(bidHash, _testCommitmentAliceBob.bidDigest); } function test_GetCommitmentDigest() public { (, uint256 bidderPk) = makeAddrAndKey("alice"); - bytes32 bidHash = preConfCommitmentStore.getBidHash( _testCommitmentAliceBob.txnHash, _testCommitmentAliceBob.bid, @@ -164,8 +171,9 @@ contract TestPreConfCommitmentStore is Test { _testCommitmentAliceBob.decayEndTimestamp ); - (uint8 v,bytes32 r, bytes32 s) = vm.sign(bidderPk, bidHash); + (uint8 v, bytes32 r, bytes32 s) = vm.sign(bidderPk, bidHash); bytes memory signature = abi.encodePacked(r, s, v); + bytes memory sharedSecretKey = bytes("0xsecret"); bytes32 preConfHash = preConfCommitmentStore.getPreConfHash( _testCommitmentAliceBob.txnHash, @@ -174,14 +182,16 @@ contract TestPreConfCommitmentStore is Test { _testCommitmentAliceBob.decayStartTimestamp, _testCommitmentAliceBob.decayEndTimestamp, bidHash, - _bytesToHexString(signature) + _bytesToHexString(signature), + _bytesToHexString(sharedSecretKey) ); - assertEq( - preConfHash, - _testCommitmentAliceBob.commitmentDigest - ); - } + assertEq(preConfHash, _testCommitmentAliceBob.commitmentDigest); + (, uint256 providerPk) = makeAddrAndKey("bob"); + (v, r, s) = vm.sign(providerPk, preConfHash); + signature = abi.encodePacked(r, s, v); + console.logBytes(signature); + } function _bytes32ToHexString( bytes32 _bytes32 @@ -199,8 +209,8 @@ contract TestPreConfCommitmentStore is Test { (address bidder, ) = makeAddrAndKey("alice"); vm.deal(bidder, 5 ether); vm.prank(bidder); - bidderRegistry.prepay{value: 2 ether}(); - + bidderRegistry.depositForSpecificWindow{value: 2 ether}(2); + // Step 1: Verify that the commitment has not been used before verifyCommitmentNotUsed( _testCommitmentAliceBob.txnHash, @@ -212,17 +222,36 @@ contract TestPreConfCommitmentStore is Test { ); // Step 2: Store the commitment - bytes32 index = storeCommitment( + bytes32 encryptedIndex = storeCommitment( _testCommitmentAliceBob.bid, _testCommitmentAliceBob.blockNumber, _testCommitmentAliceBob.txnHash, _testCommitmentAliceBob.decayStartTimestamp, _testCommitmentAliceBob.decayEndTimestamp, _testCommitmentAliceBob.bidSignature, - _testCommitmentAliceBob.commitmentSignature + _testCommitmentAliceBob.commitmentSignature, + _testCommitmentAliceBob.sharedSecretKey ); - // Step 3: Verify the stored commitment + // Step 3: Move to the next window + blockTracker.addBuilderAddress("test", address(this)); + blockTracker.recordL1Block(2, "test"); + + // Step 4: Open the commitment + bytes32 index = openCommitment( + bidder, + encryptedIndex, + _testCommitmentAliceBob.bid, + _testCommitmentAliceBob.blockNumber, + _testCommitmentAliceBob.txnHash, + _testCommitmentAliceBob.decayStartTimestamp, + _testCommitmentAliceBob.decayEndTimestamp, + _testCommitmentAliceBob.bidSignature, + _testCommitmentAliceBob.commitmentSignature, + _testCommitmentAliceBob.sharedSecretKey + ); + + // Step 5: Verify the stored commitment verifyStoredCommitment( index, _testCommitmentAliceBob.bid, @@ -231,10 +260,14 @@ contract TestPreConfCommitmentStore is Test { _testCommitmentAliceBob.decayEndTimestamp, _testCommitmentAliceBob.txnHash, _testCommitmentAliceBob.bidSignature, - _testCommitmentAliceBob.commitmentSignature + _testCommitmentAliceBob.commitmentSignature, + _testCommitmentAliceBob.sharedSecretKey ); - string memory commitmentTxnHash = preConfCommitmentStore.getTxnHashFromCommitment(index); + string memory commitmentTxnHash = preConfCommitmentStore + .getTxnHashFromCommitment(index); + console.log(commitmentTxnHash); + console.log(_testCommitmentAliceBob.txnHash); assertEq(commitmentTxnHash, _testCommitmentAliceBob.txnHash); } @@ -253,6 +286,7 @@ contract TestPreConfCommitmentStore is Test { decayStartTimestamp, decayEndTimestamp ); + bytes memory sharedSecretKey = abi.encodePacked(keccak256("0xsecret")); bytes32 preConfHash = preConfCommitmentStore.getPreConfHash( txnHash, bid, @@ -260,10 +294,11 @@ contract TestPreConfCommitmentStore is Test { decayStartTimestamp, decayEndTimestamp, bidHash, - _bytesToHexString(bidSignature) + _bytesToHexString(bidSignature), + _bytesToHexString(sharedSecretKey) ); - (bool commitmentUsed, , , , , , , , , , , , ) = preConfCommitmentStore + (bool commitmentUsed, , , , , , , , , , , , , ) = preConfCommitmentStore .commitments(preConfHash); assertEq(commitmentUsed, false); @@ -277,49 +312,92 @@ contract TestPreConfCommitmentStore is Test { uint64 decayStartTimestamp, uint64 decayEndTimestamp, bytes memory bidSignature, - bytes memory commitmentSignature + bytes memory commitmentSignature, + bytes memory sharedSecretKey ) internal returns (bytes32) { - bytes32 commitmentIndex = preConfCommitmentStore.storeCommitment( + bytes32 bidHash = preConfCommitmentStore.getBidHash( + txnHash, bid, blockNumber, + decayStartTimestamp, + decayEndTimestamp + ); + + bytes32 commitmentHash = preConfCommitmentStore.getPreConfHash( txnHash, + bid, + blockNumber, decayStartTimestamp, decayEndTimestamp, - bidSignature, - commitmentSignature + bidHash, + _bytesToHexString(bidSignature), + _bytesToHexString(sharedSecretKey) ); + bytes32 commitmentIndex = preConfCommitmentStore + .storeEncryptedCommitment(commitmentHash, commitmentSignature); + return commitmentIndex; } - function verifyStoredCommitment( - bytes32 index, + function openCommitment( + address msgSender, + bytes32 encryptedCommitmentIndex, uint64 bid, uint64 blockNumber, + string memory txnHash, uint64 decayStartTimestamp, uint64 decayEndTimestamp, - string memory txnHash, bytes memory bidSignature, - bytes memory commitmentSignature - ) public { - - - (PreConfCommitmentStore.PreConfCommitment memory commitment) = preConfCommitmentStore - .getCommitment(index); - - (, address commiterAddress) = preConfCommitmentStore.verifyPreConfCommitment( - txnHash, + bytes memory commitmentSignature, + bytes memory sharedSecretKey + ) internal returns (bytes32) { + vm.prank(msgSender); + bytes32 commitmentIndex = preConfCommitmentStore.openCommitment( + encryptedCommitmentIndex, bid, blockNumber, + txnHash, decayStartTimestamp, decayEndTimestamp, - commitment.bidHash, bidSignature, - commitmentSignature + commitmentSignature, + sharedSecretKey ); - bytes32[] memory commitments = preConfCommitmentStore.getCommitmentsByCommitter(commiterAddress); - + return commitmentIndex; + } + + function verifyStoredCommitment( + bytes32 index, + uint64 bid, + uint64 blockNumber, + uint64 decayStartTimestamp, + uint64 decayEndTimestamp, + string memory txnHash, + bytes memory bidSignature, + bytes memory commitmentSignature, + bytes memory sharedSecretKey + ) public { + PreConfCommitmentStore.PreConfCommitment + memory commitment = preConfCommitmentStore.getCommitment(index); + + (, address commiterAddress) = preConfCommitmentStore + .verifyPreConfCommitment( + txnHash, + bid, + blockNumber, + decayStartTimestamp, + decayEndTimestamp, + commitment.bidHash, + bidSignature, + commitmentSignature, + sharedSecretKey + ); + + bytes32[] memory commitments = preConfCommitmentStore + .getCommitmentsByCommitter(commiterAddress); + assert(commitments.length >= 1); assertEq( @@ -354,7 +432,9 @@ contract TestPreConfCommitmentStore is Test { (address bidder, ) = makeAddrAndKey("alice"); vm.deal(bidder, 5 ether); vm.prank(bidder); - bidderRegistry.prepay{value: 2 ether}(); + uint256 window = blockTracker.getWindowFromBlockNumber(_testCommitmentAliceBob.blockNumber); + vm.prank(bidder); + bidderRegistry.depositForSpecificWindow{value: 2 ether}(window); // Step 1: Verify that the commitment has not been used before verifyCommitmentNotUsed( _testCommitmentAliceBob.txnHash, @@ -372,20 +452,21 @@ contract TestPreConfCommitmentStore is Test { _testCommitmentAliceBob.decayStartTimestamp, _testCommitmentAliceBob.decayEndTimestamp, _testCommitmentAliceBob.bidSignature, + _testCommitmentAliceBob.commitmentSignature, + _testCommitmentAliceBob.sharedSecretKey + ); + PreConfCommitmentStore.EncrPreConfCommitment + memory storedCommitment = preConfCommitmentStore + .getEncryptedCommitment(commitmentIndex); + + assertEq( + storedCommitment.commitmentDigest, + _testCommitmentAliceBob.commitmentDigest + ); + assertEq( + storedCommitment.commitmentSignature, _testCommitmentAliceBob.commitmentSignature ); - PreConfCommitmentStore.PreConfCommitment - memory storedCommitment = preConfCommitmentStore.getCommitment( - commitmentIndex - ); - - assertEq(storedCommitment.bid, _testCommitmentAliceBob.bid); - assertEq(storedCommitment.blockNumber, _testCommitmentAliceBob.blockNumber); - assertEq(storedCommitment.txnHash, _testCommitmentAliceBob.txnHash); - assertEq(storedCommitment.bidSignature, _testCommitmentAliceBob.bidSignature); - assertEq(storedCommitment.commitmentSignature, _testCommitmentAliceBob.commitmentSignature); - assertEq(storedCommitment.decayEndTimeStamp, _testCommitmentAliceBob.decayEndTimestamp); - assertEq(storedCommitment.decayStartTimeStamp, _testCommitmentAliceBob.decayStartTimestamp); } function test_InitiateSlash() public { @@ -394,8 +475,9 @@ contract TestPreConfCommitmentStore is Test { (address bidder, ) = makeAddrAndKey("alice"); vm.deal(bidder, 5 ether); vm.prank(bidder); - bidderRegistry.prepay{value: 2 ether}(); - + uint256 depositWindow = blockTracker.getWindowFromBlockNumber(_testCommitmentAliceBob.blockNumber); + bidderRegistry.depositForSpecificWindow{value: 2 ether}(depositWindow); + // Step 1: Verify that the commitment has not been used before bytes32 bidHash = verifyCommitmentNotUsed( _testCommitmentAliceBob.txnHash, @@ -413,21 +495,23 @@ contract TestPreConfCommitmentStore is Test { _testCommitmentAliceBob.decayStartTimestamp, _testCommitmentAliceBob.decayEndTimestamp, bidHash, - _bytesToHexString(_testCommitmentAliceBob.bidSignature) + _bytesToHexString(_testCommitmentAliceBob.bidSignature), + _bytesToHexString(_testCommitmentAliceBob.sharedSecretKey) ); // Verify that the commitment has not been used before - (bool commitmentUsed, , , , , , , , , , , , ) = preConfCommitmentStore + (bool commitmentUsed, , , , , , , , , , , , , ) = preConfCommitmentStore .commitments(preConfHash); assert(commitmentUsed == false); - bytes32 index = preConfCommitmentStore.storeCommitment( + bytes32 encryptedIndex = storeCommitment( _testCommitmentAliceBob.bid, _testCommitmentAliceBob.blockNumber, _testCommitmentAliceBob.txnHash, _testCommitmentAliceBob.decayStartTimestamp, _testCommitmentAliceBob.decayEndTimestamp, _testCommitmentAliceBob.bidSignature, - _testCommitmentAliceBob.commitmentSignature + _testCommitmentAliceBob.commitmentSignature, + _testCommitmentAliceBob.sharedSecretKey ); providerRegistry.setPreconfirmationsContract( address(preConfCommitmentStore) @@ -436,25 +520,41 @@ contract TestPreConfCommitmentStore is Test { vm.deal(commiter, 5 ether); vm.prank(commiter); providerRegistry.registerAndStake{value: 4 ether}(); + uint256 blockNumber = 2; + blockTracker.addBuilderAddress("test", commiter); + blockTracker.recordL1Block(blockNumber, "test"); + bytes32 index = openCommitment( + commiter, + encryptedIndex, + _testCommitmentAliceBob.bid, + _testCommitmentAliceBob.blockNumber, + _testCommitmentAliceBob.txnHash, + _testCommitmentAliceBob.decayStartTimestamp, + _testCommitmentAliceBob.decayEndTimestamp, + _testCommitmentAliceBob.bidSignature, + _testCommitmentAliceBob.commitmentSignature, + _testCommitmentAliceBob.sharedSecretKey + ); vm.prank(feeRecipient); preConfCommitmentStore.initiateSlash(index, 100); - (commitmentUsed, , , , , , , , , , , , ) = preConfCommitmentStore + (commitmentUsed, , , , , , , , , , , , , ) = preConfCommitmentStore .commitments(index); // Verify that the commitment has been marked as used assert(commitmentUsed == true); } // commitmentHash value is internal to contract and not asserted } - + function test_InitiateReward() public { // Assuming you have a stored commitment { (address bidder, ) = makeAddrAndKey("alice"); vm.deal(bidder, 5 ether); vm.prank(bidder); - bidderRegistry.prepay{value: 2 ether}(); - + uint256 depositWindow = blockTracker.getWindowFromBlockNumber(_testCommitmentAliceBob.blockNumber); + bidderRegistry.depositForSpecificWindow{value: 2 ether}(depositWindow); + // Step 1: Verify that the commitment has not been used before bytes32 bidHash = verifyCommitmentNotUsed( _testCommitmentAliceBob.txnHash, @@ -471,30 +571,46 @@ contract TestPreConfCommitmentStore is Test { _testCommitmentAliceBob.decayStartTimestamp, _testCommitmentAliceBob.decayEndTimestamp, bidHash, - _bytesToHexString(_testCommitmentAliceBob.bidSignature) + _bytesToHexString(_testCommitmentAliceBob.bidSignature), + _bytesToHexString(_testCommitmentAliceBob.sharedSecretKey) ); // Verify that the commitment has not been used before - (bool commitmentUsed, , , , , , , , , , , , ) = preConfCommitmentStore + (bool commitmentUsed, , , , , , , , , , , , , ) = preConfCommitmentStore .commitments(preConfHash); assert(commitmentUsed == false); - bytes32 index = preConfCommitmentStore.storeCommitment( + bytes32 encryptedIndex = storeCommitment( _testCommitmentAliceBob.bid, _testCommitmentAliceBob.blockNumber, _testCommitmentAliceBob.txnHash, _testCommitmentAliceBob.decayStartTimestamp, _testCommitmentAliceBob.decayEndTimestamp, _testCommitmentAliceBob.bidSignature, - _testCommitmentAliceBob.commitmentSignature + _testCommitmentAliceBob.commitmentSignature, + _testCommitmentAliceBob.sharedSecretKey ); (address commiter, ) = makeAddrAndKey("bob"); vm.deal(commiter, 5 ether); vm.prank(commiter); providerRegistry.registerAndStake{value: 4 ether}(); + blockTracker.addBuilderAddress("test", commiter); + blockTracker.recordL1Block(_testCommitmentAliceBob.blockNumber, "test"); + bytes32 index = openCommitment( + commiter, + encryptedIndex, + _testCommitmentAliceBob.bid, + _testCommitmentAliceBob.blockNumber, + _testCommitmentAliceBob.txnHash, + _testCommitmentAliceBob.decayStartTimestamp, + _testCommitmentAliceBob.decayEndTimestamp, + _testCommitmentAliceBob.bidSignature, + _testCommitmentAliceBob.commitmentSignature, + _testCommitmentAliceBob.sharedSecretKey + ); vm.prank(feeRecipient); preConfCommitmentStore.initiateReward(index, 100); - (commitmentUsed, , , , , , , , , , , , ) = preConfCommitmentStore + (commitmentUsed, , , , , , , , , , , , , ) = preConfCommitmentStore .commitments(index); // Verify that the commitment has been marked as used assert(commitmentUsed == true); @@ -502,15 +618,16 @@ contract TestPreConfCommitmentStore is Test { } } - function test_InitiateRewardFullyDecayed() public { // Assuming you have a stored commitment { (address bidder, ) = makeAddrAndKey("alice"); + uint64 blockNumber = 66; + uint256 depositWindow = blockTracker.getWindowFromBlockNumber(blockNumber); vm.deal(bidder, 5 ether); vm.prank(bidder); - bidderRegistry.prepay{value: 2 ether}(); - + bidderRegistry.depositForSpecificWindow{value: 2 ether}(depositWindow); + // Step 1: Verify that the commitment has not been used before bytes32 bidHash = verifyCommitmentNotUsed( _testCommitmentAliceBob.txnHash, @@ -527,40 +644,57 @@ contract TestPreConfCommitmentStore is Test { _testCommitmentAliceBob.decayStartTimestamp, _testCommitmentAliceBob.decayEndTimestamp, bidHash, - _bytesToHexString(_testCommitmentAliceBob.bidSignature) + _bytesToHexString(_testCommitmentAliceBob.bidSignature), + _bytesToHexString(_testCommitmentAliceBob.sharedSecretKey) ); // Verify that the commitment has not been used before - (bool commitmentUsed, , , , , , , , , , , , ) = preConfCommitmentStore + (bool commitmentUsed, , , , , , , , , , , , , ) = preConfCommitmentStore .commitments(preConfHash); assert(commitmentUsed == false); - bytes32 index = preConfCommitmentStore.storeCommitment( + bytes32 encryptedIndex = storeCommitment( _testCommitmentAliceBob.bid, - _testCommitmentAliceBob.blockNumber, + blockNumber, _testCommitmentAliceBob.txnHash, _testCommitmentAliceBob.decayStartTimestamp, _testCommitmentAliceBob.decayEndTimestamp, _testCommitmentAliceBob.bidSignature, - _testCommitmentAliceBob.commitmentSignature + _testCommitmentAliceBob.commitmentSignature, + _testCommitmentAliceBob.sharedSecretKey ); (address commiter, ) = makeAddrAndKey("bob"); vm.deal(commiter, 5 ether); vm.prank(commiter); providerRegistry.registerAndStake{value: 4 ether}(); + blockTracker.addBuilderAddress("test", commiter); + blockTracker.recordL1Block(blockNumber, "test"); + bytes32 index = openCommitment( + commiter, + encryptedIndex, + _testCommitmentAliceBob.bid, + blockNumber, + _testCommitmentAliceBob.txnHash, + _testCommitmentAliceBob.decayStartTimestamp, + _testCommitmentAliceBob.decayEndTimestamp, + _testCommitmentAliceBob.bidSignature, + _testCommitmentAliceBob.commitmentSignature, + _testCommitmentAliceBob.sharedSecretKey + ); + uint256 window = blockTracker.getCurrentWindow(); vm.prank(feeRecipient); preConfCommitmentStore.initiateReward(index, 0); - (commitmentUsed, , , , , , , , , , , , ) = preConfCommitmentStore + (commitmentUsed, , , , , , , , , , , , , ) = preConfCommitmentStore .commitments(index); // Verify that the commitment has been marked as used assert(commitmentUsed == true); // commitmentHash value is internal to contract and not asserted - assert(bidderRegistry.bidderPrepaidBalances(bidder) == 2 ether); + assert(bidderRegistry.lockedFunds(bidder, window) == 2 ether); assert(bidderRegistry.providerAmount(commiter) == 0 ether); } - } + function _bytesToHexString( bytes memory _bytes ) public pure returns (string memory) { @@ -572,4 +706,4 @@ contract TestPreConfCommitmentStore is Test { } return string(_string); } -} +} \ No newline at end of file diff --git a/test/ProviderRegistryTest.sol b/test/ProviderRegistryTest.sol index b3da8d3..250f0db 100644 --- a/test/ProviderRegistryTest.sol +++ b/test/ProviderRegistryTest.sol @@ -5,6 +5,7 @@ import "forge-std/Test.sol"; import {ProviderRegistry} from "../contracts/ProviderRegistry.sol"; import {BidderRegistry} from "../contracts/BidderRegistry.sol"; import {PreConfCommitmentStore} from "../contracts/PreConfirmations.sol"; +import {BlockTracker} from "../contracts/BlockTracker.sol"; contract ProviderRegistryTest is Test { uint256 testNumber; @@ -15,7 +16,7 @@ contract ProviderRegistryTest is Test { address internal feeRecipient; BidderRegistry bidderRegistry; PreConfCommitmentStore preConfCommitmentStore; - + BlockTracker blockTracker; event ProviderRegistered(address indexed provider, uint256 stakedAmount); function setUp() public { @@ -30,12 +31,13 @@ contract ProviderRegistryTest is Test { feePercent, address(this) ); - - bidderRegistry = new BidderRegistry(minStake, feeRecipient, feePercent, address(this)); + blockTracker = new BlockTracker(address(this)); + bidderRegistry = new BidderRegistry(minStake, feeRecipient, feePercent, address(this), address(blockTracker)); preConfCommitmentStore = new PreConfCommitmentStore( address(providerRegistry), // Provider Registry address(bidderRegistry), // User Registry + address(blockTracker), // Block Tracker feeRecipient, // Oracle address(this) // Owner );