Skip to content

Commit

Permalink
feat!: implements epoch change (#982)
Browse files Browse the repository at this point in the history
Description
---
Add epoch events
- `End` this is added at the last base layer block that is still in the
epoch. We have to bury the block to be locked, after that we officially
end the epoch
- `Start` this will be in the first block when the epoch switch, its
parent will have `End` or it will be genesis block.

Motivation and Context
---

How Has This Been Tested?
---
I've tested multiple committees and I switched epoch many times. I've
checked the blocks and the commands in them to confirm the epoch
end/start.
There is also a cucumber test. That checks if the epoch ended with a
EpochEnd command and new one started with EpochStart command.

What process can a PR reviewer use to test or verify this change?
---
Do as above, or go a step further and try to make a transaction during
epoch switching when you also add a VNs that changes space division.


Breaking Changes
---

- [ ] None
- [ ] Requires data directory to be deleted
- [x] Other - The 2nd layer should be restarted, otherwise the blocks
validation will fail.

---------

Co-authored-by: Stan Bondi <[email protected]>
  • Loading branch information
Cifko and sdbondi authored Apr 25, 2024
1 parent 3831c1a commit dee928c
Show file tree
Hide file tree
Showing 173 changed files with 1,512 additions and 636 deletions.
1 change: 1 addition & 0 deletions applications/tari_indexer/src/event_manager.rs
Original file line number Diff line number Diff line change
Expand Up @@ -379,6 +379,7 @@ impl EventManager {
let mut stream = client
.sync_blocks(SyncBlocksRequest {
start_block_id: start_block_id.as_bytes().to_vec(),
up_to_epoch: None,
})
.await?;
while let Some(resp) = stream.next().await {
Expand Down
11 changes: 10 additions & 1 deletion applications/tari_swarm_daemon/build.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// Copyright 2024 The Tari Project
// SPDX-License-Identifier: BSD-3-Clause

use std::{env, process::Command};
use std::{env, fs, process::Command};

fn exit_on_ci() {
if option_env!("CI").is_some() {
Expand All @@ -14,6 +14,10 @@ const BUILD: &[(&str, &str)] = &[("./webui", "build")];
fn main() -> Result<(), Box<dyn std::error::Error>> {
println!("cargo:rerun-if-changed=./webui/src");

// Ensure that dist path exists
fs::create_dir_all("./webui/dist")?;
fs::File::create("./webui/dist/.gitkeep")?;

if env::var("CARGO_FEATURE_TS").is_ok() {
println!("cargo:warning=The web ui is not being compiled when we are generating typescript types/interfaces.");
return Ok(());
Expand Down Expand Up @@ -43,5 +47,10 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
_ => {},
}
}

// Ensure that .gitkeep exists
// This is a hack because the build removes .gitkeep
fs::File::create("./webui/dist/.gitkeep")?;

Ok(())
}
10 changes: 9 additions & 1 deletion applications/tari_validator_node/src/p2p/rpc/service_impl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -273,7 +273,15 @@ impl ValidatorNodeRpcService for ValidatorNodeRpcServiceImpl {
)));
}

task::spawn(BlockSyncTask::new(self.shard_state_store.clone(), start_block, sender).run());
task::spawn(
BlockSyncTask::new(
self.shard_state_store.clone(),
start_block,
req.up_to_epoch.map(|epoch| epoch.into()),
sender,
)
.run(),
);

Ok(Streaming::new(receiver))
}
Expand Down
43 changes: 36 additions & 7 deletions applications/tari_validator_node/src/p2p/rpc/sync_task.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,10 @@
use std::collections::HashSet;

use log::*;
use tari_dan_common_types::Epoch;
use tari_dan_p2p::proto::rpc::{sync_blocks_response::SyncData, QuorumCertificates, SyncBlocksResponse, Transactions};
use tari_dan_storage::{
consensus_models::{Block, BlockId, LockedBlock, QuorumCertificate, SubstateUpdate, TransactionRecord},
consensus_models::{Block, BlockId, LeafBlock, LockedBlock, QuorumCertificate, SubstateUpdate, TransactionRecord},
StateStore,
StateStoreReadTransaction,
StorageError,
Expand All @@ -29,18 +30,21 @@ type BlockBuffer = Vec<BlockData>;
pub struct BlockSyncTask<TStateStore: StateStore> {
store: TStateStore,
start_block: Block,
up_to_epoch: Option<Epoch>,
sender: mpsc::Sender<Result<SyncBlocksResponse, RpcStatus>>,
}

impl<TStateStore: StateStore> BlockSyncTask<TStateStore> {
pub fn new(
store: TStateStore,
start_block: Block,
up_to_epoch: Option<Epoch>,
sender: mpsc::Sender<Result<SyncBlocksResponse, RpcStatus>>,
) -> Self {
Self {
store,
start_block,
up_to_epoch,
sender,
}
}
Expand Down Expand Up @@ -73,14 +77,14 @@ impl<TStateStore: StateStore> BlockSyncTask<TStateStore> {
self.send_block_data(data).await?;
}

// If we didnt fill up the buffer, send the final blocks
// If we didn't fill up the buffer, send the final blocks
if num_items < buffer.capacity() {
debug!( target: LOG_TARGET, "Sync to last commit complete. Streamed {} item(s)", counter);
break;
}
}

match self.fetch_last_blocks(&mut buffer, &current_block_id) {
match self.fetch_last_blocks(&mut buffer, &current_block_id).await {
Ok(_) => (),
Err(err) => {
self.send(Err(RpcStatus::log_internal_error(LOG_TARGET)(err))).await?;
Expand Down Expand Up @@ -136,11 +140,36 @@ impl<TStateStore: StateStore> BlockSyncTask<TStateStore> {
})
}

fn fetch_last_blocks(&self, buffer: &mut BlockBuffer, current_block_id: &BlockId) -> Result<(), StorageError> {
async fn fetch_last_blocks(
&self,
buffer: &mut BlockBuffer,
current_block_id: &BlockId,
) -> Result<(), StorageError> {
// if let Some(up_to_epoch) = self.up_to_epoch {
// // Wait for the end of epoch block if the requested epoch has not yet completed
// // TODO: We should consider streaming blocks as they come in from consensus
// loop {
// let block = self.store.with_read_tx(|tx| LockedBlock::get(tx)?.get_block(tx))?;
// if block.is_epoch_end() && block.epoch() + Epoch(1) >= up_to_epoch {
// // If found the epoch end block, break.
// break;
// }
// tokio::time::sleep(Duration::from_secs(10)).await;
// }
// }
self.store.with_read_tx(|tx| {
// TODO: if there are any transactions this will break the syncing node.
let locked_block = LockedBlock::get(tx)?;
let blocks = Block::get_all_blocks_between(tx, current_block_id, locked_block.block_id(), false)?;
// TODO: if there are any transactions in the block the syncing node will reject the block

// If syncing to epoch, sync to the leaf block
let up_to_block = if self.up_to_epoch.is_none() {
let locked_block = LockedBlock::get(tx)?;
*locked_block.block_id()
} else {
let leaf_block = LeafBlock::get(tx)?;
*leaf_block.block_id()
};

let blocks = Block::get_all_blocks_between(tx, current_block_id, &up_to_block, false)?;
for block in blocks {
debug!(
target: LOG_TARGET,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@

import { useState, useEffect } from "react";
import { useParams } from "react-router-dom";
// import { transactionsGet } from '../../utils/json_rpc';
import { Accordion, AccordionDetails, AccordionSummary } from "../../Components/Accordion";
import { Grid, Table, TableContainer, TableBody, TableRow, TableCell, Button, Fade, Alert } from "@mui/material";
import Typography from "@mui/material/Typography";
Expand All @@ -35,7 +34,7 @@ import Loading from "../../Components/Loading";
import { getBlock, getIdentity } from "../../utils/json_rpc";
import Transactions from "./Transactions";
import { primitiveDateTimeToDate, primitiveDateTimeToSecs } from "../../utils/helpers";
import type { Block, TransactionAtom } from "@tariproject/typescript-bindings";
import type { Block, EpochEvent, TransactionAtom } from "@tariproject/typescript-bindings";
import type { GetIdentityResponse } from "@tariproject/typescript-bindings/validator-node-client";

export default function BlockDetails() {
Expand All @@ -48,6 +47,7 @@ export default function BlockDetails() {
const [prepare, setPrepare] = useState<TransactionAtom[]>([]);
const [localPrepared, setLocalPrepared] = useState<TransactionAtom[]>([]);
const [accept, setAccept] = useState<TransactionAtom[]>([]);
const [epochEvents, setEpochEvents] = useState<EpochEvent[]>([]);
const [identity, setIdentity] = useState<GetIdentityResponse>();
const [blockTime, setBlockTime] = useState<number>(0);

Expand All @@ -70,6 +70,7 @@ export default function BlockDetails() {
setPrepare([]);
setLocalPrepared([]);
setAccept([]);
setEpochEvents([]);
for (let command of resp.block.commands) {
if ("LocalOnly" in command) {
let newLocalOnly = command.LocalOnly;
Expand All @@ -83,6 +84,9 @@ export default function BlockDetails() {
} else if ("Accept" in command) {
let newAccept = command.Accept;
setAccept((accept: TransactionAtom[]) => [...accept, newAccept]);
} else if ("EpochEvent" in command) {
const newEpochEvent = command.EpochEvent;
setEpochEvents((epochEvents: EpochEvent[]) => [...epochEvents, newEpochEvent]);
}
}
})
Expand Down Expand Up @@ -222,9 +226,19 @@ export default function BlockDetails() {
</div>
</>
)}
{localOnly.length > 0 && (
{epochEvents.length > 0 && (
<Accordion expanded={expandedPanels.includes("panel1")} onChange={handleChange("panel1")}>
<AccordionSummary aria-controls="panel1bh-content" id="panel1bh-header">
<Typography>EpochEvent</Typography>
</AccordionSummary>
<AccordionDetails>
<ul>{epochEvents.map((evt, i) => <li key={i}>{evt}</li>)}</ul>
</AccordionDetails>
</Accordion>
)}
{localOnly.length > 0 && (
<Accordion expanded={expandedPanels.includes("panel2")} onChange={handleChange("panel2")}>
<AccordionSummary aria-controls="panel2bh-content" id="panel2bh-header">
<Typography>LocalOnly</Typography>
</AccordionSummary>
<AccordionDetails>
Expand All @@ -233,8 +247,8 @@ export default function BlockDetails() {
</Accordion>
)}
{prepare.length > 0 && (
<Accordion expanded={expandedPanels.includes("panel1")} onChange={handleChange("panel1")}>
<AccordionSummary aria-controls="panel1bh-content" id="panel1bh-header">
<Accordion expanded={expandedPanels.includes("panel3")} onChange={handleChange("panel3")}>
<AccordionSummary aria-controls="panel3bh-content" id="panel3bh-header">
<Typography>Prepare</Typography>
</AccordionSummary>
<AccordionDetails>
Expand All @@ -243,8 +257,8 @@ export default function BlockDetails() {
</Accordion>
)}
{localPrepared.length > 0 && (
<Accordion expanded={expandedPanels.includes("panel2")} onChange={handleChange("panel2")}>
<AccordionSummary aria-controls="panel2bh-content" id="panel2bh-header">
<Accordion expanded={expandedPanels.includes("panel4")} onChange={handleChange("panel4")}>
<AccordionSummary aria-controls="panel4bh-content" id="panel4bh-header">
<Typography>Local prepared</Typography>
</AccordionSummary>
<AccordionDetails>
Expand All @@ -253,8 +267,8 @@ export default function BlockDetails() {
</Accordion>
)}
{accept.length > 0 && (
<Accordion expanded={expandedPanels.includes("panel3")} onChange={handleChange("panel3")}>
<AccordionSummary aria-controls="panel3bh-content" id="panel3bh-header">
<Accordion expanded={expandedPanels.includes("panel5")} onChange={handleChange("panel5")}>
<AccordionSummary aria-controls="panel5bh-content" id="panel5bh-header">
<Typography>Accept</Typography>
</AccordionSummary>
<AccordionDetails>
Expand Down
35 changes: 18 additions & 17 deletions bindings/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,66 +4,67 @@
export * from "./src/types/AccessRule";
export * from "./src/types/Account";
export * from "./src/types/Amount";
export * from "./src/types/Arg";
export * from "./src/types/ArgDef";
export * from "./src/types/Arg";
export * from "./src/types/AuthHook";
export * from "./src/types/Block";
export * from "./src/types/BucketId";
export * from "./src/types/Claims";
export * from "./src/types/Command";
export * from "./src/types/Committee";
export * from "./src/types/CommitteeShard";
export * from "./src/types/CommitteeShardInfo";
export * from "./src/types/CommitteeShard";
export * from "./src/types/Committee";
export * from "./src/types/ComponentAccessRules";
export * from "./src/types/ComponentAddress";
export * from "./src/types/ComponentBody";
export * from "./src/types/ComponentHeader";
export * from "./src/types/ComponentKey";
export * from "./src/types/ConfidentialClaim";
export * from "./src/types/ConfidentialOutput";
export * from "./src/types/ConfidentialOutputStatement";
export * from "./src/types/ConfidentialOutput";
export * from "./src/types/ConfidentialStatement";
export * from "./src/types/ConfidentialTransferInputSelection";
export * from "./src/types/ConfidentialWithdrawProof";
export * from "./src/types/Decision";
export * from "./src/types/ElgamalVerifiableBalance";
export * from "./src/types/EntityId";
export * from "./src/types/EpochEvent";
export * from "./src/types/Epoch";
export * from "./src/types/Event";
export * from "./src/types/Evidence";
export * from "./src/types/ExecutedTransaction";
export * from "./src/types/ExecuteResult";
export * from "./src/types/FeeBreakdown";
export * from "./src/types/FeeClaim";
export * from "./src/types/FeeClaimAddress";
export * from "./src/types/FeeClaim";
export * from "./src/types/FeeCostBreakdown";
export * from "./src/types/FeeReceipt";
export * from "./src/types/FeeSource";
export * from "./src/types/FinalizeResult";
export * from "./src/types/ForeignProposal";
export * from "./src/types/ForeignProposalState";
export * from "./src/types/ForeignProposal";
export * from "./src/types/FunctionDef";
export * from "./src/types/IndexedValue";
export * from "./src/types/IndexedWellKnownTypes";
export * from "./src/types/Instruction";
export * from "./src/types/InstructionResult";
export * from "./src/types/JrpcPermission";
export * from "./src/types/Instruction";
export * from "./src/types/JrpcPermissions";
export * from "./src/types/JrpcPermission";
export * from "./src/types/LeaderFee";
export * from "./src/types/LockFlag";
export * from "./src/types/LogEntry";
export * from "./src/types/LogLevel";
export * from "./src/types/Metadata";
export * from "./src/types/NetworkCommitteeInfo";
export * from "./src/types/NodeHeight";
export * from "./src/types/NonFungible";
export * from "./src/types/NonFungibleAddress";
export * from "./src/types/NonFungibleAddressContents";
export * from "./src/types/NonFungibleAddress";
export * from "./src/types/NonFungibleContainer";
export * from "./src/types/NonFungibleId";
export * from "./src/types/NonFungibleIndex";
export * from "./src/types/NonFungibleIndexAddress";
export * from "./src/types/NonFungibleIndex";
export * from "./src/types/NonFungibleToken";
export * from "./src/types/NonFungible";
export * from "./src/types/Ordering";
export * from "./src/types/OwnerRule";
export * from "./src/types/PeerAddress";
Expand All @@ -72,41 +73,41 @@ export * from "./src/types/QuorumCertificate";
export * from "./src/types/QuorumDecision";
export * from "./src/types/RejectReason";
export * from "./src/types/RequireRule";
export * from "./src/types/Resource";
export * from "./src/types/ResourceAccessRules";
export * from "./src/types/ResourceAddress";
export * from "./src/types/ResourceContainer";
export * from "./src/types/Resource";
export * from "./src/types/ResourceType";
export * from "./src/types/RestrictedAccessRule";
export * from "./src/types/RuleRequirement";
export * from "./src/types/Shard";
export * from "./src/types/ShardEvidence";
export * from "./src/types/Substate";
export * from "./src/types/Shard";
export * from "./src/types/SubstateAddress";
export * from "./src/types/SubstateDestroyed";
export * from "./src/types/SubstateDiff";
export * from "./src/types/SubstateId";
export * from "./src/types/SubstateRecord";
export * from "./src/types/SubstateRequirement";
export * from "./src/types/Substate";
export * from "./src/types/SubstateType";
export * from "./src/types/SubstateValue";
export * from "./src/types/TemplateDef";
export * from "./src/types/TemplateDefV1";
export * from "./src/types/Transaction";
export * from "./src/types/TransactionAtom";
export * from "./src/types/TransactionPoolRecord";
export * from "./src/types/TransactionPoolStage";
export * from "./src/types/TransactionReceipt";
export * from "./src/types/TransactionReceiptAddress";
export * from "./src/types/TransactionReceipt";
export * from "./src/types/TransactionResult";
export * from "./src/types/TransactionSignature";
export * from "./src/types/TransactionStatus";
export * from "./src/types/Transaction";
export * from "./src/types/Type";
export * from "./src/types/UnclaimedConfidentialOutput";
export * from "./src/types/UnsignedTransaction";
export * from "./src/types/ValidatorSignature";
export * from "./src/types/Vault";
export * from "./src/types/VaultId";
export * from "./src/types/Vault";
export * from "./src/types/VersionedSubstateId";
export * from "./src/types/ViewableBalanceProof";
export * from "./src/helpers/helpers";
4 changes: 3 additions & 1 deletion bindings/src/types/Command.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
import type { EpochEvent } from "./EpochEvent";
import type { ForeignProposal } from "./ForeignProposal";
import type { TransactionAtom } from "./TransactionAtom";

Expand All @@ -7,4 +8,5 @@ export type Command =
| { LocalPrepared: TransactionAtom }
| { Accept: TransactionAtom }
| { ForeignProposal: ForeignProposal }
| { LocalOnly: TransactionAtom };
| { LocalOnly: TransactionAtom }
| { EpochEvent: EpochEvent };
3 changes: 3 additions & 0 deletions bindings/src/types/EpochEvent.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.

export type EpochEvent = "Start" | "End";
2 changes: 1 addition & 1 deletion bindings/src/types/ForeignProposal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,5 @@ export interface ForeignProposal {
state: ForeignProposalState;
proposed_height: NodeHeight | null;
transactions: Array<string>;
base_layer_block_height: bigint;
base_layer_block_height: number;
}
Loading

0 comments on commit dee928c

Please sign in to comment.