Skip to content

Commit

Permalink
wip batch
Browse files Browse the repository at this point in the history
  • Loading branch information
cyberhorsey committed May 26, 2023
1 parent 674619b commit 841badf
Show file tree
Hide file tree
Showing 5 changed files with 153 additions and 119 deletions.
18 changes: 13 additions & 5 deletions packages/protocol/contracts/L1/TaikoData.sol
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,12 @@ library TaikoData {
uint64 maxEthDepositsPerBlock;
uint96 maxEthDepositAmount;
uint96 minEthDepositAmount;
// how many blocks you win per batch
uint64 auctionBlockBatchSize;
// how many blocks past lastVerifiedId you can bid for
uint64 auctionBatchGap;
// how long after initial bid the auction lives for
uint256 auctionLengthInSeconds;
bool relaySignalRoot;
}

Expand All @@ -43,6 +49,7 @@ library TaikoData {
uint64 numEthDeposits;
uint256 avgProofTime;
uint256 avgProofReward;
uint256 avgSuccessfulBidFeePerGas;
}

// 3 slots
Expand Down Expand Up @@ -120,11 +127,11 @@ library TaikoData {
}

struct Bid {
uint256 minFeePerGasAcceptedInWei;
uint256 blockId;
uint256 feePerGasInWei;
uint256 batchId;
uint256 deposit;
address bidder;
uint256 bidAt;
uint256 auctionStartedAt;
}

struct State {
Expand All @@ -139,7 +146,7 @@ library TaikoData {
) forkChoiceIds;
mapping(address account => uint256 balance) taikoTokenBalances;
mapping(bytes32 txListHash => TxListInfo) txListInfo;
mapping(uint256 blockId => Bid bid) bids;
mapping(uint256 batchId => Bid bid) bids;
EthDeposit[] ethDeposits;
// Never or rarely changed
// Slot 7: never or rarely changed
Expand All @@ -155,7 +162,8 @@ library TaikoData {
uint64 __reversed71;
uint256 avgProofTime;
uint256 avgProofReward;
uint256 avgSuccessfulBidFeePerGas;
// Reserved
uint256[39] __gap;
uint256[38] __gap;
}
}
7 changes: 5 additions & 2 deletions packages/protocol/contracts/L1/TaikoEvents.sol
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,14 @@ abstract contract TaikoEvents {
event EthDeposited(TaikoData.EthDeposit deposit);

event Bid(
uint256 indexed id,
uint256 batchId,
address bidder,
uint256 bidAt,
uint256 deposit,
uint256 minFeePerGasAcceptedInWei
uint256 minFeePerGas,
uint256 weight,
uint256 auctionStartedAt,
uint256 feePerGasInWei
);

event BidDepositRefunded(
Expand Down
26 changes: 6 additions & 20 deletions packages/protocol/contracts/L1/TaikoL1.sol
Original file line number Diff line number Diff line change
Expand Up @@ -47,22 +47,18 @@ contract TaikoL1 is EssentialContract, ICrossChainSync, TaikoEvents, TaikoErrors
/**
* Propose a Taiko L2 block.
*
* @param blockId the id of the block you are submitting a bid for.
* @param minFeePerGasAcceptedInWei The minimum fee, in wei, per gas you will accept
* @param batchId the id of the batch you are submitting a bid for.
* @param feePerGasInWei The minimum fee, in wei, per gas you will accept
* being paid as a reward for proving the block.
*/

function bidForBlock(uint256 blockId, uint256 minFeePerGasAcceptedInWei)
external
payable
nonReentrant
{
LibAuction.bidForBlock({
function bidForBatch(uint256 batchId, uint256 feePerGasInWei) external payable nonReentrant {
LibAuction.bidForBatch({
state: state,
resolver: AddressResolver(this),
config: getConfig(),
blockId: blockId,
minFeePerGasAcceptedInWei: minFeePerGasAcceptedInWei
batchId: batchId,
feePerGasInWei: feePerGasInWei
});
}

Expand Down Expand Up @@ -203,16 +199,6 @@ contract TaikoL1 is EssentialContract, ICrossChainSync, TaikoEvents, TaikoErrors
return state.getStateVariables();
}

function getBidForBlock(uint256 blockId) public view returns (TaikoData.Bid memory) {
return state.bids[blockId];
}

function isBiddingOpenForBlock(uint256 blockId) public view returns (bool) {
TaikoData.Bid memory currentBid = state.bids[blockId];

return LibAuction.isBiddingOpenForBlock(getConfig(), currentBid);
}

function getConfig() public pure virtual returns (TaikoData.Config memory) {
return TaikoConfig.getConfig();
}
Expand Down
219 changes: 128 additions & 91 deletions packages/protocol/contracts/L1/libs/LibAuction.sol
Original file line number Diff line number Diff line change
Expand Up @@ -6,137 +6,174 @@

pragma solidity ^0.8.18;

import {AddressResolver} from "../../common/AddressResolver.sol";
import {LibUtils} from "./LibUtils.sol";
import {TaikoData} from "../../L1/TaikoData.sol";
import {LibAddress} from "../../libs/LibAddress.sol";
import {AddressResolver} from "../../common/AddressResolver.sol";
import {TaikoToken} from "../TaikoToken.sol";

library LibAuction {
using LibAddress for address;

// EVENTS
event Bid(
uint256 indexed id,
uint256 batchId,
address bidder,
uint256 bidAt,
uint256 deposit,
uint256 minFeePerGasAcceptedInWei
);

event BidDepositRefunded(
uint256 indexed id, address claimer, uint256 refundedAt, uint256 refund
uint256 feePerGasInWei,
uint256 auctionStartedAt
);

error L1_ID();
error L1_HALTED();
error L1_ALREADY_CLAIMED();
error L1_INVALID_BID_AMOUNT();
error L1_BID_AUCTION_DELAY_PASSED();

// bidForBlock lets a prover claim a block for only themselves to prove,
// for a limited amount of time.
// if the time passes and they dont submit a proof,
// they will lose their deposit, half to the new prover, half is burnt.
// there is a set window anyone can bid, then the winner has N time to prove it.
// after which anyone can prove.
function bidForBlock(
TaikoData.State storage state,
// ERRORS

error L1_AUCTION_BLOCKS_NOT_AVAILABLE();
error L1_AUCTION_CLOSED_FOR_BATCH();
error L1_MAX_FEE_PER_GAS_EXCEEDED(uint256 maxFeePerGasAllowed);
error L1_TRANSFER_FROM_FAILED();
error L1_INVALID_BID();

// bidForBatch allows a user to claim a batch of blocks for
// exclusive proving rights. User's bid the "mininimum amount they will accept as a proving fee per gas",
// and the lowest ones win.
// blocks are bidded for in `N` batches, where `N` is the `auctionBlockBatchSize` in the config.
// blocks are available for bidding before they are proposed, but only up to
// state.lastVerifiedBlockId + auctionBlockGap.
function bidForBatch(
AddressResolver resolver,
TaikoData.State storage state,
TaikoData.Config memory config,
uint256 blockId,
uint256 minFeePerGasAcceptedInWei
uint256 feePerGasInWei,
uint256 batchId
) internal {
// if the block hasnt been proposed yet, dont allow bid.
if (blockId <= state.lastVerifiedBlockId || blockId >= state.numBlocks) {
revert L1_ID();
// can only auction for a certain number of blocks past
// the last verifiedID.
uint256 firstBatch = (state.lastVerifiedBlockId << 8) + 1;
uint256 lastBatch = firstBatch + config.auctionBatchGap;
require(batchId >= firstBatch && batchId <= lastBatch, "not available");

if (feePerGasInWei <= state.avgSuccessfulBidFeePerGas * 10) {
revert L1_MAX_FEE_PER_GAS_EXCEEDED(state.avgSuccessfulBidFeePerGas * 10);
}

TaikoData.Bid memory currentBid = state.bids[blockId % config.ringBufferSize];
// deposit is the auction bidders "how sure they are they will be able to prove the block"
// a higher deposit ensures they are quite confident.

// if a current bid exists, and the delay after the current bid exists
// has passed, bidding is over.
if (!isBiddingOpenForBlock(config, currentBid)) {
revert L1_BID_AUCTION_DELAY_PASSED();
if (!isAuctionOpen(config, state, batchId)) {
revert L1_AUCTION_CLOSED_FOR_BATCH();
}

TaikoToken tkoToken = TaikoToken(resolver.resolve("tko_token", false));

// if there is an existing claimer,
// we compare this bid to the existing one first, to see if its a
// lower fee accepted.
if (currentBid.bidder != address(0)) {
// if 0, there is no existing bid, any value is fine.
uint256 maxRequiredFeePerGasAccepted = maxRequiredBid(state, blockId);
if (
maxRequiredFeePerGasAccepted != 0
&& minFeePerGasAcceptedInWei < maxRequiredFeePerGasAccepted
) {
revert L1_INVALID_BID_AMOUNT();
} else {
// otherwise we have a new high bid, and can refund the previous claimer deposit
try tkoToken.transfer(currentBid.bidder, currentBid.deposit) {}
catch {
// allow to fail in case they have a bad onTokenReceived
// so they cant be outbid
}
emit BidDepositRefunded(
blockId, currentBid.bidder, block.timestamp, currentBid.deposit
);
TaikoData.Bid memory bid = state.bids[batchId];

uint256 deposit = calculateRequiredDeposit(config, feePerGasInWei);

TaikoToken tkoToken = TaikoToken(resolver.resolve("taiko_token", false));

if (bid.bidder != address(0)) {
if (!isValidBid(config, bid, feePerGasInWei)) {
revert L1_INVALID_BID();
}
// refund deposit if there is an existing bid
try tkoToken.transfer(bid.bidder, bid.deposit) {}
catch {
// allow to fail in case they have a bad onTokenReceived
}
}

// transfer deposit from bidder to this contract
tkoToken.transferFrom(msg.sender, address(this), msg.value);
if (!tkoToken.transferFrom(msg.sender, address(this), deposit)) {
revert L1_TRANSFER_FROM_FAILED();
}

uint256 auctionStartedAt = bid.auctionStartedAt > 0 ? bid.auctionStartedAt : block.timestamp;

// then we can update the bid for the blockID to the new bidder
state.bids[blockId] = TaikoData.Bid({
state.bids[batchId] = TaikoData.Bid({
bidder: msg.sender,
bidAt: block.timestamp,
deposit: msg.value,
minFeePerGasAcceptedInWei: minFeePerGasAcceptedInWei,
blockId: blockId
feePerGasInWei: feePerGasInWei,
deposit: deposit,
auctionStartedAt: auctionStartedAt,
batchId: batchId
});

emit Bid(blockId, msg.sender, block.timestamp, msg.value, minFeePerGasAcceptedInWei);
emit Bid({
batchId: batchId,
bidder: msg.sender,
feePerGasInWei: feePerGasInWei,
deposit: deposit,
auctionStartedAt: auctionStartedAt
});
}

// isValidBid determines if a bid is 5% less than an existing bid.
function isValidBid(
TaikoData.Config memory config,
TaikoData.Bid memory currentBid,
uint256 feePerGasInWei
) internal pure returns (bool) {
return true;
}

// maxRequiredBid determines the highest viable amount one could bid
// on the current block. If it returns 0, any amount is accepted.
function maxRequiredBid(TaikoData.State storage state, uint256 blockId)
function calculateRequiredDeposit(TaikoData.Config memory config, uint256 feePerGasInWei)
internal
view
pure
returns (uint256)
{
TaikoData.Bid memory bid = state.bids[blockId];
if (bid.bidder == address(0)) {
return 0;
}
// todo: should multiple by 1.5, not 2
return feePerGasInWei * config.blockMaxGasLimit * config.auctionBlockBatchSize * uint256(2);
}

return bid.minFeePerGasAcceptedInWei + ((bid.minFeePerGasAcceptedInWei * 1000) / 10000);
function isAuctionOpen(
TaikoData.Config memory config,
TaikoData.State storage state,
uint256 batchId
) internal view returns (bool) {
// auction is always open if there is no winner.
if (state.bids[batchId].bidder == address(0)) return true;

// if auctionLengthInSeconds hasnt passed, the auction is still open
if (state.bids[batchId].auctionStartedAt + config.auctionLengthInSeconds > block.timestamp)
{
return true;
}
// otherwise we have a winner!
return false;
}

// isBiddingOpenForBlockId determines whether a new bid for a block
// would be accepted or not
function isBiddingOpenForBlock(TaikoData.Config memory config, TaikoData.Bid memory currentBid)
function isBlockIdInBatch(uint256 auctionBlockBatchSize, uint256 blockId, uint256 batchId)
internal
view
pure
returns (bool)
{
if (currentBid.bidder == address(0)) return true;
(uint256 startBlockId, uint256 endBlockId) =
startAndEndBlockIdsForBatch(auctionBlockBatchSize, batchId);
return blockId >= startBlockId && blockId < endBlockId;
}

return (block.timestamp - currentBid.bidAt > config.auctionDelayInSeconds);
// inclusive of startBlock and endBlock
function startAndEndBlockIdsForBatch(uint256 auctionBlockBatchSize, uint256 batchId)
internal
pure
returns (uint256 startBlockId, uint256 endBlockId)
{
return (
batchId * auctionBlockBatchSize - auctionBlockBatchSize + 1,
(batchId * auctionBlockBatchSize)
);
}

// isAuctionWinnersBidForfeited determines whether that bidder still has sole claim
// over their bid, or if they have forfeited their right, and anyone
// can now prove, to win their deposit.
// if block.timestamp minus the bidAt time is greater than than the delay in seconds
// they forfeit.
function isAuctionWinnersBidForfeited(
function batchIdForBlockId(TaikoData.State storage state, uint256 blockId)
internal
pure
returns (uint256)
{}

function isAddressWinnerOfBlockId(
TaikoData.Config memory config,
TaikoData.Bid memory currentBid
TaikoData.State storage state,
uint256 blockId,
address addr
) internal pure returns (bool) {}

function isAddressBlockAuctionCurrentWinner(
TaikoData.State storage state,
uint256 batchId,
address addr
) internal view returns (bool) {
return
block.timestamp - currentBid.bidAt > config.auctionTimeForProverToSubmitProofInSeconds;
return state.bids[batchId].bidder == addr;
}
}
2 changes: 1 addition & 1 deletion packages/protocol/contracts/L1/libs/LibVerifying.sol
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,7 @@ library LibVerifying {

TaikoData.Bid memory winningBid = state.bids[blk.blockId];

uint256 reward = uint256(fc.gasUsed) * winningBid.minFeePerGasAcceptedInWei;
uint256 reward = uint256(fc.gasUsed) * winningBid.feePerGasInWei;

unchecked {
state.accProposedAt -= blk.proposedAt;
Expand Down

0 comments on commit 841badf

Please sign in to comment.