Skip to content

Commit

Permalink
refactor(protocol): change state.transitions as a ring buffer (#14645)
Browse files Browse the repository at this point in the history
Co-authored-by: Daniel Wang <[email protected]>
Co-authored-by: David <[email protected]>
Co-authored-by: D <[email protected]>
Co-authored-by: Brecht Devos <[email protected]>
  • Loading branch information
5 people authored Sep 5, 2023
1 parent d75e1a2 commit 878964e
Show file tree
Hide file tree
Showing 14 changed files with 174 additions and 184 deletions.
20 changes: 13 additions & 7 deletions packages/protocol/contracts/L1/TaikoData.sol
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ library TaikoData {
/// @dev Struct representing input data for block metadata.
struct BlockMetadataInput {
bytes32 txListHash;
address beneficiary;
address proposer;
uint24 txListByteStart; // byte-wise start index (inclusive)
uint24 txListByteEnd; // byte-wise end index (exclusive)
bool cacheTxListInfo;
Expand All @@ -112,7 +112,7 @@ library TaikoData {
uint24 txListByteStart;
uint24 txListByteEnd;
uint32 gasLimit;
address beneficiary;
address proposer;
TaikoData.EthDeposit[] depositsProcessed;
}

Expand Down Expand Up @@ -147,7 +147,6 @@ library TaikoData {
uint64 proposedAt;
uint32 nextTransitionId;
uint32 verifiedTransitionId;
uint16 proofWindow;
}

/// @dev Struct representing information about a transaction list.
Expand Down Expand Up @@ -185,15 +184,22 @@ library TaikoData {
/// @dev Struct holding the state variables for the {TaikoL1} contract.
struct State {
// Ring buffer for proposed blocks and a some recent verified blocks.
mapping(uint64 blockId_mode_blockRingBufferSize => Block) blocks;
mapping(uint64 blockId_mod_blockRingBufferSize => Block) blocks;
// Indexing to transition ids (ring buffer not possible)
mapping(
uint64 blockId => mapping(bytes32 parentHash => uint32 transitionId)
) transitionIds;
mapping(uint64 blockId => mapping(uint32 transitionId => Transition))
transitions;
// Ring buffer for transitions
mapping(
uint64 blockId_mod_blockRingBufferSize
=> mapping(uint32 transitionId => Transition)
) transitions;
// txList cached info
mapping(bytes32 txListHash => TxListInfo) txListInfo;
mapping(uint256 depositId_mode_ethDepositRingBufferSize => uint256)
// Ring buffer for Ether deposits
mapping(uint256 depositId_mod_ethDepositRingBufferSize => uint256)
ethDeposits;
// In-protocol Taiko token balances
mapping(address account => uint256 balance) taikoTokenBalances;
SlotA slotA; // slot 7
SlotB slotB; // slot 8
Expand Down
21 changes: 4 additions & 17 deletions packages/protocol/contracts/L1/TaikoL1Base.sol
Original file line number Diff line number Diff line change
Expand Up @@ -223,14 +223,8 @@ abstract contract TaikoL1Base is
override
returns (bytes32)
{
(bool found, TaikoData.Block storage blk) = LibUtils.getL2ChainData({
state: state,
config: getConfig(),
blockId: blockId
});
return found
? state.transitions[blk.blockId][blk.verifiedTransitionId].blockHash
: bytes32(0);
return LibUtils.getVerifyingTransition(state, getConfig(), blockId)
.blockHash;
}

/// @inheritdoc ICrossChainSync
Expand All @@ -240,15 +234,8 @@ abstract contract TaikoL1Base is
override
returns (bytes32)
{
(bool found, TaikoData.Block storage blk) = LibUtils.getL2ChainData({
state: state,
config: getConfig(),
blockId: blockId
});

return found
? state.transitions[blockId][blk.verifiedTransitionId].signalRoot
: bytes32(0);
return LibUtils.getVerifyingTransition(state, getConfig(), blockId)
.signalRoot;
}

/// @notice Gets the state variables of the TaikoL1 contract.
Expand Down
86 changes: 41 additions & 45 deletions packages/protocol/contracts/L1/libs/LibProposing.sol
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import { IProver } from "../IProver.sol";
import { LibAddress } from "../../libs/LibAddress.sol";
import { LibDepositing } from "./LibDepositing.sol";
import { LibMath } from "../../libs/LibMath.sol";
import { LibTaikoToken } from "./LibTaikoToken.sol";
import { LibUtils } from "./LibUtils.sol";
import { TaikoData } from "../TaikoData.sol";
import { TaikoToken } from "../TaikoToken.sol";
Expand Down Expand Up @@ -81,15 +82,12 @@ library LibProposing {
revert L1_TOO_MANY_BLOCKS();
}

TaikoToken tt = TaikoToken(resolver.resolve("taiko_token", false));
if (state.taikoTokenBalances[assignment.prover] >= config.proofBond) {
// Safe, see the above constraint
unchecked {
state.taikoTokenBalances[assignment.prover] -= config.proofBond;
}
} else {
tt.transferFrom(assignment.prover, address(this), config.proofBond);
}
TaikoToken tt = LibTaikoToken.receiveTaikoToken({
state: state,
resolver: resolver,
from: assignment.prover,
amount: config.proofBond
});

// Pay prover after verifying assignment
if (config.skipProverAssignmentVerificaiton) {
Expand Down Expand Up @@ -144,7 +142,7 @@ library LibProposing {
);

// Reward must be minted
tt.mint(input.beneficiary, reward);
tt.mint(input.proposer, reward);
}
}
}
Expand Down Expand Up @@ -178,9 +176,9 @@ library LibProposing {
meta.txListByteStart = input.txListByteStart;
meta.txListByteEnd = input.txListByteEnd;
meta.gasLimit = config.blockMaxGasLimit;
meta.beneficiary = input.beneficiary;
meta.proposer = input.proposer;
meta.depositsProcessed =
LibDepositing.processDeposits(state, config, input.beneficiary);
LibDepositing.processDeposits(state, config, input.proposer);

// Init the block
TaikoData.Block storage blk =
Expand All @@ -193,7 +191,6 @@ library LibProposing {
blk.proposedAt = meta.timestamp;
blk.nextTransitionId = 1;
blk.verifiedTransitionId = 0;
blk.proofWindow = config.proofWindow;

emit BlockProposed({
blockId: state.slotB.numBlocks++,
Expand Down Expand Up @@ -229,48 +226,47 @@ library LibProposing {
view
returns (bool cacheTxListInfo)
{
if (input.beneficiary == address(0)) revert L1_INVALID_METADATA();
if (input.proposer == address(0)) revert L1_INVALID_METADATA();

uint64 timeNow = uint64(block.timestamp);
// handling txList
{
uint24 size = uint24(txList.length);
if (size > config.blockMaxTxListBytes) revert L1_TX_LIST();

if (input.txListByteStart > input.txListByteEnd) {
uint24 size = uint24(txList.length);
if (size > config.blockMaxTxListBytes) revert L1_TX_LIST();

if (input.txListByteStart > input.txListByteEnd) {
revert L1_TX_LIST_RANGE();
}

if (config.blockTxListExpiry == 0) {
// caching is disabled
if (input.txListByteStart != 0 || input.txListByteEnd != size) {
revert L1_TX_LIST_RANGE();
}
} else {
// caching is enabled
if (size == 0) {
// This blob shall have been submitted earlier
TaikoData.TxListInfo memory info =
state.txListInfo[input.txListHash];

if (config.blockTxListExpiry == 0) {
// caching is disabled
if (input.txListByteStart != 0 || input.txListByteEnd != size) {
if (input.txListByteEnd > info.size) {
revert L1_TX_LIST_RANGE();
}
} else {
// caching is enabled
if (size == 0) {
// This blob shall have been submitted earlier
TaikoData.TxListInfo memory info =
state.txListInfo[input.txListHash];

if (input.txListByteEnd > info.size) {
revert L1_TX_LIST_RANGE();
}

if (
info.size == 0
|| info.validSince + config.blockTxListExpiry < timeNow
) {
revert L1_TX_LIST_NOT_EXIST();
}
} else {
if (input.txListByteEnd > size) revert L1_TX_LIST_RANGE();
if (input.txListHash != keccak256(txList)) {
revert L1_TX_LIST_HASH();
}

cacheTxListInfo = input.cacheTxListInfo;
if (
info.size == 0
|| info.validSince + config.blockTxListExpiry
< block.timestamp
) {
revert L1_TX_LIST_NOT_EXIST();
}
} else {
if (input.txListByteEnd > size) revert L1_TX_LIST_RANGE();
if (input.txListHash != keccak256(txList)) {
revert L1_TX_LIST_HASH();
}

cacheTxListInfo = input.cacheTxListInfo;
}
}
}
Expand Down
42 changes: 21 additions & 21 deletions packages/protocol/contracts/L1/libs/LibProving.sol
Original file line number Diff line number Diff line change
Expand Up @@ -45,18 +45,17 @@ library LibProving {
{
if (
evidence.prover == address(0) || evidence.parentHash == 0
|| evidence.blockHash == 0
|| evidence.blockHash == evidence.parentHash
|| evidence.signalRoot == 0
|| evidence.blockHash == 0 || evidence.signalRoot == 0
) revert L1_INVALID_EVIDENCE();

TaikoData.SlotB memory b = state.slotB;
if (blockId <= b.lastVerifiedBlockId || blockId >= b.numBlocks) {
revert L1_INVALID_BLOCK_ID();
}

TaikoData.Block storage blk =
state.blocks[blockId % config.blockRingBufferSize];
uint64 slot = blockId % config.blockRingBufferSize;
TaikoData.Block storage blk = state.blocks[slot];

if (blk.blockId != blockId) revert L1_BLOCK_ID_MISMATCH();

// Check the metadata hash matches the proposed block's. This is
Expand All @@ -83,9 +82,10 @@ library LibProving {
) revert L1_NOT_PROVEABLE();
}

TaikoData.Transition storage tz;
TaikoData.Transition storage tran;

uint32 tid =
LibUtils.getTransitionId(state, blk, blockId, evidence.parentHash);
LibUtils.getTransitionId(state, blk, slot, evidence.parentHash);

if (tid == 0) {
tid = blk.nextTransitionId;
Expand All @@ -97,31 +97,31 @@ library LibProving {
++blk.nextTransitionId;
}

tz = state.transitions[blk.blockId][tid];
tran = state.transitions[slot][tid];

if (tid == 1) {
// We only write the key when tid is 1.
tz.key = evidence.parentHash;
tran.key = evidence.parentHash;
} else {
state.transitionIds[blk.blockId][evidence.parentHash] = tid;
}
} else if (evidence.prover == LibUtils.ORACLE_PROVER) {
// This is the branch the oracle prover is trying to overwrite
// We need to check the previous proof is not the same as the
// new proof
tz = state.transitions[blk.blockId][tid];
tran = state.transitions[slot][tid];
if (
tz.blockHash == evidence.blockHash
&& tz.signalRoot == evidence.signalRoot
tran.blockHash == evidence.blockHash
&& tran.signalRoot == evidence.signalRoot
) revert L1_SAME_PROOF();
} else {
revert L1_ALREADY_PROVEN();
}

tz.blockHash = evidence.blockHash;
tz.signalRoot = evidence.signalRoot;
tz.prover = evidence.prover;
tz.provenAt = uint64(block.timestamp);
tran.blockHash = evidence.blockHash;
tran.signalRoot = evidence.signalRoot;
tran.prover = evidence.prover;
tran.provenAt = uint64(block.timestamp);

IProofVerifier(resolver.resolve("proof_verifier", false)).verifyProofs(
blockId, evidence.proofs, getInstance(evidence)
Expand All @@ -144,21 +144,21 @@ library LibProving {
)
internal
view
returns (TaikoData.Transition storage tz)
returns (TaikoData.Transition storage tran)
{
TaikoData.SlotB memory b = state.slotB;
if (blockId < b.lastVerifiedBlockId || blockId >= b.numBlocks) {
revert L1_INVALID_BLOCK_ID();
}

TaikoData.Block storage blk =
state.blocks[blockId % config.blockRingBufferSize];
uint64 slot = blockId % config.blockRingBufferSize;
TaikoData.Block storage blk = state.blocks[slot];
if (blk.blockId != blockId) revert L1_BLOCK_ID_MISMATCH();

uint32 tid = LibUtils.getTransitionId(state, blk, blockId, parentHash);
uint32 tid = LibUtils.getTransitionId(state, blk, slot, parentHash);
if (tid == 0) revert L1_TRANSITION_NOT_FOUND();

tz = state.transitions[blockId][tid];
tran = state.transitions[slot][tid];
}

function getInstance(TaikoData.BlockEvidence memory evidence)
Expand Down
20 changes: 20 additions & 0 deletions packages/protocol/contracts/L1/libs/LibTaikoToken.sol
Original file line number Diff line number Diff line change
Expand Up @@ -49,4 +49,24 @@ library LibTaikoToken {
state.taikoTokenBalances[msg.sender] += amount;
}
}

function receiveTaikoToken(
TaikoData.State storage state,
AddressResolver resolver,
address from,
uint256 amount
)
internal
returns (TaikoToken tt)
{
tt = TaikoToken(resolver.resolve("taiko_token", false));
if (state.taikoTokenBalances[from] >= amount) {
// Safe, see the above constraint
unchecked {
state.taikoTokenBalances[from] -= amount;
}
} else {
tt.transferFrom(from, address(this), amount);
}
}
}
Loading

0 comments on commit 878964e

Please sign in to comment.